diff options
author | Clément Foucault <foucault.clem@gmail.com> | 2019-12-02 03:40:58 +0300 |
---|---|---|
committer | Clément Foucault <foucault.clem@gmail.com> | 2019-12-02 15:15:52 +0300 |
commit | 9516921c05bd9fee5c94942eb8e38f47ba7e4351 (patch) | |
tree | da007fc17bc6a02f849dae2e8f76f5ab304fe4dc /source/blender/draw/engines | |
parent | 1f6c3699a836d485ed37f443cd0fcd19e978dbb6 (diff) |
Overlay Engine: Refactor & Cleanup
This is the unification of all overlays into one overlay engine as described in T65347.
I went over all the code making it more future proof with less hacks and removing old / not relevent parts.
Goals / Acheivements:
- Remove internal shader usage (only drw shaders)
- Remove viewportSize and viewportSizeInv and put them in gloabl ubo
- Fixed some drawing issues: Missing probe option and Missing Alt+B clipping of some shader
- Remove old (legacy) shaders dependancy (not using view UBO).
- Less shader variation (less compilation time at first load and less patching needed for vulkan)
- removed some geom shaders when I could
- Remove static e_data (except shaders storage where it is OK)
- Clear the way to fix some anoying limitations (dithered transparency, background image compositing etc...)
- Wireframe drawing now uses the same batching capabilities as workbench & eevee (indirect drawing).
- Reduced complexity, removed ~3000 Lines of code in draw (also removed a lot of unused shader in GPU).
- Post AA to avoid complexity and cost of MSAA.
Remaining issues:
- ~~Armature edits, overlay toggles, (... others?) are not refreshing viewport after AA is complete~~
- FXAA is not the best for wires, maybe investigate SMAA
- Maybe do something more temporally stable for AA.
- ~~Paint overlays are not working with AA.~~
- ~~infront objects are difficult to select.~~
- ~~the infront wires sometimes goes through they solid counterpart (missing clear maybe?) (toggle overlays on-off when using infront+wireframe overlay in solid shading)~~
Note: I made some decision to change slightly the appearance of some objects to simplify their drawing. Namely the empty arrows end (which is now hollow/wire) and distance points of the cameras/spots being done by lines.
Reviewed By: jbakker
Differential Revision: https://developer.blender.org/D6296
Diffstat (limited to 'source/blender/draw/engines')
103 files changed, 13504 insertions, 57 deletions
diff --git a/source/blender/draw/engines/basic/basic_engine.c b/source/blender/draw/engines/basic/basic_engine.c index 0dd1a4fd686..fcbe227ca1b 100644 --- a/source/blender/draw/engines/basic/basic_engine.c +++ b/source/blender/draw/engines/basic/basic_engine.c @@ -46,8 +46,8 @@ typedef struct BASIC_StorageList { } BASIC_StorageList; typedef struct BASIC_PassList { - struct DRWPass *depth_pass; - struct DRWPass *depth_pass_cull; + struct DRWPass *depth_pass[2]; + struct DRWPass *depth_pass_cull[2]; } BASIC_PassList; typedef struct BASIC_Data { @@ -70,8 +70,8 @@ static struct { } e_data = {{{NULL}}}; /* Engine data */ typedef struct BASIC_PrivateData { - DRWShadingGroup *depth_shgrp; - DRWShadingGroup *depth_shgrp_cull; + DRWShadingGroup *depth_shgrp[2]; + DRWShadingGroup *depth_shgrp_cull[2]; } BASIC_PrivateData; /* Transient data */ /* Functions */ @@ -97,24 +97,21 @@ static void basic_cache_init(void *vedata) if (!stl->g_data) { /* Alloc transient pointers */ - stl->g_data = MEM_mallocN(sizeof(*stl->g_data), __func__); + stl->g_data = MEM_callocN(sizeof(*stl->g_data), __func__); } - { - psl->depth_pass = DRW_pass_create("Depth Pass", - DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL); - stl->g_data->depth_shgrp = DRW_shgroup_create(sh_data->depth, psl->depth_pass); - if (draw_ctx->sh_cfg == GPU_SHADER_CFG_CLIPPED) { - DRW_shgroup_state_enable(stl->g_data->depth_shgrp, DRW_STATE_CLIP_PLANES); - } + /* Twice for normal and infront objects. */ + for (int i = 0; i < 2; i++) { + DRWState clip_state = (draw_ctx->sh_cfg == GPU_SHADER_CFG_CLIPPED) ? DRW_STATE_CLIP_PLANES : 0; + DRWState infront_state = (DRW_state_is_select() && (i == 1)) ? DRW_STATE_IN_FRONT_SELECT : 0; + DRWState state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL; - psl->depth_pass_cull = DRW_pass_create("Depth Pass Cull", - DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | - DRW_STATE_CULL_BACK); - stl->g_data->depth_shgrp_cull = DRW_shgroup_create(sh_data->depth, psl->depth_pass_cull); - if (draw_ctx->sh_cfg == GPU_SHADER_CFG_CLIPPED) { - DRW_shgroup_state_enable(stl->g_data->depth_shgrp_cull, DRW_STATE_CLIP_PLANES); - } + DRW_PASS_CREATE(psl->depth_pass[i], state | clip_state | infront_state); + stl->g_data->depth_shgrp[i] = DRW_shgroup_create(sh_data->depth, psl->depth_pass[i]); + + state |= DRW_STATE_CULL_BACK; + DRW_PASS_CREATE(psl->depth_pass_cull[i], state | clip_state | infront_state); + stl->g_data->depth_shgrp_cull[i] = DRW_shgroup_create(sh_data->depth, psl->depth_pass_cull[i]); } } @@ -128,6 +125,8 @@ static void basic_cache_populate(void *vedata, Object *ob) return; } + bool do_in_front = (ob->dtx & OB_DRAWXRAY) != 0; + const DRWContextState *draw_ctx = DRW_context_state_get(); if (ob != draw_ctx->object_edit) { for (ParticleSystem *psys = ob->particlesystem.first; psys != NULL; psys = psys->next) { @@ -138,7 +137,7 @@ static void basic_cache_populate(void *vedata, Object *ob) const int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as; if (draw_as == PART_DRAW_PATH) { struct GPUBatch *hairs = DRW_cache_particles_get_hair(ob, psys, NULL); - DRW_shgroup_call(stl->g_data->depth_shgrp, hairs, NULL); + DRW_shgroup_call(stl->g_data->depth_shgrp[do_in_front], hairs, NULL); } } } @@ -155,7 +154,7 @@ static void basic_cache_populate(void *vedata, Object *ob) /* Avoid losing flat objects when in ortho views (see T56549) */ struct GPUBatch *geom = DRW_cache_object_all_edges_get(ob); if (geom) { - DRW_shgroup_call(stl->g_data->depth_shgrp, geom, ob); + DRW_shgroup_call(stl->g_data->depth_shgrp[do_in_front], geom, ob); } return; } @@ -165,7 +164,8 @@ static void basic_cache_populate(void *vedata, Object *ob) !DRW_state_is_image_render(); const bool do_cull = (draw_ctx->v3d && (draw_ctx->v3d->shading.flag & V3D_SHADING_BACKFACE_CULLING)); - DRWShadingGroup *shgrp = (do_cull) ? stl->g_data->depth_shgrp_cull : stl->g_data->depth_shgrp; + DRWShadingGroup *shgrp = (do_cull) ? stl->g_data->depth_shgrp_cull[do_in_front] : + stl->g_data->depth_shgrp[do_in_front]; if (use_sculpt_pbvh) { DRW_shgroup_call_sculpt(shgrp, ob, false, false, false); @@ -189,8 +189,10 @@ static void basic_draw_scene(void *vedata) { BASIC_PassList *psl = ((BASIC_Data *)vedata)->psl; - DRW_draw_pass(psl->depth_pass); - DRW_draw_pass(psl->depth_pass_cull); + DRW_draw_pass(psl->depth_pass[0]); + DRW_draw_pass(psl->depth_pass_cull[0]); + DRW_draw_pass(psl->depth_pass[1]); + DRW_draw_pass(psl->depth_pass_cull[1]); } static void basic_engine_free(void) diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.c b/source/blender/draw/engines/gpencil/gpencil_engine.c index 9554e9c0275..a1a1f7cc389 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.c +++ b/source/blender/draw/engines/gpencil/gpencil_engine.c @@ -33,8 +33,6 @@ #include "DNA_screen_types.h" #include "DNA_view3d_types.h" -#include "draw_mode_engines.h" - #include "GPU_texture.h" #include "gpencil_engine.h" diff --git a/source/blender/draw/engines/gpencil/gpencil_engine.h b/source/blender/draw/engines/gpencil/gpencil_engine.h index 36bc205f41a..04ed19830ed 100644 --- a/source/blender/draw/engines/gpencil/gpencil_engine.h +++ b/source/blender/draw/engines/gpencil/gpencil_engine.h @@ -25,6 +25,8 @@ #include "GPU_batch.h" +extern DrawEngineType draw_engine_gpencil_type; + struct GPENCIL_Data; struct GPENCIL_StorageList; struct MaterialGPencilStyle; diff --git a/source/blender/draw/engines/gpencil/gpencil_render.c b/source/blender/draw/engines/gpencil/gpencil_render.c index 81e48eb05f2..8c126310ea2 100644 --- a/source/blender/draw/engines/gpencil/gpencil_render.c +++ b/source/blender/draw/engines/gpencil/gpencil_render.c @@ -29,8 +29,6 @@ #include "DEG_depsgraph_query.h" -#include "draw_mode_engines.h" - #include "RE_pipeline.h" #include "gpencil_engine.h" diff --git a/source/blender/draw/engines/overlay/overlay_antialiasing.c b/source/blender/draw/engines/overlay/overlay_antialiasing.c new file mode 100644 index 00000000000..54a598633fb --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_antialiasing.c @@ -0,0 +1,184 @@ +/* + * 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 + * + * Overlay antialiasing: + * + * Most of the overlays are wires which causes a lot of flickering in motions + * due to aliasing problems. + * + * Our goal is to have a technique that works with single sample per pixel + * to avoid extra cost of managing MSAA or additional texture buffers and jitters. + * + * To solve this we use a simple and effective post-process AA. The technique + * goes like this: + * + * - During wireframe rendering, we output the line color, the line direction + * and the distance from the line for the pixel center. + * + * - Then, in a post process pass, for each pixels we gather all lines in a search area + * that could cover (even partially) the center pixel. + * We compute the coverage of each line and do a sorted alpha compositing of them. + * + * This technique has one major shortcoming compared to MSAA: + * - It handles (initial) partial visibility poorly (because of single sample). This makes + * overlaping / crossing wires a bit too thin at their intersection. + * Wireframe meshes overlaid over solid meshes can have half of the edge missing due to + * z-fighting (this has workaround). + * Another manifestation of this, is fickering of really dense wireframe if using small + * line thickness (also has workaround). + * + * The pros of this approach are many: + * - Works without geometry shader. + * - Can inflate line thickness. + * - Coverage is very close to perfect and can even be filtered (Blackman-Harris, gaussian). + * - Wires can "bleed" / overlap non-line objects since the filter is in screenspace. + * - Only uses one additional lightweight fullscreen buffer (compared to MSAA/SMAA). + * - No convergence time (compared to TAA). + */ + +#include "DRW_render.h" + +#include "ED_screen.h" + +#include "overlay_private.h" + +void OVERLAY_antialiasing_reset(OVERLAY_Data *vedata) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + pd->antialiasing.sample = 0; +} + +void OVERLAY_antialiasing_init(OVERLAY_Data *vedata) +{ + OVERLAY_FramebufferList *fbl = vedata->fbl; + OVERLAY_TextureList *txl = vedata->txl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + + if (!DRW_state_is_fbo()) { + /* Use default view */ + pd->view_default = (DRWView *)DRW_view_default_get(); + pd->antialiasing.enabled = false; + return; + } + + /* TODO Get real userpref option and remove MSAA buffer. */ + pd->antialiasing.enabled = dtxl->multisample_color != NULL; + + /* Use default view */ + pd->view_default = (DRWView *)DRW_view_default_get(); + + if (pd->antialiasing.enabled) { + DRW_texture_ensure_fullscreen_2d(&txl->overlay_color_tx, GPU_RGBA8, DRW_TEX_FILTER); + DRW_texture_ensure_fullscreen_2d(&txl->overlay_line_tx, GPU_RGBA8, 0); + + GPU_framebuffer_ensure_config( + &fbl->overlay_color_only_fb, + {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->overlay_color_tx)}); + GPU_framebuffer_ensure_config( + &fbl->overlay_default_fb, + {GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(txl->overlay_color_tx)}); + GPU_framebuffer_ensure_config(&fbl->overlay_line_fb, + {GPU_ATTACHMENT_TEXTURE(dtxl->depth), + GPU_ATTACHMENT_TEXTURE(txl->overlay_color_tx), + GPU_ATTACHMENT_TEXTURE(txl->overlay_line_tx)}); + } + else { + /* Just a copy of the defaults framebuffers. */ + GPU_framebuffer_ensure_config(&fbl->overlay_color_only_fb, + {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(dtxl->color)}); + GPU_framebuffer_ensure_config( + &fbl->overlay_default_fb, + {GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(dtxl->color)}); + GPU_framebuffer_ensure_config( + &fbl->overlay_line_fb, + {GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(dtxl->color)}); + } +} + +void OVERLAY_antialiasing_cache_init(OVERLAY_Data *vedata) +{ + OVERLAY_TextureList *txl = vedata->txl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + OVERLAY_PassList *psl = vedata->psl; + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + struct GPUShader *sh; + DRWShadingGroup *grp; + + if (pd->antialiasing.enabled) { + DRW_PASS_CREATE(psl->antialiasing_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA_PREMUL); + + sh = OVERLAY_shader_antialiasing(); + grp = DRW_shgroup_create(sh, psl->antialiasing_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_texture_ref(grp, "depthTex", &dtxl->depth); + DRW_shgroup_uniform_texture_ref(grp, "colorTex", &txl->overlay_color_tx); + DRW_shgroup_uniform_texture_ref(grp, "lineTex", &txl->overlay_line_tx); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } +} + +void OVERLAY_antialiasing_cache_finish(OVERLAY_Data *vedata) +{ + OVERLAY_FramebufferList *fbl = vedata->fbl; + OVERLAY_TextureList *txl = vedata->txl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + + if (pd->antialiasing.enabled) { + GPU_framebuffer_ensure_config(&fbl->overlay_in_front_fb, + {GPU_ATTACHMENT_TEXTURE(dtxl->depth_in_front), + GPU_ATTACHMENT_TEXTURE(txl->overlay_color_tx)}); + } + else { + GPU_framebuffer_ensure_config( + &fbl->overlay_in_front_fb, + {GPU_ATTACHMENT_TEXTURE(dtxl->depth_in_front), GPU_ATTACHMENT_TEXTURE(dtxl->color)}); + } +} + +void OVERLAY_antialiasing_start(OVERLAY_Data *vedata) +{ + OVERLAY_FramebufferList *fbl = vedata->fbl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + + if (pd->antialiasing.enabled) { + float clear_col[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + GPU_framebuffer_bind(fbl->overlay_line_fb); + GPU_framebuffer_clear_color(fbl->overlay_line_fb, clear_col); + + GPU_framebuffer_bind(fbl->overlay_default_fb); + } +} + +void OVERLAY_antialiasing_end(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + + if (pd->antialiasing.enabled) { + GPU_framebuffer_bind(dfbl->color_only_fb); + DRW_draw_pass(psl->antialiasing_ps); + + GPU_framebuffer_bind(dfbl->default_fb); + } +} diff --git a/source/blender/draw/engines/overlay/overlay_armature.c b/source/blender/draw/engines/overlay/overlay_armature.c new file mode 100644 index 00000000000..c749d902f86 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_armature.c @@ -0,0 +1,2338 @@ +/* + * 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 <stdlib.h> +#include <string.h> +#include <math.h> + +#include "DNA_armature_types.h" +#include "DNA_constraint_types.h" +#include "DNA_scene_types.h" +#include "DNA_view3d_types.h" +#include "DNA_object_types.h" + +#include "DRW_render.h" + +#include "BLI_math.h" +#include "BLI_utildefines.h" + +#include "BKE_armature.h" +#include "BKE_modifier.h" + +#include "DEG_depsgraph_query.h" + +#include "ED_armature.h" +#include "ED_view3d.h" + +#include "UI_resources.h" + +#include "draw_common.h" +#include "draw_manager_text.h" + +#include "overlay_private.h" + +#define BONE_VAR(eBone, pchan, var) ((eBone) ? (eBone->var) : (pchan->var)) +#define BONE_FLAG(eBone, pchan) ((eBone) ? (eBone->flag) : (pchan->bone->flag)) + +#define PT_DEFAULT_RAD 0.05f /* radius of the point batch. */ + +typedef struct ArmatureDrawContext { + /* Current armature object */ + Object *ob; + /* bArmature *arm; */ /* TODO */ + + union { + struct { + DRWCallBuffer *outline; + DRWCallBuffer *solid; + DRWCallBuffer *wire; + }; + struct { + DRWCallBuffer *envelope_outline; + DRWCallBuffer *envelope_solid; + DRWCallBuffer *envelope_distance; + }; + struct { + DRWCallBuffer *stick; + }; + }; + + DRWCallBuffer *dof_lines; + DRWCallBuffer *dof_sphere; + DRWCallBuffer *point_solid; + DRWCallBuffer *point_outline; + DRWShadingGroup *custom_solid; + DRWShadingGroup *custom_outline; + DRWShadingGroup *custom_wire; + GHash *custom_shapes_ghash; + + OVERLAY_ExtraCallBuffers *extras; + + /** + * Follow `TH_*` naming except for mixed colors. + */ + struct { + float select[4]; + float edge_select[4]; + float bone_select[4]; /* tint */ + float wire[4]; + float wire_edit[4]; + float bone_solid[4]; + float bone_active_unselect[4]; /* mix */ + float bone_pose[4]; + float bone_pose_active[4]; + float bone_pose_active_unselect[4]; /* mix */ + float text_hi[4]; + float text[4]; + float vertex_select[4]; + float vertex[4]; + } color; + + /* not a theme, this is an override */ + const float *const_color; + float const_wire; + + bool do_relations; + bool transparent; + bool show_relations; + + const ThemeWireColor *bcolor; /* pchan color */ +} ArmatureDrawContext; + +/** + * Return true if armature should be handled by the pose mode engine. + */ +bool OVERLAY_armature_is_pose_mode(Object *ob, const DRWContextState *draw_ctx) +{ + Object *active_ob = draw_ctx->obact; + + /* Pose armature is handled by pose mode engine. */ + if (((ob == active_ob) || (ob->mode & OB_MODE_POSE)) && + ((draw_ctx->object_mode & OB_MODE_POSE) != 0)) { + return true; + } + + /* Armature parent is also handled by pose mode engine. */ + if ((active_ob != NULL) && ((draw_ctx->object_mode & OB_MODE_WEIGHT_PAINT) != 0)) { + if (ob == draw_ctx->object_pose) { + return true; + } + } + + return false; +} + +void OVERLAY_armature_cache_init(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + + const DRWContextState *draw_ctx = DRW_context_state_get(); + pd->armature.transparent = (draw_ctx->v3d->shading.type == OB_WIRE) || + XRAY_FLAG_ENABLED(draw_ctx->v3d); + pd->armature.show_relations = ((draw_ctx->v3d->flag & V3D_HIDE_HELPLINES) == 0); + pd->armature.do_pose_fade_geom = (pd->overlay.flag & V3D_OVERLAY_BONE_SELECT) && + ((draw_ctx->object_mode & OB_MODE_WEIGHT_PAINT) == 0) && + draw_ctx->object_pose != NULL; + + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_BLEND_ADD; + DRW_PASS_CREATE(psl->armature_transp_ps, state | pd->clipping_state); + + if (pd->armature.do_pose_fade_geom) { + state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_BLEND_ALPHA; + DRW_PASS_CREATE(psl->armature_bone_select_ps, state | pd->clipping_state); + + float alpha = pd->overlay.xray_alpha_bone; + struct GPUShader *sh = OVERLAY_shader_uniform_color(); + DRWShadingGroup *grp; + + pd->armature_bone_select_act_grp = grp = DRW_shgroup_create(sh, psl->armature_bone_select_ps); + DRW_shgroup_uniform_vec4_copy(grp, "color", (float[4]){0.0f, 0.0f, 0.0f, alpha}); + + pd->armature_bone_select_grp = grp = DRW_shgroup_create(sh, psl->armature_bone_select_ps); + DRW_shgroup_uniform_vec4_copy(grp, "color", (float[4]){0.0f, 0.0f, 0.0f, pow(alpha, 4)}); + } + + for (int i = 0; i < 2; i++) { + struct GPUShader *sh; + struct GPUVertFormat *format; + DRWShadingGroup *grp = NULL; + + OVERLAY_InstanceFormats *formats = OVERLAY_shader_instance_formats_get(); + OVERLAY_ArmatureCallBuffers *cb = &pd->armature_call_buffers[i]; + DRWPass **p_armature_ps = &psl->armature_ps[i]; + + cb->custom_shapes_ghash = BLI_ghash_ptr_new(__func__); + + DRWState infront_state = (DRW_state_is_select() && (i == 1)) ? DRW_STATE_IN_FRONT_SELECT : 0; + state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CULL_BACK | + (pd->armature.transparent ? DRW_STATE_BLEND_ALPHA : DRW_STATE_WRITE_DEPTH); + DRW_PASS_CREATE(*p_armature_ps, state | pd->clipping_state | infront_state); + + DRWPass *armature_ps = *p_armature_ps; + +#define BUF_INSTANCE DRW_shgroup_call_buffer_instance +#define BUF_LINE(grp, format) DRW_shgroup_call_buffer(grp, format, GPU_PRIM_LINES) + + { + format = formats->instance_bone; + + sh = OVERLAY_shader_armature_sphere(false); + grp = DRW_shgroup_create(sh, armature_ps); + DRW_shgroup_uniform_block_persistent(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_float_copy(grp, "alpha", pd->armature.transparent ? 0.4f : 1.0f); + cb->point_solid = BUF_INSTANCE(grp, format, DRW_cache_bone_point_get()); + + sh = OVERLAY_shader_armature_sphere(true); + grp = DRW_shgroup_create(sh, armature_ps); + DRW_shgroup_state_disable(grp, DRW_STATE_CULL_BACK); + DRW_shgroup_uniform_block_persistent(grp, "globalsBlock", G_draw.block_ubo); + cb->point_outline = BUF_INSTANCE(grp, format, DRW_cache_bone_point_wire_outline_get()); + + sh = OVERLAY_shader_armature_shape(false); + cb->custom_solid = grp = DRW_shgroup_create(sh, armature_ps); + DRW_shgroup_uniform_block_persistent(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_float_copy(grp, "alpha", pd->armature.transparent ? 0.6f : 1.0f); + cb->box_solid = BUF_INSTANCE(grp, format, DRW_cache_bone_box_get()); + cb->octa_solid = BUF_INSTANCE(grp, format, DRW_cache_bone_octahedral_get()); + + sh = OVERLAY_shader_armature_shape(true); + cb->custom_outline = grp = DRW_shgroup_create(sh, armature_ps); + DRW_shgroup_state_disable(grp, DRW_STATE_CULL_BACK); + DRW_shgroup_uniform_block_persistent(grp, "globalsBlock", G_draw.block_ubo); + cb->box_outline = BUF_INSTANCE(grp, format, DRW_cache_bone_box_wire_get()); + cb->octa_outline = BUF_INSTANCE(grp, format, DRW_cache_bone_octahedral_wire_get()); + } + { + format = formats->instance_extra; + + sh = OVERLAY_shader_armature_degrees_of_freedom(); + grp = DRW_shgroup_create(sh, armature_ps); + DRW_shgroup_uniform_block_persistent(grp, "globalsBlock", G_draw.block_ubo); + cb->dof_lines = BUF_INSTANCE(grp, format, DRW_cache_bone_dof_lines_get()); + + grp = DRW_shgroup_create(sh, psl->armature_transp_ps); + DRW_shgroup_uniform_block_persistent(grp, "globalsBlock", G_draw.block_ubo); + cb->dof_sphere = BUF_INSTANCE(grp, format, DRW_cache_bone_dof_sphere_get()); + } + { + format = formats->instance_bone_stick; + + sh = OVERLAY_shader_armature_stick(); + grp = DRW_shgroup_create(sh, armature_ps); + DRW_shgroup_uniform_block_persistent(grp, "globalsBlock", G_draw.block_ubo); + cb->stick = BUF_INSTANCE(grp, format, DRW_cache_bone_stick_get()); + } + { + format = formats->instance_bone_envelope; + + sh = OVERLAY_shader_armature_envelope(false); + grp = DRW_shgroup_create(sh, armature_ps); + DRW_shgroup_uniform_block_persistent(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_bool_copy(grp, "isDistance", false); + DRW_shgroup_uniform_float_copy(grp, "alpha", pd->armature.transparent ? 0.6f : 1.0f); + cb->envelope_solid = BUF_INSTANCE(grp, format, DRW_cache_bone_envelope_solid_get()); + + format = formats->instance_bone_envelope_outline; + + sh = OVERLAY_shader_armature_envelope(true); + grp = DRW_shgroup_create(sh, armature_ps); + DRW_shgroup_state_disable(grp, DRW_STATE_CULL_BACK); + DRW_shgroup_uniform_block_persistent(grp, "globalsBlock", G_draw.block_ubo); + cb->envelope_outline = BUF_INSTANCE(grp, format, DRW_cache_bone_envelope_outline_get()); + + format = formats->instance_bone_envelope_distance; + + sh = OVERLAY_shader_armature_envelope(false); + grp = DRW_shgroup_create(sh, psl->armature_transp_ps); + DRW_shgroup_uniform_block_persistent(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_bool_copy(grp, "isDistance", true); + DRW_shgroup_state_enable(grp, DRW_STATE_CULL_FRONT); + cb->envelope_distance = BUF_INSTANCE(grp, format, DRW_cache_bone_envelope_solid_get()); + } + { + format = formats->pos_color; + + sh = OVERLAY_shader_armature_wire(); + grp = DRW_shgroup_create(sh, armature_ps); + cb->wire = BUF_LINE(grp, format); + } + } +} + +/* -------------------------------------------------------------------- */ +/** \name Shader Groups (DRW_shgroup) + * \{ */ + +static void bone_instance_data_set_angle_minmax(BoneInstanceData *data, + const float aminx, + const float aminz, + const float amaxx, + const float amaxz) +{ + data->amin_a = aminx; + data->amin_b = aminz; + data->amax_a = amaxx; + data->amax_b = amaxz; +} + +/* Encode 2 units float with byte precision into a float. */ +static float encode_2f_to_float(float a, float b) +{ + CLAMP(a, 0.0f, 1.0f); + CLAMP(b, 0.0f, 2.0f); /* Can go up to 2. Needed for wire size. */ + return (float)((int)(a * 255) | ((int)(b * 255) << 8)); +} + +void OVERLAY_bone_instance_data_set_color_hint(BoneInstanceData *data, const float hint_color[4]) +{ + /* Encoded color into 2 floats to be able to use the obmat to color the custom bones. */ + data->color_hint_a = encode_2f_to_float(hint_color[0], hint_color[1]); + data->color_hint_b = encode_2f_to_float(hint_color[2], hint_color[3]); +} + +void OVERLAY_bone_instance_data_set_color(BoneInstanceData *data, const float bone_color[4]) +{ + /* Encoded color into 2 floats to be able to use the obmat to color the custom bones. */ + data->color_a = encode_2f_to_float(bone_color[0], bone_color[1]); + data->color_b = encode_2f_to_float(bone_color[2], bone_color[3]); +} + +/* Octahedral */ +static void drw_shgroup_bone_octahedral(ArmatureDrawContext *ctx, + const float (*bone_mat)[4], + const float bone_color[4], + const float hint_color[4], + const float outline_color[4]) +{ + BoneInstanceData inst_data; + mul_m4_m4m4(inst_data.mat, ctx->ob->obmat, bone_mat); + if (ctx->solid) { + OVERLAY_bone_instance_data_set_color(&inst_data, bone_color); + OVERLAY_bone_instance_data_set_color_hint(&inst_data, hint_color); + DRW_buffer_add_entry_struct(ctx->solid, &inst_data); + } + if (outline_color[3] > 0.0f) { + OVERLAY_bone_instance_data_set_color(&inst_data, outline_color); + DRW_buffer_add_entry_struct(ctx->outline, &inst_data); + } +} + +/* Box / B-Bone */ +static void drw_shgroup_bone_box(ArmatureDrawContext *ctx, + const float (*bone_mat)[4], + const float bone_color[4], + const float hint_color[4], + const float outline_color[4]) +{ + BoneInstanceData inst_data; + mul_m4_m4m4(inst_data.mat, ctx->ob->obmat, bone_mat); + if (ctx->solid) { + OVERLAY_bone_instance_data_set_color(&inst_data, bone_color); + OVERLAY_bone_instance_data_set_color_hint(&inst_data, hint_color); + DRW_buffer_add_entry_struct(ctx->solid, &inst_data); + } + if (outline_color[3] > 0.0f) { + OVERLAY_bone_instance_data_set_color(&inst_data, outline_color); + DRW_buffer_add_entry_struct(ctx->outline, &inst_data); + } +} + +/* Wire */ +static void drw_shgroup_bone_wire(ArmatureDrawContext *ctx, + const float (*bone_mat)[4], + const float color[4]) +{ + float head[3], tail[3]; + mul_v3_m4v3(head, ctx->ob->obmat, bone_mat[3]); + add_v3_v3v3(tail, bone_mat[3], bone_mat[1]); + mul_m4_v3(ctx->ob->obmat, tail); + + DRW_buffer_add_entry(ctx->wire, head, color); + DRW_buffer_add_entry(ctx->wire, tail, color); +} + +/* Stick */ +static void drw_shgroup_bone_stick(ArmatureDrawContext *ctx, + const float (*bone_mat)[4], + const float col_wire[4], + const float col_bone[4], + const float col_head[4], + const float col_tail[4]) +{ + float head[3], tail[3]; + mul_v3_m4v3(head, ctx->ob->obmat, bone_mat[3]); + add_v3_v3v3(tail, bone_mat[3], bone_mat[1]); + mul_m4_v3(ctx->ob->obmat, tail); + + DRW_buffer_add_entry(ctx->stick, head, tail, col_wire, col_bone, col_head, col_tail); +} + +/* Envelope */ +static void drw_shgroup_bone_envelope_distance(ArmatureDrawContext *ctx, + const float (*bone_mat)[4], + const float *radius_head, + const float *radius_tail, + const float *distance) +{ + if (ctx->envelope_distance) { + float head_sph[4] = {0.0f, 0.0f, 0.0f, 1.0f}, tail_sph[4] = {0.0f, 1.0f, 0.0f, 1.0f}; + float xaxis[4] = {1.0f, 0.0f, 0.0f, 1.0f}; + /* Still less operation than m4 multiplication. */ + mul_m4_v4(bone_mat, head_sph); + mul_m4_v4(bone_mat, tail_sph); + mul_m4_v4(bone_mat, xaxis); + mul_m4_v4(ctx->ob->obmat, head_sph); + mul_m4_v4(ctx->ob->obmat, tail_sph); + mul_m4_v4(ctx->ob->obmat, xaxis); + sub_v3_v3(xaxis, head_sph); + head_sph[3] = *radius_head; + head_sph[3] += *distance; + tail_sph[3] = *radius_tail; + tail_sph[3] += *distance; + DRW_buffer_add_entry(ctx->envelope_distance, head_sph, tail_sph, xaxis); + } +} + +static void drw_shgroup_bone_envelope(ArmatureDrawContext *ctx, + const float (*bone_mat)[4], + const float bone_col[4], + const float hint_col[4], + const float outline_col[4], + const float *radius_head, + const float *radius_tail) +{ + float head_sph[4] = {0.0f, 0.0f, 0.0f, 1.0f}, tail_sph[4] = {0.0f, 1.0f, 0.0f, 1.0f}; + float xaxis[4] = {1.0f, 0.0f, 0.0f, 1.0f}; + /* Still less operation than m4 multiplication. */ + mul_m4_v4(bone_mat, head_sph); + mul_m4_v4(bone_mat, tail_sph); + mul_m4_v4(bone_mat, xaxis); + mul_m4_v4(ctx->ob->obmat, head_sph); + mul_m4_v4(ctx->ob->obmat, tail_sph); + mul_m4_v4(ctx->ob->obmat, xaxis); + head_sph[3] = *radius_head; + tail_sph[3] = *radius_tail; + + if (head_sph[3] < 0.0f || tail_sph[3] < 0.0f) { + BoneInstanceData inst_data; + if (head_sph[3] < 0.0f) { + /* Draw Tail only */ + scale_m4_fl(inst_data.mat, tail_sph[3] / PT_DEFAULT_RAD); + copy_v3_v3(inst_data.mat[3], tail_sph); + } + else { + /* Draw Head only */ + scale_m4_fl(inst_data.mat, head_sph[3] / PT_DEFAULT_RAD); + copy_v3_v3(inst_data.mat[3], head_sph); + } + + if (ctx->point_solid) { + OVERLAY_bone_instance_data_set_color(&inst_data, bone_col); + OVERLAY_bone_instance_data_set_color_hint(&inst_data, hint_col); + DRW_buffer_add_entry_struct(ctx->point_solid, &inst_data); + } + if (outline_col[3] > 0.0f) { + OVERLAY_bone_instance_data_set_color(&inst_data, outline_col); + DRW_buffer_add_entry_struct(ctx->point_outline, &inst_data); + } + } + else { + /* Draw Body */ + float tmp_sph[4]; + float len = len_v3v3(tail_sph, head_sph); + float fac_head = (len - head_sph[3]) / len; + float fac_tail = (len - tail_sph[3]) / len; + /* Small epsilon to avoid problem with float precision in shader. */ + if (len > (tail_sph[3] + head_sph[3]) + 1e-8f) { + copy_v4_v4(tmp_sph, head_sph); + interp_v4_v4v4(head_sph, tail_sph, head_sph, fac_head); + interp_v4_v4v4(tail_sph, tmp_sph, tail_sph, fac_tail); + if (ctx->envelope_solid) { + DRW_buffer_add_entry(ctx->envelope_solid, head_sph, tail_sph, bone_col, hint_col, xaxis); + } + if (outline_col[3] > 0.0f) { + DRW_buffer_add_entry(ctx->envelope_outline, head_sph, tail_sph, outline_col, xaxis); + } + } + else { + /* Distance between endpoints is too small for a capsule. Draw a Sphere instead. */ + float fac = max_ff(fac_head, 1.0f - fac_tail); + interp_v4_v4v4(tmp_sph, tail_sph, head_sph, clamp_f(fac, 0.0f, 1.0f)); + + BoneInstanceData inst_data; + scale_m4_fl(inst_data.mat, tmp_sph[3] / PT_DEFAULT_RAD); + copy_v3_v3(inst_data.mat[3], tmp_sph); + if (ctx->point_solid) { + OVERLAY_bone_instance_data_set_color(&inst_data, bone_col); + OVERLAY_bone_instance_data_set_color_hint(&inst_data, hint_col); + DRW_buffer_add_entry_struct(ctx->point_solid, &inst_data); + } + if (outline_col[3] > 0.0f) { + OVERLAY_bone_instance_data_set_color(&inst_data, outline_col); + DRW_buffer_add_entry_struct(ctx->point_outline, &inst_data); + } + } + } +} + +/* Custom (geometry) */ + +extern void drw_batch_cache_validate(Object *custom); +extern void drw_batch_cache_generate_requested(Object *custom); + +BLI_INLINE DRWCallBuffer *custom_bone_instance_shgroup(ArmatureDrawContext *ctx, + DRWShadingGroup *grp, + struct GPUBatch *custom_geom) +{ + DRWCallBuffer *buf = BLI_ghash_lookup(ctx->custom_shapes_ghash, custom_geom); + if (buf == NULL) { + OVERLAY_InstanceFormats *formats = OVERLAY_shader_instance_formats_get(); + buf = DRW_shgroup_call_buffer_instance(grp, formats->instance_bone, custom_geom); + BLI_ghash_insert(ctx->custom_shapes_ghash, custom_geom, buf); + } + return buf; +} + +static void drw_shgroup_bone_custom_solid(ArmatureDrawContext *ctx, + const float (*bone_mat)[4], + const float bone_color[4], + const float hint_color[4], + const float outline_color[4], + Object *custom) +{ + /* TODO(fclem) arg... less than ideal but we never iter on this object + * to assure batch cache is valid. */ + drw_batch_cache_validate(custom); + + struct GPUBatch *surf = DRW_cache_object_surface_get(custom); + struct GPUBatch *edges = DRW_cache_object_edge_detection_get(custom, NULL); + struct GPUBatch *ledges = DRW_cache_object_loose_edges_get(custom); + BoneInstanceData inst_data; + DRWCallBuffer *buf; + + if (surf || edges || ledges) { + mul_m4_m4m4(inst_data.mat, ctx->ob->obmat, bone_mat); + } + + if (surf && ctx->custom_solid) { + buf = custom_bone_instance_shgroup(ctx, ctx->custom_solid, surf); + OVERLAY_bone_instance_data_set_color_hint(&inst_data, hint_color); + OVERLAY_bone_instance_data_set_color(&inst_data, bone_color); + DRW_buffer_add_entry_struct(buf, inst_data.mat); + } + + if (edges && ctx->custom_outline) { + buf = custom_bone_instance_shgroup(ctx, ctx->custom_outline, edges); + OVERLAY_bone_instance_data_set_color(&inst_data, outline_color); + DRW_buffer_add_entry_struct(buf, inst_data.mat); + } + + if (ledges) { + buf = custom_bone_instance_shgroup(ctx, ctx->custom_wire, ledges); + OVERLAY_bone_instance_data_set_color_hint(&inst_data, outline_color); + OVERLAY_bone_instance_data_set_color(&inst_data, outline_color); + DRW_buffer_add_entry_struct(buf, inst_data.mat); + } + + /* TODO(fclem) needs to be moved elsewhere. */ + drw_batch_cache_generate_requested(custom); +} + +static void drw_shgroup_bone_custom_wire(ArmatureDrawContext *ctx, + const float (*bone_mat)[4], + const float color[4], + Object *custom) +{ + /* TODO(fclem) arg... less than ideal but we never iter on this object + * to assure batch cache is valid. */ + drw_batch_cache_validate(custom); + + struct GPUBatch *geom = DRW_cache_object_all_edges_get(custom); + + if (geom) { + DRWCallBuffer *buf = custom_bone_instance_shgroup(ctx, ctx->custom_wire, geom); + BoneInstanceData inst_data; + mul_m4_m4m4(inst_data.mat, ctx->ob->obmat, bone_mat); + OVERLAY_bone_instance_data_set_color_hint(&inst_data, color); + OVERLAY_bone_instance_data_set_color(&inst_data, color); + DRW_buffer_add_entry_struct(buf, inst_data.mat); + } + + /* TODO(fclem) needs to be moved elsewhere. */ + drw_batch_cache_generate_requested(custom); +} + +static void drw_shgroup_bone_custom_empty(ArmatureDrawContext *ctx, + const float (*bone_mat)[4], + const float color[4], + Object *custom) +{ + float final_color[4] = {color[0], color[1], color[2], 1.0f}; + float mat[4][4]; + mul_m4_m4m4(mat, ctx->ob->obmat, bone_mat); + + switch (custom->empty_drawtype) { + case OB_PLAINAXES: + case OB_SINGLE_ARROW: + case OB_CUBE: + case OB_CIRCLE: + case OB_EMPTY_SPHERE: + case OB_EMPTY_CONE: + case OB_ARROWS: + OVERLAY_empty_shape( + ctx->extras, mat, custom->empty_drawsize, custom->empty_drawtype, final_color); + break; + case OB_EMPTY_IMAGE: + break; + } +} + +/* Head and tail sphere */ +static void drw_shgroup_bone_point(ArmatureDrawContext *ctx, + const float (*bone_mat)[4], + const float bone_color[4], + const float hint_color[4], + const float outline_color[4]) +{ + BoneInstanceData inst_data; + mul_m4_m4m4(inst_data.mat, ctx->ob->obmat, bone_mat); + if (ctx->point_solid) { + OVERLAY_bone_instance_data_set_color(&inst_data, bone_color); + OVERLAY_bone_instance_data_set_color_hint(&inst_data, hint_color); + DRW_buffer_add_entry_struct(ctx->point_solid, &inst_data); + } + if (outline_color[3] > 0.0f) { + OVERLAY_bone_instance_data_set_color(&inst_data, outline_color); + DRW_buffer_add_entry_struct(ctx->point_outline, &inst_data); + } +} + +/* Axes */ +static void drw_shgroup_bone_axes(ArmatureDrawContext *ctx, + const float (*bone_mat)[4], + const float color[4]) +{ + float mat[4][4]; + mul_m4_m4m4(mat, ctx->ob->obmat, bone_mat); + /* Move to bone tail. */ + add_v3_v3(mat[3], mat[1]); + OVERLAY_empty_shape(ctx->extras, mat, 0.25f, OB_ARROWS, color); +} + +/* Relationship lines */ +static void drw_shgroup_bone_relationship_lines_ex(ArmatureDrawContext *ctx, + const float start[3], + const float end[3], + const float color[4]) +{ + float s[3], e[3]; + mul_v3_m4v3(s, ctx->ob->obmat, start); + mul_v3_m4v3(e, ctx->ob->obmat, end); + /* reverse order to have less stipple overlap */ + OVERLAY_extra_line_dashed(ctx->extras, s, e, color); +} + +static void drw_shgroup_bone_relationship_lines(ArmatureDrawContext *ctx, + const float start[3], + const float end[3]) +{ + drw_shgroup_bone_relationship_lines_ex(ctx, start, end, ctx->color.wire); +} + +static void drw_shgroup_bone_ik_lines(ArmatureDrawContext *ctx, + const float start[3], + const float end[3]) +{ + float fcolor[4] = {0.8f, 0.5f, 0.0f, 1.0f}; /* add theme! */ + drw_shgroup_bone_relationship_lines_ex(ctx, start, end, fcolor); +} + +static void drw_shgroup_bone_ik_no_target_lines(ArmatureDrawContext *ctx, + const float start[3], + const float end[3]) +{ + float fcolor[4] = {0.8f, 0.8f, 0.2f, 1.0f}; /* add theme! */ + drw_shgroup_bone_relationship_lines_ex(ctx, start, end, fcolor); +} + +static void drw_shgroup_bone_ik_spline_lines(ArmatureDrawContext *ctx, + const float start[3], + const float end[3]) +{ + float fcolor[4] = {0.8f, 0.8f, 0.2f, 1.0f}; /* add theme! */ + drw_shgroup_bone_relationship_lines_ex(ctx, start, end, fcolor); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Drawing Theme Helpers + * + * Note, this section is duplicate of code in 'drawarmature.c'. + * + * \{ */ + +/* values of colCode for set_pchan_color */ +enum { + PCHAN_COLOR_NORMAL = 0, /* normal drawing */ + PCHAN_COLOR_SOLID, /* specific case where "solid" color is needed */ + PCHAN_COLOR_CONSTS, /* "constraint" colors (which may/may-not be suppressed) */ + + PCHAN_COLOR_SPHEREBONE_BASE, /* for the 'stick' of sphere (envelope) bones */ + PCHAN_COLOR_SPHEREBONE_END, /* for the ends of sphere (envelope) bones */ + PCHAN_COLOR_LINEBONE, /* for the middle of line-bones */ +}; + +/* This function sets the color-set for coloring a certain bone */ +static void set_pchan_colorset(ArmatureDrawContext *ctx, Object *ob, bPoseChannel *pchan) +{ + bPose *pose = (ob) ? ob->pose : NULL; + bArmature *arm = (ob) ? ob->data : NULL; + bActionGroup *grp = NULL; + short color_index = 0; + + /* sanity check */ + if (ELEM(NULL, ob, arm, pose, pchan)) { + ctx->bcolor = NULL; + return; + } + + /* only try to set custom color if enabled for armature */ + if (arm->flag & ARM_COL_CUSTOM) { + /* currently, a bone can only use a custom color set if it's group (if it has one), + * has been set to use one + */ + if (pchan->agrp_index) { + grp = (bActionGroup *)BLI_findlink(&pose->agroups, (pchan->agrp_index - 1)); + if (grp) { + color_index = grp->customCol; + } + } + } + + /* bcolor is a pointer to the color set to use. If NULL, then the default + * color set (based on the theme colors for 3d-view) is used. + */ + if (color_index > 0) { + bTheme *btheme = UI_GetTheme(); + ctx->bcolor = &btheme->tarm[(color_index - 1)]; + } + else if (color_index == -1) { + /* use the group's own custom color set (grp is always != NULL here) */ + ctx->bcolor = &grp->cs; + } + else { + ctx->bcolor = NULL; + } +} + +/* This function is for brightening/darkening a given color (like UI_GetThemeColorShade3ubv()) */ +static void cp_shade_color3ub(uchar cp[3], const int offset) +{ + int r, g, b; + + r = offset + (int)cp[0]; + CLAMP(r, 0, 255); + g = offset + (int)cp[1]; + CLAMP(g, 0, 255); + b = offset + (int)cp[2]; + CLAMP(b, 0, 255); + + cp[0] = r; + cp[1] = g; + cp[2] = b; +} + +static void cp_shade_color3f(float cp[3], const float offset) +{ + add_v3_fl(cp, offset); + CLAMP(cp[0], 0, 255); + CLAMP(cp[1], 0, 255); + CLAMP(cp[2], 0, 255); +} + +/* This function sets the gl-color for coloring a certain bone (based on bcolor) */ +static bool set_pchan_color(const ArmatureDrawContext *ctx, + short colCode, + const int boneflag, + const short constflag, + float r_color[4]) +{ + float *fcolor = r_color; + const ThemeWireColor *bcolor = ctx->bcolor; + + switch (colCode) { + case PCHAN_COLOR_NORMAL: { + if (bcolor) { + uchar cp[4] = {255}; + + if (boneflag & BONE_DRAW_ACTIVE) { + copy_v3_v3_uchar(cp, bcolor->active); + if (!(boneflag & BONE_SELECTED)) { + cp_shade_color3ub(cp, -80); + } + } + else if (boneflag & BONE_SELECTED) { + copy_v3_v3_uchar(cp, bcolor->select); + } + else { + /* a bit darker than solid */ + copy_v3_v3_uchar(cp, bcolor->solid); + cp_shade_color3ub(cp, -50); + } + + rgb_uchar_to_float(fcolor, cp); + } + else { + if ((boneflag & BONE_DRAW_ACTIVE) && (boneflag & BONE_SELECTED)) { + UI_GetThemeColor4fv(TH_BONE_POSE_ACTIVE, fcolor); + } + else if (boneflag & BONE_DRAW_ACTIVE) { + UI_GetThemeColorBlendShade4fv(TH_WIRE, TH_BONE_POSE, 0.15f, 0, fcolor); + } + else if (boneflag & BONE_SELECTED) { + UI_GetThemeColor4fv(TH_BONE_POSE, fcolor); + } + else { + UI_GetThemeColor4fv(TH_WIRE, fcolor); + } + } + + return true; + } + case PCHAN_COLOR_SOLID: { + UI_GetThemeColor4fv(TH_BONE_SOLID, fcolor); + + if (bcolor) { + float solid_bcolor[3]; + rgb_uchar_to_float(solid_bcolor, (uchar *)bcolor->solid); + interp_v3_v3v3(fcolor, fcolor, solid_bcolor, 1.0f); + } + + return true; + } + case PCHAN_COLOR_CONSTS: { + if ((bcolor == NULL) || (bcolor->flag & TH_WIRECOLOR_CONSTCOLS)) { + uchar cp[4]; + if (constflag & PCHAN_HAS_TARGET) { + rgba_uchar_args_set(cp, 255, 150, 0, 80); + } + else if (constflag & PCHAN_HAS_IK) { + rgba_uchar_args_set(cp, 255, 255, 0, 80); + } + else if (constflag & PCHAN_HAS_SPLINEIK) { + rgba_uchar_args_set(cp, 200, 255, 0, 80); + } + else if (constflag & PCHAN_HAS_CONST) { + rgba_uchar_args_set(cp, 0, 255, 120, 80); + } + else { + return false; + } + + rgba_uchar_to_float(fcolor, cp); + + return true; + } + return false; + } + case PCHAN_COLOR_SPHEREBONE_BASE: { + if (bcolor) { + uchar cp[4] = {255}; + + if (boneflag & BONE_DRAW_ACTIVE) { + copy_v3_v3_uchar(cp, bcolor->active); + } + else if (boneflag & BONE_SELECTED) { + copy_v3_v3_uchar(cp, bcolor->select); + } + else { + copy_v3_v3_uchar(cp, bcolor->solid); + } + + rgb_uchar_to_float(fcolor, cp); + } + else { + if (boneflag & BONE_DRAW_ACTIVE) { + UI_GetThemeColorShade4fv(TH_BONE_POSE, 40, fcolor); + } + else if (boneflag & BONE_SELECTED) { + UI_GetThemeColor4fv(TH_BONE_POSE, fcolor); + } + else { + UI_GetThemeColor4fv(TH_BONE_SOLID, fcolor); + } + } + + return true; + } + case PCHAN_COLOR_SPHEREBONE_END: { + if (bcolor) { + uchar cp[4] = {255}; + + if (boneflag & BONE_DRAW_ACTIVE) { + copy_v3_v3_uchar(cp, bcolor->active); + cp_shade_color3ub(cp, 10); + } + else if (boneflag & BONE_SELECTED) { + copy_v3_v3_uchar(cp, bcolor->select); + cp_shade_color3ub(cp, -30); + } + else { + copy_v3_v3_uchar(cp, bcolor->solid); + cp_shade_color3ub(cp, -30); + } + + rgb_uchar_to_float(fcolor, cp); + } + else { + if (boneflag & BONE_DRAW_ACTIVE) { + UI_GetThemeColorShade4fv(TH_BONE_POSE, 10, fcolor); + } + else if (boneflag & BONE_SELECTED) { + UI_GetThemeColorShade4fv(TH_BONE_POSE, -30, fcolor); + } + else { + UI_GetThemeColorShade4fv(TH_BONE_SOLID, -30, fcolor); + } + } + break; + } + case PCHAN_COLOR_LINEBONE: { + /* inner part in background color or constraint */ + if ((constflag) && ((bcolor == NULL) || (bcolor->flag & TH_WIRECOLOR_CONSTCOLS))) { + uchar cp[4]; + if (constflag & PCHAN_HAS_TARGET) { + rgba_uchar_args_set(cp, 255, 150, 0, 255); + } + else if (constflag & PCHAN_HAS_IK) { + rgba_uchar_args_set(cp, 255, 255, 0, 255); + } + else if (constflag & PCHAN_HAS_SPLINEIK) { + rgba_uchar_args_set(cp, 200, 255, 0, 255); + } + else if (constflag & PCHAN_HAS_CONST) { + rgba_uchar_args_set(cp, 0, 255, 120, 255); + } + else if (constflag) { + UI_GetThemeColor4ubv(TH_BONE_POSE, cp); + } /* PCHAN_HAS_ACTION */ + + rgb_uchar_to_float(fcolor, cp); + } + else { + if (bcolor) { + const uchar *cp = bcolor->solid; + rgb_uchar_to_float(fcolor, (uchar *)cp); + fcolor[3] = 204.f / 255.f; + } + else { + UI_GetThemeColorShade4fv(TH_BACK, -30, fcolor); + } + } + + return true; + } + } + + return false; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Drawing Color Helpers + * \{ */ + +static const float *get_bone_solid_color(const ArmatureDrawContext *ctx, + const EditBone *UNUSED(eBone), + const bPoseChannel *pchan, + const bArmature *arm, + const int boneflag, + const short constflag) +{ + if (ctx->const_color) { + return ctx->color.bone_solid; + } + + if (arm->flag & ARM_POSEMODE) { + static float disp_color[4]; + copy_v4_v4(disp_color, pchan->draw_data->solid_color); + set_pchan_color(ctx, PCHAN_COLOR_SOLID, boneflag, constflag, disp_color); + return disp_color; + } + + return ctx->color.bone_solid; +} + +static const float *get_bone_solid_with_consts_color(const ArmatureDrawContext *ctx, + const EditBone *eBone, + const bPoseChannel *pchan, + const bArmature *arm, + const int boneflag, + const short constflag) +{ + if (ctx->const_color) { + return ctx->color.bone_solid; + } + + const float *col = get_bone_solid_color(ctx, eBone, pchan, arm, boneflag, constflag); + + static float consts_color[4]; + if (set_pchan_color(ctx, PCHAN_COLOR_CONSTS, boneflag, constflag, consts_color)) { + interp_v3_v3v3(consts_color, col, consts_color, 0.5f); + } + else { + copy_v4_v4(consts_color, col); + } + return consts_color; +} + +static float get_bone_wire_thickness(const ArmatureDrawContext *ctx, int boneflag) +{ + if (ctx->const_color) { + return ctx->const_wire; + } + else if (boneflag & (BONE_DRAW_ACTIVE | BONE_SELECTED)) { + return 2.0f; + } + else { + return 1.0f; + } +} + +static const float *get_bone_wire_color(const ArmatureDrawContext *ctx, + const EditBone *eBone, + const bPoseChannel *pchan, + const bArmature *arm, + const int boneflag, + const short constflag) +{ + static float disp_color[4]; + + if (ctx->const_color) { + copy_v3_v3(disp_color, ctx->const_color); + } + else if (eBone) { + if (boneflag & BONE_SELECTED) { + if (boneflag & BONE_DRAW_ACTIVE) { + copy_v3_v3(disp_color, ctx->color.edge_select); + } + else { + copy_v3_v3(disp_color, ctx->color.bone_select); + } + } + else { + if (boneflag & BONE_DRAW_ACTIVE) { + copy_v3_v3(disp_color, ctx->color.bone_active_unselect); + } + else { + copy_v3_v3(disp_color, ctx->color.wire_edit); + } + } + } + else if (arm->flag & ARM_POSEMODE) { + copy_v4_v4(disp_color, pchan->draw_data->wire_color); + set_pchan_color(ctx, PCHAN_COLOR_NORMAL, boneflag, constflag, disp_color); + } + else { + copy_v3_v3(disp_color, ctx->color.vertex); + } + + disp_color[3] = get_bone_wire_thickness(ctx, boneflag); + + return disp_color; +} + +#define HINT_MUL 0.5f +#define HINT_SHADE 0.2f + +static void bone_hint_color_shade(float hint_color[4], const float color[4]) +{ + mul_v3_v3fl(hint_color, color, HINT_MUL); + cp_shade_color3f(hint_color, -HINT_SHADE); + hint_color[3] = 1.0f; +} + +static const float *get_bone_hint_color(const ArmatureDrawContext *ctx, + const EditBone *eBone, + const bPoseChannel *pchan, + const bArmature *arm, + const int boneflag, + const short constflag) +{ + static float hint_color[4] = {0.0f, 0.0f, 0.0f, 1.0f}; + + if (ctx->const_color) { + bone_hint_color_shade(hint_color, ctx->color.bone_solid); + } + else { + const float *wire_color = get_bone_wire_color(ctx, eBone, pchan, arm, boneflag, constflag); + bone_hint_color_shade(hint_color, wire_color); + } + + return hint_color; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Helper Utils + * \{ */ + +static void pchan_draw_data_init(bPoseChannel *pchan) +{ + if (pchan->draw_data != NULL) { + if (pchan->draw_data->bbone_matrix_len != pchan->bone->segments) { + MEM_SAFE_FREE(pchan->draw_data); + } + } + + if (pchan->draw_data == NULL) { + pchan->draw_data = MEM_mallocN( + sizeof(*pchan->draw_data) + sizeof(Mat4) * pchan->bone->segments, __func__); + pchan->draw_data->bbone_matrix_len = pchan->bone->segments; + } +} + +static void draw_bone_update_disp_matrix_default(EditBone *eBone, bPoseChannel *pchan) +{ + float s[4][4], ebmat[4][4]; + float length; + float(*bone_mat)[4]; + float(*disp_mat)[4]; + float(*disp_tail_mat)[4]; + + /* TODO : This should be moved to depsgraph or armature refresh + * and not be tight to the draw pass creation. + * This would refresh armature without invalidating the draw cache */ + if (pchan) { + length = pchan->bone->length; + bone_mat = pchan->pose_mat; + disp_mat = pchan->disp_mat; + disp_tail_mat = pchan->disp_tail_mat; + } + else { + eBone->length = len_v3v3(eBone->tail, eBone->head); + ED_armature_ebone_to_mat4(eBone, ebmat); + + length = eBone->length; + bone_mat = ebmat; + disp_mat = eBone->disp_mat; + disp_tail_mat = eBone->disp_tail_mat; + } + + scale_m4_fl(s, length); + mul_m4_m4m4(disp_mat, bone_mat, s); + copy_m4_m4(disp_tail_mat, disp_mat); + translate_m4(disp_tail_mat, 0.0f, 1.0f, 0.0f); +} + +/* compute connected child pointer for B-Bone drawing */ +static void edbo_compute_bbone_child(bArmature *arm) +{ + EditBone *eBone; + + for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { + eBone->bbone_child = NULL; + } + + for (eBone = arm->edbo->first; eBone; eBone = eBone->next) { + if (eBone->parent && (eBone->flag & BONE_CONNECTED)) { + eBone->parent->bbone_child = eBone; + } + } +} + +/* A version of BKE_pchan_bbone_spline_setup() for previewing editmode curve settings. */ +static void ebone_spline_preview(EditBone *ebone, float result_array[MAX_BBONE_SUBDIV][4][4]) +{ + BBoneSplineParameters param; + EditBone *prev, *next; + float imat[4][4], bonemat[4][4]; + float tmp[3]; + + memset(¶m, 0, sizeof(param)); + + param.segments = ebone->segments; + param.length = ebone->length; + + /* Get "next" and "prev" bones - these are used for handle calculations. */ + if (ebone->bbone_prev_type == BBONE_HANDLE_AUTO) { + /* Use connected parent. */ + if (ebone->flag & BONE_CONNECTED) { + prev = ebone->parent; + } + else { + prev = NULL; + } + } + else { + prev = ebone->bbone_prev; + } + + if (ebone->bbone_next_type == BBONE_HANDLE_AUTO) { + /* Use connected child. */ + next = ebone->bbone_child; + } + else { + next = ebone->bbone_next; + } + + /* compute handles from connected bones */ + if (prev || next) { + ED_armature_ebone_to_mat4(ebone, imat); + invert_m4(imat); + + if (prev) { + param.use_prev = true; + + if (ebone->bbone_prev_type == BBONE_HANDLE_RELATIVE) { + zero_v3(param.prev_h); + } + else if (ebone->bbone_prev_type == BBONE_HANDLE_TANGENT) { + sub_v3_v3v3(tmp, prev->tail, prev->head); + sub_v3_v3v3(tmp, ebone->head, tmp); + mul_v3_m4v3(param.prev_h, imat, tmp); + } + else { + param.prev_bbone = (prev->segments > 1); + + mul_v3_m4v3(param.prev_h, imat, prev->head); + } + + if (!param.prev_bbone) { + ED_armature_ebone_to_mat4(prev, bonemat); + mul_m4_m4m4(param.prev_mat, imat, bonemat); + } + } + + if (next) { + param.use_next = true; + + if (ebone->bbone_next_type == BBONE_HANDLE_RELATIVE) { + copy_v3_fl3(param.next_h, 0.0f, param.length, 0.0); + } + else if (ebone->bbone_next_type == BBONE_HANDLE_TANGENT) { + sub_v3_v3v3(tmp, next->tail, next->head); + add_v3_v3v3(tmp, ebone->tail, tmp); + mul_v3_m4v3(param.next_h, imat, tmp); + } + else { + param.next_bbone = (next->segments > 1); + + mul_v3_m4v3(param.next_h, imat, next->tail); + } + + ED_armature_ebone_to_mat4(next, bonemat); + mul_m4_m4m4(param.next_mat, imat, bonemat); + } + } + + param.ease1 = ebone->ease1; + param.ease2 = ebone->ease2; + param.roll1 = ebone->roll1; + param.roll2 = ebone->roll2; + + if (prev && (ebone->flag & BONE_ADD_PARENT_END_ROLL)) { + param.roll1 += prev->roll2; + } + + param.scale_in_x = ebone->scale_in_x; + param.scale_in_y = ebone->scale_in_y; + + param.scale_out_x = ebone->scale_out_x; + param.scale_out_y = ebone->scale_out_y; + + param.curve_in_x = ebone->curve_in_x; + param.curve_in_y = ebone->curve_in_y; + + param.curve_out_x = ebone->curve_out_x; + param.curve_out_y = ebone->curve_out_y; + + ebone->segments = BKE_pchan_bbone_spline_compute(¶m, false, (Mat4 *)result_array); +} + +static void draw_bone_update_disp_matrix_bbone(EditBone *eBone, bPoseChannel *pchan) +{ + float s[4][4], ebmat[4][4]; + float length, xwidth, zwidth; + float(*bone_mat)[4]; + short bbone_segments; + + /* TODO : This should be moved to depsgraph or armature refresh + * and not be tight to the draw pass creation. + * This would refresh armature without invalidating the draw cache */ + if (pchan) { + length = pchan->bone->length; + xwidth = pchan->bone->xwidth; + zwidth = pchan->bone->zwidth; + bone_mat = pchan->pose_mat; + bbone_segments = pchan->bone->segments; + } + else { + eBone->length = len_v3v3(eBone->tail, eBone->head); + ED_armature_ebone_to_mat4(eBone, ebmat); + + length = eBone->length; + xwidth = eBone->xwidth; + zwidth = eBone->zwidth; + bone_mat = ebmat; + bbone_segments = eBone->segments; + } + + size_to_mat4(s, (const float[3]){xwidth, length / bbone_segments, zwidth}); + + /* Compute BBones segment matrices... */ + /* Note that we need this even for one-segment bones, because box drawing need specific weirdo + * matrix for the box, that we cannot use to draw end points & co. */ + if (pchan) { + Mat4 *bbones_mat = (Mat4 *)pchan->draw_data->bbone_matrix; + if (bbone_segments > 1) { + BKE_pchan_bbone_spline_setup(pchan, false, false, bbones_mat); + + for (int i = bbone_segments; i--; bbones_mat++) { + mul_m4_m4m4(bbones_mat->mat, bbones_mat->mat, s); + mul_m4_m4m4(bbones_mat->mat, bone_mat, bbones_mat->mat); + } + } + else { + mul_m4_m4m4(bbones_mat->mat, bone_mat, s); + } + } + else { + float(*bbones_mat)[4][4] = eBone->disp_bbone_mat; + + if (bbone_segments > 1) { + ebone_spline_preview(eBone, bbones_mat); + + for (int i = bbone_segments; i--; bbones_mat++) { + mul_m4_m4m4(*bbones_mat, *bbones_mat, s); + mul_m4_m4m4(*bbones_mat, bone_mat, *bbones_mat); + } + } + else { + mul_m4_m4m4(*bbones_mat, bone_mat, s); + } + } + + /* Grrr... We need default display matrix to draw end points, axes, etc. :( */ + draw_bone_update_disp_matrix_default(eBone, pchan); +} + +static void draw_bone_update_disp_matrix_custom(bPoseChannel *pchan) +{ + float s[4][4]; + float length; + float(*bone_mat)[4]; + float(*disp_mat)[4]; + float(*disp_tail_mat)[4]; + + /* See TODO above */ + length = PCHAN_CUSTOM_DRAW_SIZE(pchan); + bone_mat = pchan->custom_tx ? pchan->custom_tx->pose_mat : pchan->pose_mat; + disp_mat = pchan->disp_mat; + disp_tail_mat = pchan->disp_tail_mat; + + scale_m4_fl(s, length); + mul_m4_m4m4(disp_mat, bone_mat, s); + copy_m4_m4(disp_tail_mat, disp_mat); + translate_m4(disp_tail_mat, 0.0f, 1.0f, 0.0f); +} + +static void draw_axes(ArmatureDrawContext *ctx, EditBone *eBone, bPoseChannel *pchan) +{ + float final_col[4]; + const float *col = (ctx->const_color) ? + ctx->const_color : + (BONE_FLAG(eBone, pchan) & BONE_SELECTED) ? ctx->color.text_hi : + ctx->color.text; + copy_v4_v4(final_col, col); + /* Mix with axes color. */ + final_col[3] = (ctx->const_color) ? 1.0 : (BONE_FLAG(eBone, pchan) & BONE_SELECTED) ? 0.3 : 0.8; + drw_shgroup_bone_axes(ctx, BONE_VAR(eBone, pchan, disp_mat), final_col); +} + +static void draw_points(ArmatureDrawContext *ctx, + const EditBone *eBone, + const bPoseChannel *pchan, + const bArmature *arm, + const int boneflag, + const short constflag, + const int select_id) +{ + float col_solid_root[4], col_solid_tail[4], col_wire_root[4], col_wire_tail[4]; + float col_hint_root[4], col_hint_tail[4]; + + copy_v4_v4(col_solid_root, ctx->color.bone_solid); + copy_v4_v4(col_solid_tail, ctx->color.bone_solid); + copy_v4_v4(col_wire_root, (ctx->const_color) ? ctx->const_color : ctx->color.vertex); + copy_v4_v4(col_wire_tail, (ctx->const_color) ? ctx->const_color : ctx->color.vertex); + + const bool is_envelope_draw = (arm->drawtype == ARM_ENVELOPE); + const float envelope_ignore = -1.0f; + + col_wire_tail[3] = col_wire_root[3] = get_bone_wire_thickness(ctx, boneflag); + + /* Edit bone points can be selected */ + if (eBone) { + if (eBone->flag & BONE_ROOTSEL) { + copy_v3_v3(col_wire_root, ctx->color.vertex_select); + } + if (eBone->flag & BONE_TIPSEL) { + copy_v3_v3(col_wire_tail, ctx->color.vertex_select); + } + } + else if (arm->flag & ARM_POSEMODE) { + const float *solid_color = get_bone_solid_color(ctx, eBone, pchan, arm, boneflag, constflag); + const float *wire_color = get_bone_wire_color(ctx, eBone, pchan, arm, boneflag, constflag); + copy_v4_v4(col_wire_tail, wire_color); + copy_v4_v4(col_wire_root, wire_color); + copy_v4_v4(col_solid_tail, solid_color); + copy_v4_v4(col_solid_root, solid_color); + } + + bone_hint_color_shade(col_hint_root, (ctx->const_color) ? col_solid_root : col_wire_root); + bone_hint_color_shade(col_hint_tail, (ctx->const_color) ? col_solid_tail : col_wire_tail); + + /* Draw root point if we are not connected to our parent */ + if (!(eBone ? (eBone->parent && (eBone->flag & BONE_CONNECTED)) : + (pchan->bone->parent && (pchan->bone->flag & BONE_CONNECTED)))) { + if (select_id != -1) { + DRW_select_load_id(select_id | BONESEL_ROOT); + } + + if (eBone) { + if (is_envelope_draw) { + drw_shgroup_bone_envelope(ctx, + eBone->disp_mat, + col_solid_root, + col_hint_root, + col_wire_root, + &eBone->rad_head, + &envelope_ignore); + } + else { + drw_shgroup_bone_point(ctx, eBone->disp_mat, col_solid_root, col_hint_root, col_wire_root); + } + } + else { + Bone *bone = pchan->bone; + if (is_envelope_draw) { + drw_shgroup_bone_envelope(ctx, + pchan->disp_mat, + col_solid_root, + col_hint_root, + col_wire_root, + &bone->rad_head, + &envelope_ignore); + } + else { + drw_shgroup_bone_point(ctx, pchan->disp_mat, col_solid_root, col_hint_root, col_wire_root); + } + } + } + + /* Draw tip point */ + if (select_id != -1) { + DRW_select_load_id(select_id | BONESEL_TIP); + } + + if (is_envelope_draw) { + const float *rad_tail = eBone ? &eBone->rad_tail : &pchan->bone->rad_tail; + drw_shgroup_bone_envelope(ctx, + BONE_VAR(eBone, pchan, disp_mat), + col_solid_tail, + col_hint_tail, + col_wire_tail, + &envelope_ignore, + rad_tail); + } + else { + drw_shgroup_bone_point( + ctx, BONE_VAR(eBone, pchan, disp_tail_mat), col_solid_tail, col_hint_tail, col_wire_tail); + } + + if (select_id != -1) { + DRW_select_load_id(-1); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Draw Bones + * \{ */ + +static void draw_bone_custom_shape(ArmatureDrawContext *ctx, + EditBone *eBone, + bPoseChannel *pchan, + bArmature *arm, + const int boneflag, + const short constflag, + const int select_id) +{ + const float *col_solid = get_bone_solid_color(ctx, eBone, pchan, arm, boneflag, constflag); + const float *col_wire = get_bone_wire_color(ctx, eBone, pchan, arm, boneflag, constflag); + const float *col_hint = get_bone_hint_color(ctx, eBone, pchan, arm, boneflag, constflag); + const float(*disp_mat)[4] = pchan->disp_mat; + + if (select_id != -1) { + DRW_select_load_id(select_id | BONESEL_BONE); + } + + if (pchan->custom->type == OB_EMPTY) { + Object *ob = pchan->custom; + if (ob->empty_drawtype != OB_EMPTY_IMAGE) { + drw_shgroup_bone_custom_empty(ctx, disp_mat, col_wire, pchan->custom); + } + } + if ((boneflag & BONE_DRAWWIRE) == 0) { + drw_shgroup_bone_custom_solid(ctx, disp_mat, col_solid, col_hint, col_wire, pchan->custom); + } + else { + drw_shgroup_bone_custom_wire(ctx, disp_mat, col_wire, pchan->custom); + } + + if (select_id != -1) { + DRW_select_load_id(-1); + } +} + +static void draw_bone_envelope(ArmatureDrawContext *ctx, + EditBone *eBone, + bPoseChannel *pchan, + bArmature *arm, + const int boneflag, + const short constflag, + const int select_id) +{ + const float *col_solid = get_bone_solid_with_consts_color( + ctx, eBone, pchan, arm, boneflag, constflag); + const float *col_wire = get_bone_wire_color(ctx, eBone, pchan, arm, boneflag, constflag); + const float *col_hint = get_bone_hint_color(ctx, eBone, pchan, arm, boneflag, constflag); + + float *rad_head, *rad_tail, *distance; + if (eBone) { + rad_tail = &eBone->rad_tail; + distance = &eBone->dist; + rad_head = (eBone->parent && (boneflag & BONE_CONNECTED)) ? &eBone->parent->rad_tail : + &eBone->rad_head; + } + else { + rad_tail = &pchan->bone->rad_tail; + distance = &pchan->bone->dist; + rad_head = (pchan->parent && (boneflag & BONE_CONNECTED)) ? &pchan->parent->bone->rad_tail : + &pchan->bone->rad_head; + } + + if ((select_id == -1) && (boneflag & BONE_NO_DEFORM) == 0 && + ((boneflag & BONE_SELECTED) || (eBone && (boneflag & (BONE_ROOTSEL | BONE_TIPSEL))))) { + drw_shgroup_bone_envelope_distance( + ctx, BONE_VAR(eBone, pchan, disp_mat), rad_head, rad_tail, distance); + } + + if (select_id != -1) { + DRW_select_load_id(select_id | BONESEL_BONE); + } + + drw_shgroup_bone_envelope( + ctx, BONE_VAR(eBone, pchan, disp_mat), col_solid, col_hint, col_wire, rad_head, rad_tail); + + if (select_id != -1) { + DRW_select_load_id(-1); + } + + draw_points(ctx, eBone, pchan, arm, boneflag, constflag, select_id); +} + +static void draw_bone_line(ArmatureDrawContext *ctx, + EditBone *eBone, + bPoseChannel *pchan, + bArmature *arm, + const int boneflag, + const short constflag, + const int select_id) +{ + const float *col_bone = get_bone_solid_with_consts_color( + ctx, eBone, pchan, arm, boneflag, constflag); + const float *col_wire = get_bone_wire_color(ctx, eBone, pchan, arm, boneflag, constflag); + const float no_display[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + const float *col_head = no_display; + const float *col_tail = col_bone; + + if (ctx->const_color != NULL) { + col_wire = no_display; /* actually shrink the display. */ + col_bone = col_head = col_tail = ctx->const_color; + } + else { + if (eBone) { + if (eBone->flag & BONE_TIPSEL) { + col_tail = ctx->color.vertex_select; + } + if (boneflag & BONE_SELECTED) { + col_bone = ctx->color.edge_select; + } + col_wire = ctx->color.wire; + } + + /* Draw root point if we are not connected to our parent. */ + if (!(eBone ? (eBone->parent && (eBone->flag & BONE_CONNECTED)) : + (pchan->bone->parent && (pchan->bone->flag & BONE_CONNECTED)))) { + + if (eBone) { + col_head = (eBone->flag & BONE_ROOTSEL) ? ctx->color.vertex_select : col_bone; + } + else { + col_head = col_bone; + } + } + } + + if (select_id == -1) { + /* Not in selection mode, draw everything at once. */ + drw_shgroup_bone_stick( + ctx, BONE_VAR(eBone, pchan, disp_mat), col_wire, col_bone, col_head, col_tail); + } + else { + /* In selection mode, draw bone, root and tip separately. */ + DRW_select_load_id(select_id | BONESEL_BONE); + drw_shgroup_bone_stick( + ctx, BONE_VAR(eBone, pchan, disp_mat), col_wire, col_bone, no_display, no_display); + + if (col_head[3] > 0.0f) { + DRW_select_load_id(select_id | BONESEL_ROOT); + drw_shgroup_bone_stick( + ctx, BONE_VAR(eBone, pchan, disp_mat), col_wire, no_display, col_head, no_display); + } + + DRW_select_load_id(select_id | BONESEL_TIP); + drw_shgroup_bone_stick( + ctx, BONE_VAR(eBone, pchan, disp_mat), col_wire, no_display, no_display, col_tail); + + DRW_select_load_id(-1); + } +} + +static void draw_bone_wire(ArmatureDrawContext *ctx, + EditBone *eBone, + bPoseChannel *pchan, + bArmature *arm, + const int boneflag, + const short constflag, + const int select_id) +{ + const float *col_wire = get_bone_wire_color(ctx, eBone, pchan, arm, boneflag, constflag); + + if (select_id != -1) { + DRW_select_load_id(select_id | BONESEL_BONE); + } + + if (pchan) { + Mat4 *bbones_mat = (Mat4 *)pchan->draw_data->bbone_matrix; + BLI_assert(bbones_mat != NULL); + + for (int i = pchan->bone->segments; i--; bbones_mat++) { + drw_shgroup_bone_wire(ctx, bbones_mat->mat, col_wire); + } + } + else if (eBone) { + for (int i = 0; i < eBone->segments; i++) { + drw_shgroup_bone_wire(ctx, eBone->disp_bbone_mat[i], col_wire); + } + } + + if (select_id != -1) { + DRW_select_load_id(-1); + } + + if (eBone) { + draw_points(ctx, eBone, pchan, arm, boneflag, constflag, select_id); + } +} + +static void draw_bone_box(ArmatureDrawContext *ctx, + EditBone *eBone, + bPoseChannel *pchan, + bArmature *arm, + const int boneflag, + const short constflag, + const int select_id) +{ + const float *col_solid = get_bone_solid_with_consts_color( + ctx, eBone, pchan, arm, boneflag, constflag); + const float *col_wire = get_bone_wire_color(ctx, eBone, pchan, arm, boneflag, constflag); + const float *col_hint = get_bone_hint_color(ctx, eBone, pchan, arm, boneflag, constflag); + + if (select_id != -1) { + DRW_select_load_id(select_id | BONESEL_BONE); + } + + if (pchan) { + Mat4 *bbones_mat = (Mat4 *)pchan->draw_data->bbone_matrix; + BLI_assert(bbones_mat != NULL); + + for (int i = pchan->bone->segments; i--; bbones_mat++) { + drw_shgroup_bone_box(ctx, bbones_mat->mat, col_solid, col_hint, col_wire); + } + } + else if (eBone) { + for (int i = 0; i < eBone->segments; i++) { + drw_shgroup_bone_box(ctx, eBone->disp_bbone_mat[i], col_solid, col_hint, col_wire); + } + } + + if (select_id != -1) { + DRW_select_load_id(-1); + } + + if (eBone) { + draw_points(ctx, eBone, pchan, arm, boneflag, constflag, select_id); + } +} + +static void draw_bone_octahedral(ArmatureDrawContext *ctx, + EditBone *eBone, + bPoseChannel *pchan, + bArmature *arm, + const int boneflag, + const short constflag, + const int select_id) +{ + const float *col_solid = get_bone_solid_with_consts_color( + ctx, eBone, pchan, arm, boneflag, constflag); + const float *col_wire = get_bone_wire_color(ctx, eBone, pchan, arm, boneflag, constflag); + const float *col_hint = get_bone_hint_color(ctx, eBone, pchan, arm, boneflag, constflag); + + if (select_id != -1) { + DRW_select_load_id(select_id | BONESEL_BONE); + } + + drw_shgroup_bone_octahedral( + ctx, BONE_VAR(eBone, pchan, disp_mat), col_solid, col_hint, col_wire); + + if (select_id != -1) { + DRW_select_load_id(-1); + } + + draw_points(ctx, eBone, pchan, arm, boneflag, constflag, select_id); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Draw Degrees of Freedom + * \{ */ + +static void draw_bone_degrees_of_freedom(ArmatureDrawContext *ctx, bPoseChannel *pchan) +{ + BoneInstanceData inst_data; + float tmp[4][4], posetrans[4][4]; + float xminmax[2], zminmax[2]; + float color[4]; + + if (ctx->dof_sphere == NULL) { + return; + } + + /* *0.5f here comes from M_PI/360.0f when rotations were still in degrees */ + xminmax[0] = sinf(pchan->limitmin[0] * 0.5f); + xminmax[1] = sinf(pchan->limitmax[0] * 0.5f); + zminmax[0] = sinf(pchan->limitmin[2] * 0.5f); + zminmax[1] = sinf(pchan->limitmax[2] * 0.5f); + + unit_m4(posetrans); + translate_m4(posetrans, pchan->pose_mat[3][0], pchan->pose_mat[3][1], pchan->pose_mat[3][2]); + /* in parent-bone pose space... */ + if (pchan->parent) { + copy_m4_m4(tmp, pchan->parent->pose_mat); + zero_v3(tmp[3]); + mul_m4_m4m4(posetrans, posetrans, tmp); + } + /* ... but own restspace */ + mul_m4_m4m3(posetrans, posetrans, pchan->bone->bone_mat); + + float scale = pchan->bone->length * pchan->size[1]; + scale_m4_fl(tmp, scale); + tmp[1][1] = -tmp[1][1]; + mul_m4_m4m4(posetrans, posetrans, tmp); + + /* into world space. */ + mul_m4_m4m4(inst_data.mat, ctx->ob->obmat, posetrans); + + if ((pchan->ikflag & BONE_IK_XLIMIT) && (pchan->ikflag & BONE_IK_ZLIMIT)) { + bone_instance_data_set_angle_minmax( + &inst_data, xminmax[0], zminmax[0], xminmax[1], zminmax[1]); + + copy_v4_fl4(color, 0.25f, 0.25f, 0.25f, 0.25f); + DRW_buffer_add_entry(ctx->dof_sphere, color, &inst_data); + + copy_v4_fl4(color, 0.0f, 0.0f, 0.0f, 1.0f); + DRW_buffer_add_entry(ctx->dof_lines, color, &inst_data); + } + if (pchan->ikflag & BONE_IK_XLIMIT) { + bone_instance_data_set_angle_minmax(&inst_data, xminmax[0], 0.0f, xminmax[1], 0.0f); + copy_v4_fl4(color, 1.0f, 0.0f, 0.0f, 1.0f); + DRW_buffer_add_entry(ctx->dof_lines, color, &inst_data); + } + if (pchan->ikflag & BONE_IK_ZLIMIT) { + bone_instance_data_set_angle_minmax(&inst_data, 0.0f, zminmax[0], 0.0f, zminmax[1]); + copy_v4_fl4(color, 0.0f, 0.0f, 1.0f, 1.0f); + DRW_buffer_add_entry(ctx->dof_lines, color, &inst_data); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Draw Relationships + * \{ */ + +static void pchan_draw_ik_lines(ArmatureDrawContext *ctx, + bPoseChannel *pchan, + const bool only_temp, + const int constflag) +{ + bConstraint *con; + bPoseChannel *parchan; + float *line_start = NULL, *line_end = NULL; + + for (con = pchan->constraints.first; con; con = con->next) { + if (con->enforce == 0.0f) { + continue; + } + + switch (con->type) { + case CONSTRAINT_TYPE_KINEMATIC: { + bKinematicConstraint *data = (bKinematicConstraint *)con->data; + int segcount = 0; + + /* if only_temp, only draw if it is a temporary ik-chain */ + if (only_temp && !(data->flag & CONSTRAINT_IK_TEMP)) { + continue; + } + + /* exclude tip from chain? */ + parchan = ((data->flag & CONSTRAINT_IK_TIP) == 0) ? pchan->parent : pchan; + line_start = parchan->pose_tail; + + /* Find the chain's root */ + while (parchan->parent) { + segcount++; + if (segcount == data->rootbone || segcount > 255) { + break; /* 255 is weak */ + } + parchan = parchan->parent; + } + + if (parchan) { + line_end = parchan->pose_head; + + if (constflag & PCHAN_HAS_TARGET) { + drw_shgroup_bone_ik_lines(ctx, line_start, line_end); + } + else { + drw_shgroup_bone_ik_no_target_lines(ctx, line_start, line_end); + } + } + break; + } + case CONSTRAINT_TYPE_SPLINEIK: { + bSplineIKConstraint *data = (bSplineIKConstraint *)con->data; + int segcount = 0; + + /* don't draw if only_temp, as Spline IK chains cannot be temporary */ + if (only_temp) { + continue; + } + + parchan = pchan; + line_start = parchan->pose_tail; + + /* Find the chain's root */ + while (parchan->parent) { + segcount++; + /* FIXME: revise the breaking conditions */ + if (segcount == data->chainlen || segcount > 255) { + break; /* 255 is weak */ + } + parchan = parchan->parent; + } + /* Only draw line in case our chain is more than one bone long! */ + if (parchan != pchan) { /* XXX revise the breaking conditions to only stop at the tail? */ + line_end = parchan->pose_head; + drw_shgroup_bone_ik_spline_lines(ctx, line_start, line_end); + } + break; + } + } + } +} + +static void draw_bone_relations(ArmatureDrawContext *ctx, + EditBone *ebone, + bPoseChannel *pchan, + bArmature *arm, + const int boneflag, + const short constflag) +{ + if (ebone && ebone->parent) { + if (ctx->do_relations) { + /* Always draw for unconnected bones, regardless of selection, + * since riggers will want to know about the links between bones + */ + if ((boneflag & BONE_CONNECTED) == 0) { + drw_shgroup_bone_relationship_lines(ctx, ebone->head, ebone->parent->tail); + } + } + } + else if (pchan && pchan->parent) { + if (ctx->do_relations) { + /* Only draw if bone or its parent is selected - reduces viewport complexity with complex + * rigs */ + if ((boneflag & BONE_SELECTED) || + (pchan->parent->bone && (pchan->parent->bone->flag & BONE_SELECTED))) { + if ((boneflag & BONE_CONNECTED) == 0) { + drw_shgroup_bone_relationship_lines(ctx, pchan->pose_head, pchan->parent->pose_tail); + } + } + } + + /* Draw a line to IK root bone if bone is selected. */ + if (arm->flag & ARM_POSEMODE) { + if (constflag & (PCHAN_HAS_IK | PCHAN_HAS_SPLINEIK)) { + if (boneflag & BONE_SELECTED) { + pchan_draw_ik_lines(ctx, pchan, !ctx->do_relations, constflag); + } + } + } + } +} + +static void draw_bone_name(ArmatureDrawContext *ctx, + EditBone *eBone, + bPoseChannel *pchan, + bArmature *arm, + const int boneflag) +{ + struct DRWTextStore *dt = DRW_text_cache_ensure(); + uchar color[4]; + float vec[3]; + + bool highlight = (pchan && (arm->flag & ARM_POSEMODE) && (boneflag & BONE_SELECTED)) || + (eBone && (eBone->flag & BONE_SELECTED)); + + UI_GetThemeColor4ubv(highlight ? TH_TEXT_HI : TH_TEXT, color); + + float *head = pchan ? pchan->pose_head : eBone->head; + float *tail = pchan ? pchan->pose_tail : eBone->tail; + mid_v3_v3v3(vec, head, tail); + mul_m4_v3(ctx->ob->obmat, vec); + + DRW_text_cache_add(dt, + vec, + (pchan) ? pchan->name : eBone->name, + (pchan) ? strlen(pchan->name) : strlen(eBone->name), + 10, + 0, + DRW_TEXT_CACHE_GLOBALSPACE | DRW_TEXT_CACHE_STRING_PTR, + color); +} +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Main Draw Loops + * \{ */ + +static void draw_armature_edit(ArmatureDrawContext *ctx) +{ + Object *ob = ctx->ob; + EditBone *eBone; + int index; + const bool is_select = DRW_state_is_select(); + const bool show_text = DRW_state_show_text(); + + const Object *ob_orig = DEG_get_original_object(ob); + /* FIXME(campbell): We should be able to use the CoW object, + * however the active bone isn't updated. Long term solution is an 'EditArmature' struct. + * for now we can draw from the original armature. See: T66773. */ + // bArmature *arm = ob->data; + bArmature *arm = ob_orig->data; + + edbo_compute_bbone_child(arm); + + for (eBone = arm->edbo->first, index = ob_orig->runtime.select_id; eBone; + eBone = eBone->next, index += 0x10000) { + if (eBone->layer & arm->layer) { + if ((eBone->flag & BONE_HIDDEN_A) == 0) { + const int select_id = is_select ? index : (uint)-1; + const short constflag = 0; + + /* catch exception for bone with hidden parent */ + int boneflag = eBone->flag; + if ((eBone->parent) && !EBONE_VISIBLE(arm, eBone->parent)) { + boneflag &= ~BONE_CONNECTED; + } + + /* set temporary flag for drawing bone as active, but only if selected */ + if (eBone == arm->act_edbone) { + boneflag |= BONE_DRAW_ACTIVE; + } + + draw_bone_relations(ctx, eBone, NULL, arm, boneflag, constflag); + + if (arm->drawtype == ARM_ENVELOPE) { + draw_bone_update_disp_matrix_default(eBone, NULL); + draw_bone_envelope(ctx, eBone, NULL, arm, boneflag, constflag, select_id); + } + else if (arm->drawtype == ARM_LINE) { + draw_bone_update_disp_matrix_default(eBone, NULL); + draw_bone_line(ctx, eBone, NULL, arm, boneflag, constflag, select_id); + } + else if (arm->drawtype == ARM_WIRE) { + draw_bone_update_disp_matrix_bbone(eBone, NULL); + draw_bone_wire(ctx, eBone, NULL, arm, boneflag, constflag, select_id); + } + else if (arm->drawtype == ARM_B_BONE) { + draw_bone_update_disp_matrix_bbone(eBone, NULL); + draw_bone_box(ctx, eBone, NULL, arm, boneflag, constflag, select_id); + } + else { + draw_bone_update_disp_matrix_default(eBone, NULL); + draw_bone_octahedral(ctx, eBone, NULL, arm, boneflag, constflag, select_id); + } + + if (show_text && (arm->flag & ARM_DRAWNAMES)) { + draw_bone_name(ctx, eBone, NULL, arm, boneflag); + } + + if (arm->flag & ARM_DRAWAXES) { + draw_axes(ctx, eBone, NULL); + } + } + } + } +} + +static void draw_armature_pose(ArmatureDrawContext *ctx) +{ + Object *ob = ctx->ob; + const DRWContextState *draw_ctx = DRW_context_state_get(); + const Scene *scene = draw_ctx->scene; + bArmature *arm = ob->data; + bPoseChannel *pchan; + int index = -1; + const bool show_text = DRW_state_show_text(); + + /* We can't safely draw non-updated pose, might contain NULL bone pointers... */ + if (ob->pose->flag & POSE_RECALC) { + return; + } + + bool is_pose_select = false; + /* Object can be edited in the scene. */ + if ((ob->base_flag & (BASE_FROM_SET | BASE_FROM_DUPLI)) == 0) { + if ((draw_ctx->object_mode & OB_MODE_POSE) || (ob == draw_ctx->object_pose)) { + arm->flag |= ARM_POSEMODE; + } + is_pose_select = + /* If we're in pose-mode or object-mode with the ability to enter pose mode. */ + ( + /* Draw as if in pose mode (when selection is possible). */ + (arm->flag & ARM_POSEMODE) || + /* When we're in object mode, which may select bones. */ + ((ob->mode & OB_MODE_POSE) && + ( + /* Switch from object mode when object lock is disabled. */ + ((draw_ctx->object_mode == OB_MODE_OBJECT) && + (scene->toolsettings->object_flag & SCE_OBJECT_MODE_LOCK) == 0) || + /* Allow selection when in weight-paint mode + * (selection code ensures this wont become active). */ + ((draw_ctx->object_mode == OB_MODE_WEIGHT_PAINT) && + (draw_ctx->object_pose != NULL))))) && + DRW_state_is_select(); + + if (is_pose_select) { + const Object *ob_orig = DEG_get_original_object(ob); + index = ob_orig->runtime.select_id; + } + } + + for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next, index += 0x10000) { + Bone *bone = pchan->bone; + const bool bone_visible = (bone->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG)) == 0; + + if (bone_visible) { + if (bone->layer & arm->layer) { + const bool draw_dofs = !is_pose_select && ctx->show_relations && + (arm->flag & ARM_POSEMODE) && (bone->flag & BONE_SELECTED) && + ((ob->base_flag & BASE_FROM_DUPLI) == 0) && + (pchan->ikflag & (BONE_IK_XLIMIT | BONE_IK_ZLIMIT)); + const int select_id = is_pose_select ? index : (uint)-1; + const short constflag = pchan->constflag; + + pchan_draw_data_init(pchan); + + if (!ctx->const_color) { + set_pchan_colorset(ctx, ob, pchan); + } + + int boneflag = bone->flag; + /* catch exception for bone with hidden parent */ + boneflag = bone->flag; + if ((bone->parent) && (bone->parent->flag & (BONE_HIDDEN_P | BONE_HIDDEN_PG))) { + boneflag &= ~BONE_CONNECTED; + } + + /* set temporary flag for drawing bone as active, but only if selected */ + if (bone == arm->act_bone) { + boneflag |= BONE_DRAW_ACTIVE; + } + + draw_bone_relations(ctx, NULL, pchan, arm, boneflag, constflag); + + if ((pchan->custom) && !(arm->flag & ARM_NO_CUSTOM)) { + draw_bone_update_disp_matrix_custom(pchan); + draw_bone_custom_shape(ctx, NULL, pchan, arm, boneflag, constflag, select_id); + } + else if (arm->drawtype == ARM_ENVELOPE) { + draw_bone_update_disp_matrix_default(NULL, pchan); + draw_bone_envelope(ctx, NULL, pchan, arm, boneflag, constflag, select_id); + } + else if (arm->drawtype == ARM_LINE) { + draw_bone_update_disp_matrix_default(NULL, pchan); + draw_bone_line(ctx, NULL, pchan, arm, boneflag, constflag, select_id); + } + else if (arm->drawtype == ARM_WIRE) { + draw_bone_update_disp_matrix_bbone(NULL, pchan); + draw_bone_wire(ctx, NULL, pchan, arm, boneflag, constflag, select_id); + } + else if (arm->drawtype == ARM_B_BONE) { + draw_bone_update_disp_matrix_bbone(NULL, pchan); + draw_bone_box(ctx, NULL, pchan, arm, boneflag, constflag, select_id); + } + else { + draw_bone_update_disp_matrix_default(NULL, pchan); + draw_bone_octahedral(ctx, NULL, pchan, arm, boneflag, constflag, select_id); + } + + if (draw_dofs) { + draw_bone_degrees_of_freedom(ctx, pchan); + } + + if (show_text && (arm->flag & ARM_DRAWNAMES)) { + draw_bone_name(ctx, NULL, pchan, arm, boneflag); + } + + if (arm->flag & ARM_DRAWAXES) { + draw_axes(ctx, NULL, pchan); + } + } + } + } + + arm->flag &= ~ARM_POSEMODE; +} + +static void armature_context_setup(ArmatureDrawContext *ctx, + OVERLAY_PrivateData *pd, + Object *ob, + const bool do_envelope_dist, + const bool is_pose_mode, + float *const_color) +{ + const bool is_xray = (ob->dtx & OB_DRAWXRAY) != 0 || + (pd->armature.do_pose_fade_geom && is_pose_mode); + const bool is_filled = !pd->armature.transparent || do_envelope_dist; + bArmature *arm = ob->data; + OVERLAY_ArmatureCallBuffers *cb = &pd->armature_call_buffers[is_xray]; + + switch (arm->drawtype) { + case ARM_ENVELOPE: + ctx->envelope_outline = cb->envelope_outline; + ctx->envelope_solid = (is_filled) ? cb->envelope_solid : NULL; + ctx->envelope_distance = (do_envelope_dist) ? cb->envelope_distance : NULL; + break; + case ARM_LINE: + ctx->stick = cb->stick; + break; + case ARM_WIRE: + ctx->wire = cb->wire; + break; + case ARM_B_BONE: + ctx->outline = cb->box_outline; + ctx->solid = (is_filled) ? cb->box_solid : NULL; + break; + case ARM_OCTA: + ctx->outline = cb->octa_outline; + ctx->solid = (is_filled) ? cb->octa_solid : NULL; + break; + } + ctx->ob = ob; + ctx->extras = &pd->extra_call_buffers[is_xray]; + ctx->dof_lines = cb->dof_lines; + ctx->dof_sphere = cb->dof_sphere; + ctx->point_solid = (is_filled) ? cb->point_solid : NULL; + ctx->point_outline = cb->point_outline; + ctx->custom_solid = (is_filled) ? cb->custom_solid : NULL; + ctx->custom_outline = cb->custom_outline; + ctx->custom_wire = cb->custom_solid; /* Use same shader. */ + ctx->custom_shapes_ghash = cb->custom_shapes_ghash; + ctx->transparent = pd->armature.transparent; + ctx->show_relations = pd->armature.show_relations; + ctx->const_color = const_color; + ctx->const_wire = (((ob->base_flag & BASE_SELECTED) || (arm->drawtype == ARM_WIRE)) ? + 1.5f : + ((ctx->transparent) ? 1.0f : 0.0f)); + + /** See: 'set_pchan_color'*/ +#define NO_ALPHA(c) (((c)[3] = 1.0f), (c)) + + UI_GetThemeColor3fv(TH_SELECT, NO_ALPHA(ctx->color.select)); + UI_GetThemeColorShade3fv(TH_EDGE_SELECT, 60, NO_ALPHA(ctx->color.edge_select)); + UI_GetThemeColorShade3fv(TH_EDGE_SELECT, -20, NO_ALPHA(ctx->color.bone_select)); + UI_GetThemeColor3fv(TH_WIRE, NO_ALPHA(ctx->color.wire)); + UI_GetThemeColor3fv(TH_WIRE_EDIT, NO_ALPHA(ctx->color.wire_edit)); + UI_GetThemeColor3fv(TH_BONE_SOLID, NO_ALPHA(ctx->color.bone_solid)); + UI_GetThemeColorBlendShade3fv( + TH_WIRE_EDIT, TH_EDGE_SELECT, 0.15f, 0, NO_ALPHA(ctx->color.bone_active_unselect)); + UI_GetThemeColor3fv(TH_BONE_POSE, NO_ALPHA(ctx->color.bone_pose)); + UI_GetThemeColor3fv(TH_BONE_POSE_ACTIVE, NO_ALPHA(ctx->color.bone_pose_active)); + UI_GetThemeColorBlendShade3fv( + TH_WIRE, TH_BONE_POSE, 0.15f, 0, NO_ALPHA(ctx->color.bone_pose_active_unselect)); + UI_GetThemeColor3fv(TH_TEXT_HI, NO_ALPHA(ctx->color.text_hi)); + UI_GetThemeColor3fv(TH_TEXT, NO_ALPHA(ctx->color.text)); + UI_GetThemeColor3fv(TH_VERTEX_SELECT, NO_ALPHA(ctx->color.vertex_select)); + UI_GetThemeColor3fv(TH_VERTEX, NO_ALPHA(ctx->color.vertex)); + +#undef NO_ALPHA +} + +void OVERLAY_edit_armature_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + ArmatureDrawContext arm_ctx; + armature_context_setup(&arm_ctx, pd, ob, true, false, NULL); + draw_armature_edit(&arm_ctx); +} + +void OVERLAY_pose_armature_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + ArmatureDrawContext arm_ctx; + armature_context_setup(&arm_ctx, pd, ob, true, true, NULL); + draw_armature_pose(&arm_ctx); +} + +void OVERLAY_armature_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + OVERLAY_PrivateData *pd = vedata->stl->pd; + ArmatureDrawContext arm_ctx; + float *color; + DRW_object_wire_theme_get(ob, draw_ctx->view_layer, &color); + armature_context_setup(&arm_ctx, pd, ob, false, false, color); + draw_armature_pose(&arm_ctx); +} + +static bool POSE_is_driven_by_active_armature(Object *ob) +{ + Object *ob_arm = modifiers_isDeformedByArmature(ob); + if (ob_arm) { + const DRWContextState *draw_ctx = DRW_context_state_get(); + bool is_active = OVERLAY_armature_is_pose_mode(ob_arm, draw_ctx); + if (!is_active && ob_arm->proxy_from) { + is_active = OVERLAY_armature_is_pose_mode(ob_arm->proxy_from, draw_ctx); + } + return is_active; + } + else { + Object *ob_mesh_deform = modifiers_isDeformedByMeshDeform(ob); + if (ob_mesh_deform) { + /* Recursive. */ + return POSE_is_driven_by_active_armature(ob_mesh_deform); + } + } + return false; +} + +void OVERLAY_pose_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + + struct GPUBatch *geom = DRW_cache_object_surface_get(ob); + if (geom) { + if (POSE_is_driven_by_active_armature(ob)) { + DRW_shgroup_call(pd->armature_bone_select_act_grp, geom, ob); + } + else { + DRW_shgroup_call(pd->armature_bone_select_grp, geom, ob); + } + } +} + +void OVERLAY_armature_cache_finish(OVERLAY_Data *vedata) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + + for (int i = 0; i < 2; i++) { + if (pd->armature_call_buffers[i].custom_shapes_ghash) { + /* TODO(fclem): Do not free it for each frame but reuse it. Avoiding alloc cost. */ + BLI_ghash_free(pd->armature_call_buffers[i].custom_shapes_ghash, NULL, NULL); + } + } +} + +void OVERLAY_armature_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + + DRW_draw_pass(psl->armature_transp_ps); + DRW_draw_pass(psl->armature_ps[0]); +} + +void OVERLAY_armature_in_front_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + + if (psl->armature_bone_select_ps == NULL) { + DRW_draw_pass(psl->armature_ps[1]); + } +} + +void OVERLAY_pose_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + + if (psl->armature_bone_select_ps != NULL) { + DRW_draw_pass(psl->armature_bone_select_ps); + + if (DRW_state_is_fbo()) { + GPU_framebuffer_clear_depth(dfbl->default_fb, 1.0f); + } + /* Selection still works because we are drawing only the pose bones in this case. */ + + DRW_draw_pass(psl->armature_ps[1]); + } +}
\ No newline at end of file diff --git a/source/blender/draw/engines/overlay/overlay_edit_curve.c b/source/blender/draw/engines/overlay/overlay_edit_curve.c new file mode 100644 index 00000000000..81253347632 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_edit_curve.c @@ -0,0 +1,127 @@ +/* + * 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_curve_types.h" + +#include "overlay_private.h" + +void OVERLAY_edit_curve_cache_init(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + const DRWContextState *draw_ctx = DRW_context_state_get(); + View3D *v3d = draw_ctx->v3d; + DRWShadingGroup *grp; + GPUShader *sh; + DRWState state; + + pd->edit_curve.show_handles = (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_CU_HANDLES) != 0; + pd->shdata.edit_curve_normal_length = v3d->overlay.normals_length; + + /* Run Twice for in-front passes. */ + for (int i = 0; i < 2; i++) { + state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH; + state |= ((i == 0) ? DRW_STATE_DEPTH_LESS_EQUAL : DRW_STATE_DEPTH_ALWAYS); + DRW_PASS_CREATE(psl->edit_curve_wire_ps[i], state | pd->clipping_state); + + sh = OVERLAY_shader_edit_curve_wire(); + pd->edit_curve_normal_grp[i] = grp = DRW_shgroup_create(sh, psl->edit_curve_wire_ps[i]); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_float_copy(grp, "normalSize", pd->shdata.edit_curve_normal_length); + + pd->edit_curve_wire_grp[i] = grp = DRW_shgroup_create(sh, psl->edit_curve_wire_ps[i]); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_float_copy(grp, "normalSize", 0.0f); + } + { + state = DRW_STATE_WRITE_COLOR; + DRW_PASS_CREATE(psl->edit_curve_handle_ps, state | pd->clipping_state); + + sh = OVERLAY_shader_edit_curve_handle(); + pd->edit_curve_handle_grp = grp = DRW_shgroup_create(sh, psl->edit_curve_handle_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_bool_copy(grp, "showCurveHandles", pd->edit_curve.show_handles); + DRW_shgroup_state_enable(grp, DRW_STATE_BLEND_ALPHA); + + sh = OVERLAY_shader_edit_curve_point(); + pd->edit_curve_points_grp = grp = DRW_shgroup_create(sh, psl->edit_curve_handle_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + } +} + +void OVERLAY_edit_curve_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + bool draw_normals = (pd->overlay.edit_flag & V3D_OVERLAY_EDIT_CU_NORMALS) != 0; + bool do_xray = (ob->dtx & OB_DRAWXRAY) != 0; + + Curve *cu = ob->data; + struct GPUBatch *geom; + + geom = DRW_cache_curve_edge_wire_get(ob); + if (geom) { + DRW_shgroup_call_no_cull(pd->edit_curve_wire_grp[do_xray], geom, ob); + } + + if ((cu->flag & CU_3D) && draw_normals) { + geom = DRW_cache_curve_edge_normal_get(ob); + DRW_shgroup_call_instances(pd->edit_curve_normal_grp[do_xray], ob, geom, 3); + } + + geom = DRW_cache_curve_edge_overlay_get(ob); + if (geom) { + DRW_shgroup_call_no_cull(pd->edit_curve_handle_grp, geom, ob); + } + + geom = DRW_cache_curve_vert_overlay_get(ob, pd->edit_curve.show_handles); + if (geom) { + DRW_shgroup_call_no_cull(pd->edit_curve_points_grp, geom, ob); + } +} + +void OVERLAY_edit_surf_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + struct GPUBatch *geom; + + geom = DRW_cache_curve_edge_overlay_get(ob); + if (geom) { + DRW_shgroup_call_no_cull(pd->edit_curve_handle_grp, geom, ob); + } + + geom = DRW_cache_curve_vert_overlay_get(ob, false); + if (geom) { + DRW_shgroup_call_no_cull(pd->edit_curve_points_grp, geom, ob); + } +} + +void OVERLAY_edit_curve_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + + DRW_draw_pass(psl->edit_curve_wire_ps[0]); + DRW_draw_pass(psl->edit_curve_wire_ps[1]); + + DRW_draw_pass(psl->edit_curve_handle_ps); +}
\ No newline at end of file diff --git a/source/blender/draw/engines/overlay/overlay_edit_mesh.c b/source/blender/draw/engines/overlay/overlay_edit_mesh.c new file mode 100644 index 00000000000..e03ed7f2d00 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_edit_mesh.c @@ -0,0 +1,419 @@ +/* + * 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 "ED_view3d.h" + +#include "DNA_mesh_types.h" + +#include "BKE_editmesh.h" + +#include "draw_cache_impl.h" +#include "draw_manager_text.h" + +#include "overlay_private.h" + +#define OVERLAY_EDIT_TEXT \ + (V3D_OVERLAY_EDIT_EDGE_LEN | V3D_OVERLAY_EDIT_FACE_AREA | V3D_OVERLAY_EDIT_FACE_ANG | \ + V3D_OVERLAY_EDIT_EDGE_ANG | V3D_OVERLAY_EDIT_INDICES) + +void OVERLAY_edit_mesh_init(OVERLAY_Data *vedata) +{ + OVERLAY_TextureList *txl = vedata->txl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + const DRWContextState *draw_ctx = DRW_context_state_get(); + + pd->edit_mesh.do_zbufclip = XRAY_FLAG_ENABLED(draw_ctx->v3d); + + if (!pd->edit_mesh.do_zbufclip) { + /* Small texture which will have very small impact on rendertime. */ + DRW_texture_ensure_2d(&txl->dummy_depth_tx, 1, 1, GPU_DEPTH_COMPONENT24, 0); + } + + /* Create view with depth offset */ + DRWView *default_view = (DRWView *)DRW_view_default_get(); + /* Don't use AA view (pd->view_default) because edit mode already has anti-aliasing. */ + pd->view_edit_faces = default_view; + pd->view_edit_faces_cage = DRW_view_create_with_zoffset(default_view, draw_ctx->rv3d, 0.5f); + pd->view_edit_edges = DRW_view_create_with_zoffset(default_view, draw_ctx->rv3d, 1.0f); + pd->view_edit_verts = DRW_view_create_with_zoffset(default_view, draw_ctx->rv3d, 1.5f); +} + +void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata) +{ + OVERLAY_TextureList *txl = vedata->txl; + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + OVERLAY_ShadingData *shdata = &pd->shdata; + DRWShadingGroup *grp = NULL; + GPUShader *sh = NULL; + DRWState state = 0; + + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + + const DRWContextState *draw_ctx = DRW_context_state_get(); + ToolSettings *tsettings = draw_ctx->scene->toolsettings; + View3D *v3d = draw_ctx->v3d; + bool select_vert = pd->edit_mesh.select_vert = (tsettings->selectmode & SCE_SELECT_VERTEX) != 0; + bool select_face = pd->edit_mesh.select_face = (tsettings->selectmode & SCE_SELECT_FACE) != 0; + bool select_edge = pd->edit_mesh.select_edge = (tsettings->selectmode & SCE_SELECT_EDGE) != 0; + + bool do_occlude_wire = (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_OCCLUDE_WIRE) != 0; + bool show_face_dots = (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_FACE_DOT) != 0 || + pd->edit_mesh.do_zbufclip; + + pd->edit_mesh.ghost_ob = 0; + pd->edit_mesh.edit_ob = 0; + pd->edit_mesh.do_faces = true; + pd->edit_mesh.do_edges = true; + + int *mask = shdata->data_mask; + mask[0] = 0xFF; /* Face Flag */ + mask[1] = 0xFF; /* Edge Flag */ + + const int flag = pd->edit_mesh.flag = v3d->overlay.edit_flag; + + SET_FLAG_FROM_TEST(mask[0], flag & V3D_OVERLAY_EDIT_FACES, VFLAG_FACE_SELECTED); + SET_FLAG_FROM_TEST(mask[0], flag & V3D_OVERLAY_EDIT_FREESTYLE_FACE, VFLAG_FACE_FREESTYLE); + SET_FLAG_FROM_TEST(mask[1], flag & V3D_OVERLAY_EDIT_FREESTYLE_EDGE, VFLAG_EDGE_FREESTYLE); + SET_FLAG_FROM_TEST(mask[1], flag & V3D_OVERLAY_EDIT_SEAMS, VFLAG_EDGE_SEAM); + SET_FLAG_FROM_TEST(mask[1], flag & V3D_OVERLAY_EDIT_SHARP, VFLAG_EDGE_SHARP); + SET_FLAG_FROM_TEST(mask[2], flag & V3D_OVERLAY_EDIT_CREASES, 0xFF); + SET_FLAG_FROM_TEST(mask[3], flag & V3D_OVERLAY_EDIT_BWEIGHTS, 0xFF); + + if ((flag & V3D_OVERLAY_EDIT_FACES) == 0) { + pd->edit_mesh.do_faces = false; + pd->edit_mesh.do_zbufclip = false; + } + if ((flag & V3D_OVERLAY_EDIT_EDGES) == 0) { + if ((tsettings->selectmode & SCE_SELECT_EDGE) == 0) { + if ((v3d->shading.type < OB_SOLID) || (v3d->shading.flag & V3D_SHADING_XRAY)) { + /* Special case, when drawing wire, draw edges, see: T67637. */ + } + else { + pd->edit_mesh.do_edges = false; + } + } + } + + float backwire_opacity = (pd->edit_mesh.do_zbufclip) ? v3d->overlay.backwire_opacity : 1.0f; + float face_alpha = (do_occlude_wire || !pd->edit_mesh.do_faces) ? 0.0f : 1.0f; + GPUTexture **depth_tex = (pd->edit_mesh.do_zbufclip) ? &dtxl->depth : &txl->dummy_depth_tx; + + if (select_face && !pd->edit_mesh.do_faces && pd->edit_mesh.do_edges) { + /* Force display of face centers in this case because that's + * the only way to see if a face is selected. */ + show_face_dots = true; + } + + { + /* TODO(fclem) Shouldn't this be going into the paint overlay? */ + state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL; + DRW_PASS_CREATE(psl->edit_mesh_weight_ps, state | pd->clipping_state); + + sh = OVERLAY_shader_paint_weight(); + pd->edit_mesh_weight_grp = grp = DRW_shgroup_create(sh, psl->edit_mesh_weight_ps); + DRW_shgroup_uniform_float_copy(grp, "opacity", 1.0); + DRW_shgroup_uniform_bool_copy(grp, "drawContours", false); + DRW_shgroup_uniform_texture(grp, "colorramp", G_draw.weight_ramp); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + } + /* Run Twice for in-front passes. */ + for (int i = 0; i < 2; i++) { + /* Complementary Depth Pass */ + state = DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_CULL_BACK; + DRW_PASS_CREATE(psl->edit_mesh_depth_ps[i], state | pd->clipping_state); + + sh = OVERLAY_shader_depth_only(); + pd->edit_mesh_depth_grp[i] = DRW_shgroup_create(sh, psl->edit_mesh_depth_ps[i]); + } + { + /* Normals */ + state = DRW_STATE_WRITE_DEPTH | DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | + (pd->edit_mesh.do_zbufclip ? DRW_STATE_BLEND_ALPHA : 0); + DRW_PASS_CREATE(psl->edit_mesh_normals_ps, state | pd->clipping_state); + + sh = OVERLAY_shader_edit_mesh_normal(); + pd->edit_mesh_normals_grp = grp = DRW_shgroup_create(sh, psl->edit_mesh_normals_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_float_copy(grp, "normalSize", v3d->overlay.normals_length); + DRW_shgroup_uniform_float_copy(grp, "alpha", backwire_opacity); + DRW_shgroup_uniform_texture_ref(grp, "depthTex", depth_tex); + } + { + /* Mesh Analysis Pass */ + state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_BLEND_ALPHA; + DRW_PASS_CREATE(psl->edit_mesh_analysis_ps, state | pd->clipping_state); + + sh = OVERLAY_shader_edit_mesh_analysis(); + pd->edit_mesh_analysis_grp = grp = DRW_shgroup_create(sh, psl->edit_mesh_analysis_ps); + DRW_shgroup_uniform_texture(grp, "weightTex", G_draw.weight_ramp); + } + /* Run Twice for in-front passes. */ + for (int i = 0; i < 2; i++) { + GPUShader *edge_sh = OVERLAY_shader_edit_mesh_edge(!select_vert); + GPUShader *face_sh = OVERLAY_shader_edit_mesh_face(); + const bool do_zbufclip = (i == 0 && pd->edit_mesh.do_zbufclip); + DRWState state_common = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | + DRW_STATE_BLEND_ALPHA; + /* Faces */ + /* Cage geom needs to be offsetted to avoid Z-fighting. */ + for (int j = 0; j < 2; j++) { + DRWPass **edit_face_ps = (j == 0) ? &psl->edit_mesh_faces_ps[i] : + &psl->edit_mesh_faces_cage_ps[i]; + DRWShadingGroup **shgrp = (j == 0) ? &pd->edit_mesh_faces_grp[i] : + &pd->edit_mesh_faces_cage_grp[i]; + state = state_common; + DRW_PASS_CREATE(*edit_face_ps, state | pd->clipping_state); + + grp = *shgrp = DRW_shgroup_create(face_sh, *edit_face_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_ivec4(grp, "dataMask", mask, 1); + DRW_shgroup_uniform_float_copy(grp, "alpha", face_alpha); + DRW_shgroup_uniform_bool_copy(grp, "selectFaces", select_face); + } + + if (do_zbufclip) { + state_common |= DRW_STATE_WRITE_DEPTH; + // state_common &= ~DRW_STATE_BLEND_ALPHA; + } + + /* Edges */ + /* Change first vertex convention to match blender loop structure. */ + state = state_common | DRW_STATE_FIRST_VERTEX_CONVENTION; + DRW_PASS_CREATE(psl->edit_mesh_edges_ps[i], state | pd->clipping_state); + + grp = pd->edit_mesh_edges_grp[i] = DRW_shgroup_create(edge_sh, psl->edit_mesh_edges_ps[i]); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_ivec4(grp, "dataMask", mask, 1); + DRW_shgroup_uniform_float_copy(grp, "alpha", backwire_opacity); + DRW_shgroup_uniform_texture_ref(grp, "depthTex", depth_tex); + DRW_shgroup_uniform_bool_copy(grp, "selectEdges", pd->edit_mesh.do_edges || select_edge); + + /* Verts */ + DRW_PASS_CREATE(psl->edit_mesh_verts_ps[i], state | pd->clipping_state); + + if (select_vert) { + sh = OVERLAY_shader_edit_mesh_vert(); + grp = pd->edit_mesh_verts_grp[i] = DRW_shgroup_create(sh, psl->edit_mesh_verts_ps[i]); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_float_copy(grp, "alpha", backwire_opacity); + DRW_shgroup_uniform_texture_ref(grp, "depthTex", depth_tex); + + sh = OVERLAY_shader_edit_mesh_skin_root(); + grp = pd->edit_mesh_skin_roots_grp[i] = DRW_shgroup_create(sh, psl->edit_mesh_verts_ps[i]); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + } + /* Facedots */ + if (select_face && show_face_dots) { + sh = OVERLAY_shader_edit_mesh_facedot(); + grp = pd->edit_mesh_facedots_grp[i] = DRW_shgroup_create(sh, psl->edit_mesh_verts_ps[i]); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_float_copy(grp, "alpha", backwire_opacity); + DRW_shgroup_uniform_texture_ref(grp, "depthTex", depth_tex); + DRW_shgroup_state_enable(grp, DRW_STATE_WRITE_DEPTH); + } + else { + pd->edit_mesh_facedots_grp[i] = NULL; + } + } +} + +static void overlay_edit_mesh_add_ob_to_pass(OVERLAY_PrivateData *pd, Object *ob, bool in_front) +{ + struct GPUBatch *geom_tris, *geom_verts, *geom_edges, *geom_fcenter, *skin_roots, *circle; + DRWShadingGroup *vert_shgrp, *edge_shgrp, *fdot_shgrp, *face_shgrp, *skin_roots_shgrp; + + bool has_edit_mesh_cage = false; + bool has_skin_roots = false; + /* TODO: Should be its own function. */ + Mesh *me = (Mesh *)ob->data; + BMEditMesh *embm = me->edit_mesh; + if (embm) { + has_edit_mesh_cage = embm->mesh_eval_cage && (embm->mesh_eval_cage != embm->mesh_eval_final); + has_skin_roots = CustomData_get_offset(&embm->bm->vdata, CD_MVERT_SKIN) != -1; + } + + vert_shgrp = pd->edit_mesh_verts_grp[in_front]; + edge_shgrp = pd->edit_mesh_edges_grp[in_front]; + fdot_shgrp = pd->edit_mesh_facedots_grp[in_front]; + face_shgrp = (has_edit_mesh_cage) ? pd->edit_mesh_faces_cage_grp[in_front] : + pd->edit_mesh_faces_grp[in_front]; + skin_roots_shgrp = pd->edit_mesh_skin_roots_grp[in_front]; + + geom_edges = DRW_mesh_batch_cache_get_edit_edges(ob->data); + geom_tris = DRW_mesh_batch_cache_get_edit_triangles(ob->data); + DRW_shgroup_call_no_cull(edge_shgrp, geom_edges, ob); + DRW_shgroup_call_no_cull(face_shgrp, geom_tris, ob); + + if (pd->edit_mesh.select_vert) { + geom_verts = DRW_mesh_batch_cache_get_edit_vertices(ob->data); + DRW_shgroup_call_no_cull(vert_shgrp, geom_verts, ob); + + if (has_skin_roots) { + circle = DRW_cache_circle_get(); + skin_roots = DRW_mesh_batch_cache_get_edit_skin_roots(ob->data); + DRW_shgroup_call_instances_with_attribs(skin_roots_shgrp, ob, circle, skin_roots); + } + } + + if (fdot_shgrp) { + geom_fcenter = DRW_mesh_batch_cache_get_edit_facedots(ob->data); + DRW_shgroup_call_no_cull(fdot_shgrp, geom_fcenter, ob); + } +} + +void OVERLAY_edit_mesh_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + struct GPUBatch *geom = NULL; + + bool do_in_front = (ob->dtx & OB_DRAWXRAY) != 0; + bool do_occlude_wire = (pd->edit_mesh.flag & V3D_OVERLAY_EDIT_OCCLUDE_WIRE) != 0; + bool do_show_weight = (pd->edit_mesh.flag & V3D_OVERLAY_EDIT_WEIGHT) != 0; + bool do_show_mesh_analysis = (pd->edit_mesh.flag & V3D_OVERLAY_EDIT_STATVIS) != 0; + bool fnormals_do = (pd->edit_mesh.flag & V3D_OVERLAY_EDIT_FACE_NORMALS) != 0; + bool vnormals_do = (pd->edit_mesh.flag & V3D_OVERLAY_EDIT_VERT_NORMALS) != 0; + bool lnormals_do = (pd->edit_mesh.flag & V3D_OVERLAY_EDIT_LOOP_NORMALS) != 0; + + if (do_show_weight) { + geom = DRW_cache_mesh_surface_weights_get(ob); + DRW_shgroup_call_no_cull(pd->edit_mesh_weight_grp, geom, ob); + } + else if (do_show_mesh_analysis && !pd->xray_enabled) { + geom = DRW_cache_mesh_surface_mesh_analysis_get(ob); + if (geom) { + DRW_shgroup_call_no_cull(pd->edit_mesh_analysis_grp, geom, ob); + } + } + + if (do_occlude_wire || do_in_front) { + geom = DRW_cache_mesh_surface_get(ob); + DRW_shgroup_call_no_cull(pd->edit_mesh_depth_grp[do_in_front], geom, ob); + } + + if (vnormals_do || lnormals_do || fnormals_do) { + struct GPUBatch *normal_geom = DRW_cache_normal_arrow_get(); + if (vnormals_do) { + geom = DRW_mesh_batch_cache_get_edit_vnors(ob->data); + DRW_shgroup_call_instances_with_attribs(pd->edit_mesh_normals_grp, ob, normal_geom, geom); + } + if (lnormals_do) { + geom = DRW_mesh_batch_cache_get_edit_lnors(ob->data); + DRW_shgroup_call_instances_with_attribs(pd->edit_mesh_normals_grp, ob, normal_geom, geom); + } + if (fnormals_do) { + geom = DRW_mesh_batch_cache_get_edit_facedots(ob->data); + DRW_shgroup_call_instances_with_attribs(pd->edit_mesh_normals_grp, ob, normal_geom, geom); + } + } + + if (pd->edit_mesh.do_zbufclip) { + overlay_edit_mesh_add_ob_to_pass(pd, ob, false); + } + else { + overlay_edit_mesh_add_ob_to_pass(pd, ob, do_in_front); + } + + pd->edit_mesh.ghost_ob += (ob->dtx & OB_DRAWXRAY) ? 1 : 0; + pd->edit_mesh.edit_ob += 1; + + if (DRW_state_show_text() && (pd->edit_mesh.flag & OVERLAY_EDIT_TEXT)) { + const DRWContextState *draw_ctx = DRW_context_state_get(); + DRW_text_edit_mesh_measure_stats(draw_ctx->ar, draw_ctx->v3d, ob, &draw_ctx->scene->unit); + } +} + +static void overlay_edit_mesh_draw_components(OVERLAY_PassList *psl, + OVERLAY_PrivateData *pd, + bool in_front) +{ + DRW_view_set_active(pd->view_edit_faces); + DRW_draw_pass(psl->edit_mesh_faces_ps[in_front]); + + DRW_view_set_active(pd->view_edit_faces_cage); + DRW_draw_pass(psl->edit_mesh_faces_cage_ps[in_front]); + + DRW_view_set_active(pd->view_edit_edges); + DRW_draw_pass(psl->edit_mesh_edges_ps[in_front]); + + DRW_view_set_active(pd->view_edit_verts); + DRW_draw_pass(psl->edit_mesh_verts_ps[in_front]); + + DRW_view_set_active(pd->view_default); +} + +void OVERLAY_edit_mesh_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + OVERLAY_FramebufferList *fbl = vedata->fbl; + + DRW_draw_pass(psl->edit_mesh_weight_ps); + DRW_draw_pass(psl->edit_mesh_analysis_ps); + + DRW_draw_pass(psl->edit_mesh_depth_ps[NOT_IN_FRONT]); + + if (pd->edit_mesh.do_zbufclip) { + DRW_draw_pass(psl->edit_mesh_depth_ps[IN_FRONT]); + + /* render facefill */ + DRW_view_set_active(pd->view_edit_faces); + DRW_draw_pass(psl->edit_mesh_faces_ps[NOT_IN_FRONT]); + + DRW_view_set_active(pd->view_edit_faces_cage); + DRW_draw_pass(psl->edit_mesh_faces_cage_ps[NOT_IN_FRONT]); + + DRW_view_set_active(pd->view_default); + + GPU_framebuffer_bind(fbl->overlay_in_front_fb); + GPU_framebuffer_clear_depth(fbl->overlay_in_front_fb, 1.0f); + DRW_draw_pass(psl->edit_mesh_normals_ps); + + DRW_view_set_active(pd->view_edit_edges); + DRW_draw_pass(psl->edit_mesh_edges_ps[NOT_IN_FRONT]); + + DRW_view_set_active(pd->view_edit_verts); + DRW_draw_pass(psl->edit_mesh_verts_ps[NOT_IN_FRONT]); + + GPU_framebuffer_bind(fbl->overlay_default_fb); + } + else { + const DRWContextState *draw_ctx = DRW_context_state_get(); + View3D *v3d = draw_ctx->v3d; + + DRW_draw_pass(psl->edit_mesh_normals_ps); + overlay_edit_mesh_draw_components(psl, pd, false); + + if (v3d->shading.type == OB_SOLID && pd->edit_mesh.ghost_ob == 1 && + pd->edit_mesh.edit_ob == 1) { + /* In the case of single ghost object edit (common case for retopology): + * we clear the depth buffer so that only the depth of the retopo mesh + * is occluding the edit cage. */ + GPU_framebuffer_clear_depth(fbl->overlay_default_fb, 1.0f); + } + + DRW_draw_pass(psl->edit_mesh_depth_ps[IN_FRONT]); + overlay_edit_mesh_draw_components(psl, pd, true); + } +} diff --git a/source/blender/draw/engines/overlay/overlay_edit_text.c b/source/blender/draw/engines/overlay/overlay_edit_text.c new file mode 100644 index 00000000000..d8edf34168e --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_edit_text.c @@ -0,0 +1,201 @@ +/* + * 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 "BKE_font.h" + +#include "DNA_curve_types.h" + +#include "overlay_private.h" + +void OVERLAY_edit_text_cache_init(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + const DRWContextState *draw_ctx = DRW_context_state_get(); + View3D *v3d = draw_ctx->v3d; + DRWShadingGroup *grp; + GPUShader *sh; + DRWState state; + + pd->edit_curve.show_handles = (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_CU_HANDLES) != 0; + pd->shdata.edit_curve_normal_length = v3d->overlay.normals_length; + + /* Run Twice for in-front passes. */ + for (int i = 0; i < 2; i++) { + state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH; + state |= ((i == 0) ? DRW_STATE_DEPTH_LESS_EQUAL : DRW_STATE_DEPTH_ALWAYS); + DRW_PASS_CREATE(psl->edit_text_wire_ps[i], state | pd->clipping_state); + + sh = OVERLAY_shader_uniform_color(); + pd->edit_text_wire_grp[i] = grp = DRW_shgroup_create(sh, psl->edit_text_wire_ps[i]); + DRW_shgroup_uniform_vec4_copy(grp, "color", G_draw.block.colorWire); + } + { + state = DRW_STATE_WRITE_COLOR | DRW_STATE_LOGIC_INVERT; + DRW_PASS_CREATE(psl->edit_text_overlay_ps, state | pd->clipping_state); + + sh = OVERLAY_shader_uniform_color(); + pd->edit_text_overlay_grp = DRW_shgroup_create(sh, psl->edit_text_overlay_ps); + } +} + +/* Use 2D quad corners to create a matrix that set + * a [-1..1] quad at the right position. */ +static void v2_quad_corners_to_mat4(float corners[4][2], float r_mat[4][4]) +{ + unit_m4(r_mat); + sub_v2_v2v2(r_mat[0], corners[1], corners[0]); + sub_v2_v2v2(r_mat[1], corners[3], corners[0]); + mul_v2_fl(r_mat[0], 0.5f); + mul_v2_fl(r_mat[1], 0.5f); + copy_v2_v2(r_mat[3], corners[0]); + add_v2_v2(r_mat[3], r_mat[0]); + add_v2_v2(r_mat[3], r_mat[1]); +} + +static void edit_text_cache_populate_select(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + const Curve *cu = ob->data; + EditFont *ef = cu->editfont; + float final_mat[4][4], box[4][2]; + struct GPUBatch *geom = DRW_cache_quad_get(); + + for (int i = 0; i < ef->selboxes_len; i++) { + EditFontSelBox *sb = &ef->selboxes[i]; + + float selboxw; + if (i + 1 != ef->selboxes_len) { + if (ef->selboxes[i + 1].y == sb->y) { + selboxw = ef->selboxes[i + 1].x - sb->x; + } + else { + selboxw = sb->w; + } + } + else { + selboxw = sb->w; + } + /* NOTE: v2_quad_corners_to_mat4 don't need the 3rd corner. */ + if (sb->rot == 0.0f) { + copy_v2_fl2(box[0], sb->x, sb->y); + copy_v2_fl2(box[1], sb->x + selboxw, sb->y); + copy_v2_fl2(box[3], sb->x, sb->y + sb->h); + } + else { + float mat[2][2]; + angle_to_mat2(mat, sb->rot); + copy_v2_fl2(box[0], sb->x, sb->y); + mul_v2_v2fl(box[1], mat[0], selboxw); + add_v2_v2(box[1], &sb->x); + mul_v2_v2fl(box[3], mat[1], sb->h); + add_v2_v2(box[3], &sb->x); + } + v2_quad_corners_to_mat4(box, final_mat); + mul_m4_m4m4(final_mat, ob->obmat, final_mat); + + DRW_shgroup_call_obmat(pd->edit_text_overlay_grp, geom, final_mat); + } +} + +static void edit_text_cache_populate_cursor(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + const Curve *cu = ob->data; + EditFont *edit_font = cu->editfont; + float(*cursor)[2] = edit_font->textcurs; + float mat[4][4]; + + v2_quad_corners_to_mat4(cursor, mat); + mul_m4_m4m4(mat, ob->obmat, mat); + + struct GPUBatch *geom = DRW_cache_quad_get(); + DRW_shgroup_call_obmat(pd->edit_text_overlay_grp, geom, mat); +} + +static void edit_text_cache_populate_boxes(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_ExtraCallBuffers *cb = OVERLAY_extra_call_buffer_get(vedata, ob); + const Curve *cu = ob->data; + + for (int i = 0; i < cu->totbox; i++) { + TextBox *tb = &cu->tb[i]; + const bool is_active = (i == (cu->actbox - 1)); + float *color = is_active ? G_draw.block.colorActive : G_draw.block.colorWire; + + if ((tb->w != 0.0f) || (tb->h != 0.0f)) { + float vecs[4][3]; + vecs[0][0] = vecs[1][0] = vecs[2][0] = vecs[3][0] = cu->xof + tb->x; + vecs[0][1] = vecs[1][1] = vecs[2][1] = vecs[3][1] = cu->yof + tb->y + cu->fsize_realtime; + vecs[0][2] = vecs[1][2] = vecs[2][2] = vecs[3][2] = 0.001; + + vecs[1][0] += tb->w; + vecs[2][0] += tb->w; + vecs[2][1] -= tb->h; + vecs[3][1] -= tb->h; + + for (int j = 0; j < 4; j++) { + mul_v3_m4v3(vecs[j], ob->obmat, vecs[j]); + } + for (int j = 0; j < 4; j++) { + OVERLAY_extra_line_dashed(cb, vecs[j], vecs[(j + 1) % 4], color); + } + } + } +} + +void OVERLAY_edit_text_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + Curve *cu = ob->data; + struct GPUBatch *geom; + bool do_in_front = (ob->dtx & OB_DRAWXRAY) != 0; + + bool has_surface = (cu->flag & (CU_FRONT | CU_BACK)) || cu->ext1 != 0.0f || cu->ext2 != 0.0f; + if ((cu->flag & CU_FAST) || !has_surface) { + geom = DRW_cache_text_edge_wire_get(ob); + if (geom) { + DRW_shgroup_call(pd->edit_text_wire_grp[do_in_front], geom, ob); + } + } + else { + /* object mode draws */ + } + + edit_text_cache_populate_select(vedata, ob); + edit_text_cache_populate_cursor(vedata, ob); + edit_text_cache_populate_boxes(vedata, ob); +} + +void OVERLAY_edit_text_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + + DRW_view_set_active(NULL); + + DRW_draw_pass(psl->edit_text_wire_ps[0]); + DRW_draw_pass(psl->edit_text_wire_ps[1]); + + DRW_draw_pass(psl->edit_text_overlay_ps); +}
\ No newline at end of file diff --git a/source/blender/draw/engines/overlay/overlay_engine.c b/source/blender/draw/engines/overlay/overlay_engine.c new file mode 100644 index 00000000000..667172e5bb5 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_engine.c @@ -0,0 +1,491 @@ +/* + * 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 + * + * Engine for drawing a selection map where the pixels indicate the selection indices. + */ + +#include "DRW_engine.h" +#include "DRW_render.h" + +#include "ED_view3d.h" + +#include "BKE_object.h" + +#include "overlay_engine.h" +#include "overlay_private.h" + +/* -------------------------------------------------------------------- */ +/** \name Engine Callbacks + * \{ */ + +static void OVERLAY_engine_init(void *vedata) +{ + OVERLAY_Data *data = vedata; + OVERLAY_StorageList *stl = data->stl; + const DRWContextState *draw_ctx = DRW_context_state_get(); + const RegionView3D *rv3d = draw_ctx->rv3d; + const View3D *v3d = draw_ctx->v3d; + + if (!stl->pd) { + /* Alloc transient pointers */ + stl->pd = MEM_callocN(sizeof(*stl->pd), __func__); + } + + OVERLAY_PrivateData *pd = stl->pd; + View3DOverlay overlay; + short v3d_flag, v3d_gridflag; + + pd->hide_overlays = (v3d->flag2 & V3D_HIDE_OVERLAYS) != 0; + pd->ctx_mode = CTX_data_mode_enum_ex( + draw_ctx->object_edit, draw_ctx->obact, draw_ctx->object_mode); + + if (!pd->hide_overlays) { + overlay = v3d->overlay; + v3d_flag = v3d->flag; + v3d_gridflag = v3d->gridflag; + } + else { + memset(&overlay, 0, sizeof(overlay)); + v3d_flag = 0; + v3d_gridflag = 0; + overlay.flag = V3D_OVERLAY_HIDE_TEXT | V3D_OVERLAY_HIDE_MOTION_PATHS | V3D_OVERLAY_HIDE_BONES | + V3D_OVERLAY_HIDE_OBJECT_XTRAS | V3D_OVERLAY_HIDE_OBJECT_ORIGINS; + } + + if (v3d->shading.type == OB_WIRE) { + overlay.flag |= V3D_OVERLAY_WIREFRAMES; + } + + /* Check if anything changed, and if so, reset AA. */ + if (v3d_flag != pd->v3d_flag || pd->v3d_gridflag != v3d_gridflag || + memcmp(&pd->overlay, &overlay, sizeof(overlay))) { + pd->overlay = overlay; + pd->v3d_flag = v3d_flag; + pd->v3d_gridflag = v3d_gridflag; + OVERLAY_antialiasing_reset(vedata); + } + + pd->wireframe_mode = (v3d->shading.type == OB_WIRE); + pd->clipping_state = (rv3d->rflag & RV3D_CLIPPING) ? DRW_STATE_CLIP_PLANES : 0; + 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); + + OVERLAY_antialiasing_init(vedata); + + switch (stl->pd->ctx_mode) { + case CTX_MODE_EDIT_MESH: + OVERLAY_edit_mesh_init(vedata); + break; + default: + /* Nothing to do. */ + break; + } + OVERLAY_facing_init(vedata); + OVERLAY_grid_init(vedata); + OVERLAY_image_init(vedata); + OVERLAY_outline_init(vedata); + OVERLAY_wireframe_init(vedata); +} + +static void OVERLAY_cache_init(void *vedata) +{ + OVERLAY_Data *data = vedata; + OVERLAY_StorageList *stl = data->stl; + OVERLAY_PrivateData *pd = stl->pd; + + switch (pd->ctx_mode) { + case CTX_MODE_EDIT_MESH: + OVERLAY_edit_mesh_cache_init(vedata); + break; + case CTX_MODE_EDIT_SURFACE: + case CTX_MODE_EDIT_CURVE: + OVERLAY_edit_curve_cache_init(vedata); + break; + case CTX_MODE_EDIT_TEXT: + OVERLAY_edit_text_cache_init(vedata); + break; + case CTX_MODE_EDIT_ARMATURE: + break; + case CTX_MODE_EDIT_METABALL: + break; + case CTX_MODE_EDIT_LATTICE: + OVERLAY_edit_lattice_cache_init(vedata); + break; + case CTX_MODE_PARTICLE: + OVERLAY_edit_particle_cache_init(vedata); + break; + case CTX_MODE_POSE: + case CTX_MODE_PAINT_WEIGHT: + case CTX_MODE_PAINT_VERTEX: + case CTX_MODE_PAINT_TEXTURE: + OVERLAY_paint_cache_init(vedata); + break; + 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_SCULPT_GPENCIL: + case CTX_MODE_WEIGHT_GPENCIL: + break; + default: + BLI_assert(!"Draw mode invalid"); + break; + } + OVERLAY_antialiasing_cache_init(vedata); + OVERLAY_armature_cache_init(vedata); + OVERLAY_extra_cache_init(vedata); + OVERLAY_facing_cache_init(vedata); + OVERLAY_grid_cache_init(vedata); + OVERLAY_image_cache_init(vedata); + OVERLAY_metaball_cache_init(vedata); + OVERLAY_motion_path_cache_init(vedata); + OVERLAY_outline_cache_init(vedata); + OVERLAY_particle_cache_init(vedata); + OVERLAY_wireframe_cache_init(vedata); +} + +BLI_INLINE OVERLAY_DupliData *OVERLAY_duplidata_get(Object *ob, void *vedata, bool *do_init) +{ + OVERLAY_DupliData **dupli_data = (OVERLAY_DupliData **)DRW_duplidata_get(vedata); + *do_init = false; + if (!ELEM(ob->type, OB_MESH, OB_SURF, OB_LATTICE, OB_CURVE, OB_FONT)) { + return NULL; + } + + if (dupli_data) { + if (*dupli_data == NULL) { + *dupli_data = MEM_callocN(sizeof(OVERLAY_DupliData), __func__); + *do_init = true; + } + else if ((*dupli_data)->base_flag != ob->base_flag) { + /* Select state might have change, reinit. */ + *do_init = true; + } + return *dupli_data; + } + return NULL; +} + +static void OVERLAY_cache_populate(void *vedata, Object *ob) +{ + OVERLAY_Data *data = vedata; + OVERLAY_PrivateData *pd = data->stl->pd; + const DRWContextState *draw_ctx = DRW_context_state_get(); + const bool is_select = DRW_state_is_select(); + const bool renderable = DRW_object_is_renderable(ob); + const bool in_pose_mode = ob->type == OB_ARMATURE && OVERLAY_armature_is_pose_mode(ob, draw_ctx); + const bool in_edit_mode = BKE_object_is_in_editmode(ob); + const bool in_particle_edit_mode = ob->mode == OB_MODE_PARTICLE_EDIT; + 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 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 && + (pd->wireframe_mode || !pd->hide_overlays); + const bool draw_outlines = !in_edit_mode && !in_paint_mode && renderable && + (pd->v3d_flag & V3D_SELECT_OUTLINE) && + ((ob->base_flag & BASE_SELECTED) || + (is_select && ob->type == OB_LIGHTPROBE)); + const bool draw_bone_selection = (ob->type == OB_MESH) && pd->armature.do_pose_fade_geom && + !is_select; + const bool draw_extras = + ((pd->overlay.flag & V3D_OVERLAY_HIDE_OBJECT_XTRAS) == 0) || + /* Show if this is the camera we're looking through since it's useful for selecting. */ + ((draw_ctx->rv3d->persp == RV3D_CAMOB) && ((ID *)draw_ctx->v3d->camera == ob->id.orig_id)); + + const bool draw_motion_paths = (pd->overlay.flag & V3D_OVERLAY_HIDE_MOTION_PATHS) == 0; + + bool do_init; + OVERLAY_DupliData *dupli = OVERLAY_duplidata_get(ob, vedata, &do_init); + + if (draw_facing) { + OVERLAY_facing_cache_populate(vedata, ob); + } + if (draw_wires) { + OVERLAY_wireframe_cache_populate(vedata, ob, dupli, do_init); + } + if (draw_outlines) { + OVERLAY_outline_cache_populate(vedata, ob, dupli, do_init); + } + if (draw_bone_selection) { + OVERLAY_pose_cache_populate(vedata, ob); + } + + if (in_edit_mode) { + switch (ob->type) { + case OB_MESH: + OVERLAY_edit_mesh_cache_populate(vedata, ob); + break; + case OB_ARMATURE: + if (draw_bones) { + OVERLAY_edit_armature_cache_populate(vedata, ob); + } + break; + case OB_CURVE: + OVERLAY_edit_curve_cache_populate(vedata, ob); + break; + case OB_SURF: + OVERLAY_edit_surf_cache_populate(vedata, ob); + break; + case OB_LATTICE: + OVERLAY_edit_lattice_cache_populate(vedata, ob); + break; + case OB_MBALL: + OVERLAY_edit_metaball_cache_populate(vedata, ob); + break; + case OB_FONT: + OVERLAY_edit_text_cache_populate(vedata, ob); + break; + } + } + else if (in_pose_mode && draw_bones) { + OVERLAY_pose_armature_cache_populate(vedata, ob); + } + else if (in_paint_mode) { + switch (draw_ctx->object_mode) { + case OB_MODE_VERTEX_PAINT: + OVERLAY_paint_vertex_cache_populate(vedata, ob); + break; + case OB_MODE_WEIGHT_PAINT: + OVERLAY_paint_weight_cache_populate(vedata, ob); + break; + case OB_MODE_TEXTURE_PAINT: + OVERLAY_paint_texture_cache_populate(vedata, ob); + break; + default: + break; + } + } + else if (in_particle_edit_mode) { + OVERLAY_edit_particle_cache_populate(vedata, ob); + } + + if (in_sculpt_mode) { + OVERLAY_sculpt_cache_populate(vedata, ob); + } + + if (draw_motion_paths) { + OVERLAY_motion_path_cache_populate(vedata, ob); + } + + switch (ob->type) { + case OB_ARMATURE: + if (draw_bones && (is_select || (!in_edit_mode && !in_pose_mode))) { + OVERLAY_armature_cache_populate(vedata, ob); + } + break; + case OB_MBALL: + if (!in_edit_mode) { + OVERLAY_metaball_cache_populate(vedata, ob); + } + break; + case OB_GPENCIL: + OVERLAY_gpencil_cache_populate(vedata, ob); + break; + } + /* Non-Meshes */ + if (draw_extras) { + switch (ob->type) { + case OB_EMPTY: + OVERLAY_empty_cache_populate(vedata, ob); + break; + case OB_LAMP: + OVERLAY_light_cache_populate(vedata, ob); + break; + case OB_CAMERA: + OVERLAY_camera_cache_populate(vedata, ob); + break; + case OB_SPEAKER: + OVERLAY_speaker_cache_populate(vedata, ob); + break; + case OB_LIGHTPROBE: + OVERLAY_lightprobe_cache_populate(vedata, ob); + break; + case OB_LATTICE: + OVERLAY_lattice_cache_populate(vedata, ob); + break; + } + } + + if (!BLI_listbase_is_empty(&ob->particlesystem)) { + OVERLAY_particle_cache_populate(vedata, ob); + } + + /* Relationship, object center, bounbox ... */ + OVERLAY_extra_cache_populate(vedata, ob); + + if (dupli) { + dupli->base_flag = ob->base_flag; + } +} + +static void OVERLAY_cache_finish(void *vedata) +{ + /* TODO(fclem) Only do this when really needed. */ + { + /* HACK we allocate the infront depth here to avoid the overhead when if is not needed. */ + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + + DRW_texture_ensure_fullscreen_2d(&dtxl->depth_in_front, GPU_DEPTH24_STENCIL8, 0); + + GPU_framebuffer_ensure_config( + &dfbl->default_fb, + {GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(dtxl->color)}); + GPU_framebuffer_ensure_config( + &dfbl->in_front_fb, + {GPU_ATTACHMENT_TEXTURE(dtxl->depth_in_front), GPU_ATTACHMENT_TEXTURE(dtxl->color)}); + } + + OVERLAY_antialiasing_cache_finish(vedata); + OVERLAY_armature_cache_finish(vedata); + OVERLAY_image_cache_finish(vedata); +} + +static void OVERLAY_draw_scene(void *vedata) +{ + OVERLAY_Data *data = vedata; + OVERLAY_PrivateData *pd = data->stl->pd; + OVERLAY_FramebufferList *fbl = data->fbl; + + OVERLAY_antialiasing_start(vedata); + + DRW_view_set_active(pd->view_default); + + OVERLAY_image_draw(vedata); + OVERLAY_facing_draw(vedata); + OVERLAY_wireframe_draw(vedata); + OVERLAY_armature_draw(vedata); + OVERLAY_particle_draw(vedata); + OVERLAY_metaball_draw(vedata); + OVERLAY_extra_draw(vedata); + + DRW_view_set_active(NULL); + + OVERLAY_grid_draw(vedata); + OVERLAY_outline_draw(vedata); + + DRW_view_set_active(pd->view_default); + + if (DRW_state_is_fbo()) { + GPU_framebuffer_bind(fbl->overlay_in_front_fb); + + /* If we are not in solid shading mode, we clear the depth. */ + if (pd->clear_in_front) { + /* TODO(fclem) This clear should be done in a global place. */ + GPU_framebuffer_clear_depth(fbl->overlay_in_front_fb, 1.0f); + } + } + + OVERLAY_wireframe_in_front_draw(vedata); + OVERLAY_armature_in_front_draw(vedata); + OVERLAY_extra_in_front_draw(vedata); + OVERLAY_metaball_in_front_draw(vedata); + OVERLAY_image_in_front_draw(vedata); + + if (DRW_state_is_fbo()) { + GPU_framebuffer_bind(fbl->overlay_default_fb); + } + + OVERLAY_motion_path_draw(vedata); + OVERLAY_extra_centers_draw(vedata); + + switch (pd->ctx_mode) { + case CTX_MODE_EDIT_MESH: + OVERLAY_edit_mesh_draw(vedata); + break; + case CTX_MODE_EDIT_SURFACE: + case CTX_MODE_EDIT_CURVE: + OVERLAY_edit_curve_draw(vedata); + break; + case CTX_MODE_EDIT_TEXT: + /* Text overlay need final color for color inversion. */ + OVERLAY_antialiasing_end(vedata); + OVERLAY_edit_text_draw(vedata); + return; /* WATCH! dont do AA twice. */ + case CTX_MODE_EDIT_LATTICE: + OVERLAY_edit_lattice_draw(vedata); + break; + case CTX_MODE_POSE: + /* Pain overlay needs final color because of multiply blend mode. */ + OVERLAY_antialiasing_end(vedata); + OVERLAY_paint_draw(vedata); + OVERLAY_pose_draw(vedata); + return; /* WATCH! dont do AA twice. */ + case CTX_MODE_PAINT_WEIGHT: + case CTX_MODE_PAINT_VERTEX: + case CTX_MODE_PAINT_TEXTURE: + /* Pain overlay need final color because of multiply blend mode. */ + OVERLAY_antialiasing_end(vedata); + OVERLAY_paint_draw(vedata); + return; /* WATCH! dont do AA twice. */ + case CTX_MODE_PARTICLE: + OVERLAY_edit_particle_draw(vedata); + break; + case CTX_MODE_SCULPT: + OVERLAY_sculpt_draw(vedata); + break; + default: + break; + } + + OVERLAY_antialiasing_end(vedata); +} + +static void OVERLAY_engine_free(void) +{ + OVERLAY_shader_free(); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Engine Type + * \{ */ + +static const DrawEngineDataSize overlay_data_size = DRW_VIEWPORT_DATA_SIZE(OVERLAY_Data); + +DrawEngineType draw_engine_overlay_type = { + NULL, + NULL, + N_("Overlay"), + &overlay_data_size, + &OVERLAY_engine_init, + &OVERLAY_engine_free, + &OVERLAY_cache_init, + &OVERLAY_cache_populate, + &OVERLAY_cache_finish, + NULL, + &OVERLAY_draw_scene, + NULL, + NULL, + NULL, +}; + +/** \} */ + +#undef SELECT_ENGINE diff --git a/source/blender/draw/engines/overlay/overlay_engine.h b/source/blender/draw/engines/overlay/overlay_engine.h new file mode 100644 index 00000000000..795e3805037 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_engine.h @@ -0,0 +1,28 @@ +/* + * 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 + */ + +#ifndef __OVERLAY_ENGINE_H__ +#define __OVERLAY_ENGINE_H__ + +extern DrawEngineType draw_engine_overlay_type; + +#endif /* __OVERLAY_ENGINE_H__ */ diff --git a/source/blender/draw/engines/overlay/overlay_extra.c b/source/blender/draw/engines/overlay/overlay_extra.c new file mode 100644 index 00000000000..d088142bdef --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_extra.c @@ -0,0 +1,1586 @@ +/* + * 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 "UI_resources.h" + +#include "BKE_anim.h" +#include "BKE_camera.h" +#include "BKE_constraint.h" +#include "BKE_curve.h" +#include "BKE_mball.h" +#include "BKE_mesh.h" +#include "BKE_movieclip.h" +#include "BKE_modifier.h" +#include "BKE_object.h" +#include "BKE_tracking.h" + +#include "DNA_camera_types.h" +#include "DNA_constraint_types.h" +#include "DNA_gpencil_types.h" +#include "DNA_lightprobe_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meta_types.h" +#include "DNA_modifier_types.h" +#include "DNA_object_force_types.h" +#include "DNA_rigidbody_types.h" +#include "DNA_smoke_types.h" + +#include "DEG_depsgraph_query.h" + +#include "ED_view3d.h" + +#include "GPU_draw.h" + +#include "overlay_private.h" + +#include "draw_common.h" +#include "draw_manager_text.h" + +void OVERLAY_extra_cache_init(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + + DRW_PASS_CREATE(psl->extra_blend_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA); + DRW_PASS_CREATE(psl->extra_centers_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA); + + for (int i = 0; i < 2; i++) { + /* Non Meshes Pass (Camera, empties, lights ...) */ + struct GPUShader *sh; + struct GPUVertFormat *format; + DRWShadingGroup *grp, *grp_sub; + + OVERLAY_InstanceFormats *formats = OVERLAY_shader_instance_formats_get(); + OVERLAY_ExtraCallBuffers *cb = &pd->extra_call_buffers[i]; + DRWPass **p_extra_ps = &psl->extra_ps[i]; + + DRWState infront_state = (DRW_state_is_select() && (i == 1)) ? DRW_STATE_IN_FRONT_SELECT : 0; + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL; + DRW_PASS_CREATE(*p_extra_ps, state | pd->clipping_state | infront_state); + + DRWPass *extra_ps = *p_extra_ps; + +#define BUF_INSTANCE DRW_shgroup_call_buffer_instance +#define BUF_POINT(grp, format) DRW_shgroup_call_buffer(grp, format, GPU_PRIM_POINTS) +#define BUF_LINE(grp, format) DRW_shgroup_call_buffer(grp, format, GPU_PRIM_LINES) + + /* Sorted by shader to avoid state changes during render. */ + { + format = formats->instance_extra; + sh = OVERLAY_shader_extra(); + + grp = DRW_shgroup_create(sh, extra_ps); + DRW_shgroup_uniform_block_persistent(grp, "globalsBlock", G_draw.block_ubo); + + grp_sub = DRW_shgroup_create_sub(grp); + cb->camera_distances = BUF_INSTANCE(grp_sub, format, DRW_cache_camera_distances_get()); + cb->camera_frame = BUF_INSTANCE(grp_sub, format, DRW_cache_camera_frame_get()); + cb->camera_tria[0] = BUF_INSTANCE(grp_sub, format, DRW_cache_camera_tria_wire_get()); + cb->camera_tria[1] = BUF_INSTANCE(grp_sub, format, DRW_cache_camera_tria_get()); + cb->empty_axes = BUF_INSTANCE(grp_sub, format, DRW_cache_bone_arrows_get()); + cb->empty_capsule_body = BUF_INSTANCE(grp_sub, format, DRW_cache_empty_capsule_body_get()); + cb->empty_capsule_cap = BUF_INSTANCE(grp_sub, format, DRW_cache_empty_capsule_cap_get()); + cb->empty_circle = BUF_INSTANCE(grp_sub, format, DRW_cache_circle_get()); + cb->empty_cone = BUF_INSTANCE(grp_sub, format, DRW_cache_empty_cone_get()); + cb->empty_cube = BUF_INSTANCE(grp_sub, format, DRW_cache_empty_cube_get()); + cb->empty_cylinder = BUF_INSTANCE(grp_sub, format, DRW_cache_empty_cylinder_get()); + cb->empty_image_frame = BUF_INSTANCE(grp_sub, format, DRW_cache_quad_wires_get()); + cb->empty_plain_axes = BUF_INSTANCE(grp_sub, format, DRW_cache_plain_axes_get()); + cb->empty_single_arrow = BUF_INSTANCE(grp_sub, format, DRW_cache_single_arrow_get()); + cb->empty_sphere = BUF_INSTANCE(grp_sub, format, DRW_cache_empty_sphere_get()); + cb->empty_sphere_solid = BUF_INSTANCE(grp_sub, format, DRW_cache_sphere_get()); + cb->field_cone_limit = BUF_INSTANCE(grp_sub, format, DRW_cache_field_cone_limit_get()); + cb->field_curve = BUF_INSTANCE(grp_sub, format, DRW_cache_field_curve_get()); + cb->field_force = BUF_INSTANCE(grp_sub, format, DRW_cache_field_force_get()); + cb->field_sphere_limit = BUF_INSTANCE(grp_sub, format, DRW_cache_field_sphere_limit_get()); + cb->field_tube_limit = BUF_INSTANCE(grp_sub, format, DRW_cache_field_tube_limit_get()); + cb->field_vortex = BUF_INSTANCE(grp_sub, format, DRW_cache_field_vortex_get()); + cb->field_wind = BUF_INSTANCE(grp_sub, format, DRW_cache_field_wind_get()); + cb->light_area[0] = BUF_INSTANCE(grp_sub, format, DRW_cache_light_area_disk_lines_get()); + cb->light_area[1] = BUF_INSTANCE(grp_sub, format, DRW_cache_light_area_square_lines_get()); + cb->light_point = BUF_INSTANCE(grp_sub, format, DRW_cache_light_point_lines_get()); + cb->light_spot = BUF_INSTANCE(grp_sub, format, DRW_cache_light_spot_lines_get()); + cb->light_sun = BUF_INSTANCE(grp_sub, format, DRW_cache_light_sun_lines_get()); + cb->probe_cube = BUF_INSTANCE(grp_sub, format, DRW_cache_lightprobe_planar_get()); + cb->probe_grid = BUF_INSTANCE(grp_sub, format, DRW_cache_lightprobe_grid_get()); + cb->probe_planar = BUF_INSTANCE(grp_sub, format, DRW_cache_lightprobe_planar_get()); + cb->speaker = BUF_INSTANCE(grp_sub, format, DRW_cache_speaker_get()); + + grp_sub = DRW_shgroup_create_sub(grp); + DRW_shgroup_state_enable(grp_sub, DRW_STATE_DEPTH_ALWAYS); + DRW_shgroup_state_disable(grp_sub, DRW_STATE_DEPTH_LESS_EQUAL); + cb->origin_xform = BUF_INSTANCE(grp_sub, format, DRW_cache_bone_arrows_get()); + } + { + format = formats->instance_extra; + grp = DRW_shgroup_create(sh, psl->extra_blend_ps); /* NOTE: not the same pass! */ + DRW_shgroup_uniform_block_persistent(grp, "globalsBlock", G_draw.block_ubo); + + grp_sub = DRW_shgroup_create_sub(grp); + DRW_shgroup_state_enable(grp_sub, DRW_STATE_CULL_BACK); + cb->camera_volume = BUF_INSTANCE(grp_sub, format, DRW_cache_camera_volume_get()); + cb->camera_volume_frame = BUF_INSTANCE(grp_sub, format, DRW_cache_camera_volume_wire_get()); + cb->light_spot_cone_back = BUF_INSTANCE(grp_sub, format, DRW_cache_light_spot_volume_get()); + + grp_sub = DRW_shgroup_create_sub(grp); + DRW_shgroup_state_enable(grp_sub, DRW_STATE_CULL_FRONT); + cb->light_spot_cone_front = BUF_INSTANCE(grp_sub, format, DRW_cache_light_spot_volume_get()); + } + { + format = formats->instance_pos; + sh = OVERLAY_shader_extra_groundline(); + + grp = DRW_shgroup_create(sh, extra_ps); + DRW_shgroup_uniform_block_persistent(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_state_enable(grp, DRW_STATE_BLEND_ALPHA); + + cb->groundline = BUF_INSTANCE(grp, format, DRW_cache_groundline_get()); + } + { + sh = OVERLAY_shader_extra_wire(false); + + grp = DRW_shgroup_create(sh, extra_ps); + DRW_shgroup_uniform_block_persistent(grp, "globalsBlock", G_draw.block_ubo); + + cb->extra_dashed_lines = BUF_LINE(grp, formats->pos_color); + cb->extra_lines = BUF_LINE(grp, formats->wire_extra); + } + { + sh = OVERLAY_shader_extra_wire(true); + + cb->extra_wire = grp = DRW_shgroup_create(sh, extra_ps); + DRW_shgroup_uniform_block_persistent(grp, "globalsBlock", G_draw.block_ubo); + } + { + sh = OVERLAY_shader_extra_loose_point(); + + cb->extra_loose_points = grp = DRW_shgroup_create(sh, extra_ps); + DRW_shgroup_uniform_block_persistent(grp, "globalsBlock", G_draw.block_ubo); + } + { + format = formats->pos; + sh = OVERLAY_shader_extra_point(); + + grp = DRW_shgroup_create(sh, psl->extra_centers_ps); /* NOTE: not the same pass! */ + DRW_shgroup_uniform_block_persistent(grp, "globalsBlock", G_draw.block_ubo); + + grp_sub = DRW_shgroup_create_sub(grp); + DRW_shgroup_uniform_vec4_copy(grp_sub, "color", G_draw.block.colorActive); + cb->center_active = BUF_POINT(grp_sub, format); + + grp_sub = DRW_shgroup_create_sub(grp); + DRW_shgroup_uniform_vec4_copy(grp_sub, "color", G_draw.block.colorSelect); + cb->center_selected = BUF_POINT(grp_sub, format); + + grp_sub = DRW_shgroup_create_sub(grp); + DRW_shgroup_uniform_vec4_copy(grp_sub, "color", G_draw.block.colorDeselect); + cb->center_deselected = BUF_POINT(grp_sub, format); + + grp_sub = DRW_shgroup_create_sub(grp); + DRW_shgroup_uniform_vec4_copy(grp_sub, "color", G_draw.block.colorLibrarySelect); + cb->center_selected_lib = BUF_POINT(grp_sub, format); + + grp_sub = DRW_shgroup_create_sub(grp); + DRW_shgroup_uniform_vec4_copy(grp_sub, "color", G_draw.block.colorLibrary); + cb->center_deselected_lib = BUF_POINT(grp_sub, format); + } + } +} + +void OVERLAY_extra_line_dashed(OVERLAY_ExtraCallBuffers *cb, + const float start[3], + const float end[3], + const float color[4]) +{ + DRW_buffer_add_entry(cb->extra_dashed_lines, end, color); + DRW_buffer_add_entry(cb->extra_dashed_lines, start, color); +} + +void OVERLAY_extra_line(OVERLAY_ExtraCallBuffers *cb, + const float start[3], + const float end[3], + const int color_id) +{ + DRW_buffer_add_entry(cb->extra_lines, start, &color_id); + DRW_buffer_add_entry(cb->extra_lines, end, &color_id); +} + +OVERLAY_ExtraCallBuffers *OVERLAY_extra_call_buffer_get(OVERLAY_Data *vedata, Object *ob) +{ + bool do_in_front = (ob->dtx & OB_DRAWXRAY) != 0; + OVERLAY_PrivateData *pd = vedata->stl->pd; + return &pd->extra_call_buffers[do_in_front]; +} + +void OVERLAY_extra_loose_points(OVERLAY_ExtraCallBuffers *cb, + struct GPUBatch *geom, + const float mat[4][4], + const float color[4]) +{ + float draw_mat[4][4]; + pack_v4_in_mat4(draw_mat, mat, color); + DRW_shgroup_call_obmat(cb->extra_loose_points, geom, draw_mat); +} + +void OVERLAY_extra_wire(OVERLAY_ExtraCallBuffers *cb, + struct GPUBatch *geom, + const float mat[4][4], + const float color[4]) +{ + float draw_mat[4][4]; + pack_v4_in_mat4(draw_mat, mat, color); + DRW_shgroup_call_obmat(cb->extra_wire, geom, draw_mat); +} + +/* -------------------------------------------------------------------- */ +/** \name Empties + * \{ */ + +void OVERLAY_empty_shape(OVERLAY_ExtraCallBuffers *cb, + const float mat[4][4], + const float draw_size, + const char draw_type, + const float color[4]) +{ + float instdata[4][4]; + pack_fl_in_mat4(instdata, mat, draw_size); + + switch (draw_type) { + case OB_PLAINAXES: + DRW_buffer_add_entry(cb->empty_plain_axes, color, instdata); + break; + case OB_SINGLE_ARROW: + DRW_buffer_add_entry(cb->empty_single_arrow, color, instdata); + break; + case OB_CUBE: + DRW_buffer_add_entry(cb->empty_cube, color, instdata); + break; + case OB_CIRCLE: + DRW_buffer_add_entry(cb->empty_circle, color, instdata); + break; + case OB_EMPTY_SPHERE: + DRW_buffer_add_entry(cb->empty_sphere, color, instdata); + break; + case OB_EMPTY_CONE: + DRW_buffer_add_entry(cb->empty_cone, color, instdata); + break; + case OB_ARROWS: + DRW_buffer_add_entry(cb->empty_axes, color, instdata); + break; + case OB_EMPTY_IMAGE: + /* This only show the frame. See OVERLAY_image_empty_cache_populate() for the image. */ + DRW_buffer_add_entry(cb->empty_image_frame, color, instdata); + break; + } +} + +void OVERLAY_empty_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + if (((ob->base_flag & BASE_FROM_DUPLI) != 0) && ((ob->transflag & OB_DUPLICOLLECTION) != 0) && + ob->instance_collection) { + return; + } + + OVERLAY_ExtraCallBuffers *cb = OVERLAY_extra_call_buffer_get(vedata, ob); + const DRWContextState *draw_ctx = DRW_context_state_get(); + ViewLayer *view_layer = draw_ctx->view_layer; + float *color; + + switch (ob->empty_drawtype) { + case OB_PLAINAXES: + case OB_SINGLE_ARROW: + case OB_CUBE: + case OB_CIRCLE: + case OB_EMPTY_SPHERE: + case OB_EMPTY_CONE: + case OB_ARROWS: + DRW_object_wire_theme_get(ob, view_layer, &color); + OVERLAY_empty_shape(cb, ob->obmat, ob->empty_drawsize, ob->empty_drawtype, color); + break; + case OB_EMPTY_IMAGE: + OVERLAY_image_empty_cache_populate(vedata, ob); + break; + } +} + +static void OVERLAY_bounds( + OVERLAY_ExtraCallBuffers *cb, Object *ob, int theme_id, char boundtype, bool around_origin) +{ + float color[4], center[3], size[3], tmp[4][4], final_mat[4][4]; + BoundBox bb_local; + + if (ob->type == OB_MBALL && !BKE_mball_is_basis(ob)) { + return; + } + + BoundBox *bb = BKE_object_boundbox_get(ob); + + if (bb == NULL) { + const float min[3] = {-1.0f, -1.0f, -1.0f}, max[3] = {1.0f, 1.0f, 1.0f}; + bb = &bb_local; + BKE_boundbox_init_from_minmax(bb, min, max); + } + + UI_GetThemeColor4fv(theme_id, color); + BKE_boundbox_calc_size_aabb(bb, size); + + if (around_origin) { + zero_v3(center); + } + else { + BKE_boundbox_calc_center_aabb(bb, center); + } + + switch (boundtype) { + case OB_BOUND_BOX: + size_to_mat4(tmp, size); + copy_v3_v3(tmp[3], center); + mul_m4_m4m4(tmp, ob->obmat, tmp); + DRW_buffer_add_entry(cb->empty_cube, color, tmp); + break; + case OB_BOUND_SPHERE: + size[0] = max_fff(size[0], size[1], size[2]); + size[1] = size[2] = size[0]; + size_to_mat4(tmp, size); + copy_v3_v3(tmp[3], center); + mul_m4_m4m4(tmp, ob->obmat, tmp); + DRW_buffer_add_entry(cb->empty_sphere, color, tmp); + break; + case OB_BOUND_CYLINDER: + size[0] = max_ff(size[0], size[1]); + size[1] = size[0]; + size_to_mat4(tmp, size); + copy_v3_v3(tmp[3], center); + mul_m4_m4m4(tmp, ob->obmat, tmp); + DRW_buffer_add_entry(cb->empty_cylinder, color, tmp); + break; + case OB_BOUND_CONE: + size[0] = max_ff(size[0], size[1]); + size[1] = size[0]; + size_to_mat4(tmp, size); + copy_v3_v3(tmp[3], center); + /* Cone batch has base at 0 and is pointing towards +Y. */ + swap_v3_v3(tmp[1], tmp[2]); + tmp[3][2] -= size[2]; + mul_m4_m4m4(tmp, ob->obmat, tmp); + DRW_buffer_add_entry(cb->empty_cone, color, tmp); + break; + case OB_BOUND_CAPSULE: + size[0] = max_ff(size[0], size[1]); + size[1] = size[0]; + scale_m4_fl(tmp, size[0]); + copy_v2_v2(tmp[3], center); + tmp[3][2] = center[2] + max_ff(0.0f, size[2] - size[0]); + mul_m4_m4m4(final_mat, ob->obmat, tmp); + DRW_buffer_add_entry(cb->empty_capsule_cap, color, final_mat); + negate_v3(tmp[2]); + tmp[3][2] = center[2] - max_ff(0.0f, size[2] - size[0]); + mul_m4_m4m4(final_mat, ob->obmat, tmp); + DRW_buffer_add_entry(cb->empty_capsule_cap, color, final_mat); + tmp[2][2] = max_ff(0.0f, size[2] * 2.0f - size[0] * 2.0f); + mul_m4_m4m4(final_mat, ob->obmat, tmp); + DRW_buffer_add_entry(cb->empty_capsule_body, color, final_mat); + break; + } +} + +static void OVERLAY_collision(OVERLAY_ExtraCallBuffers *cb, Object *ob, int theme_id) +{ + switch (ob->rigidbody_object->shape) { + case RB_SHAPE_BOX: + OVERLAY_bounds(cb, ob, theme_id, OB_BOUND_BOX, true); + break; + case RB_SHAPE_SPHERE: + OVERLAY_bounds(cb, ob, theme_id, OB_BOUND_SPHERE, true); + break; + case RB_SHAPE_CONE: + OVERLAY_bounds(cb, ob, theme_id, OB_BOUND_CONE, true); + break; + case RB_SHAPE_CYLINDER: + OVERLAY_bounds(cb, ob, theme_id, OB_BOUND_CYLINDER, true); + break; + case RB_SHAPE_CAPSULE: + OVERLAY_bounds(cb, ob, theme_id, OB_BOUND_CAPSULE, true); + break; + } +} + +static void OVERLAY_texture_space(OVERLAY_ExtraCallBuffers *cb, Object *ob, int theme_id) +{ + if (ob->data == NULL) { + return; + } + + ID *ob_data = ob->data; + float *texcoloc = NULL; + float *texcosize = NULL; + + switch (GS(ob_data->name)) { + case ID_ME: + BKE_mesh_texspace_get_reference((Mesh *)ob_data, NULL, &texcoloc, &texcosize); + break; + case ID_CU: { + Curve *cu = (Curve *)ob_data; + BKE_curve_texspace_ensure(cu); + texcoloc = cu->loc; + texcosize = cu->size; + break; + } + case ID_MB: { + MetaBall *mb = (MetaBall *)ob_data; + texcoloc = mb->loc; + texcosize = mb->size; + break; + } + default: + BLI_assert(0); + } + + float mat[4][4], color[4]; + size_to_mat4(mat, texcosize); + copy_v3_v3(mat[3], texcoloc); + + mul_m4_m4m4(mat, ob->obmat, mat); + + UI_GetThemeColor4fv(theme_id, color); + + DRW_buffer_add_entry(cb->empty_cube, color, mat); +} + +static void OVERLAY_forcefield(OVERLAY_ExtraCallBuffers *cb, Object *ob, ViewLayer *view_layer) +{ + int theme_id = DRW_object_wire_theme_get(ob, view_layer, NULL); + float *color = DRW_color_background_blend_get(theme_id); + PartDeflect *pd = ob->pd; + Curve *cu = (ob->type == OB_CURVE) ? ob->data : NULL; + + union { + float mat[4][4]; + struct { + float _pad00[3], size_x; + float _pad01[3], size_y; + float _pad02[3], size_z; + float pos[3], _pad03[1]; + }; + } instdata; + + copy_m4_m4(instdata.mat, ob->obmat); + instdata.size_x = instdata.size_y = instdata.size_z = ob->empty_drawsize; + + switch (pd->forcefield) { + case PFIELD_FORCE: + DRW_buffer_add_entry(cb->field_force, color, &instdata); + break; + case PFIELD_WIND: + instdata.size_z = pd->f_strength; + DRW_buffer_add_entry(cb->field_wind, color, &instdata); + break; + case PFIELD_VORTEX: + instdata.size_y = (pd->f_strength < 0.0f) ? -instdata.size_y : instdata.size_y; + DRW_buffer_add_entry(cb->field_vortex, color, &instdata); + break; + case PFIELD_GUIDE: + if (cu && (cu->flag & CU_PATH) && ob->runtime.curve_cache->path && + ob->runtime.curve_cache->path->data) { + instdata.size_x = instdata.size_y = instdata.size_z = pd->f_strength; + float pos[3], tmp[3]; + where_on_path(ob, 0.0f, pos, tmp, NULL, NULL, NULL); + copy_v3_v3(instdata.pos, ob->obmat[3]); + translate_m4(instdata.mat, pos[0], pos[1], pos[2]); + DRW_buffer_add_entry(cb->field_curve, color, &instdata); + + where_on_path(ob, 1.0f, pos, tmp, NULL, NULL, NULL); + copy_v3_v3(instdata.pos, ob->obmat[3]); + translate_m4(instdata.mat, pos[0], pos[1], pos[2]); + DRW_buffer_add_entry(cb->field_sphere_limit, color, &instdata); + /* Restore */ + copy_v3_v3(instdata.pos, ob->obmat[3]); + } + break; + } + + if (pd->falloff == PFIELD_FALL_TUBE) { + if (pd->flag & (PFIELD_USEMAX | PFIELD_USEMAXR)) { + instdata.size_z = (pd->flag & PFIELD_USEMAX) ? pd->maxdist : 0.0f; + instdata.size_x = (pd->flag & PFIELD_USEMAXR) ? pd->maxrad : 1.0f; + instdata.size_y = instdata.size_x; + DRW_buffer_add_entry(cb->field_tube_limit, color, &instdata); + } + if (pd->flag & (PFIELD_USEMIN | PFIELD_USEMINR)) { + instdata.size_z = (pd->flag & PFIELD_USEMIN) ? pd->mindist : 0.0f; + instdata.size_x = (pd->flag & PFIELD_USEMINR) ? pd->minrad : 1.0f; + instdata.size_y = instdata.size_x; + DRW_buffer_add_entry(cb->field_tube_limit, color, &instdata); + } + } + else if (pd->falloff == PFIELD_FALL_CONE) { + if (pd->flag & (PFIELD_USEMAX | PFIELD_USEMAXR)) { + float radius = DEG2RADF((pd->flag & PFIELD_USEMAXR) ? pd->maxrad : 1.0f); + float distance = (pd->flag & PFIELD_USEMAX) ? pd->maxdist : 0.0f; + instdata.size_x = distance * sinf(radius); + instdata.size_z = distance * cosf(radius); + instdata.size_y = instdata.size_x; + DRW_buffer_add_entry(cb->field_cone_limit, color, &instdata); + } + if (pd->flag & (PFIELD_USEMIN | PFIELD_USEMINR)) { + float radius = DEG2RADF((pd->flag & PFIELD_USEMINR) ? pd->minrad : 1.0f); + float distance = (pd->flag & PFIELD_USEMIN) ? pd->mindist : 0.0f; + instdata.size_x = distance * sinf(radius); + instdata.size_z = distance * cosf(radius); + instdata.size_y = instdata.size_x; + DRW_buffer_add_entry(cb->field_cone_limit, color, &instdata); + } + } + else if (pd->falloff == PFIELD_FALL_SPHERE) { + if (pd->flag & PFIELD_USEMAX) { + instdata.size_x = instdata.size_y = instdata.size_z = pd->maxdist; + DRW_buffer_add_entry(cb->field_sphere_limit, color, &instdata); + } + if (pd->flag & PFIELD_USEMIN) { + instdata.size_x = instdata.size_y = instdata.size_z = pd->mindist; + DRW_buffer_add_entry(cb->field_sphere_limit, color, &instdata); + } + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Lights + * \{ */ + +void OVERLAY_light_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_ExtraCallBuffers *cb = OVERLAY_extra_call_buffer_get(vedata, ob); + const DRWContextState *draw_ctx = DRW_context_state_get(); + ViewLayer *view_layer = draw_ctx->view_layer; + + Light *la = ob->data; + float *color_p; + DRW_object_wire_theme_get(ob, view_layer, &color_p); + /* Remove the alpha. */ + float color[4] = {color_p[0], color_p[1], color_p[2], 1.0f}; + /* Pack render data into object matrix. */ + union { + float mat[4][4]; + struct { + float _pad00[3]; + union { + float area_size_x; + float spot_cosine; + }; + float _pad01[3]; + union { + float area_size_y; + float spot_blend; + }; + float _pad02[3], clip_sta; + float pos[3], clip_end; + }; + } instdata; + + copy_m4_m4(instdata.mat, ob->obmat); + /* FIXME / TODO: clipend has no meaning nowadays. + * In EEVEE, Only clipsta is used shadowmaping. + * Clip end is computed automatically based on light power. */ + instdata.clip_end = la->clipend; + instdata.clip_sta = la->clipsta; + + DRW_buffer_add_entry(cb->groundline, instdata.pos); + + if (la->type == LA_LOCAL) { + instdata.area_size_x = instdata.area_size_y = la->area_size; + DRW_buffer_add_entry(cb->light_point, color, &instdata); + } + else if (la->type == LA_SUN) { + DRW_buffer_add_entry(cb->light_sun, color, &instdata); + } + else if (la->type == LA_SPOT) { + /* For cycles and eevee the spot attenuation is + * y = (1/(1 + x^2) - a)/((1 - a) b) + * We solve the case where spot attenuation y = 1 and y = 0 + * root for y = 1 is (-1 - c) / c + * root for y = 0 is (1 - a) / a + * and use that to position the blend circle. */ + float a = cosf(la->spotsize * 0.5f); + float b = la->spotblend; + float c = a * b - a - b; + /* Optimized version or root1 / root0 */ + instdata.spot_blend = sqrtf((-a - c * a) / (c - c * a)); + instdata.spot_cosine = a; + /* HACK: We pack the area size in alpha color. This is decoded by the shader. */ + color[3] = -max_ff(la->area_size, FLT_MIN); + DRW_buffer_add_entry(cb->light_spot, color, &instdata); + + if ((la->mode & LA_SHOW_CONE) && !DRW_state_is_select()) { + float color_inside[4] = {0.0f, 0.0f, 0.0f, 0.5f}; + float color_outside[4] = {1.0f, 1.0f, 1.0f, 0.3f}; + DRW_buffer_add_entry(cb->light_spot_cone_front, color_inside, &instdata); + DRW_buffer_add_entry(cb->light_spot_cone_back, color_outside, &instdata); + } + } + else if (la->type == LA_AREA) { + bool uniform_scale = !ELEM(la->area_shape, LA_AREA_RECT, LA_AREA_ELLIPSE); + int sqr = ELEM(la->area_shape, LA_AREA_SQUARE, LA_AREA_RECT); + instdata.area_size_x = la->area_size; + instdata.area_size_y = uniform_scale ? la->area_size : la->area_sizey; + DRW_buffer_add_entry(cb->light_area[sqr], color, &instdata); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Lightprobe + * \{ */ + +void OVERLAY_lightprobe_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_ExtraCallBuffers *cb = OVERLAY_extra_call_buffer_get(vedata, ob); + const DRWContextState *draw_ctx = DRW_context_state_get(); + ViewLayer *view_layer = draw_ctx->view_layer; + float *color_p; + DRW_object_wire_theme_get(ob, view_layer, &color_p); + const LightProbe *prb = (LightProbe *)ob->data; + const bool show_clipping = (prb->flag & LIGHTPROBE_FLAG_SHOW_CLIP_DIST) != 0; + const bool show_parallax = (prb->flag & LIGHTPROBE_FLAG_SHOW_PARALLAX) != 0; + const bool show_influence = (prb->flag & LIGHTPROBE_FLAG_SHOW_INFLUENCE) != 0; + + union { + float mat[4][4]; + struct { + float _pad00[4]; + float _pad01[4]; + float _pad02[3], clip_sta; + float pos[3], clip_end; + }; + } instdata; + + copy_m4_m4(instdata.mat, ob->obmat); + + switch (prb->type) { + case LIGHTPROBE_TYPE_CUBE: + instdata.clip_sta = show_clipping ? prb->clipsta : -1.0; + instdata.clip_end = show_clipping ? prb->clipend : -1.0; + DRW_buffer_add_entry(cb->probe_grid, color_p, &instdata); + DRW_buffer_add_entry(cb->groundline, instdata.pos); + + if (show_influence) { + char shape = (prb->attenuation_type == LIGHTPROBE_SHAPE_BOX) ? OB_CUBE : OB_EMPTY_SPHERE; + float f = 1.0f - prb->falloff; + OVERLAY_empty_shape(cb, ob->obmat, prb->distinf, shape, color_p); + OVERLAY_empty_shape(cb, ob->obmat, prb->distinf * f, shape, color_p); + } + + if (show_parallax) { + char shape = (prb->parallax_type == LIGHTPROBE_SHAPE_BOX) ? OB_CUBE : OB_EMPTY_SPHERE; + float dist = ((prb->flag & LIGHTPROBE_FLAG_CUSTOM_PARALLAX) != 0) ? prb->distpar : + prb->distinf; + OVERLAY_empty_shape(cb, ob->obmat, dist, shape, color_p); + } + break; + case LIGHTPROBE_TYPE_GRID: + instdata.clip_sta = show_clipping ? prb->clipsta : -1.0; + instdata.clip_end = show_clipping ? prb->clipend : -1.0; + DRW_buffer_add_entry(cb->probe_grid, color_p, &instdata); + + if (show_influence) { + float f = 1.0f - prb->falloff; + OVERLAY_empty_shape(cb, ob->obmat, 1.0 + prb->distinf, OB_CUBE, color_p); + OVERLAY_empty_shape(cb, ob->obmat, 1.0 + prb->distinf * f, OB_CUBE, color_p); + } + break; + case LIGHTPROBE_TYPE_PLANAR: + DRW_buffer_add_entry(cb->probe_planar, color_p, &instdata); + + if (show_influence) { + normalize_v3_length(instdata.mat[2], prb->distinf); + DRW_buffer_add_entry(cb->empty_cube, color_p, &instdata); + mul_v3_fl(instdata.mat[2], 1.0f - prb->falloff); + DRW_buffer_add_entry(cb->empty_cube, color_p, &instdata); + } + zero_v3(instdata.mat[2]); + DRW_buffer_add_entry(cb->empty_cube, color_p, &instdata); + + normalize_m4_m4(instdata.mat, ob->obmat); + OVERLAY_empty_shape(cb, instdata.mat, ob->empty_drawsize, OB_SINGLE_ARROW, color_p); + break; + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Speaker + * \{ */ + +void OVERLAY_speaker_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_ExtraCallBuffers *cb = OVERLAY_extra_call_buffer_get(vedata, ob); + const DRWContextState *draw_ctx = DRW_context_state_get(); + ViewLayer *view_layer = draw_ctx->view_layer; + float *color_p; + DRW_object_wire_theme_get(ob, view_layer, &color_p); + + DRW_buffer_add_entry(cb->speaker, color_p, ob->obmat); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Camera + * \{ */ + +typedef union OVERLAY_CameraInstanceData { + /* Pack render data into object matrix and object color. */ + struct { + float color[4]; + float mat[4][4]; + }; + struct { + float _pad0[2]; + float volume_sta; + union { + float depth; + float focus; + float volume_end; + }; + float _pad00[3]; + union { + float corner_x; + float dist_color_id; + }; + float _pad01[3]; + union { + float corner_y; + }; + float _pad02[3]; + union { + float center_x; + float clip_sta; + float mist_sta; + }; + float pos[3]; + union { + float center_y; + float clip_end; + float mist_end; + }; + }; +} OVERLAY_CameraInstanceData; + +static void camera_view3d_reconstruction(OVERLAY_ExtraCallBuffers *cb, + Scene *scene, + View3D *v3d, + Object *camera_object, + Object *ob, + const float color[4]) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + const bool is_select = DRW_state_is_select(); + const Object *orig_camera_object = DEG_get_original_object(camera_object); + + MovieClip *clip = BKE_object_movieclip_get(scene, ob, false); + if (clip == NULL) { + return; + } + + const bool is_solid_bundle = (v3d->bundle_drawtype == OB_EMPTY_SPHERE) && + ((v3d->shading.type != OB_SOLID) || !XRAY_FLAG_ENABLED(v3d)); + + MovieTracking *tracking = &clip->tracking; + /* Index must start in 1, to mimic BKE_tracking_track_get_indexed. */ + int track_index = 1; + + uchar text_color_selected[4], text_color_unselected[4]; + float bundle_color_unselected[4], bundle_color_solid[4]; + + UI_GetThemeColor4ubv(TH_SELECT, text_color_selected); + UI_GetThemeColor4ubv(TH_TEXT, text_color_unselected); + UI_GetThemeColor4fv(TH_WIRE, bundle_color_unselected); + UI_GetThemeColor4fv(TH_BUNDLE_SOLID, bundle_color_solid); + + float camera_mat[4][4], normal_mat[4][4]; + BKE_tracking_get_camera_object_matrix(scene, ob, camera_mat); + + normalize_m4_m4(normal_mat, ob->obmat); + + LISTBASE_FOREACH (MovieTrackingObject *, tracking_object, &tracking->objects) { + float tracking_object_mat[4][4]; + + if (tracking_object->flag & TRACKING_OBJECT_CAMERA) { + copy_m4_m4(tracking_object_mat, camera_mat); + } + else { + const int framenr = BKE_movieclip_remap_scene_to_clip_frame( + clip, DEG_get_ctime(draw_ctx->depsgraph)); + float object_mat[4][4]; + BKE_tracking_camera_get_reconstructed_interpolate( + tracking, tracking_object, framenr, object_mat); + + invert_m4(object_mat); + mul_m4_m4m4(tracking_object_mat, normal_mat, object_mat); + } + + ListBase *tracksbase = BKE_tracking_object_get_tracks(tracking, tracking_object); + for (MovieTrackingTrack *track = tracksbase->first; track; track = track->next) { + if ((track->flag & TRACK_HAS_BUNDLE) == 0) { + continue; + } + bool is_selected = TRACK_SELECTED(track); + + float bundle_mat[4][4]; + copy_m4_m4(bundle_mat, tracking_object_mat); + translate_m4(bundle_mat, track->bundle_pos[0], track->bundle_pos[1], track->bundle_pos[2]); + + const float *bundle_color; + if (track->flag & TRACK_CUSTOMCOLOR) { + bundle_color = track->color; + } + else if (is_solid_bundle) { + bundle_color = bundle_color_solid; + } + else if (is_selected) { + bundle_color = color; + } + else { + bundle_color = bundle_color_unselected; + } + + if (is_select) { + DRW_select_load_id(orig_camera_object->runtime.select_id | (track_index << 16)); + track_index++; + } + + if (is_solid_bundle) { + if (is_selected) { + OVERLAY_empty_shape(cb, bundle_mat, v3d->bundle_size, v3d->bundle_drawtype, color); + } + + const float bundle_color_v4[4] = { + bundle_color[0], + bundle_color[1], + bundle_color[2], + 1.0f, + }; + + bundle_mat[3][3] = v3d->bundle_size; /* See shader. */ + DRW_buffer_add_entry(cb->empty_sphere_solid, bundle_color_v4, bundle_mat); + } + else { + OVERLAY_empty_shape(cb, bundle_mat, v3d->bundle_size, v3d->bundle_drawtype, bundle_color); + } + + if ((v3d->flag2 & V3D_SHOW_BUNDLENAME) && !is_select) { + struct DRWTextStore *dt = DRW_text_cache_ensure(); + + DRW_text_cache_add(dt, + bundle_mat[3], + track->name, + strlen(track->name), + 10, + 0, + DRW_TEXT_CACHE_GLOBALSPACE | DRW_TEXT_CACHE_STRING_PTR, + is_selected ? text_color_selected : text_color_unselected); + } + } + + if ((v3d->flag2 & V3D_SHOW_CAMERAPATH) && (tracking_object->flag & TRACKING_OBJECT_CAMERA) && + !is_select) { + MovieTrackingReconstruction *reconstruction; + reconstruction = BKE_tracking_object_get_reconstruction(tracking, tracking_object); + + if (reconstruction->camnr) { + MovieReconstructedCamera *camera = reconstruction->cameras; + float v0[3], v1[3]; + for (int a = 0; a < reconstruction->camnr; a++, camera++) { + copy_v3_v3(v0, v1); + copy_v3_v3(v1, camera->mat[3]); + mul_m4_v3(camera_mat, v1); + if (a > 0) { + /* This one is suboptimal (gl_lines instead of gl_line_strip) + * but we keep this for simplicity */ + OVERLAY_extra_line(cb, v0, v1, TH_CAMERA_PATH); + } + } + } + } + } +} + +static float camera_offaxis_shiftx_get(Scene *scene, + Object *ob, + const OVERLAY_CameraInstanceData *instdata, + bool right_eye) +{ + Camera *cam = ob->data; + if (cam->stereo.convergence_mode == CAM_S3D_OFFAXIS) { + const char *viewnames[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + const float shiftx = BKE_camera_multiview_shift_x(&scene->r, ob, viewnames[right_eye]); + const float delta_shiftx = shiftx - cam->shiftx; + const float width = instdata->corner_x * 2.0f; + return delta_shiftx * width; + } + else { + return 0.0; + } +} +/** + * Draw the stereo 3d support elements (cameras, plane, volume). + * They are only visible when not looking through the camera: + */ +static void camera_stereoscopy_extra(OVERLAY_ExtraCallBuffers *cb, + Scene *scene, + View3D *v3d, + Object *ob, + const OVERLAY_CameraInstanceData *instdata) +{ + OVERLAY_CameraInstanceData stereodata = *instdata; + Camera *cam = ob->data; + const bool is_select = DRW_state_is_select(); + const char *viewnames[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + + const bool is_stereo3d_cameras = (v3d->stereo3d_flag & V3D_S3D_DISPCAMERAS) != 0; + const bool is_stereo3d_plane = (v3d->stereo3d_flag & V3D_S3D_DISPPLANE) != 0; + const bool is_stereo3d_volume = (v3d->stereo3d_flag & V3D_S3D_DISPVOLUME) != 0; + + for (int eye = 0; eye < 2; eye++) { + ob = BKE_camera_multiview_render(scene, ob, viewnames[eye]); + BKE_camera_multiview_model_matrix(&scene->r, ob, viewnames[eye], stereodata.mat); + + stereodata.corner_x = instdata->corner_x; + stereodata.corner_y = instdata->corner_y; + stereodata.center_x = instdata->center_x + camera_offaxis_shiftx_get(scene, ob, instdata, eye); + stereodata.center_y = instdata->center_y; + stereodata.depth = instdata->depth; + + if (is_stereo3d_cameras) { + DRW_buffer_add_entry_struct(cb->camera_frame, &stereodata); + + /* Connecting line between cameras. */ + OVERLAY_extra_line_dashed(cb, stereodata.pos, instdata->pos, G_draw.block.colorWire); + } + + if (is_stereo3d_volume && !is_select) { + float r = (eye == 1) ? 2.0f : 1.0f; + + stereodata.volume_sta = -cam->clip_start; + stereodata.volume_end = -cam->clip_end; + /* Encode eye + intensity and alpha (see shader) */ + copy_v2_fl2(stereodata.color, r + 0.15f, 1.0f); + DRW_buffer_add_entry_struct(cb->camera_volume_frame, &stereodata); + + if (v3d->stereo3d_volume_alpha > 0.0f) { + /* Encode eye + intensity and alpha (see shader) */ + copy_v2_fl2(stereodata.color, r + 0.999f, v3d->stereo3d_volume_alpha); + DRW_buffer_add_entry_struct(cb->camera_volume, &stereodata); + } + /* restore */ + copy_v3_v3(stereodata.color, instdata->color); + } + } + + if (is_stereo3d_plane && !is_select) { + if (cam->stereo.convergence_mode == CAM_S3D_TOE) { + /* There is no real convergence plane but we highlight the center + * point where the views are pointing at. */ + // zero_v3(stereodata.mat[0]); /* We reconstruct from Z and Y */ + // zero_v3(stereodata.mat[1]); /* Y doesn't change */ + zero_v3(stereodata.mat[2]); + zero_v3(stereodata.mat[3]); + for (int i = 0; i < 2; i++) { + float mat[4][4]; + /* Need normalized version here. */ + BKE_camera_multiview_model_matrix(&scene->r, ob, viewnames[i], mat); + add_v3_v3(stereodata.mat[2], mat[2]); + madd_v3_v3fl(stereodata.mat[3], mat[3], 0.5f); + } + normalize_v3(stereodata.mat[2]); + cross_v3_v3v3(stereodata.mat[0], stereodata.mat[1], stereodata.mat[2]); + } + else if (cam->stereo.convergence_mode == CAM_S3D_PARALLEL) { + /* Show plane at the given distance between the views even if it makes no sense. */ + zero_v3(stereodata.pos); + for (int i = 0; i < 2; i++) { + float mat[4][4]; + BKE_camera_multiview_model_matrix_scaled(&scene->r, ob, viewnames[i], mat); + madd_v3_v3fl(stereodata.pos, mat[3], 0.5f); + } + } + else if (cam->stereo.convergence_mode == CAM_S3D_OFFAXIS) { + /* Nothing to do. Everything is already setup. */ + } + stereodata.volume_sta = -cam->stereo.convergence_distance; + stereodata.volume_end = -cam->stereo.convergence_distance; + /* Encode eye + intensity and alpha (see shader) */ + copy_v2_fl2(stereodata.color, 0.1f, 1.0f); + DRW_buffer_add_entry_struct(cb->camera_volume_frame, &stereodata); + + if (v3d->stereo3d_convergence_alpha > 0.0f) { + /* Encode eye + intensity and alpha (see shader) */ + copy_v2_fl2(stereodata.color, 0.0f, v3d->stereo3d_convergence_alpha); + DRW_buffer_add_entry_struct(cb->camera_volume, &stereodata); + } + } +} + +void OVERLAY_camera_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_ExtraCallBuffers *cb = OVERLAY_extra_call_buffer_get(vedata, ob); + OVERLAY_CameraInstanceData instdata; + + const DRWContextState *draw_ctx = DRW_context_state_get(); + ViewLayer *view_layer = draw_ctx->view_layer; + View3D *v3d = draw_ctx->v3d; + Scene *scene = draw_ctx->scene; + RegionView3D *rv3d = draw_ctx->rv3d; + + Camera *cam = ob->data; + Object *camera_object = DEG_get_evaluated_object(draw_ctx->depsgraph, v3d->camera); + const bool is_select = DRW_state_is_select(); + const bool is_active = (ob == camera_object); + const bool look_through = (is_active && (rv3d->persp == RV3D_CAMOB)); + + const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0; + const bool is_stereo3d_view = (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D); + const bool is_stereo3d_display_extra = is_active && is_multiview && (!look_through) && + ((v3d->stereo3d_flag) != 0); + const bool is_selection_camera_stereo = is_select && look_through && is_multiview && + is_stereo3d_view; + + float vec[4][3], asp[2], shift[2], scale[3], drawsize, center[2], corner[2]; + + float *color_p; + DRW_object_wire_theme_get(ob, view_layer, &color_p); + copy_v4_v4(instdata.color, color_p); + + normalize_m4_m4(instdata.mat, ob->obmat); + + /* BKE_camera_multiview_model_matrix already accounts for scale, don't do it here. */ + if (is_selection_camera_stereo) { + copy_v3_fl(scale, 1.0f); + } + else { + copy_v3_fl3(scale, len_v3(ob->obmat[0]), len_v3(ob->obmat[1]), len_v3(ob->obmat[2])); + invert_v3(scale); + } + + BKE_camera_view_frame_ex( + scene, cam, cam->drawsize, look_through, scale, asp, shift, &drawsize, vec); + + /* Apply scale to simplify the rest of the drawing. */ + invert_v3(scale); + for (int i = 0; i < 4; i++) { + mul_v3_v3(vec[i], scale); + /* Project to z=-1 plane. Makes positionning / scaling easier. (see shader) */ + mul_v2_fl(vec[i], 1.0f / fabsf(vec[i][2])); + } + + /* Frame coords */ + mid_v2_v2v2(center, vec[0], vec[2]); + sub_v2_v2v2(corner, vec[0], center); + instdata.corner_x = corner[0]; + instdata.corner_y = corner[1]; + instdata.center_x = center[0]; + instdata.center_y = center[1]; + instdata.depth = vec[0][2]; + + if (look_through) { + if (!DRW_state_is_image_render()) { + /* Only draw the frame. */ + if (is_multiview) { + float mat[4][4]; + const bool is_right = v3d->multiview_eye == STEREO_RIGHT_ID; + const char *view_name = is_right ? STEREO_RIGHT_NAME : STEREO_LEFT_NAME; + BKE_camera_multiview_model_matrix(&scene->r, ob, view_name, mat); + instdata.center_x += camera_offaxis_shiftx_get(scene, ob, &instdata, is_right); + for (int i = 0; i < 4; i++) { + /* Partial copy to avoid overriding packed data. */ + copy_v3_v3(instdata.mat[i], mat[i]); + } + } + instdata.depth = -instdata.depth; /* Hides the back of the camera wires (see shader). */ + DRW_buffer_add_entry_struct(cb->camera_frame, &instdata); + } + } + else { + /* Stereo cameras, volumes, plane drawing. */ + if (is_stereo3d_display_extra) { + camera_stereoscopy_extra(cb, scene, v3d, ob, &instdata); + } + else { + DRW_buffer_add_entry_struct(cb->camera_frame, &instdata); + } + } + + if (!look_through) { + /* Triangle. */ + float tria_size = 0.7f * drawsize / fabsf(instdata.depth); + float tria_margin = 0.1f * drawsize / fabsf(instdata.depth); + instdata.center_x = center[0]; + instdata.center_y = center[1] + instdata.corner_y + tria_margin + tria_size; + instdata.corner_x = instdata.corner_y = -tria_size; + DRW_buffer_add_entry_struct(cb->camera_tria[is_active], &instdata); + } + + if (cam->flag & CAM_SHOWLIMITS) { + /* Scale focus point. */ + mul_v3_fl(instdata.mat[0], cam->drawsize); + mul_v3_fl(instdata.mat[1], cam->drawsize); + + instdata.dist_color_id = (is_active) ? 3 : 2; + instdata.focus = -BKE_camera_object_dof_distance(ob); + instdata.clip_sta = cam->clip_start; + instdata.clip_end = cam->clip_end; + DRW_buffer_add_entry_struct(cb->camera_distances, &instdata); + } + + if (cam->flag & CAM_SHOWMIST) { + World *world = scene->world; + if (world) { + instdata.dist_color_id = (is_active) ? 1 : 0; + instdata.focus = 1.0f; /* Disable */ + instdata.mist_sta = world->miststa; + instdata.mist_end = world->miststa + world->mistdist; + DRW_buffer_add_entry_struct(cb->camera_distances, &instdata); + } + } + + /* Motion Tracking. */ + if ((v3d->flag2 & V3D_SHOW_RECONSTRUCTION) != 0) { + camera_view3d_reconstruction(cb, scene, v3d, camera_object, ob, color_p); + } + + /* Background images. */ + if (look_through && (cam->flag & CAM_SHOW_BG_IMAGE) && !BLI_listbase_is_empty(&cam->bg_images)) { + OVERLAY_image_camera_cache_populate(vedata, ob); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Relationships & constraints + * \{ */ + +static void OVERLAY_relationship_lines(OVERLAY_ExtraCallBuffers *cb, + Depsgraph *depsgraph, + Scene *scene, + Object *ob) +{ + float *relation_color = G_draw.block.colorWire; + float *constraint_color = G_draw.block.colorGridAxisZ; /* ? */ + + if (ob->parent && (DRW_object_visibility_in_active_context(ob->parent) & OB_VISIBLE_SELF)) { + float *parent_pos = ob->runtime.parent_display_origin; + OVERLAY_extra_line_dashed(cb, parent_pos, ob->obmat[3], relation_color); + } + + if (ob->rigidbody_constraint) { + Object *rbc_ob1 = ob->rigidbody_constraint->ob1; + Object *rbc_ob2 = ob->rigidbody_constraint->ob2; + if (rbc_ob1 && (DRW_object_visibility_in_active_context(rbc_ob1) & OB_VISIBLE_SELF)) { + OVERLAY_extra_line_dashed(cb, rbc_ob1->obmat[3], ob->obmat[3], relation_color); + } + if (rbc_ob2 && (DRW_object_visibility_in_active_context(rbc_ob2) & OB_VISIBLE_SELF)) { + OVERLAY_extra_line_dashed(cb, rbc_ob2->obmat[3], ob->obmat[3], relation_color); + } + } + + /* Drawing the constraint lines */ + if (!BLI_listbase_is_empty(&ob->constraints)) { + bConstraint *curcon; + bConstraintOb *cob; + ListBase *list = &ob->constraints; + + cob = BKE_constraints_make_evalob(depsgraph, scene, ob, NULL, CONSTRAINT_OBTYPE_OBJECT); + + for (curcon = list->first; curcon; curcon = curcon->next) { + if (ELEM(curcon->type, CONSTRAINT_TYPE_FOLLOWTRACK, CONSTRAINT_TYPE_OBJECTSOLVER)) { + /* special case for object solver and follow track constraints because they don't fill + * constraint targets properly (design limitation -- scene is needed for their target + * but it can't be accessed from get_targets callback) */ + Object *camob = NULL; + + if (curcon->type == CONSTRAINT_TYPE_FOLLOWTRACK) { + bFollowTrackConstraint *data = (bFollowTrackConstraint *)curcon->data; + camob = data->camera ? data->camera : scene->camera; + } + else if (curcon->type == CONSTRAINT_TYPE_OBJECTSOLVER) { + bObjectSolverConstraint *data = (bObjectSolverConstraint *)curcon->data; + camob = data->camera ? data->camera : scene->camera; + } + + if (camob) { + OVERLAY_extra_line_dashed(cb, camob->obmat[3], ob->obmat[3], constraint_color); + } + } + else { + const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon); + + if ((cti && cti->get_constraint_targets) && (curcon->flag & CONSTRAINT_EXPAND)) { + ListBase targets = {NULL, NULL}; + bConstraintTarget *ct; + + cti->get_constraint_targets(curcon, &targets); + + for (ct = targets.first; ct; ct = ct->next) { + /* calculate target's matrix */ + if (cti->get_target_matrix) { + cti->get_target_matrix(depsgraph, curcon, cob, ct, DEG_get_ctime(depsgraph)); + } + else { + unit_m4(ct->matrix); + } + OVERLAY_extra_line_dashed(cb, ct->matrix[3], ob->obmat[3], constraint_color); + } + + if (cti->flush_constraint_targets) { + cti->flush_constraint_targets(curcon, &targets, 1); + } + } + } + } + BKE_constraints_clear_evalob(cob); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \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]; + UI_GetThemeColor4ubv(theme_id, color); + 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 = give_current_material(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 + * \{ */ + +static void OVERLAY_volume_extra(OVERLAY_ExtraCallBuffers *cb, + OVERLAY_Data *data, + Object *ob, + ModifierData *md, + Scene *scene, + float *color) +{ + SmokeModifierData *smd = (SmokeModifierData *)md; + SmokeDomainSettings *sds = smd->domain; + + /* Don't show smoke before simulation starts, this could be made an option in the future. */ + const bool draw_velocity = (sds->draw_velocity && sds->fluid && + CFRA >= sds->point_cache[0]->startframe); + + /* Small cube showing voxel size. */ + { + float min[3]; + madd_v3fl_v3fl_v3fl_v3i(min, sds->p0, sds->cell_size, sds->res_min); + float voxel_cubemat[4][4] = {{0.0f}}; + /* scale small cube to voxel size */ + voxel_cubemat[0][0] = 1.0f / (float)sds->base_res[0]; + voxel_cubemat[1][1] = 1.0f / (float)sds->base_res[1]; + voxel_cubemat[2][2] = 1.0f / (float)sds->base_res[2]; + voxel_cubemat[3][3] = 1.0f; + /* translate small cube to corner */ + copy_v3_v3(voxel_cubemat[3], min); + /* move small cube into the domain (otherwise its centered on vertex of domain object) */ + translate_m4(voxel_cubemat, 1.0f, 1.0f, 1.0f); + mul_m4_m4m4(voxel_cubemat, ob->obmat, voxel_cubemat); + + DRW_buffer_add_entry(cb->empty_cube, color, voxel_cubemat); + } + + if (draw_velocity) { + const bool use_needle = (sds->vector_draw_type == VECTOR_DRAW_NEEDLE); + int line_count = (use_needle) ? 6 : 1; + int slice_axis = -1; + line_count *= sds->res[0] * sds->res[1] * sds->res[2]; + + if (sds->slice_method == MOD_SMOKE_SLICE_AXIS_ALIGNED && + sds->axis_slice_method == AXIS_SLICE_SINGLE) { + float viewinv[4][4]; + DRW_view_viewmat_get(NULL, viewinv, true); + + const int axis = (sds->slice_axis == SLICE_AXIS_AUTO) ? axis_dominant_v3_single(viewinv[2]) : + sds->slice_axis - 1; + slice_axis = axis; + line_count /= sds->res[axis]; + } + + GPU_create_smoke_velocity(smd); + + GPUShader *sh = OVERLAY_shader_volume_velocity(use_needle); + DRWShadingGroup *grp = DRW_shgroup_create(sh, data->psl->extra_ps[0]); + DRW_shgroup_uniform_texture(grp, "velocityX", sds->tex_velocity_x); + DRW_shgroup_uniform_texture(grp, "velocityY", sds->tex_velocity_y); + DRW_shgroup_uniform_texture(grp, "velocityZ", sds->tex_velocity_z); + DRW_shgroup_uniform_float_copy(grp, "displaySize", sds->vector_scale); + DRW_shgroup_uniform_float_copy(grp, "slicePosition", sds->slice_depth); + DRW_shgroup_uniform_vec3_copy(grp, "cellSize", sds->cell_size); + DRW_shgroup_uniform_vec3_copy(grp, "domainOriginOffset", sds->p0); + DRW_shgroup_uniform_ivec3_copy(grp, "adaptiveCellOffset", sds->res_min); + DRW_shgroup_uniform_int_copy(grp, "sliceAxis", slice_axis); + DRW_shgroup_call_procedural_lines(grp, ob, line_count); + + BLI_addtail(&data->stl->pd->smoke_domains, BLI_genericNodeN(smd)); + } +} + +static void OVERLAY_volume_free_smoke_textures(OVERLAY_Data *data) +{ + /* Free Smoke Textures after rendering */ + /* XXX This is a waste of processing and GPU bandwidth if nothing + * is updated. But the problem is since Textures are stored in the + * modifier we don't want them to take precious VRAM if the + * modifier is not used for display. We should share them for + * all viewport in a redraw at least. */ + LinkData *link; + while ((link = BLI_pophead(&data->stl->pd->smoke_domains))) { + SmokeModifierData *smd = (SmokeModifierData *)link->data; + GPU_free_smoke_velocity(smd); + MEM_freeN(link); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ + +static void OVERLAY_object_center(OVERLAY_ExtraCallBuffers *cb, + Object *ob, + OVERLAY_PrivateData *pd, + ViewLayer *view_layer) +{ + const bool is_library = ob->id.us > 1 || ID_IS_LINKED(ob); + + if (ob == OBACT(view_layer)) { + DRW_buffer_add_entry(cb->center_active, ob->obmat[3]); + } + else if (ob->base_flag & BASE_SELECTED) { + DRWCallBuffer *cbuf = (is_library) ? cb->center_selected_lib : cb->center_selected; + DRW_buffer_add_entry(cbuf, ob->obmat[3]); + } + else if (pd->v3d_flag & V3D_DRAW_CENTERS) { + DRWCallBuffer *cbuf = (is_library) ? cb->center_deselected_lib : cb->center_deselected; + DRW_buffer_add_entry(cbuf, ob->obmat[3]); + } +} + +static void OVERLAY_object_name(Object *ob, int theme_id) +{ + struct DRWTextStore *dt = DRW_text_cache_ensure(); + uchar color[4]; + UI_GetThemeColor4ubv(theme_id, color); + + DRW_text_cache_add(dt, + ob->obmat[3], + ob->id.name + 2, + strlen(ob->id.name + 2), + 10, + 0, + DRW_TEXT_CACHE_GLOBALSPACE | DRW_TEXT_CACHE_STRING_PTR, + color); +} + +void OVERLAY_extra_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_ExtraCallBuffers *cb = OVERLAY_extra_call_buffer_get(vedata, ob); + OVERLAY_PrivateData *pd = vedata->stl->pd; + const DRWContextState *draw_ctx = DRW_context_state_get(); + ViewLayer *view_layer = draw_ctx->view_layer; + Scene *scene = draw_ctx->scene; + ModifierData *md = NULL; + + const bool is_select_mode = DRW_state_is_select(); + const bool is_paint_mode = (draw_ctx->object_mode & + (OB_MODE_ALL_PAINT | OB_MODE_ALL_PAINT_GPENCIL)) != 0; + const bool from_dupli = (ob->base_flag & (BASE_FROM_SET | BASE_FROM_DUPLI)) != 0; + const bool has_bounds = !ELEM(ob->type, OB_LAMP, OB_CAMERA, OB_EMPTY, OB_SPEAKER, OB_LIGHTPROBE); + const bool has_texspace = has_bounds && + !ELEM(ob->type, OB_EMPTY, OB_LATTICE, OB_ARMATURE, OB_GPENCIL); + + const bool draw_relations = ((pd->v3d_flag & V3D_HIDE_HELPLINES) == 0) && !is_select_mode; + const bool draw_obcenters = !is_paint_mode && + (pd->overlay.flag & V3D_OVERLAY_HIDE_OBJECT_ORIGINS) == 0; + const bool draw_texspace = (ob->dtx & OB_TEXSPACE) && has_texspace; + const bool draw_obname = (ob->dtx & OB_DRAWNAME) && DRW_state_show_text(); + const bool draw_bounds = has_bounds && ((ob->dt == OB_BOUNDBOX) || + ((ob->dtx & OB_DRAWBOUNDOX) && !from_dupli)); + const bool draw_xform = draw_ctx->object_mode == OB_MODE_OBJECT && + (scene->toolsettings->transform_flag & SCE_XFORM_DATA_ORIGIN) && + (ob->base_flag & BASE_SELECTED) && !is_select_mode; + const bool draw_volume = !from_dupli && (md = modifiers_findByType(ob, eModifierType_Smoke)) && + (modifier_isEnabled(scene, md, eModifierMode_Realtime)) && + (((SmokeModifierData *)md)->domain != NULL); + + float *color; + int theme_id = DRW_object_wire_theme_get(ob, view_layer, &color); + + if (ob->pd && ob->pd->forcefield) { + OVERLAY_forcefield(cb, ob, view_layer); + } + + if (draw_bounds) { + OVERLAY_bounds(cb, ob, theme_id, ob->boundtype, false); + } + /* Helpers for when we're transforming origins. */ + if (draw_xform) { + float color_xform[4] = {0.75f, 0.75f, 0.75f, 0.5f}; + DRW_buffer_add_entry(cb->origin_xform, color_xform, ob->obmat); + } + /* don't show object extras in set's */ + if (!from_dupli) { + if (draw_obcenters) { + OVERLAY_object_center(cb, ob, pd, view_layer); + } + if (draw_relations) { + OVERLAY_relationship_lines(cb, draw_ctx->depsgraph, draw_ctx->scene, ob); + } + if (draw_obname) { + OVERLAY_object_name(ob, theme_id); + } + if (draw_texspace) { + OVERLAY_texture_space(cb, ob, theme_id); + } + if (ob->rigidbody_object != NULL) { + OVERLAY_collision(cb, ob, theme_id); + } + if (ob->dtx & OB_AXIS) { + DRW_buffer_add_entry(cb->empty_axes, color, ob->obmat); + } + if (draw_volume) { + OVERLAY_volume_extra(cb, vedata, ob, md, scene, color); + } + } +} + +void OVERLAY_extra_draw(OVERLAY_Data *vedata) +{ + OVERLAY_FramebufferList *fbl = vedata->fbl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + OVERLAY_PassList *psl = vedata->psl; + + DRW_draw_pass(psl->extra_blend_ps); + + if (pd->antialiasing.enabled) { + GPU_framebuffer_bind(fbl->overlay_line_fb); + } + + DRW_draw_pass(psl->extra_ps[0]); + + if (pd->antialiasing.enabled) { + GPU_framebuffer_bind(fbl->overlay_default_fb); + } +} + +void OVERLAY_extra_in_front_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + + DRW_draw_pass(psl->extra_ps[1]); + + OVERLAY_volume_free_smoke_textures(vedata); +} + +void OVERLAY_extra_centers_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + + DRW_draw_pass(psl->extra_centers_ps); +} diff --git a/source/blender/draw/engines/overlay/overlay_facing.c b/source/blender/draw/engines/overlay/overlay_facing.c new file mode 100644 index 00000000000..a69d7537637 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_facing.c @@ -0,0 +1,64 @@ +/* + * 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 "overlay_private.h" + +void OVERLAY_facing_init(OVERLAY_Data *UNUSED(vedata)) +{ +} + +void OVERLAY_facing_cache_init(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_BLEND_ALPHA; + DRW_PASS_CREATE(psl->facing_ps, state | pd->clipping_state); + + GPUShader *sh = OVERLAY_shader_facing(); + pd->facing_grp = DRW_shgroup_create(sh, psl->facing_ps); +} + +void OVERLAY_facing_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + + struct GPUBatch *geom = DRW_cache_object_surface_get(ob); + if (geom) { + DRW_shgroup_call(pd->facing_grp, geom, ob); + } +} + +void OVERLAY_facing_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + + /* We need to match underlying geometry pass, at the cost of bypassing TAA. */ + DRW_view_set_active(NULL); + + DRW_draw_pass(psl->facing_ps); + + DRW_view_set_active(pd->view_default); +} diff --git a/source/blender/draw/engines/overlay/overlay_grid.c b/source/blender/draw/engines/overlay/overlay_grid.c new file mode 100644 index 00000000000..7dea6c198d0 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_grid.c @@ -0,0 +1,220 @@ +/* + * 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_camera_types.h" + +#include "DEG_depsgraph_query.h" + +#include "ED_view3d.h" + +#include "overlay_private.h" + +enum { + SHOW_AXIS_X = (1 << 0), + SHOW_AXIS_Y = (1 << 1), + SHOW_AXIS_Z = (1 << 2), + SHOW_GRID = (1 << 3), + PLANE_XY = (1 << 4), + PLANE_XZ = (1 << 5), + PLANE_YZ = (1 << 6), + CLIP_ZPOS = (1 << 7), + CLIP_ZNEG = (1 << 8), + GRID_BACK = (1 << 9), +}; + +void OVERLAY_grid_init(OVERLAY_Data *vedata) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + OVERLAY_ShadingData *shd = &pd->shdata; + + const DRWContextState *draw_ctx = DRW_context_state_get(); + View3D *v3d = draw_ctx->v3d; + Scene *scene = draw_ctx->scene; + RegionView3D *rv3d = draw_ctx->rv3d; + + const bool show_axis_x = (pd->v3d_gridflag & V3D_SHOW_X) != 0; + const bool show_axis_y = (pd->v3d_gridflag & V3D_SHOW_Y) != 0; + const bool show_axis_z = (pd->v3d_gridflag & V3D_SHOW_Z) != 0; + const bool show_floor = (pd->v3d_gridflag & V3D_SHOW_FLOOR) != 0; + const bool show_ortho_grid = (pd->v3d_gridflag & V3D_SHOW_ORTHO_GRID) != 0; + + shd->grid_flag = 0; + + if (pd->hide_overlays || !(show_axis_y || show_axis_z || show_floor || show_ortho_grid)) { + return; + } + + float viewinv[4][4], wininv[4][4]; + float viewmat[4][4], winmat[4][4]; + DRW_view_winmat_get(NULL, winmat, false); + DRW_view_winmat_get(NULL, wininv, true); + DRW_view_viewmat_get(NULL, viewmat, false); + DRW_view_viewmat_get(NULL, viewinv, true); + + /* if perps */ + if (winmat[3][3] == 0.0f || rv3d->view == RV3D_VIEW_USER) { + if (show_axis_x) { + shd->grid_flag |= PLANE_XY | SHOW_AXIS_X; + } + if (show_axis_y) { + shd->grid_flag |= PLANE_XY | SHOW_AXIS_Y; + } + if (show_floor) { + shd->grid_flag |= PLANE_XY | SHOW_GRID; + } + } + else { + if (show_ortho_grid && ELEM(rv3d->view, RV3D_VIEW_RIGHT, RV3D_VIEW_LEFT)) { + shd->grid_flag = PLANE_YZ | SHOW_AXIS_Y | SHOW_AXIS_Z | SHOW_GRID | GRID_BACK; + } + else if (show_ortho_grid && ELEM(rv3d->view, RV3D_VIEW_TOP, RV3D_VIEW_BOTTOM)) { + shd->grid_flag = PLANE_XY | SHOW_AXIS_X | SHOW_AXIS_Y | SHOW_GRID | GRID_BACK; + } + else if (show_ortho_grid && ELEM(rv3d->view, RV3D_VIEW_FRONT, RV3D_VIEW_BACK)) { + shd->grid_flag = PLANE_XZ | SHOW_AXIS_X | SHOW_AXIS_Z | SHOW_GRID | GRID_BACK; + } + } + + shd->grid_axes[0] = (float)((shd->grid_flag & (PLANE_XZ | PLANE_XY)) != 0); + shd->grid_axes[1] = (float)((shd->grid_flag & (PLANE_YZ | PLANE_XY)) != 0); + shd->grid_axes[2] = (float)((shd->grid_flag & (PLANE_YZ | PLANE_XZ)) != 0); + + /* Z axis if needed */ + if (((rv3d->view == RV3D_VIEW_USER) || (rv3d->persp != RV3D_ORTHO)) && show_axis_z) { + shd->zpos_flag = SHOW_AXIS_Z; + + float zvec[3], campos[3]; + negate_v3_v3(zvec, viewinv[2]); + copy_v3_v3(campos, viewinv[3]); + + /* z axis : chose the most facing plane */ + if (fabsf(zvec[0]) < fabsf(zvec[1])) { + shd->zpos_flag |= PLANE_XZ; + } + else { + shd->zpos_flag |= PLANE_YZ; + } + + shd->zneg_flag = shd->zpos_flag; + + /* Persp : If camera is below floor plane, we switch clipping + * Ortho : If eye vector is looking up, we switch clipping */ + if (((winmat[3][3] == 0.0f) && (campos[2] > 0.0f)) || + ((winmat[3][3] != 0.0f) && (zvec[2] < 0.0f))) { + shd->zpos_flag |= CLIP_ZPOS; + shd->zneg_flag |= CLIP_ZNEG; + } + else { + shd->zpos_flag |= CLIP_ZNEG; + shd->zneg_flag |= CLIP_ZPOS; + } + + shd->zplane_axes[0] = (float)((shd->zpos_flag & (PLANE_XZ | PLANE_XY)) != 0); + shd->zplane_axes[1] = (float)((shd->zpos_flag & (PLANE_YZ | PLANE_XY)) != 0); + shd->zplane_axes[2] = (float)((shd->zpos_flag & (PLANE_YZ | PLANE_XZ)) != 0); + } + else { + shd->zneg_flag = shd->zpos_flag = CLIP_ZNEG | CLIP_ZPOS; + } + + float dist; + if (rv3d->persp == RV3D_CAMOB && v3d->camera && v3d->camera->type == OB_CAMERA) { + Object *camera_object = DEG_get_evaluated_object(draw_ctx->depsgraph, v3d->camera); + dist = ((Camera *)(camera_object->data))->clip_end; + } + else { + dist = v3d->clip_end; + } + + if (winmat[3][3] == 0.0f) { + shd->grid_mesh_size = dist; + } + else { + float viewdist = 1.0f / min_ff(fabsf(winmat[0][0]), fabsf(winmat[1][1])); + shd->grid_mesh_size = viewdist * dist; + } + + shd->grid_distance = dist / 2.0f; + shd->grid_line_size = max_ff(0.0f, U.pixelsize - 1.0f) * 0.5f; + + ED_view3d_grid_steps(scene, v3d, rv3d, shd->grid_steps); +} + +void OVERLAY_grid_cache_init(OVERLAY_Data *vedata) +{ + OVERLAY_ShadingData *shd = &vedata->stl->pd->shdata; + OVERLAY_PassList *psl = vedata->psl; + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + + psl->grid_ps = NULL; + + if (shd->grid_flag == 0 || !DRW_state_is_fbo()) { + return; + } + + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA; + DRW_PASS_CREATE(psl->grid_ps, state); + + GPUShader *sh = OVERLAY_shader_grid(); + struct GPUBatch *geom = DRW_cache_grid_get(); + + /* Create 3 quads to render ordered transparency Z axis */ + DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->grid_ps); + DRW_shgroup_uniform_int(grp, "gridFlag", &shd->zneg_flag, 1); + DRW_shgroup_uniform_vec3(grp, "planeAxes", shd->zplane_axes, 1); + DRW_shgroup_uniform_float(grp, "gridDistance", &shd->grid_distance, 1); + DRW_shgroup_uniform_float_copy(grp, "lineKernel", shd->grid_line_size); + DRW_shgroup_uniform_float_copy(grp, "meshSize", shd->grid_mesh_size); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth); + DRW_shgroup_call(grp, geom, NULL); + + grp = DRW_shgroup_create(sh, psl->grid_ps); + DRW_shgroup_uniform_int(grp, "gridFlag", &shd->grid_flag, 1); + DRW_shgroup_uniform_vec3(grp, "planeAxes", shd->grid_axes, 1); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth); + DRW_shgroup_uniform_float(grp, "gridSteps", shd->grid_steps, ARRAY_SIZE(shd->grid_steps)); + DRW_shgroup_call(grp, geom, NULL); + + grp = DRW_shgroup_create(sh, psl->grid_ps); + DRW_shgroup_uniform_int(grp, "gridFlag", &shd->zpos_flag, 1); + DRW_shgroup_uniform_vec3(grp, "planeAxes", shd->zplane_axes, 1); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth); + DRW_shgroup_call(grp, geom, NULL); +} + +void OVERLAY_grid_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_FramebufferList *fbl = vedata->fbl; + + if (psl->grid_ps) { + GPU_framebuffer_bind(fbl->overlay_color_only_fb); + DRW_draw_pass(psl->grid_ps); + + GPU_framebuffer_bind(fbl->overlay_default_fb); + } +} diff --git a/source/blender/draw/engines/overlay/overlay_image.c b/source/blender/draw/engines/overlay/overlay_image.c new file mode 100644 index 00000000000..1984e3283a9 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_image.c @@ -0,0 +1,470 @@ +/* + * 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 "BKE_camera.h" +#include "BKE_image.h" +#include "BKE_movieclip.h" +#include "BKE_object.h" + +#include "DNA_camera_types.h" +#include "DNA_screen_types.h" + +#include "DEG_depsgraph_query.h" + +#include "ED_view3d.h" + +#include "IMB_imbuf_types.h" + +#include "overlay_private.h" + +void OVERLAY_image_init(OVERLAY_Data *vedata) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + OVERLAY_PrivateData *pd = vedata->stl->pd; + + pd->view_reference_images = DRW_view_create_with_zoffset( + pd->view_default, draw_ctx->rv3d, -1.0f); +} + +void OVERLAY_image_cache_init(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + DRWState state; + + state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_GREATER | DRW_STATE_BLEND_ALPHA_UNDER_PREMUL; + DRW_PASS_CREATE(psl->image_background_under_ps, state); + + state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_BLEND_ALPHA; + DRW_PASS_CREATE(psl->image_background_over_ps, state); + + state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS; + DRW_PASS_CREATE(psl->image_empties_ps, state | pd->clipping_state); + + state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_LESS_EQUAL | DRW_STATE_BLEND_ALPHA; + DRW_PASS_CREATE(psl->image_empties_back_ps, state | pd->clipping_state); + DRW_PASS_CREATE(psl->image_empties_blend_ps, state | pd->clipping_state); + + state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA; + DRW_PASS_CREATE(psl->image_empties_front_ps, state); + DRW_PASS_CREATE(psl->image_foreground_ps, state); +} + +static void overlay_image_calc_aspect(Image *ima, const int size[2], float r_image_aspect[2]) +{ + float ima_x, ima_y; + if (ima) { + ima_x = size[0]; + ima_y = size[1]; + } + else { + /* if no image, make it a 1x1 empty square, honor scale & offset */ + ima_x = ima_y = 1.0f; + } + /* Get the image aspect even if the buffer is invalid */ + float sca_x = 1.0f, sca_y = 1.0f; + if (ima) { + if (ima->aspx > ima->aspy) { + sca_y = ima->aspy / ima->aspx; + } + else if (ima->aspx < ima->aspy) { + sca_x = ima->aspx / ima->aspy; + } + } + + const float scale_x_inv = ima_x * sca_x; + const float scale_y_inv = ima_y * sca_y; + if (scale_x_inv > scale_y_inv) { + r_image_aspect[0] = 1.0f; + r_image_aspect[1] = scale_y_inv / scale_x_inv; + } + else { + r_image_aspect[0] = scale_x_inv / scale_y_inv; + r_image_aspect[1] = 1.0f; + } +} + +static void camera_background_images_stereo_setup(Scene *scene, + View3D *v3d, + Image *ima, + ImageUser *iuser) +{ + if (BKE_image_is_stereo(ima)) { + iuser->flag |= IMA_SHOW_STEREO; + + if ((scene->r.scemode & R_MULTIVIEW) == 0) { + iuser->multiview_eye = STEREO_LEFT_ID; + } + else if (v3d->stereo3d_camera != STEREO_3D_ID) { + /* show only left or right camera */ + iuser->multiview_eye = v3d->stereo3d_camera; + } + + BKE_image_multiview_index(ima, iuser); + } + else { + iuser->flag &= ~IMA_SHOW_STEREO; + } +} + +static struct GPUTexture *image_camera_background_texture_get(CameraBGImage *bgpic, + const DRWContextState *draw_ctx, + OVERLAY_PrivateData *pd, + float *r_aspect, + bool *r_use_alpha_premult) +{ + Image *image = bgpic->ima; + ImageUser *iuser = &bgpic->iuser; + MovieClip *clip = NULL; + GPUTexture *tex = NULL; + Scene *scene = draw_ctx->scene; + float aspect_x, aspect_y; + int width, height; + int ctime = (int)DEG_get_ctime(draw_ctx->depsgraph); + *r_use_alpha_premult = false; + + switch (bgpic->source) { + case CAM_BGIMG_SOURCE_IMAGE: + if (image == NULL) { + return NULL; + } + *r_use_alpha_premult = (image->alpha_mode == IMA_ALPHA_PREMUL); + + BKE_image_user_frame_calc(image, iuser, ctime); + if (image->source == IMA_SRC_SEQUENCE && !(iuser->flag & IMA_USER_FRAME_IN_RANGE)) { + /* Frame is out of range, dont show. */ + return NULL; + } + else { + camera_background_images_stereo_setup(scene, draw_ctx->v3d, image, iuser); + } + + ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, NULL); + if (ibuf == NULL) { + return NULL; + } + + tex = GPU_texture_from_blender(image, iuser, GL_TEXTURE_2D); + if (tex == NULL) { + return NULL; + } + + aspect_x = bgpic->ima->aspx; + aspect_y = bgpic->ima->aspy; + + width = ibuf->x; + height = ibuf->y; + + BKE_image_release_ibuf(image, ibuf, NULL); + break; + + case CAM_BGIMG_SOURCE_MOVIE: + if (bgpic->flag & CAM_BGIMG_FLAG_CAMERACLIP) { + if (scene->camera) { + clip = BKE_object_movieclip_get(scene, scene->camera, true); + } + } + else { + clip = bgpic->clip; + } + + if (clip == NULL) { + return NULL; + } + + BKE_movieclip_user_set_frame(&bgpic->cuser, ctime); + tex = GPU_texture_from_movieclip(clip, &bgpic->cuser, GL_TEXTURE_2D); + if (tex == NULL) { + return NULL; + } + + aspect_x = clip->aspx; + aspect_y = clip->aspy; + + BKE_movieclip_get_size(clip, &bgpic->cuser, &width, &height); + + /* Save for freeing. */ + BLI_addtail(&pd->bg_movie_clips, BLI_genericNodeN(clip)); + break; + + default: + /* Unsupported type. */ + return NULL; + } + + *r_aspect = (width * aspect_x) / (height * aspect_y); + return tex; +} + +static void OVERLAY_image_free_movieclips_textures(OVERLAY_Data *data) +{ + /* Free Movie clip textures after rendering */ + LinkData *link; + while ((link = BLI_pophead(&data->stl->pd->bg_movie_clips))) { + MovieClip *clip = (MovieClip *)link->data; + GPU_free_texture_movieclip(clip); + MEM_freeN(link); + } +} + +static void image_camera_background_matrix_get(const Camera *cam, + const CameraBGImage *bgpic, + const DRWContextState *draw_ctx, + const float image_aspect, + float rmat[4][4]) +{ + float rotate[4][4], scale[4][4], translate[4][4]; + + axis_angle_to_mat4_single(rotate, 'Z', -bgpic->rotation); + unit_m4(scale); + unit_m4(translate); + + /* Normalized Object space camera frame corners. */ + float cam_corners[4][3]; + BKE_camera_view_frame(draw_ctx->scene, cam, cam_corners); + float cam_width = fabsf(cam_corners[0][0] - cam_corners[3][0]); + float cam_height = fabsf(cam_corners[0][1] - cam_corners[1][1]); + float cam_aspect = cam_width / cam_height; + + if (bgpic->flag & CAM_BGIMG_FLAG_CAMERA_CROP) { + /* Crop. */ + if (image_aspect > cam_aspect) { + scale[0][0] *= cam_height * image_aspect; + scale[1][1] *= cam_height; + } + else { + scale[0][0] *= cam_width; + scale[1][1] *= cam_width / image_aspect; + } + } + else if (bgpic->flag & CAM_BGIMG_FLAG_CAMERA_ASPECT) { + /* Fit. */ + if (image_aspect > cam_aspect) { + scale[0][0] *= cam_width; + scale[1][1] *= cam_width / image_aspect; + } + else { + scale[0][0] *= cam_height * image_aspect; + scale[1][1] *= cam_height; + } + } + else { + /* Stretch. */ + scale[0][0] *= cam_width; + scale[1][1] *= cam_height; + } + + translate[3][0] = bgpic->offset[0]; + translate[3][1] = bgpic->offset[1]; + translate[3][2] = cam_corners[0][2]; + /* These lines are for keeping 2.80 behavior and could be removed to keep 2.79 behavior. */ + translate[3][0] *= min_ff(1.0f, cam_aspect); + translate[3][1] /= max_ff(1.0f, cam_aspect) * (image_aspect / cam_aspect); + /* quad is -1..1 so divide by 2. */ + scale[0][0] *= 0.5f * bgpic->scale * ((bgpic->flag & CAM_BGIMG_FLAG_FLIP_X) ? -1.0 : 1.0); + scale[1][1] *= 0.5f * bgpic->scale * ((bgpic->flag & CAM_BGIMG_FLAG_FLIP_Y) ? -1.0 : 1.0); + /* Camera shift. (middle of cam_corners) */ + translate[3][0] += (cam_corners[0][0] + cam_corners[2][0]) * 0.5f; + translate[3][1] += (cam_corners[0][1] + cam_corners[2][1]) * 0.5f; + + mul_m4_series(rmat, translate, rotate, scale); +} + +void OVERLAY_image_camera_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + OVERLAY_PassList *psl = vedata->psl; + const DRWContextState *draw_ctx = DRW_context_state_get(); + Camera *cam = ob->data; + + const bool show_frame = BKE_object_empty_image_frame_is_visible_in_view3d(ob, draw_ctx->rv3d); + + if (!show_frame || DRW_state_is_select()) { + return; + } + + float norm_obmat[4][4]; + normalize_m4_m4(norm_obmat, ob->obmat); + + for (CameraBGImage *bgpic = cam->bg_images.first; bgpic; bgpic = bgpic->next) { + if (bgpic->flag & CAM_BGIMG_FLAG_DISABLED) { + continue; + } + + float aspect = 1.0; + bool use_alpha_premult; + float mat[4][4]; + + /* retrieve the image we want to show, continue to next when no image could be found */ + GPUTexture *tex = image_camera_background_texture_get( + bgpic, draw_ctx, pd, &aspect, &use_alpha_premult); + + if (tex) { + image_camera_background_matrix_get(cam, bgpic, draw_ctx, aspect, mat); + + mul_m4_m4m4(mat, norm_obmat, mat); + const bool is_foreground = (bgpic->flag & CAM_BGIMG_FLAG_FOREGROUND) != 0; + + /* When drawing background we do 2 passes. + * - One alpha over, which works where background is visible. + * - One alpha under, works under partially visible objects. (only in cycles) + * This approach is not ideal and should be revisited. + **/ + for (int i = 0; i < (is_foreground ? 1 : 2); i++) { + DRWPass *pass = is_foreground ? psl->image_foreground_ps : + ((i == 0) ? psl->image_background_under_ps : + psl->image_background_over_ps); + GPUShader *sh = OVERLAY_shader_image(); + DRWShadingGroup *grp = DRW_shgroup_create(sh, pass); + float color[4] = {1.0f, 1.0f, 1.0f, bgpic->alpha}; + DRW_shgroup_uniform_texture(grp, "imgTexture", tex); + DRW_shgroup_uniform_bool_copy(grp, "imgPremultiplied", use_alpha_premult); + DRW_shgroup_uniform_bool_copy(grp, "imgAlphaBlend", true); + DRW_shgroup_uniform_bool_copy(grp, "imgLinear", !DRW_state_do_color_management()); + DRW_shgroup_uniform_bool_copy(grp, "depthSet", true); + DRW_shgroup_uniform_vec4_copy(grp, "color", color); + DRW_shgroup_call_obmat(grp, DRW_cache_quad_get(), mat); + } + } + } +} + +void OVERLAY_image_empty_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_PassList *psl = vedata->psl; + const DRWContextState *draw_ctx = DRW_context_state_get(); + const RegionView3D *rv3d = draw_ctx->rv3d; + GPUTexture *tex = NULL; + Image *ima = ob->data; + float mat[4][4]; + + const bool show_frame = BKE_object_empty_image_frame_is_visible_in_view3d(ob, rv3d); + const bool show_image = show_frame && BKE_object_empty_image_data_is_visible_in_view3d(ob, rv3d); + const bool use_alpha_blend = (ob->empty_image_flag & OB_EMPTY_IMAGE_USE_ALPHA_BLEND) != 0; + const bool use_alpha_premult = ima && (ima->alpha_mode == IMA_ALPHA_PREMUL); + + if (!show_frame) { + return; + } + + { + /* Calling 'BKE_image_get_size' may free the texture. Get the size from 'tex' instead, + * see: T59347 */ + int size[2] = {0}; + if (ima != NULL) { + tex = GPU_texture_from_blender(ima, ob->iuser, GL_TEXTURE_2D); + if (tex) { + size[0] = GPU_texture_orig_width(tex); + size[1] = GPU_texture_orig_height(tex); + } + } + CLAMP_MIN(size[0], 1); + CLAMP_MIN(size[1], 1); + + float image_aspect[2]; + overlay_image_calc_aspect(ob->data, size, image_aspect); + + copy_m4_m4(mat, ob->obmat); + mul_v3_fl(mat[0], image_aspect[0] * 0.5f * ob->empty_drawsize); + mul_v3_fl(mat[1], image_aspect[1] * 0.5f * ob->empty_drawsize); + madd_v3_v3fl(mat[3], mat[0], ob->ima_ofs[0] * 2.0f + 1.0f); + madd_v3_v3fl(mat[3], mat[1], ob->ima_ofs[1] * 2.0f + 1.0f); + } + + /* Use the actual depth if we are doing depth tests to determine the distance to the object */ + char depth_mode = DRW_state_is_depth() ? OB_EMPTY_IMAGE_DEPTH_DEFAULT : ob->empty_image_depth; + DRWPass *pass = NULL; + switch (depth_mode) { + case OB_EMPTY_IMAGE_DEPTH_DEFAULT: + pass = (use_alpha_blend) ? psl->image_empties_blend_ps : psl->image_empties_ps; + break; + case OB_EMPTY_IMAGE_DEPTH_BACK: + pass = psl->image_empties_back_ps; + break; + case OB_EMPTY_IMAGE_DEPTH_FRONT: + pass = psl->image_empties_front_ps; + break; + } + + if (show_frame) { + OVERLAY_ExtraCallBuffers *cb = OVERLAY_extra_call_buffer_get(vedata, ob); + float *color; + DRW_object_wire_theme_get(ob, draw_ctx->view_layer, &color); + OVERLAY_empty_shape(cb, mat, 1.0f, OB_EMPTY_IMAGE, color); + } + + if (show_image && tex && ((ob->color[3] > 0.0f) || !use_alpha_blend)) { + GPUShader *sh = OVERLAY_shader_image(); + DRWShadingGroup *grp = DRW_shgroup_create(sh, pass); + DRW_shgroup_uniform_texture(grp, "imgTexture", tex); + DRW_shgroup_uniform_bool_copy(grp, "imgPremultiplied", use_alpha_premult); + DRW_shgroup_uniform_bool_copy(grp, "imgAlphaBlend", use_alpha_blend); + DRW_shgroup_uniform_bool_copy(grp, "imgLinear", false); + DRW_shgroup_uniform_bool_copy(grp, "depthSet", depth_mode != OB_EMPTY_IMAGE_DEPTH_DEFAULT); + DRW_shgroup_uniform_vec4_copy(grp, "color", ob->color); + DRW_shgroup_call_obmat(grp, DRW_cache_quad_get(), mat); + } +} + +void OVERLAY_image_cache_finish(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + + DRW_pass_sort_shgroup_reverse(psl->image_background_under_ps); + DRW_pass_sort_shgroup_z(psl->image_empties_blend_ps); + DRW_pass_sort_shgroup_z(psl->image_empties_front_ps); + DRW_pass_sort_shgroup_z(psl->image_empties_back_ps); +} + +void OVERLAY_image_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + + DRW_view_set_active(pd->view_reference_images); + + DRW_draw_pass(psl->image_background_over_ps); + DRW_draw_pass(psl->image_background_under_ps); + DRW_draw_pass(psl->image_empties_back_ps); + + DRW_draw_pass(psl->image_empties_ps); + DRW_draw_pass(psl->image_empties_blend_ps); + + DRW_view_set_active(pd->view_default); +} + +void OVERLAY_image_in_front_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + + DRW_view_set_active(pd->view_reference_images); + + DRW_draw_pass(psl->image_empties_front_ps); + DRW_draw_pass(psl->image_foreground_ps); + + DRW_view_set_active(pd->view_default); + + OVERLAY_image_free_movieclips_textures(vedata); +} diff --git a/source/blender/draw/engines/overlay/overlay_lattice.c b/source/blender/draw/engines/overlay/overlay_lattice.c new file mode 100644 index 00000000000..4e4081ee499 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_lattice.c @@ -0,0 +1,78 @@ +/* + * 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 "overlay_private.h" + +void OVERLAY_edit_lattice_cache_init(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + struct GPUShader *sh; + DRWShadingGroup *grp; + + { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL; + DRW_PASS_CREATE(psl->edit_lattice_ps, state | pd->clipping_state); + + sh = OVERLAY_shader_edit_lattice_wire(); + pd->edit_lattice_wires_grp = grp = DRW_shgroup_create(sh, psl->edit_lattice_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_texture(grp, "weightTex", G_draw.weight_ramp); + + sh = OVERLAY_shader_edit_lattice_point(); + pd->edit_lattice_points_grp = grp = DRW_shgroup_create(sh, psl->edit_lattice_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + } +} + +void OVERLAY_edit_lattice_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + struct GPUBatch *geom; + + geom = DRW_cache_lattice_wire_get(ob, true); + DRW_shgroup_call(pd->edit_lattice_wires_grp, geom, ob); + + geom = DRW_cache_lattice_vert_overlay_get(ob); + DRW_shgroup_call(pd->edit_lattice_points_grp, geom, ob); +} + +void OVERLAY_lattice_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_ExtraCallBuffers *cb = OVERLAY_extra_call_buffer_get(vedata, ob); + const DRWContextState *draw_ctx = DRW_context_state_get(); + + float *color; + DRW_object_wire_theme_get(ob, draw_ctx->view_layer, &color); + + struct GPUBatch *geom = DRW_cache_lattice_wire_get(ob, false); + OVERLAY_extra_wire(cb, geom, ob->obmat, color); +} + +void OVERLAY_edit_lattice_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + + DRW_draw_pass(psl->edit_lattice_ps); +} diff --git a/source/blender/draw/engines/overlay/overlay_metaball.c b/source/blender/draw/engines/overlay/overlay_metaball.c new file mode 100644 index 00000000000..a634aed812c --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_metaball.c @@ -0,0 +1,143 @@ +/* + * 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_meta_types.h" + +#include "BKE_object.h" + +#include "DEG_depsgraph_query.h" + +#include "ED_mball.h" + +#include "overlay_private.h" + +void OVERLAY_metaball_cache_init(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + + OVERLAY_InstanceFormats *formats = OVERLAY_shader_instance_formats_get(); + +#define BUF_INSTANCE DRW_shgroup_call_buffer_instance + + for (int i = 0; i < 2; i++) { + DRWState infront_state = (DRW_state_is_select() && (i == 1)) ? DRW_STATE_IN_FRONT_SELECT : 0; + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL; + DRW_PASS_CREATE(psl->metaball_ps[i], state | pd->clipping_state | infront_state); + + /* Reuse armature shader as it's perfect to outline ellipsoids. */ + struct GPUVertFormat *format = formats->instance_bone; + struct GPUShader *sh = OVERLAY_shader_armature_sphere(true); + DRWShadingGroup *grp = DRW_shgroup_create(sh, psl->metaball_ps[i]); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + pd->mball.handle[i] = BUF_INSTANCE(grp, format, DRW_cache_bone_point_wire_outline_get()); + } +} + +static void metaball_instance_data_set( + BoneInstanceData *data, Object *ob, const float *pos, const float radius, const float color[4]) +{ + /* Bone point radius is 0.05. Compensate for that. */ + mul_v3_v3fl(data->mat[0], ob->obmat[0], radius / 0.05f); + mul_v3_v3fl(data->mat[1], ob->obmat[1], radius / 0.05f); + mul_v3_v3fl(data->mat[2], ob->obmat[2], radius / 0.05f); + mul_v3_m4v3(data->mat[3], ob->obmat, pos); + /* WATCH: Reminder, alpha is wiresize. */ + OVERLAY_bone_instance_data_set_color(data, color); +} + +void OVERLAY_edit_metaball_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + const bool do_in_front = (ob->dtx & OB_DRAWXRAY) != 0; + const bool is_select = DRW_state_is_select(); + OVERLAY_PrivateData *pd = vedata->stl->pd; + MetaBall *mb = ob->data; + + const float *color; + const float col_radius[4] = {0.63f, 0.19f, 0.19f, 1.0f}; /* 0x3030A0 */ + const float col_radius_select[4] = {0.94f, 0.63f, 0.63f, 1.0f}; /* 0xA0A0F0 */ + const float col_stiffness[4] = {0.19f, 0.63f, 0.19f, 1.0f}; /* 0x30A030 */ + const float col_stiffness_select[4] = {0.63f, 0.94f, 0.63f, 1.0f}; /* 0xA0F0A0 */ + + int select_id = 0; + if (is_select) { + const Object *orig_object = DEG_get_original_object(ob); + select_id = orig_object->runtime.select_id; + } + + LISTBASE_FOREACH (MetaElem *, ml, mb->editelems) { + const bool is_selected = (ml->flag & SELECT) != 0; + const bool is_scale_radius = (ml->flag & MB_SCALE_RAD) != 0; + float stiffness_radius = ml->rad * atanf(ml->s) / (float)M_PI_2; + BoneInstanceData instdata; + + if (is_select) { + DRW_select_load_id(select_id | MBALLSEL_RADIUS); + } + color = (is_selected && is_scale_radius) ? col_radius_select : col_radius; + metaball_instance_data_set(&instdata, ob, &ml->x, ml->rad, color); + DRW_buffer_add_entry_struct(pd->mball.handle[do_in_front], &instdata); + + if (is_select) { + DRW_select_load_id(select_id | MBALLSEL_STIFF); + } + color = (is_selected && !is_scale_radius) ? col_stiffness_select : col_stiffness; + metaball_instance_data_set(&instdata, ob, &ml->x, stiffness_radius, color); + DRW_buffer_add_entry_struct(pd->mball.handle[do_in_front], &instdata); + + select_id += 0x10000; + } +} + +void OVERLAY_metaball_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + const bool do_in_front = (ob->dtx & OB_DRAWXRAY) != 0; + OVERLAY_PrivateData *pd = vedata->stl->pd; + MetaBall *mb = ob->data; + const DRWContextState *draw_ctx = DRW_context_state_get(); + + float *color; + DRW_object_wire_theme_get(ob, draw_ctx->view_layer, &color); + + LISTBASE_FOREACH (MetaElem *, ml, &mb->elems) { + /* Draw radius only. */ + BoneInstanceData instdata; + metaball_instance_data_set(&instdata, ob, &ml->x, ml->rad, color); + DRW_buffer_add_entry_struct(pd->mball.handle[do_in_front], &instdata); + } +} + +void OVERLAY_metaball_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + + DRW_draw_pass(psl->metaball_ps[0]); +} + +void OVERLAY_metaball_in_front_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + + DRW_draw_pass(psl->metaball_ps[1]); +} diff --git a/source/blender/draw/engines/overlay/overlay_motion_path.c b/source/blender/draw/engines/overlay/overlay_motion_path.c new file mode 100644 index 00000000000..349f17d3d7d --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_motion_path.c @@ -0,0 +1,231 @@ +/* + * 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 "BLI_string.h" + +#include "DNA_armature_types.h" + +#include "DEG_depsgraph_query.h" + +#include "GPU_batch.h" + +#include "UI_resources.h" + +#include "draw_manager_text.h" + +#include "overlay_private.h" + +void OVERLAY_motion_path_cache_init(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + DRWShadingGroup *grp; + GPUShader *sh; + + DRWState state = DRW_STATE_WRITE_COLOR; + DRW_PASS_CREATE(psl->motion_paths_ps, state | pd->clipping_state); + + sh = OVERLAY_shader_motion_path_line(); + pd->motion_path_lines_grp = grp = DRW_shgroup_create(sh, psl->motion_paths_ps); + DRW_shgroup_uniform_block_persistent(grp, "globalsBlock", G_draw.block_ubo); + + sh = OVERLAY_shader_motion_path_vert(); + pd->motion_path_points_grp = grp = DRW_shgroup_create(sh, psl->motion_paths_ps); + DRW_shgroup_uniform_block_persistent(grp, "globalsBlock", G_draw.block_ubo); +} + +/* Just convert the CPU cache to GPU cache. */ +/* T0D0(fclem) This should go into a draw_cache_impl_motionpath. */ +static GPUVertBuf *mpath_vbo_get(bMotionPath *mpath) +{ + if (!mpath->points_vbo) { + GPUVertFormat format = {0}; + /* Match structure of bMotionPathVert. */ + GPU_vertformat_attr_add(&format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); + GPU_vertformat_attr_add(&format, "flag", GPU_COMP_I32, 1, GPU_FETCH_INT); + mpath->points_vbo = GPU_vertbuf_create_with_format(&format); + GPU_vertbuf_data_alloc(mpath->points_vbo, mpath->length); + /* meh... a useless memcpy. */ + memcpy(mpath->points_vbo->data, mpath->points, sizeof(bMotionPathVert) * mpath->length); + } + return mpath->points_vbo; +} + +static GPUBatch *mpath_batch_line_get(bMotionPath *mpath) +{ + if (!mpath->batch_line) { + mpath->batch_line = GPU_batch_create(GPU_PRIM_LINE_STRIP, mpath_vbo_get(mpath), NULL); + } + return mpath->batch_line; +} + +static GPUBatch *mpath_batch_points_get(bMotionPath *mpath) +{ + if (!mpath->batch_points) { + mpath->batch_points = GPU_batch_create(GPU_PRIM_POINTS, mpath_vbo_get(mpath), NULL); + } + return mpath->batch_points; +} + +static void motion_path_get_frame_range_to_draw(bAnimVizSettings *avs, + bMotionPath *mpath, + int current_frame, + int *r_start, + int *r_end, + int *r_step) +{ + int start, end; + + if (avs->path_type == MOTIONPATH_TYPE_ACFRA) { + start = current_frame - avs->path_bc; + end = current_frame + avs->path_ac + 1; + } + else { + start = avs->path_sf; + end = avs->path_ef; + } + + if (start > end) { + SWAP(int, start, end); + } + + CLAMP(start, mpath->start_frame, mpath->end_frame); + CLAMP(end, mpath->start_frame, mpath->end_frame); + + *r_start = start; + *r_end = end; + *r_step = max_ii(avs->path_step, 1); +} + +static void motion_path_cache(OVERLAY_Data *vedata, + Object *ob, + bPoseChannel *pchan, + bAnimVizSettings *avs, + bMotionPath *mpath) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + const DRWContextState *draw_ctx = DRW_context_state_get(); + struct DRWTextStore *dt = DRW_text_cache_ensure(); + int txt_flag = DRW_TEXT_CACHE_GLOBALSPACE | DRW_TEXT_CACHE_ASCII; + int cfra = (int)DEG_get_ctime(draw_ctx->depsgraph); + bool selected = (pchan) ? (pchan->bone->flag & BONE_SELECTED) : (ob->base_flag & BASE_SELECTED); + bool show_keyframes = (avs->path_viewflag & MOTIONPATH_VIEW_KFRAS) != 0; + bool show_keyframes_no = (avs->path_viewflag & MOTIONPATH_VIEW_KFNOS) != 0; + bool show_frame_no = (avs->path_viewflag & MOTIONPATH_VIEW_FNUMS) != 0; + bool show_lines = (mpath->flag & MOTIONPATH_FLAG_LINES) != 0; + float no_custom_col[3] = {-1.0f, -1.0f, -1.0f}; + float *color = (mpath->flag & MOTIONPATH_FLAG_CUSTOM) ? mpath->color : no_custom_col; + + int sfra, efra, stepsize; + motion_path_get_frame_range_to_draw(avs, mpath, cfra, &sfra, &efra, &stepsize); + + int len = efra - sfra; + if (len == 0) { + return; + } + int start_index = sfra - mpath->start_frame; + + /* Draw curve-line of path. */ + if (show_lines) { + int motion_path_settings[4] = {cfra, sfra, efra, mpath->start_frame}; + DRWShadingGroup *grp = DRW_shgroup_create_sub(pd->motion_path_lines_grp); + DRW_shgroup_uniform_ivec4_copy(grp, "mpathLineSettings", motion_path_settings); + DRW_shgroup_uniform_int_copy(grp, "lineThickness", mpath->line_thickness); + 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); + } + + /* Draw points. */ + { + int pt_size = max_ii(mpath->line_thickness - 1, 1); + int motion_path_settings[4] = {pt_size, cfra, mpath->start_frame, stepsize}; + DRWShadingGroup *grp = DRW_shgroup_create_sub(pd->motion_path_points_grp); + DRW_shgroup_uniform_ivec4_copy(grp, "mpathPointSettings", motion_path_settings); + 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); + } + + /* Draw frame numbers at each framestep value */ + if (show_frame_no || (show_keyframes_no && show_keyframes)) { + int i; + uchar col[4], col_kf[4]; + UI_GetThemeColor3ubv(TH_TEXT_HI, col); + UI_GetThemeColor3ubv(TH_VERTEX_SELECT, col_kf); + col[3] = col_kf[3] = 255; + + bMotionPathVert *mpv = mpath->points + start_index; + for (i = 0; i < len; i += stepsize, mpv += stepsize) { + int frame = sfra + i; + char numstr[32]; + size_t numstr_len; + bool is_keyframe = (mpv->flag & MOTIONPATH_VERT_KEY) != 0; + + if ((show_keyframes && show_keyframes_no && is_keyframe) || (show_frame_no && (i == 0))) { + numstr_len = BLI_snprintf(numstr, sizeof(numstr), " %d", frame); + DRW_text_cache_add( + dt, mpv->co, numstr, numstr_len, 0, 0, txt_flag, (is_keyframe) ? col_kf : col); + } + else if (show_frame_no) { + bMotionPathVert *mpvP = (mpv - stepsize); + bMotionPathVert *mpvN = (mpv + stepsize); + /* Only draw framenum if several consecutive highlighted points don't occur on same point. + */ + if ((equals_v3v3(mpv->co, mpvP->co) == 0) || (equals_v3v3(mpv->co, mpvN->co) == 0)) { + numstr_len = BLI_snprintf(numstr, sizeof(numstr), " %d", frame); + DRW_text_cache_add(dt, mpv->co, numstr, numstr_len, 0, 0, txt_flag, col); + } + } + } + } +} + +void OVERLAY_motion_path_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + + if (ob->type == OB_ARMATURE) { + if (OVERLAY_armature_is_pose_mode(ob, draw_ctx)) { + for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + if (pchan->mpath) { + motion_path_cache(vedata, ob, pchan, &ob->pose->avs, pchan->mpath); + } + } + } + } + + if (ob->mpath) { + motion_path_cache(vedata, ob, NULL, &ob->avs, ob->mpath); + } +} + +void OVERLAY_motion_path_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + + DRW_draw_pass(psl->motion_paths_ps); +} diff --git a/source/blender/draw/engines/overlay/overlay_outline.c b/source/blender/draw/engines/overlay/overlay_outline.c new file mode 100644 index 00000000000..a0c7f575677 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_outline.c @@ -0,0 +1,353 @@ +/* + * 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_lightprobe_types.h" + +#include "UI_resources.h" + +#include "overlay_private.h" + +void OVERLAY_outline_init(OVERLAY_Data *vedata) +{ + OVERLAY_FramebufferList *fbl = vedata->fbl; + OVERLAY_TextureList *txl = vedata->txl; + + if (DRW_state_is_fbo()) { + /* TODO only alloc if needed. */ + /* XXX TODO GPU_R16UI can overflow, it would cause no harm + * (only bad colored or missing outlines) but we should + * use 32bits only if the scene have that many objects */ + DRW_texture_ensure_fullscreen_2d(&txl->temp_depth_tx, GPU_DEPTH24_STENCIL8, 0); + DRW_texture_ensure_fullscreen_2d(&txl->outlines_id_tx, GPU_R16UI, 0); + GPU_framebuffer_ensure_config( + &fbl->outlines_prepass_fb, + {GPU_ATTACHMENT_TEXTURE(txl->temp_depth_tx), GPU_ATTACHMENT_TEXTURE(txl->outlines_id_tx)}); + + for (int i = 0; i < 2; i++) { + DRW_texture_ensure_fullscreen_2d(&txl->outlines_color_tx[i], GPU_RGBA8, DRW_TEX_FILTER); + GPU_framebuffer_ensure_config( + &fbl->outlines_process_fb[i], + {GPU_ATTACHMENT_NONE, GPU_ATTACHMENT_TEXTURE(txl->outlines_color_tx[i])}); + } + } +} + +static int shgroup_theme_id_to_outline_id(int theme_id, const int base_flag) +{ + if (UNLIKELY(base_flag & BASE_FROM_DUPLI)) { + switch (theme_id) { + case TH_ACTIVE: + case TH_SELECT: + return 2; + case TH_TRANSFORM: + return 0; + default: + return -1; + } + } + + switch (theme_id) { + case TH_ACTIVE: + return 3; + case TH_SELECT: + return 1; + case TH_TRANSFORM: + return 0; + default: + return -1; + } +} + +static DRWShadingGroup *shgroup_theme_id_to_outline_or_null(OVERLAY_PrivateData *pd, + int theme_id, + const int base_flag) +{ + int outline_id = shgroup_theme_id_to_outline_id(theme_id, base_flag); + switch (outline_id) { + case 3: /* TH_ACTIVE */ + return pd->outlines_active_grp; + case 2: /* Duplis */ + return pd->outlines_select_dupli_grp; + case 1: /* TH_SELECT */ + return pd->outlines_select_grp; + case 0: /* TH_TRANSFORM */ + return pd->outlines_transform_grp; + default: + return NULL; + } +} + +static DRWShadingGroup *shgroup_theme_id_to_probe_outline_or_null(OVERLAY_PrivateData *pd, + int theme_id, + const int base_flag) +{ + int outline_id = shgroup_theme_id_to_outline_id(theme_id, base_flag); + switch (outline_id) { + case 3: /* TH_ACTIVE */ + return pd->outlines_probe_active_grp; + case 2: /* Duplis */ + return pd->outlines_probe_select_dupli_grp; + case 1: /* TH_SELECT */ + return pd->outlines_probe_select_grp; + case 0: /* TH_TRANSFORM */ + return pd->outlines_probe_transform_grp; + default: + return NULL; + } +} + +static DRWShadingGroup *outline_shgroup(DRWPass *pass, int outline_id, GPUShader *sh) +{ + DRWShadingGroup *grp = DRW_shgroup_create(sh, pass); + DRW_shgroup_uniform_int_copy(grp, "outlineId", outline_id); + return grp; +} + +void OVERLAY_outline_cache_init(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_TextureList *txl = vedata->txl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + DRWShadingGroup *grp = NULL; + + const float outline_width = UI_GetThemeValuef(TH_OUTLINE_WIDTH); + const bool do_outline_expand = (U.pixelsize > 1.0) || (outline_width > 2.0f); + const bool do_large_expand = ((U.pixelsize > 1.0) && (outline_width > 2.0f)) || + (outline_width > 4.0f); + { + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL; + DRW_PASS_CREATE(psl->outlines_prepass_ps, state | pd->clipping_state); + + GPUShader *sh_grid = OVERLAY_shader_outline_prepass_grid(); + GPUShader *sh_geom = OVERLAY_shader_outline_prepass(pd->xray_enabled_and_not_wire); + GPUShader *sh = OVERLAY_shader_outline_prepass(false); + + pd->outlines_transform_grp = outline_shgroup(psl->outlines_prepass_ps, 0, sh_geom); + pd->outlines_select_grp = outline_shgroup(psl->outlines_prepass_ps, 1, sh_geom); + pd->outlines_select_dupli_grp = outline_shgroup(psl->outlines_prepass_ps, 2, sh_geom); + pd->outlines_active_grp = outline_shgroup(psl->outlines_prepass_ps, 3, sh_geom); + + pd->outlines_probe_transform_grp = outline_shgroup(psl->outlines_prepass_ps, 0, sh); + pd->outlines_probe_select_grp = outline_shgroup(psl->outlines_prepass_ps, 1, sh); + pd->outlines_probe_select_dupli_grp = outline_shgroup(psl->outlines_prepass_ps, 2, sh); + pd->outlines_probe_active_grp = outline_shgroup(psl->outlines_prepass_ps, 3, sh); + + pd->outlines_probe_grid_grp = grp = DRW_shgroup_create(sh_grid, psl->outlines_prepass_ps); + DRW_shgroup_uniform_block_persistent(grp, "globalsBlock", G_draw.block_ubo); + } + + /* outlines_prepass_ps is still needed for selection of probes. */ + if (!(pd->v3d_flag & V3D_SELECT_OUTLINE)) { + return; + } + + { + DRW_PASS_CREATE(psl->outlines_detect_ps, DRW_STATE_WRITE_COLOR); + DRW_PASS_CREATE(psl->outlines_expand_ps, DRW_STATE_WRITE_COLOR); + DRW_PASS_CREATE(psl->outlines_bleed_ps, DRW_STATE_WRITE_COLOR); + DRW_PASS_CREATE(psl->outlines_resolve_ps, DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA); + + GPUShader *sh = OVERLAY_shader_outline_detect(pd->xray_enabled_and_not_wire); + + grp = DRW_shgroup_create(sh, psl->outlines_detect_ps); + /* Don't occlude the "outline" detection pass if in xray mode (too much flickering). */ + DRW_shgroup_uniform_float_copy(grp, "alphaOcclu", (pd->xray_enabled) ? 1.0f : 0.35f); + DRW_shgroup_uniform_texture_ref(grp, "outlineId", &txl->outlines_id_tx); + DRW_shgroup_uniform_texture_ref(grp, "outlineDepth", &txl->temp_depth_tx); + DRW_shgroup_uniform_texture_ref(grp, "sceneDepth", &dtxl->depth); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + + if (do_outline_expand) { + sh = OVERLAY_shader_outline_expand(do_large_expand); + grp = DRW_shgroup_create(sh, psl->outlines_expand_ps); + DRW_shgroup_uniform_texture_ref(grp, "outlineColor", &txl->outlines_color_tx[0]); + DRW_shgroup_uniform_bool_copy(grp, "doExpand", true); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + + sh = OVERLAY_shader_outline_expand(false); + grp = DRW_shgroup_create(sh, psl->outlines_bleed_ps); + DRW_shgroup_uniform_texture_ref(grp, "outlineColor", &txl->outlines_color_tx[1]); + DRW_shgroup_uniform_bool_copy(grp, "doExpand", false); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } + else { + sh = OVERLAY_shader_outline_expand(false); + grp = DRW_shgroup_create(sh, psl->outlines_expand_ps); + DRW_shgroup_uniform_texture_ref(grp, "outlineColor", &txl->outlines_color_tx[0]); + DRW_shgroup_uniform_bool_copy(grp, "doExpand", false); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } + + GPUTexture **outline_tx = &txl->outlines_color_tx[do_outline_expand ? 0 : 1]; + sh = OVERLAY_shader_outline_resolve(); + + grp = DRW_shgroup_create(sh, psl->outlines_resolve_ps); + DRW_shgroup_uniform_texture_ref(grp, "outlineBluredColor", outline_tx); + DRW_shgroup_uniform_vec2_copy(grp, "rcpDimensions", DRW_viewport_invert_size_get()); + DRW_shgroup_call_procedural_triangles(grp, NULL, 1); + } +} + +static void outline_lightprobe(OVERLAY_PrivateData *pd, Object *ob, ViewLayer *view_layer) +{ + DRWShadingGroup *grp; + LightProbe *prb = (LightProbe *)ob->data; + int theme_id = DRW_object_wire_theme_get(ob, view_layer, NULL); + + if (prb->type == LIGHTPROBE_TYPE_GRID) { + float corner[3]; + float increment[3][3]; + /* Update transforms */ + float cell_dim[3], half_cell_dim[3]; + cell_dim[0] = 2.0f / (float)(prb->grid_resolution_x); + cell_dim[1] = 2.0f / (float)(prb->grid_resolution_y); + cell_dim[2] = 2.0f / (float)(prb->grid_resolution_z); + + mul_v3_v3fl(half_cell_dim, cell_dim, 0.5f); + + /* First cell. */ + copy_v3_fl(corner, -1.0f); + add_v3_v3(corner, half_cell_dim); + mul_m4_v3(ob->obmat, corner); + + /* Opposite neighbor cell. */ + copy_v3_fl3(increment[0], cell_dim[0], 0.0f, 0.0f); + copy_v3_fl3(increment[1], 0.0f, cell_dim[1], 0.0f); + copy_v3_fl3(increment[2], 0.0f, 0.0f, cell_dim[2]); + + for (int i = 0; i < 3; i++) { + add_v3_v3(increment[i], half_cell_dim); + add_v3_fl(increment[i], -1.0f); + mul_m4_v3(ob->obmat, increment[i]); + sub_v3_v3(increment[i], corner); + } + + int outline_id = shgroup_theme_id_to_outline_id(theme_id, ob->base_flag); + uint cell_count = prb->grid_resolution_x * prb->grid_resolution_y * prb->grid_resolution_z; + grp = DRW_shgroup_create_sub(pd->outlines_probe_grid_grp); + DRW_shgroup_uniform_int_copy(grp, "outlineId", outline_id); + DRW_shgroup_uniform_vec3_copy(grp, "corner", corner); + DRW_shgroup_uniform_vec3_copy(grp, "increment_x", increment[0]); + DRW_shgroup_uniform_vec3_copy(grp, "increment_y", increment[1]); + DRW_shgroup_uniform_vec3_copy(grp, "increment_z", increment[2]); + DRW_shgroup_uniform_ivec3_copy(grp, "grid_resolution", &prb->grid_resolution_x); + DRW_shgroup_call_procedural_points(grp, NULL, cell_count); + } + else if (prb->type == LIGHTPROBE_TYPE_PLANAR && (prb->flag & LIGHTPROBE_FLAG_SHOW_DATA)) { + grp = shgroup_theme_id_to_probe_outline_or_null(pd, theme_id, ob->base_flag); + DRW_shgroup_call_no_cull(grp, DRW_cache_quad_get(), ob); + } +} + +void OVERLAY_outline_cache_populate(OVERLAY_Data *vedata, + Object *ob, + OVERLAY_DupliData *dupli, + bool init_dupli) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + const DRWContextState *draw_ctx = DRW_context_state_get(); + struct GPUBatch *geom; + DRWShadingGroup *shgroup = NULL; + + if (ob->type == OB_LIGHTPROBE) { + outline_lightprobe(pd, ob, draw_ctx->view_layer); + return; + } + + if (dupli && !init_dupli) { + geom = dupli->outline_geom; + shgroup = dupli->outline_shgrp; + } + else { + /* This fixes only the biggest case which is a plane in ortho view. */ + int flat_axis = 0; + bool is_flat_object_viewed_from_side = ((draw_ctx->rv3d->persp == RV3D_ORTHO) && + DRW_object_is_flat(ob, &flat_axis) && + DRW_object_axis_orthogonal_to_view(ob, flat_axis)); + + if (pd->xray_enabled_and_not_wire || is_flat_object_viewed_from_side) { + geom = DRW_cache_object_edge_detection_get(ob, NULL); + } + else { + geom = DRW_cache_object_surface_get(ob); + } + + if (geom) { + int theme_id = DRW_object_wire_theme_get(ob, draw_ctx->view_layer, NULL); + shgroup = shgroup_theme_id_to_outline_or_null(pd, theme_id, ob->base_flag); + } + } + + if (shgroup && geom) { + DRW_shgroup_call(shgroup, geom, ob); + } + + if (init_dupli) { + dupli->outline_shgrp = shgroup; + dupli->outline_geom = geom; + } +} + +void OVERLAY_outline_draw(OVERLAY_Data *vedata) +{ + OVERLAY_FramebufferList *fbl = vedata->fbl; + OVERLAY_PassList *psl = vedata->psl; + float clearcol[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + + bool do_outlines = psl->outlines_prepass_ps != NULL && + !DRW_pass_is_empty(psl->outlines_prepass_ps); + + if (DRW_state_is_fbo() && do_outlines) { + DRW_stats_group_start("Outlines"); + + /* Render filled polygon on a separate framebuffer */ + GPU_framebuffer_bind(fbl->outlines_prepass_fb); + GPU_framebuffer_clear_color_depth(fbl->outlines_prepass_fb, clearcol, 1.0f); + DRW_draw_pass(psl->outlines_prepass_ps); + + /* Search outline pixels */ + GPU_framebuffer_bind(fbl->outlines_process_fb[0]); + DRW_draw_pass(psl->outlines_detect_ps); + + /* Expand outline to form a 3px wide line */ + GPU_framebuffer_bind(fbl->outlines_process_fb[1]); + DRW_draw_pass(psl->outlines_expand_ps); + + /* Bleed color so the AA can do it's stuff */ + GPU_framebuffer_bind(fbl->outlines_process_fb[0]); + DRW_draw_pass(psl->outlines_bleed_ps); + + /* restore main framebuffer */ + GPU_framebuffer_bind(fbl->overlay_default_fb); + DRW_draw_pass(psl->outlines_resolve_ps); + + DRW_stats_group_end(); + } + else if (DRW_state_is_select()) { + /* Render probes spheres/planes so we can select them. */ + DRW_draw_pass(psl->outlines_prepass_ps); + } +} diff --git a/source/blender/draw/engines/overlay/overlay_paint.c b/source/blender/draw/engines/overlay/overlay_paint.c new file mode 100644 index 00000000000..4e73770472d --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_paint.c @@ -0,0 +1,211 @@ +/* + * 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_mesh_types.h" + +#include "DEG_depsgraph_query.h" + +#include "overlay_private.h" + +void OVERLAY_paint_cache_init(OVERLAY_Data *vedata) +{ + const DRWContextState *draw_ctx = DRW_context_state_get(); + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + struct GPUShader *sh; + DRWShadingGroup *grp; + DRWState state; + + const bool use_alpha_blending = (draw_ctx->v3d->shading.type == OB_WIRE); + const bool draw_contours = (pd->overlay.wpaint_flag & V3D_OVERLAY_WPAINT_CONTOURS) != 0; + float opacity = 0.0f; + + switch (pd->ctx_mode) { + case CTX_MODE_POSE: + case CTX_MODE_PAINT_WEIGHT: { + opacity = pd->overlay.weight_paint_mode_opacity; + if (opacity > 0.0f) { + state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL; + state |= use_alpha_blending ? DRW_STATE_BLEND_ALPHA : DRW_STATE_BLEND_MUL; + DRW_PASS_CREATE(psl->paint_color_ps, state | pd->clipping_state); + + sh = OVERLAY_shader_paint_weight(); + pd->paint_surf_grp = grp = DRW_shgroup_create(sh, psl->paint_color_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_bool_copy(grp, "drawContours", draw_contours); + DRW_shgroup_uniform_bool_copy(grp, "useAlphaBlend", use_alpha_blending); + DRW_shgroup_uniform_float_copy(grp, "opacity", opacity); + DRW_shgroup_uniform_texture(grp, "colorramp", G_draw.weight_ramp); + } + break; + } + case CTX_MODE_PAINT_VERTEX: { + opacity = pd->overlay.vertex_paint_mode_opacity; + if (opacity > 0.0f) { + state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL; + state |= use_alpha_blending ? DRW_STATE_BLEND_ALPHA : DRW_STATE_BLEND_MUL; + DRW_PASS_CREATE(psl->paint_color_ps, state | pd->clipping_state); + + sh = OVERLAY_shader_paint_vertcol(); + pd->paint_surf_grp = grp = DRW_shgroup_create(sh, psl->paint_color_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_bool_copy(grp, "useAlphaBlend", use_alpha_blending); + DRW_shgroup_uniform_float_copy(grp, "opacity", opacity); + } + break; + } + case CTX_MODE_PAINT_TEXTURE: { + const ImagePaintSettings *imapaint = &draw_ctx->scene->toolsettings->imapaint; + const bool mask_enabled = imapaint->flag & IMAGEPAINT_PROJECT_LAYER_STENCIL && + imapaint->stencil != NULL; + + opacity = mask_enabled ? pd->overlay.texture_paint_mode_opacity : 0.0f; + if (opacity > 0.0f) { + state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_BLEND_ALPHA; + DRW_PASS_CREATE(psl->paint_color_ps, state | pd->clipping_state); + + GPUTexture *tex = GPU_texture_from_blender(imapaint->stencil, NULL, GL_TEXTURE_2D); + + const bool mask_premult = (imapaint->stencil->alpha_mode == IMA_ALPHA_PREMUL); + const bool mask_inverted = (imapaint->flag & IMAGEPAINT_PROJECT_LAYER_STENCIL_INV) != 0; + sh = OVERLAY_shader_paint_texture(); + pd->paint_surf_grp = grp = DRW_shgroup_create(sh, psl->paint_color_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_float_copy(grp, "opacity", opacity); + DRW_shgroup_uniform_bool_copy(grp, "maskPremult", mask_premult); + DRW_shgroup_uniform_vec3_copy(grp, "maskColor", imapaint->stencil_col); + DRW_shgroup_uniform_bool_copy(grp, "maskInvertStencil", mask_inverted); + DRW_shgroup_uniform_texture(grp, "maskImage", tex); + } + break; + } + default: + BLI_assert(0); + break; + } + + if (opacity <= 0.0f) { + psl->paint_color_ps = NULL; + pd->paint_surf_grp = NULL; + } + + { + state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL; + DRW_PASS_CREATE(psl->paint_overlay_ps, state | pd->clipping_state); + sh = OVERLAY_shader_paint_face(); + pd->paint_face_grp = grp = DRW_shgroup_create(sh, psl->paint_overlay_ps); + DRW_shgroup_uniform_vec4_copy(grp, "color", (float[4]){1.0f, 1.0f, 1.0f, 0.2f}); + DRW_shgroup_state_enable(grp, DRW_STATE_BLEND_ALPHA); + + sh = OVERLAY_shader_paint_wire(); + pd->paint_wire_selected_grp = grp = DRW_shgroup_create(sh, psl->paint_overlay_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_bool_copy(grp, "useSelect", true); + DRW_shgroup_state_enable(grp, DRW_STATE_BLEND_ALPHA); + + pd->paint_wire_grp = grp = DRW_shgroup_create(sh, psl->paint_overlay_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_bool_copy(grp, "useSelect", false); + DRW_shgroup_state_enable(grp, DRW_STATE_BLEND_ALPHA); + + sh = OVERLAY_shader_paint_point(); + pd->paint_point_grp = grp = DRW_shgroup_create(sh, psl->paint_overlay_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + } +} + +void OVERLAY_paint_texture_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + struct GPUBatch *geom = NULL; + + const Mesh *me_orig = DEG_get_original_object(ob)->data; + const bool use_face_sel = (me_orig->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + + if (pd->paint_surf_grp) { + geom = DRW_cache_mesh_surface_texpaint_single_get(ob); + DRW_shgroup_call(pd->paint_surf_grp, geom, ob); + } + + if (use_face_sel) { + geom = DRW_cache_mesh_surface_get(ob); + DRW_shgroup_call(pd->paint_face_grp, geom, ob); + } +} + +void OVERLAY_paint_vertex_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + struct GPUBatch *geom = NULL; + + const Mesh *me = ob->data; + const Mesh *me_orig = DEG_get_original_object(ob)->data; + const bool use_wire = (pd->overlay.paint_flag & V3D_OVERLAY_PAINT_WIRE) != 0; + const bool use_face_sel = (me_orig->editflag & ME_EDIT_PAINT_FACE_SEL) != 0; + const bool use_vert_sel = (me_orig->editflag & ME_EDIT_PAINT_VERT_SEL) != 0; + + if (pd->paint_surf_grp) { + if (ob->mode == OB_MODE_VERTEX_PAINT) { + if (me->mloopcol == NULL) { + return; + } + geom = DRW_cache_mesh_surface_vertpaint_get(ob); + } + else { + geom = DRW_cache_mesh_surface_weights_get(ob); + } + DRW_shgroup_call(pd->paint_surf_grp, geom, ob); + } + + if (use_face_sel || use_wire) { + geom = DRW_cache_mesh_surface_edges_get(ob); + DRW_shgroup_call(use_face_sel ? pd->paint_wire_selected_grp : pd->paint_wire_grp, geom, ob); + } + + if (use_face_sel) { + geom = DRW_cache_mesh_surface_get(ob); + DRW_shgroup_call(pd->paint_face_grp, geom, ob); + } + + if (use_vert_sel) { + geom = DRW_cache_mesh_all_verts_get(ob); + DRW_shgroup_call(pd->paint_point_grp, geom, ob); + } +} + +void OVERLAY_paint_weight_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_paint_vertex_cache_populate(vedata, ob); +} + +void OVERLAY_paint_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + + if (psl->paint_color_ps) { + DRW_view_set_active(NULL); + DRW_draw_pass(psl->paint_color_ps); + } + DRW_draw_pass(psl->paint_overlay_ps); +} diff --git a/source/blender/draw/engines/overlay/overlay_particle.c b/source/blender/draw/engines/overlay/overlay_particle.c new file mode 100644 index 00000000000..f7b6fa7919b --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_particle.c @@ -0,0 +1,217 @@ +/* + * 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 "DEG_depsgraph_query.h" + +#include "DNA_particle_types.h" + +#include "BKE_pointcache.h" + +#include "ED_particle.h" + +#include "overlay_private.h" + +/* -------------------------------------------------------------------- */ +/** \name Edit Particles + * \{ */ + +void OVERLAY_edit_particle_cache_init(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + const DRWContextState *draw_ctx = DRW_context_state_get(); + ParticleEditSettings *pset = PE_settings(draw_ctx->scene); + GPUShader *sh; + DRWShadingGroup *grp; + + pd->edit_particle.use_weight = (pset->brushtype == PE_BRUSH_WEIGHT); + pd->edit_particle.select_mode = pset->selectmode; + + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL; + DRW_PASS_CREATE(psl->edit_particle_ps, state | pd->clipping_state); + + sh = OVERLAY_shader_edit_particle_strand(); + pd->edit_particle_strand_grp = grp = DRW_shgroup_create(sh, psl->edit_particle_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_bool_copy(grp, "useWeight", pd->edit_particle.use_weight); + DRW_shgroup_uniform_texture(grp, "weightTex", G_draw.weight_ramp); + + sh = OVERLAY_shader_edit_particle_point(); + pd->edit_particle_point_grp = grp = DRW_shgroup_create(sh, psl->edit_particle_ps); + DRW_shgroup_uniform_block(grp, "globalsBlock", G_draw.block_ubo); +} + +void OVERLAY_edit_particle_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + const DRWContextState *draw_ctx = DRW_context_state_get(); + Scene *scene_orig = (Scene *)DEG_get_original_id(&draw_ctx->scene->id); + + /* Usually the edit structure is created by Particle Edit Mode Toggle + * operator, but sometimes it's invoked after tagging hair as outdated + * (for example, when toggling edit mode). That makes it impossible to + * create edit structure for until after next dependency graph evaluation. + * + * Ideally, the edit structure will be created here already via some + * dependency graph callback or so, but currently trying to make it nicer + * only causes bad level calls and breaks design from the past. + */ + Object *ob_orig = DEG_get_original_object(ob); + PTCacheEdit *edit = PE_create_current(draw_ctx->depsgraph, scene_orig, ob_orig); + if (edit == NULL) { + /* Happens when trying to edit particles in EMITTER mode without + * having them cached. + */ + return; + } + /* NOTE: We need to pass evaluated particle system, which we need + * to find first. + */ + ParticleSystem *psys = ob->particlesystem.first; + LISTBASE_FOREACH (ParticleSystem *, psys_orig, &ob_orig->particlesystem) { + if (PE_get_current_from_psys(psys_orig) == edit) { + break; + } + psys = psys->next; + } + if (psys == NULL) { + printf("Error getting evaluated particle system for edit.\n"); + return; + } + + struct GPUBatch *geom; + { + geom = DRW_cache_particles_get_edit_strands(ob, psys, edit, pd->edit_particle.use_weight); + DRW_shgroup_call(pd->edit_particle_strand_grp, geom, NULL); + } + + if (pd->edit_particle.select_mode == SCE_SELECT_POINT) { + geom = DRW_cache_particles_get_edit_inner_points(ob, psys, edit); + DRW_shgroup_call(pd->edit_particle_point_grp, geom, NULL); + } + + if (ELEM(pd->edit_particle.select_mode, SCE_SELECT_POINT, SCE_SELECT_END)) { + geom = DRW_cache_particles_get_edit_tip_points(ob, psys, edit); + DRW_shgroup_call(pd->edit_particle_point_grp, geom, NULL); + } +} + +void OVERLAY_edit_particle_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + + DRW_draw_pass(psl->edit_particle_ps); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Particles + * \{ */ + +void OVERLAY_particle_cache_init(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + const DRWContextState *draw_ctx = DRW_context_state_get(); + ParticleEditSettings *pset = PE_settings(draw_ctx->scene); + GPUShader *sh; + DRWShadingGroup *grp; + + pd->edit_particle.use_weight = (pset->brushtype == PE_BRUSH_WEIGHT); + pd->edit_particle.select_mode = pset->selectmode; + + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL; + DRW_PASS_CREATE(psl->particle_ps, state | pd->clipping_state); + + sh = OVERLAY_shader_particle_dot(); + pd->particle_dots_grp = grp = DRW_shgroup_create(sh, psl->particle_ps); + DRW_shgroup_uniform_block_persistent(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_texture_persistent(grp, "weightTex", G_draw.ramp); + + sh = OVERLAY_shader_particle_shape(); + pd->particle_shapes_grp = grp = DRW_shgroup_create(sh, psl->particle_ps); + DRW_shgroup_uniform_block_persistent(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_texture_persistent(grp, "weightTex", G_draw.ramp); +} + +void OVERLAY_particle_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + + LISTBASE_FOREACH (ParticleSystem *, psys, &ob->particlesystem) { + if (!DRW_object_is_visible_psys_in_active_context(ob, psys)) { + continue; + } + + ParticleSettings *part = psys->part; + int draw_as = (part->draw_as == PART_DRAW_REND) ? part->ren_as : part->draw_as; + + if (part->type == PART_HAIR) { + /* Hairs should have been rendered by the render engine.*/ + continue; + } + + if (!ELEM(draw_as, PART_DRAW_NOT, PART_DRAW_OB, PART_DRAW_GR)) { + struct GPUBatch *geom = DRW_cache_particles_get_dots(ob, psys); + struct GPUBatch *shape = NULL; + DRWShadingGroup *grp; + + /* TODO(fclem) Here would be a good place for preemptive culling. */ + + /* fclem: Is color even usefull in our modern context? */ + Material *ma = give_current_material(ob, part->omat); + float color[4] = {0.6f, 0.6f, 0.6f, part->draw_size}; + if (ma != NULL) { + copy_v3_v3(color, &ma->r); + } + + switch (draw_as) { + default: + case PART_DRAW_DOT: + grp = DRW_shgroup_create_sub(pd->particle_dots_grp); + DRW_shgroup_uniform_vec4_copy(grp, "color", color); + DRW_shgroup_call(grp, geom, NULL); + break; + case PART_DRAW_AXIS: + case PART_DRAW_CIRC: + case PART_DRAW_CROSS: + grp = DRW_shgroup_create_sub(pd->particle_shapes_grp); + DRW_shgroup_uniform_vec4_copy(grp, "color", color); + shape = DRW_cache_particles_get_prim(draw_as); + DRW_shgroup_call_instances_with_attribs(grp, NULL, shape, geom); + break; + } + } + } +} + +void OVERLAY_particle_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + + DRW_draw_pass(psl->particle_ps); +} + +/** \} */ diff --git a/source/blender/draw/engines/overlay/overlay_private.h b/source/blender/draw/engines/overlay/overlay_private.h new file mode 100644 index 00000000000..6b030b65ac0 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_private.h @@ -0,0 +1,585 @@ +/* + * 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 DNA + */ + +#ifndef __OVERLAY_PRIVATE_H__ +#define __OVERLAY_PRIVATE_H__ + +#ifdef __APPLE__ +# define USE_GEOM_SHADER_WORKAROUND 1 +#else +# define USE_GEOM_SHADER_WORKAROUND 0 +#endif + +typedef struct OVERLAY_FramebufferList { + struct GPUFrameBuffer *overlay_default_fb; + struct GPUFrameBuffer *overlay_line_fb; + struct GPUFrameBuffer *overlay_color_only_fb; + struct GPUFrameBuffer *overlay_in_front_fb; + struct GPUFrameBuffer *outlines_prepass_fb; + struct GPUFrameBuffer *outlines_process_fb[2]; +} OVERLAY_FramebufferList; + +typedef struct OVERLAY_TextureList { + struct GPUTexture *temp_depth_tx; + struct GPUTexture *dummy_depth_tx; + struct GPUTexture *outlines_id_tx; + struct GPUTexture *outlines_color_tx[2]; + struct GPUTexture *overlay_color_tx; + struct GPUTexture *overlay_color_history_tx; + struct GPUTexture *overlay_line_tx; + struct GPUTexture *edit_mesh_occlude_wire_tx; +} OVERLAY_TextureList; + +#define NOT_IN_FRONT 0 +#define IN_FRONT 1 + +typedef struct OVERLAY_PassList { + DRWPass *antialiasing_ps; + DRWPass *armature_ps[2]; + DRWPass *armature_bone_select_ps; + DRWPass *armature_transp_ps; + DRWPass *edit_curve_wire_ps[2]; + DRWPass *edit_curve_handle_ps; + DRWPass *edit_lattice_ps; + DRWPass *edit_mesh_depth_ps[2]; + DRWPass *edit_mesh_verts_ps[2]; + DRWPass *edit_mesh_edges_ps[2]; + DRWPass *edit_mesh_faces_ps[2]; + DRWPass *edit_mesh_faces_cage_ps[2]; + DRWPass *edit_mesh_analysis_ps; + DRWPass *edit_mesh_normals_ps; + DRWPass *edit_mesh_weight_ps; + DRWPass *edit_particle_ps; + DRWPass *edit_text_overlay_ps; + DRWPass *edit_text_wire_ps[2]; + DRWPass *extra_ps[2]; + DRWPass *extra_blend_ps; + DRWPass *extra_centers_ps; + DRWPass *facing_ps; + DRWPass *grid_ps; + DRWPass *image_background_under_ps; + DRWPass *image_background_over_ps; + DRWPass *image_empties_ps; + DRWPass *image_empties_back_ps; + DRWPass *image_empties_blend_ps; + DRWPass *image_empties_front_ps; + DRWPass *image_foreground_ps; + DRWPass *metaball_ps[2]; + DRWPass *motion_paths_ps; + DRWPass *outlines_prepass_ps; + DRWPass *outlines_detect_ps; + DRWPass *outlines_expand_ps; + DRWPass *outlines_bleed_ps; + DRWPass *outlines_resolve_ps; + DRWPass *paint_color_ps; + DRWPass *paint_overlay_ps; + DRWPass *particle_ps; + DRWPass *sculpt_mask_ps; + DRWPass *wireframe_ps; + DRWPass *wireframe_xray_ps; +} OVERLAY_PassList; + +/* Data used by GLSL shader. To be used as UBO. */ +typedef struct OVERLAY_ShadingData { + /** Grid */ + float grid_axes[3], grid_distance; + float zplane_axes[3], grid_mesh_size; + float grid_steps[8]; + float inv_viewport_size[2]; + float grid_line_size; + int grid_flag; + int zpos_flag; + int zneg_flag; + /** Wireframe */ + float wire_step_param; + /** Edit Curve */ + float edit_curve_normal_length; + /** Edit Mesh */ + int data_mask[4]; +} OVERLAY_ShadingData; + +typedef struct OVERLAY_ExtraCallBuffers { + DRWCallBuffer *camera_frame; + DRWCallBuffer *camera_tria[2]; + DRWCallBuffer *camera_distances; + DRWCallBuffer *camera_volume; + DRWCallBuffer *camera_volume_frame; + + DRWCallBuffer *center_active; + DRWCallBuffer *center_selected; + DRWCallBuffer *center_deselected; + DRWCallBuffer *center_selected_lib; + DRWCallBuffer *center_deselected_lib; + + DRWCallBuffer *empty_axes; + DRWCallBuffer *empty_capsule_body; + DRWCallBuffer *empty_capsule_cap; + DRWCallBuffer *empty_circle; + DRWCallBuffer *empty_cone; + DRWCallBuffer *empty_cube; + DRWCallBuffer *empty_cylinder; + DRWCallBuffer *empty_image_frame; + DRWCallBuffer *empty_plain_axes; + DRWCallBuffer *empty_single_arrow; + DRWCallBuffer *empty_sphere; + DRWCallBuffer *empty_sphere_solid; + + DRWCallBuffer *extra_dashed_lines; + DRWCallBuffer *extra_lines; + + DRWCallBuffer *field_curve; + DRWCallBuffer *field_force; + DRWCallBuffer *field_vortex; + DRWCallBuffer *field_wind; + DRWCallBuffer *field_cone_limit; + DRWCallBuffer *field_sphere_limit; + DRWCallBuffer *field_tube_limit; + + DRWCallBuffer *groundline; + + DRWCallBuffer *light_point; + DRWCallBuffer *light_sun; + DRWCallBuffer *light_spot; + DRWCallBuffer *light_spot_cone_back; + DRWCallBuffer *light_spot_cone_front; + DRWCallBuffer *light_area[2]; + + DRWCallBuffer *origin_xform; + + DRWCallBuffer *probe_planar; + DRWCallBuffer *probe_cube; + DRWCallBuffer *probe_grid; + + DRWCallBuffer *speaker; + + DRWShadingGroup *extra_wire; + DRWShadingGroup *extra_loose_points; +} OVERLAY_ExtraCallBuffers; + +typedef struct OVERLAY_ArmatureCallBuffers { + DRWCallBuffer *box_outline; + DRWCallBuffer *box_solid; + + DRWCallBuffer *dof_lines; + DRWCallBuffer *dof_sphere; + + DRWCallBuffer *envelope_distance; + DRWCallBuffer *envelope_outline; + DRWCallBuffer *envelope_solid; + + DRWCallBuffer *octa_outline; + DRWCallBuffer *octa_solid; + + DRWCallBuffer *point_outline; + DRWCallBuffer *point_solid; + + DRWCallBuffer *stick; + + DRWCallBuffer *wire; + + DRWShadingGroup *custom_solid; + DRWShadingGroup *custom_outline; + GHash *custom_shapes_ghash; +} OVERLAY_ArmatureCallBuffers; + +typedef struct OVERLAY_PrivateData { + DRWShadingGroup *armature_bone_select_act_grp; + DRWShadingGroup *armature_bone_select_grp; + DRWShadingGroup *edit_curve_normal_grp[2]; + DRWShadingGroup *edit_curve_wire_grp[2]; + DRWShadingGroup *edit_curve_handle_grp; + DRWShadingGroup *edit_curve_points_grp; + DRWShadingGroup *edit_lattice_points_grp; + DRWShadingGroup *edit_lattice_wires_grp; + DRWShadingGroup *edit_mesh_depth_grp[2]; + DRWShadingGroup *edit_mesh_faces_grp[2]; + DRWShadingGroup *edit_mesh_faces_cage_grp[2]; + DRWShadingGroup *edit_mesh_verts_grp[2]; + DRWShadingGroup *edit_mesh_edges_grp[2]; + DRWShadingGroup *edit_mesh_facedots_grp[2]; + DRWShadingGroup *edit_mesh_skin_roots_grp[2]; + DRWShadingGroup *edit_mesh_normals_grp; + DRWShadingGroup *edit_mesh_analysis_grp; + DRWShadingGroup *edit_mesh_weight_grp; + DRWShadingGroup *edit_particle_strand_grp; + DRWShadingGroup *edit_particle_point_grp; + DRWShadingGroup *edit_text_overlay_grp; + DRWShadingGroup *edit_text_wire_grp[2]; + DRWShadingGroup *facing_grp; + DRWShadingGroup *motion_path_lines_grp; + DRWShadingGroup *motion_path_points_grp; + DRWShadingGroup *outlines_active_grp; + DRWShadingGroup *outlines_select_grp; + DRWShadingGroup *outlines_select_dupli_grp; + DRWShadingGroup *outlines_transform_grp; + DRWShadingGroup *outlines_probe_transform_grp; + DRWShadingGroup *outlines_probe_select_grp; + DRWShadingGroup *outlines_probe_select_dupli_grp; + DRWShadingGroup *outlines_probe_active_grp; + DRWShadingGroup *outlines_probe_grid_grp; + DRWShadingGroup *paint_surf_grp; + DRWShadingGroup *paint_wire_grp; + DRWShadingGroup *paint_wire_selected_grp; + DRWShadingGroup *paint_point_grp; + DRWShadingGroup *paint_face_grp; + DRWShadingGroup *particle_dots_grp; + DRWShadingGroup *particle_shapes_grp; + DRWShadingGroup *sculpt_mask_grp; + DRWShadingGroup *wires_grp[2][2]; /* With and without coloring. */ + DRWShadingGroup *wires_all_grp[2][2]; /* With and without coloring. */ + DRWShadingGroup *wires_sculpt_grp[2]; + + DRWView *view_default; + DRWView *view_wires; + DRWView *view_edit_faces; + DRWView *view_edit_faces_cage; + DRWView *view_edit_edges; + DRWView *view_edit_verts; + DRWView *view_reference_images; + + /** TODO get rid of this. */ + ListBase smoke_domains; + ListBase bg_movie_clips; + + /** Two instances for in_front option and without. */ + OVERLAY_ExtraCallBuffers extra_call_buffers[2]; + + OVERLAY_ArmatureCallBuffers armature_call_buffers[2]; + + View3DOverlay overlay; + enum eContextObjectMode ctx_mode; + bool clear_in_front; + bool wireframe_mode; + bool hide_overlays; + bool xray_enabled; + bool xray_enabled_and_not_wire; + short v3d_flag; /* TODO move to View3DOverlay */ + short v3d_gridflag; /* TODO move to View3DOverlay */ + DRWState clipping_state; + OVERLAY_ShadingData shdata; + + struct { + short sample; + short target_sample; + float prev_persmat[4][4]; + bool enabled; + } antialiasing; + struct { + bool show_handles; + } edit_curve; + struct { + int ghost_ob; + int edit_ob; + bool do_zbufclip; + bool do_faces; + bool do_edges; + bool select_vert; + bool select_face; + bool select_edge; + int flag; /** Copy of v3d->overlay.edit_flag. */ + } edit_mesh; + struct { + bool use_weight; + int select_mode; + } edit_particle; + struct { + bool transparent; + bool show_relations; + bool do_pose_fade_geom; + } armature; + struct { + DRWCallBuffer *handle[2]; + } mball; +} OVERLAY_PrivateData; /* Transient data */ + +typedef struct OVERLAY_StorageList { + struct OVERLAY_PrivateData *pd; +} OVERLAY_StorageList; + +typedef struct OVERLAY_Data { + void *engine_type; + OVERLAY_FramebufferList *fbl; + OVERLAY_TextureList *txl; + OVERLAY_PassList *psl; + OVERLAY_StorageList *stl; +} OVERLAY_Data; + +typedef struct OVERLAY_DupliData { + DRWShadingGroup *wire_shgrp; + DRWShadingGroup *outline_shgrp; + DRWShadingGroup *extra_shgrp; + struct GPUBatch *wire_geom; + struct GPUBatch *outline_geom; + struct GPUBatch *extra_geom; + short base_flag; +} OVERLAY_DupliData; + +typedef struct BoneInstanceData { + /* Keep sync with bone instance vertex format (OVERLAY_InstanceFormats) */ + union { + float mat[4][4]; + struct { + float _pad0[3], color_hint_a; + float _pad1[3], color_hint_b; + float _pad2[3], color_a; + float _pad3[3], color_b; + }; + struct { + float _pad00[3], amin_a; + float _pad01[3], amin_b; + float _pad02[3], amax_a; + float _pad03[3], amax_b; + }; + }; +} BoneInstanceData; + +typedef struct OVERLAY_InstanceFormats { + struct GPUVertFormat *instance_pos; + struct GPUVertFormat *instance_extra; + struct GPUVertFormat *instance_bone; + struct GPUVertFormat *instance_bone_outline; + struct GPUVertFormat *instance_bone_envelope; + struct GPUVertFormat *instance_bone_envelope_distance; + struct GPUVertFormat *instance_bone_envelope_outline; + struct GPUVertFormat *instance_bone_stick; + struct GPUVertFormat *pos; + struct GPUVertFormat *pos_color; + struct GPUVertFormat *wire_extra; +} OVERLAY_InstanceFormats; + +/* Pack data into the last row of the 4x4 matrix. It will be decoded by the vertex shader. */ +BLI_INLINE void pack_data_in_mat4( + float rmat[4][4], const float mat[4][4], float a, float b, float c, float d) +{ + copy_m4_m4(rmat, mat); + rmat[0][3] = a; + rmat[1][3] = b; + rmat[2][3] = c; + rmat[3][3] = d; +} + +BLI_INLINE void pack_v4_in_mat4(float rmat[4][4], const float mat[4][4], const float v[4]) +{ + pack_data_in_mat4(rmat, mat, v[0], v[1], v[2], v[3]); +} + +BLI_INLINE void pack_fl_in_mat4(float rmat[4][4], const float mat[4][4], float a) +{ + copy_m4_m4(rmat, mat); + rmat[3][3] = a; +} + +void OVERLAY_antialiasing_reset(OVERLAY_Data *vedata); +void OVERLAY_antialiasing_init(OVERLAY_Data *vedata); +void OVERLAY_antialiasing_cache_init(OVERLAY_Data *vedata); +void OVERLAY_antialiasing_cache_finish(OVERLAY_Data *vedata); +void OVERLAY_antialiasing_start(OVERLAY_Data *vedata); +void OVERLAY_antialiasing_end(OVERLAY_Data *vedata); + +bool OVERLAY_armature_is_pose_mode(Object *ob, const struct DRWContextState *draw_ctx); +void OVERLAY_armature_cache_init(OVERLAY_Data *vedata); +void OVERLAY_armature_cache_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_edit_armature_cache_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_pose_armature_cache_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_armature_cache_finish(OVERLAY_Data *vedata); +void OVERLAY_armature_draw(OVERLAY_Data *vedata); +void OVERLAY_armature_in_front_draw(OVERLAY_Data *vedata); +void OVERLAY_pose_cache_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_pose_draw(OVERLAY_Data *vedata); + +void OVERLAY_bone_instance_data_set_color_hint(BoneInstanceData *data, const float hint_color[4]); +void OVERLAY_bone_instance_data_set_color(BoneInstanceData *data, const float bone_color[4]); + +void OVERLAY_edit_curve_init(OVERLAY_Data *vedata); +void OVERLAY_edit_curve_cache_init(OVERLAY_Data *vedata); +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_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); +void OVERLAY_edit_lattice_draw(OVERLAY_Data *vedata); + +void OVERLAY_edit_text_cache_init(OVERLAY_Data *vedata); +void OVERLAY_edit_text_cache_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_edit_text_draw(OVERLAY_Data *vedata); + +void OVERLAY_edit_mesh_init(OVERLAY_Data *vedata); +void OVERLAY_edit_mesh_cache_init(OVERLAY_Data *vedata); +void OVERLAY_edit_mesh_cache_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_edit_mesh_draw(OVERLAY_Data *vedata); + +void OVERLAY_edit_particle_cache_init(OVERLAY_Data *vedata); +void OVERLAY_edit_particle_cache_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_edit_particle_draw(OVERLAY_Data *vedata); + +void OVERLAY_extra_cache_init(OVERLAY_Data *vedata); +void OVERLAY_extra_cache_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_extra_draw(OVERLAY_Data *vedata); +void OVERLAY_extra_in_front_draw(OVERLAY_Data *vedata); +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); + +OVERLAY_ExtraCallBuffers *OVERLAY_extra_call_buffer_get(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_extra_line_dashed(OVERLAY_ExtraCallBuffers *cb, + const float start[3], + const float end[3], + const float color[4]); +void OVERLAY_extra_line(OVERLAY_ExtraCallBuffers *cb, + const float start[3], + const float end[3], + const int color_id); +void OVERLAY_empty_shape(OVERLAY_ExtraCallBuffers *cb, + const float mat[4][4], + const float draw_size, + const char draw_type, + const float color[4]); +void OVERLAY_extra_loose_points(OVERLAY_ExtraCallBuffers *cb, + struct GPUBatch *geom, + const float mat[4][4], + const float color[4]); +void OVERLAY_extra_wire(OVERLAY_ExtraCallBuffers *cb, + struct GPUBatch *geom, + const float mat[4][4], + const float color[4]); + +void OVERLAY_facing_init(OVERLAY_Data *vedata); +void OVERLAY_facing_cache_init(OVERLAY_Data *vedata); +void OVERLAY_facing_cache_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_facing_draw(OVERLAY_Data *vedata); + +void OVERLAY_grid_init(OVERLAY_Data *vedata); +void OVERLAY_grid_cache_init(OVERLAY_Data *vedata); +void OVERLAY_grid_draw(OVERLAY_Data *vedata); + +void OVERLAY_image_init(OVERLAY_Data *vedata); +void OVERLAY_image_cache_init(OVERLAY_Data *vedata); +void OVERLAY_image_camera_cache_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_image_empty_cache_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_image_cache_finish(OVERLAY_Data *vedata); +void OVERLAY_image_draw(OVERLAY_Data *vedata); +void OVERLAY_image_in_front_draw(OVERLAY_Data *vedata); + +void OVERLAY_metaball_cache_init(OVERLAY_Data *vedata); +void OVERLAY_edit_metaball_cache_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_metaball_cache_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_metaball_draw(OVERLAY_Data *vedata); +void OVERLAY_metaball_in_front_draw(OVERLAY_Data *vedata); + +void OVERLAY_motion_path_cache_init(OVERLAY_Data *vedata); +void OVERLAY_motion_path_cache_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_motion_path_draw(OVERLAY_Data *vedata); + +void OVERLAY_outline_init(OVERLAY_Data *vedata); +void OVERLAY_outline_cache_init(OVERLAY_Data *vedata); +void OVERLAY_outline_cache_populate(OVERLAY_Data *vedata, + Object *ob, + OVERLAY_DupliData *dupli, + bool init_dupli); +void OVERLAY_outline_draw(OVERLAY_Data *vedata); + +void OVERLAY_paint_cache_init(OVERLAY_Data *vedata); +void OVERLAY_paint_texture_cache_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_paint_vertex_cache_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_paint_weight_cache_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_paint_draw(OVERLAY_Data *vedata); + +void OVERLAY_particle_cache_init(OVERLAY_Data *vedata); +void OVERLAY_particle_cache_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_particle_draw(OVERLAY_Data *vedata); + +void OVERLAY_sculpt_cache_init(OVERLAY_Data *vedata); +void OVERLAY_sculpt_cache_populate(OVERLAY_Data *vedata, Object *ob); +void OVERLAY_sculpt_draw(OVERLAY_Data *vedata); + +void OVERLAY_wireframe_init(OVERLAY_Data *vedata); +void OVERLAY_wireframe_cache_init(OVERLAY_Data *vedata); +void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata, + Object *ob, + OVERLAY_DupliData *dupli, + bool init_dupli); +void OVERLAY_wireframe_draw(OVERLAY_Data *vedata); +void OVERLAY_wireframe_in_front_draw(OVERLAY_Data *vedata); + +GPUShader *OVERLAY_shader_antialiasing(void); +GPUShader *OVERLAY_shader_armature_degrees_of_freedom(void); +GPUShader *OVERLAY_shader_armature_envelope(bool use_outline); +GPUShader *OVERLAY_shader_armature_shape(bool use_outline); +GPUShader *OVERLAY_shader_armature_sphere(bool use_outline); +GPUShader *OVERLAY_shader_armature_stick(void); +GPUShader *OVERLAY_shader_armature_wire(void); +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_lattice_point(void); +GPUShader *OVERLAY_shader_edit_lattice_wire(void); +GPUShader *OVERLAY_shader_edit_mesh_analysis(void); +GPUShader *OVERLAY_shader_edit_mesh_edge(bool use_flat_interp); +GPUShader *OVERLAY_shader_edit_mesh_face(void); +GPUShader *OVERLAY_shader_edit_mesh_facedot(void); +GPUShader *OVERLAY_shader_edit_mesh_normal(void); +GPUShader *OVERLAY_shader_edit_mesh_skin_root(void); +GPUShader *OVERLAY_shader_edit_mesh_vert(void); +GPUShader *OVERLAY_shader_edit_particle_strand(void); +GPUShader *OVERLAY_shader_edit_particle_point(void); +GPUShader *OVERLAY_shader_extra(void); +GPUShader *OVERLAY_shader_extra_groundline(void); +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_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_grid(void); +GPUShader *OVERLAY_shader_outline_resolve(void); +GPUShader *OVERLAY_shader_outline_expand(bool high_dpi); +GPUShader *OVERLAY_shader_outline_detect(bool use_wire); +GPUShader *OVERLAY_shader_paint_face(void); +GPUShader *OVERLAY_shader_paint_point(void); +GPUShader *OVERLAY_shader_paint_texture(void); +GPUShader *OVERLAY_shader_paint_vertcol(void); +GPUShader *OVERLAY_shader_paint_weight(void); +GPUShader *OVERLAY_shader_paint_wire(void); +GPUShader *OVERLAY_shader_particle_dot(void); +GPUShader *OVERLAY_shader_particle_shape(void); +GPUShader *OVERLAY_shader_sculpt_mask(void); +GPUShader *OVERLAY_shader_volume_velocity(bool use_needle); +GPUShader *OVERLAY_shader_wireframe(void); +GPUShader *OVERLAY_shader_wireframe_select(void); + +OVERLAY_InstanceFormats *OVERLAY_shader_instance_formats_get(void); + +void OVERLAY_shader_free(void); + +#endif /* __OVERLAY_PRIVATE_H__ */
\ No newline at end of file diff --git a/source/blender/draw/engines/overlay/overlay_sculpt.c b/source/blender/draw/engines/overlay/overlay_sculpt.c new file mode 100644 index 00000000000..3b7f12ed804 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_sculpt.c @@ -0,0 +1,65 @@ +/* + * 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 "overlay_private.h" + +#include "BKE_pbvh.h" +#include "BKE_paint.h" +#include "BKE_subdiv_ccg.h" + +void OVERLAY_sculpt_cache_init(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + DRWShadingGroup *grp; + + DRWState state = DRW_STATE_WRITE_COLOR | DRW_STATE_DEPTH_EQUAL | DRW_STATE_BLEND_ALPHA; + DRW_PASS_CREATE(psl->sculpt_mask_ps, state | pd->clipping_state); + + GPUShader *sh = OVERLAY_shader_sculpt_mask(); + pd->sculpt_mask_grp = grp = DRW_shgroup_create(sh, psl->sculpt_mask_ps); + DRW_shgroup_uniform_float_copy(grp, "maskOpacity", pd->overlay.sculpt_mode_mask_opacity); +} + +void OVERLAY_sculpt_cache_populate(OVERLAY_Data *vedata, Object *ob) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + const DRWContextState *draw_ctx = DRW_context_state_get(); + PBVH *pbvh = ob->sculpt->pbvh; + + const bool use_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d); + + if (use_pbvh || !ob->sculpt->deform_modifiers_active || ob->sculpt->shapekey_active) { + if (pbvh_has_mask(pbvh)) { + DRW_shgroup_call_sculpt(pd->sculpt_mask_grp, ob, false, true, false); + } + } +} + +void OVERLAY_sculpt_draw(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + + DRW_draw_pass(psl->sculpt_mask_ps); +} diff --git a/source/blender/draw/engines/overlay/overlay_shader.c b/source/blender/draw/engines/overlay/overlay_shader.c new file mode 100644 index 00000000000..fa495d153e9 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_shader.c @@ -0,0 +1,1337 @@ +/* + * 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 "GPU_shader.h" + +#include "UI_resources.h" + +#include "overlay_private.h" + +extern char datatoc_antialiasing_frag_glsl[]; +extern char datatoc_antialiasing_vert_glsl[]; +extern char datatoc_armature_dof_vert_glsl[]; +extern char datatoc_armature_envelope_distance_frag_glsl[]; +extern char datatoc_armature_envelope_outline_vert_glsl[]; +extern char datatoc_armature_envelope_solid_frag_glsl[]; +extern char datatoc_armature_envelope_solid_vert_glsl[]; +extern char datatoc_armature_shape_outline_geom_glsl[]; +extern char datatoc_armature_shape_outline_vert_glsl[]; +extern char datatoc_armature_shape_solid_frag_glsl[]; +extern char datatoc_armature_shape_solid_vert_glsl[]; +extern char datatoc_armature_sphere_outline_vert_glsl[]; +extern char datatoc_armature_sphere_solid_frag_glsl[]; +extern char datatoc_armature_sphere_solid_vert_glsl[]; +extern char datatoc_armature_stick_frag_glsl[]; +extern char datatoc_armature_stick_vert_glsl[]; +extern char datatoc_armature_wire_vert_glsl[]; +extern char datatoc_depth_only_vert_glsl[]; +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_lattice_point_vert_glsl[]; +extern char datatoc_edit_lattice_wire_vert_glsl[]; +extern char datatoc_edit_mesh_common_lib_glsl[]; +extern char datatoc_edit_mesh_frag_glsl[]; +extern char datatoc_edit_mesh_geom_glsl[]; +extern char datatoc_edit_mesh_vert_glsl[]; +extern char datatoc_edit_mesh_normal_vert_glsl[]; +extern char datatoc_edit_mesh_skin_root_vert_glsl[]; +extern char datatoc_edit_mesh_analysis_vert_glsl[]; +extern char datatoc_edit_mesh_analysis_frag_glsl[]; +extern char datatoc_edit_particle_strand_vert_glsl[]; +extern char datatoc_edit_particle_point_vert_glsl[]; +extern char datatoc_extra_frag_glsl[]; +extern char datatoc_extra_vert_glsl[]; +extern char datatoc_extra_groundline_vert_glsl[]; +extern char datatoc_extra_loose_point_vert_glsl[]; +extern char datatoc_extra_loose_point_frag_glsl[]; +extern char datatoc_extra_point_vert_glsl[]; +extern char datatoc_extra_wire_frag_glsl[]; +extern char datatoc_extra_wire_vert_glsl[]; +extern char datatoc_facing_frag_glsl[]; +extern char datatoc_facing_vert_glsl[]; +extern char datatoc_grid_frag_glsl[]; +extern char datatoc_grid_vert_glsl[]; +extern char datatoc_image_frag_glsl[]; +extern char datatoc_image_vert_glsl[]; +extern char datatoc_motion_path_line_vert_glsl[]; +extern char datatoc_motion_path_line_geom_glsl[]; +extern char datatoc_motion_path_point_vert_glsl[]; +extern char datatoc_outline_detect_frag_glsl[]; +extern char datatoc_outline_expand_frag_glsl[]; +extern char datatoc_outline_prepass_frag_glsl[]; +extern char datatoc_outline_prepass_geom_glsl[]; +extern char datatoc_outline_prepass_vert_glsl[]; +extern char datatoc_outline_lightprobe_grid_vert_glsl[]; +extern char datatoc_outline_resolve_frag_glsl[]; +extern char datatoc_paint_face_vert_glsl[]; +extern char datatoc_paint_point_vert_glsl[]; +extern char datatoc_paint_texture_frag_glsl[]; +extern char datatoc_paint_texture_vert_glsl[]; +extern char datatoc_paint_vertcol_frag_glsl[]; +extern char datatoc_paint_vertcol_vert_glsl[]; +extern char datatoc_paint_weight_frag_glsl[]; +extern char datatoc_paint_weight_vert_glsl[]; +extern char datatoc_paint_wire_vert_glsl[]; +extern char datatoc_particle_vert_glsl[]; +extern char datatoc_particle_frag_glsl[]; +extern char datatoc_sculpt_mask_vert_glsl[]; +extern char datatoc_volume_velocity_vert_glsl[]; +extern char datatoc_wireframe_vert_glsl[]; +extern char datatoc_wireframe_geom_glsl[]; +extern char datatoc_wireframe_frag_glsl[]; + +extern char datatoc_gpu_shader_depth_only_frag_glsl[]; +extern char datatoc_gpu_shader_point_varying_color_frag_glsl[]; +extern char datatoc_gpu_shader_3D_smooth_color_frag_glsl[]; +extern char datatoc_gpu_shader_uniform_color_frag_glsl[]; +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_common_colormanagement_lib_glsl[]; +extern char datatoc_common_fullscreen_vert_glsl[]; +extern char datatoc_common_fxaa_lib_glsl[]; +extern char datatoc_common_smaa_lib_glsl[]; +extern char datatoc_common_globals_lib_glsl[]; +extern char datatoc_common_view_lib_glsl[]; + +typedef struct OVERLAY_Shaders { + GPUShader *antialiasing; + GPUShader *armature_dof; + GPUShader *armature_envelope_outline; + GPUShader *armature_envelope_solid; + GPUShader *armature_shape_outline; + GPUShader *armature_shape_solid; + GPUShader *armature_sphere_outline; + GPUShader *armature_sphere_solid; + GPUShader *armature_stick; + GPUShader *armature_wire; + GPUShader *depth_only; + GPUShader *edit_curve_handle; + GPUShader *edit_curve_point; + GPUShader *edit_curve_wire; + GPUShader *edit_lattice_point; + GPUShader *edit_lattice_wire; + GPUShader *edit_mesh_vert; + GPUShader *edit_mesh_edge; + GPUShader *edit_mesh_edge_flat; + GPUShader *edit_mesh_face; + GPUShader *edit_mesh_facedot; + GPUShader *edit_mesh_skin_root; + GPUShader *edit_mesh_vnormals; + GPUShader *edit_mesh_normals; + GPUShader *edit_mesh_fnormals; + GPUShader *edit_mesh_analysis; + GPUShader *edit_particle_strand; + GPUShader *edit_particle_point; + GPUShader *extra; + GPUShader *extra_groundline; + GPUShader *extra_wire[2]; + GPUShader *extra_point; + GPUShader *extra_loose_point; + GPUShader *facing; + GPUShader *grid; + GPUShader *image; + GPUShader *motion_path_line; + GPUShader *motion_path_vert; + GPUShader *outline_prepass; + GPUShader *outline_prepass_wire; + GPUShader *outline_prepass_lightprobe_grid; + GPUShader *outline_resolve; + GPUShader *outline_fade; + GPUShader *outline_fade_large; + GPUShader *outline_detect; + GPUShader *outline_detect_wire; + GPUShader *paint_face; + GPUShader *paint_point; + GPUShader *paint_texture; + GPUShader *paint_vertcol; + GPUShader *paint_weight; + GPUShader *paint_wire; + GPUShader *particle_dot; + GPUShader *particle_shape; + GPUShader *sculpt_mask; + GPUShader *uniform_color; + GPUShader *volume_velocity_needle_sh; + GPUShader *volume_velocity_sh; + GPUShader *wireframe_select; + GPUShader *wireframe; +} OVERLAY_Shaders; + +static struct { + OVERLAY_Shaders sh_data[GPU_SHADER_CFG_LEN]; +} e_data = {{{NULL}}}; + +GPUShader *OVERLAY_shader_antialiasing(void) +{ + OVERLAY_Shaders *sh_data = &e_data.sh_data[0]; + if (!sh_data->antialiasing) { + sh_data->antialiasing = GPU_shader_create_from_arrays({ + .vert = (const char *[]){datatoc_common_globals_lib_glsl, + datatoc_antialiasing_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_common_globals_lib_glsl, + datatoc_antialiasing_frag_glsl, + NULL}, + .defs = (const char *[]){NULL}, + }); + } + return sh_data->antialiasing; +} + +GPUShader *OVERLAY_shader_depth_only(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->depth_only) { + sh_data->depth_only = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_depth_only_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_depth_only_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->depth_only; +} + +GPUShader *OVERLAY_shader_edit_mesh_vert(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_mesh_vert) { + sh_data->edit_mesh_vert = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_edit_mesh_common_lib_glsl, + datatoc_edit_mesh_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_point_varying_color_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, "#define VERT\n", NULL}, + }); + } + return sh_data->edit_mesh_vert; +} + +GPUShader *OVERLAY_shader_edit_mesh_edge(bool use_flat_interp) +{ + 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]; + GPUShader **sh = use_flat_interp ? &sh_data->edit_mesh_edge_flat : &sh_data->edit_mesh_edge; + if (*sh == NULL) { + /* Use geometry shader to draw edge wire-frame. This ensure us + * the same result across platforms and more flexibility. + * But we pay the cost of running a geometry shader. + * In the future we might consider using only the vertex shader + * and loading data manually with buffer textures. */ + const bool use_geom_shader = true; + const bool use_smooth_wires = (U.gpu_flag & USER_GPU_FLAG_NO_EDIT_MODE_SMOOTH_WIRE) == 0; + const char *geom_sh_code[] = {use_geom_shader ? sh_cfg->lib : NULL, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_edit_mesh_geom_glsl, + NULL}; + const char *vert_sh_code[] = {sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_edit_mesh_common_lib_glsl, + datatoc_edit_mesh_vert_glsl, + NULL}; + const char *frag_sh_code[] = {sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_edit_mesh_common_lib_glsl, + datatoc_edit_mesh_frag_glsl, + NULL}; + const char *defs[] = {sh_cfg->def, + use_geom_shader ? "#define USE_GEOM_SHADER\n" : "", + use_smooth_wires ? "#define USE_SMOOTH_WIRE\n" : "", + use_flat_interp ? "#define FLAT\n" : "", + "#define EDGE\n", + NULL}; + + *sh = GPU_shader_create_from_arrays({ + .vert = vert_sh_code, + .frag = frag_sh_code, + .geom = geom_sh_code, + .defs = defs, + }); + } + return *sh; +} + +GPUShader *OVERLAY_shader_armature_sphere(bool use_outline) +{ + 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]; + const char extensions[] = "#extension GL_ARB_conservative_depth : enable\n"; + if (use_outline && !sh_data->armature_sphere_outline) { + sh_data->armature_sphere_outline = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_armature_sphere_outline_vert_glsl, + NULL}, + .frag = (const char *[]){extensions, + datatoc_common_view_lib_glsl, + datatoc_gpu_shader_flat_color_frag_glsl, + NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + else if (!sh_data->armature_sphere_solid) { + sh_data->armature_sphere_solid = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_armature_sphere_solid_vert_glsl, + NULL}, + .frag = (const char *[]){extensions, + datatoc_common_view_lib_glsl, + datatoc_armature_sphere_solid_frag_glsl, + NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return use_outline ? sh_data->armature_sphere_outline : sh_data->armature_sphere_solid; +} + +GPUShader *OVERLAY_shader_armature_shape(bool use_outline) +{ + 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 (use_outline && !sh_data->armature_shape_outline) { + sh_data->armature_shape_outline = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_armature_shape_outline_vert_glsl, + NULL}, + .geom = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_armature_shape_outline_geom_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_flat_color_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + else if (!sh_data->armature_shape_solid) { + sh_data->armature_shape_solid = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_armature_shape_solid_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_armature_shape_solid_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return use_outline ? sh_data->armature_shape_outline : sh_data->armature_shape_solid; +} + +GPUShader *OVERLAY_shader_armature_envelope(bool use_outline) +{ + 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 (use_outline && !sh_data->armature_envelope_outline) { + sh_data->armature_envelope_outline = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_armature_envelope_outline_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_flat_color_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + else if (!sh_data->armature_envelope_solid) { + sh_data->armature_envelope_solid = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_armature_envelope_solid_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_armature_envelope_solid_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return use_outline ? sh_data->armature_envelope_outline : sh_data->armature_envelope_solid; +} + +GPUShader *OVERLAY_shader_armature_stick(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->armature_stick) { + sh_data->armature_stick = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_armature_stick_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_armature_stick_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->armature_stick; +} + +GPUShader *OVERLAY_shader_armature_degrees_of_freedom(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->armature_dof) { + sh_data->armature_dof = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_armature_dof_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_flat_color_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->armature_dof; +} + +GPUShader *OVERLAY_shader_armature_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->armature_wire) { + sh_data->armature_wire = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_armature_wire_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_flat_color_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->armature_wire; +} + +GPUShader *OVERLAY_shader_edit_curve_handle(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_curve_handle) { + sh_data->edit_curve_handle = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_edit_curve_handle_vert_glsl, + NULL}, + .geom = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_edit_curve_handle_geom_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_curve_handle; +} + +GPUShader *OVERLAY_shader_edit_curve_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_curve_point) { + sh_data->edit_curve_point = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_edit_curve_point_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_point_varying_color_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->edit_curve_point; +} + +GPUShader *OVERLAY_shader_edit_curve_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_curve_wire) { + sh_data->edit_curve_wire = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_edit_curve_wire_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_flat_color_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, "#define IN_PLACE_INSTANCES\n", NULL}, + }); + } + return sh_data->edit_curve_wire; +} + +GPUShader *OVERLAY_shader_edit_lattice_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_lattice_point) { + sh_data->edit_lattice_point = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_edit_lattice_point_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_point_varying_color_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->edit_lattice_point; +} + +GPUShader *OVERLAY_shader_edit_lattice_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_lattice_wire) { + sh_data->edit_lattice_wire = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_edit_lattice_wire_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_lattice_wire; +} + +GPUShader *OVERLAY_shader_edit_mesh_face(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_mesh_face) { + sh_data->edit_mesh_face = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_edit_mesh_common_lib_glsl, + datatoc_edit_mesh_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_3D_smooth_color_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, "#define FACE\n", NULL}, + }); + } + return sh_data->edit_mesh_face; +} + +GPUShader *OVERLAY_shader_edit_mesh_facedot(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_mesh_facedot) { + sh_data->edit_mesh_facedot = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_edit_mesh_common_lib_glsl, + datatoc_edit_mesh_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_point_varying_color_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, "#define FACEDOT\n", NULL}, + }); + } + return sh_data->edit_mesh_facedot; +} + +GPUShader *OVERLAY_shader_edit_mesh_normal(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_mesh_normals) { + sh_data->edit_mesh_normals = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_edit_mesh_normal_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_flat_color_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, "#define INSTANCED_ATTRIB\n", NULL}, + }); + } + return sh_data->edit_mesh_normals; +} + +GPUShader *OVERLAY_shader_edit_mesh_analysis(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_mesh_analysis) { + sh_data->edit_mesh_analysis = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_edit_mesh_analysis_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_edit_mesh_analysis_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->edit_mesh_analysis; +} + +GPUShader *OVERLAY_shader_edit_mesh_skin_root(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_mesh_skin_root) { + sh_data->edit_mesh_skin_root = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_edit_mesh_skin_root_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_flat_color_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, "#define INSTANCED_ATTRIB\n", NULL}, + }); + } + return sh_data->edit_mesh_skin_root; +} + +GPUShader *OVERLAY_shader_edit_particle_strand(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_particle_strand) { + sh_data->edit_particle_strand = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_edit_particle_strand_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_particle_strand; +} + +GPUShader *OVERLAY_shader_edit_particle_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_particle_point) { + sh_data->edit_particle_point = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_edit_particle_point_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_point_varying_color_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->edit_particle_point; +} + +GPUShader *OVERLAY_shader_extra(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->extra) { + sh_data->extra = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_extra_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_common_view_lib_glsl, datatoc_extra_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->extra; +} + +GPUShader *OVERLAY_shader_extra_groundline(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->extra_groundline) { + sh_data->extra_groundline = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_extra_groundline_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_common_view_lib_glsl, datatoc_extra_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->extra_groundline; +} + +GPUShader *OVERLAY_shader_extra_wire(bool use_object) +{ + 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->extra_wire[use_object]) { + char colorids[1024]; + /* NOTE: define all ids we need here. */ + BLI_snprintf(colorids, + sizeof(colorids), + "#define TH_ACTIVE %d\n" + "#define TH_SELECT %d\n" + "#define TH_TRANSFORM %d\n" + "#define TH_WIRE %d\n" + "#define TH_CAMERA_PATH %d\n", + TH_ACTIVE, + TH_SELECT, + TH_TRANSFORM, + TH_WIRE, + TH_CAMERA_PATH); + sh_data->extra_wire[use_object] = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_extra_wire_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_common_view_lib_glsl, datatoc_extra_wire_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, + colorids, + (use_object) ? "#define OBJECT_WIRE \n" : NULL, + NULL}, + }); + } + return sh_data->extra_wire[use_object]; +} + +GPUShader *OVERLAY_shader_extra_loose_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->extra_loose_point) { + sh_data->extra_loose_point = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_extra_loose_point_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_common_globals_lib_glsl, + datatoc_extra_loose_point_frag_glsl, + NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->extra_loose_point; +} + +GPUShader *OVERLAY_shader_extra_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->extra_point) { + sh_data->extra_point = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_extra_point_vert_glsl, + NULL}, + .frag = + (const char *[]){datatoc_gpu_shader_point_varying_color_varying_outline_aa_frag_glsl, + NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->extra_point; +} + +GPUShader *OVERLAY_shader_facing(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->facing) { + sh_data->facing = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_facing_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_facing_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->facing; +} + +GPUShader *OVERLAY_shader_grid(void) +{ + OVERLAY_Shaders *sh_data = &e_data.sh_data[0]; + if (!sh_data->grid) { + sh_data->grid = GPU_shader_create_from_arrays({ + .vert = (const char *[]){datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_grid_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_grid_frag_glsl, + NULL}, + }); + } + return sh_data->grid; +} + +GPUShader *OVERLAY_shader_image(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->image) { + sh_data->image = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_image_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_common_colormanagement_lib_glsl, + datatoc_image_frag_glsl, + NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->image; +} + +GPUShader *OVERLAY_shader_motion_path_line(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->motion_path_line) { + sh_data->motion_path_line = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_motion_path_line_vert_glsl, + NULL}, + .geom = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_motion_path_line_geom_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_3D_smooth_color_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->motion_path_line; +} + +GPUShader *OVERLAY_shader_motion_path_vert(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->motion_path_vert) { + sh_data->motion_path_vert = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_motion_path_point_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_point_varying_color_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->motion_path_vert; +} + +GPUShader *OVERLAY_shader_outline_prepass(bool use_wire) +{ + 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 (use_wire && !sh_data->outline_prepass_wire) { + sh_data->outline_prepass_wire = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_outline_prepass_vert_glsl, + NULL}, + .geom = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_outline_prepass_geom_glsl, + NULL}, + .frag = (const char *[]){datatoc_outline_prepass_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, "#define USE_GEOM\n", NULL}, + }); + } + else if (!sh_data->outline_prepass) { + sh_data->outline_prepass = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_outline_prepass_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_outline_prepass_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return use_wire ? sh_data->outline_prepass_wire : sh_data->outline_prepass; +} + +GPUShader *OVERLAY_shader_outline_prepass_grid(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_lightprobe_grid) { + sh_data->outline_prepass_lightprobe_grid = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_common_globals_lib_glsl, + datatoc_outline_lightprobe_grid_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_outline_prepass_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->outline_prepass_lightprobe_grid; +} + +GPUShader *OVERLAY_shader_outline_resolve(void) +{ + OVERLAY_Shaders *sh_data = &e_data.sh_data[0]; + if (!sh_data->outline_resolve) { + sh_data->outline_resolve = DRW_shader_create_with_lib(datatoc_common_fullscreen_vert_glsl, + NULL, + datatoc_outline_resolve_frag_glsl, + datatoc_common_fxaa_lib_glsl, + "#define FXAA_ALPHA\n" + "#define USE_FXAA\n"); + } + return sh_data->outline_resolve; +} + +GPUShader *OVERLAY_shader_outline_expand(bool high_dpi) +{ + OVERLAY_Shaders *sh_data = &e_data.sh_data[0]; + if (high_dpi && !sh_data->outline_fade_large) { + sh_data->outline_fade_large = DRW_shader_create_fullscreen(datatoc_outline_expand_frag_glsl, + "#define LARGE_OUTLINE\n"); + } + else if (!sh_data->outline_fade) { + sh_data->outline_fade = DRW_shader_create_fullscreen(datatoc_outline_expand_frag_glsl, NULL); + } + return (high_dpi) ? sh_data->outline_fade_large : sh_data->outline_fade; +} + +GPUShader *OVERLAY_shader_outline_detect(bool use_wire) +{ + OVERLAY_Shaders *sh_data = &e_data.sh_data[0]; + if (use_wire && !sh_data->outline_detect_wire) { + sh_data->outline_detect_wire = DRW_shader_create_with_lib(datatoc_common_fullscreen_vert_glsl, + NULL, + datatoc_outline_detect_frag_glsl, + datatoc_common_globals_lib_glsl, + "#define WIRE\n"); + } + else if (!sh_data->outline_detect) { + sh_data->outline_detect = DRW_shader_create_with_lib(datatoc_common_fullscreen_vert_glsl, + NULL, + datatoc_outline_detect_frag_glsl, + datatoc_common_globals_lib_glsl, + NULL); + } + return (use_wire) ? sh_data->outline_detect_wire : sh_data->outline_detect; +} + +GPUShader *OVERLAY_shader_paint_face(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->paint_face) { + sh_data->paint_face = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_paint_face_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_uniform_color_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->paint_face; +} + +GPUShader *OVERLAY_shader_paint_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->paint_point) { + sh_data->paint_point = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_paint_point_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_point_varying_color_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->paint_point; +} + +GPUShader *OVERLAY_shader_paint_texture(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->paint_texture) { + sh_data->paint_texture = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_paint_texture_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_common_colormanagement_lib_glsl, + datatoc_paint_texture_frag_glsl, + NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->paint_texture; +} + +GPUShader *OVERLAY_shader_paint_vertcol(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->paint_vertcol) { + sh_data->paint_vertcol = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_paint_vertcol_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_common_globals_lib_glsl, + datatoc_paint_vertcol_frag_glsl, + NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->paint_vertcol; +} + +GPUShader *OVERLAY_shader_paint_weight(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->paint_weight) { + sh_data->paint_weight = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_paint_weight_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_common_globals_lib_glsl, + datatoc_paint_weight_frag_glsl, + NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->paint_weight; +} + +GPUShader *OVERLAY_shader_paint_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->paint_wire) { + sh_data->paint_wire = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_paint_wire_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_flat_color_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->paint_wire; +} + +GPUShader *OVERLAY_shader_particle_dot(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->particle_dot) { + sh_data->particle_dot = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_particle_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_particle_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, "#define USE_DOTS\n", NULL}, + }); + } + return sh_data->particle_dot; +} + +GPUShader *OVERLAY_shader_particle_shape(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->particle_shape) { + sh_data->particle_shape = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_common_view_lib_glsl, + datatoc_particle_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_flat_color_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, "#define INSTANCED_ATTRIB\n", NULL}, + }); + } + return sh_data->particle_shape; +} + +GPUShader *OVERLAY_shader_sculpt_mask(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->sculpt_mask) { + sh_data->sculpt_mask = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_sculpt_mask_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->sculpt_mask; +} + +struct GPUShader *OVERLAY_shader_uniform_color(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->uniform_color) { + sh_data->uniform_color = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_depth_only_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_uniform_color_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, NULL}, + }); + } + return sh_data->uniform_color; +} + +struct GPUShader *OVERLAY_shader_volume_velocity(bool use_needle) +{ + OVERLAY_Shaders *sh_data = &e_data.sh_data[0]; + if (use_needle && !sh_data->volume_velocity_needle_sh) { + sh_data->volume_velocity_needle_sh = DRW_shader_create_with_lib( + datatoc_volume_velocity_vert_glsl, + NULL, + datatoc_gpu_shader_flat_color_frag_glsl, + datatoc_common_view_lib_glsl, + "#define USE_NEEDLE\n"); + } + else if (!sh_data->volume_velocity_sh) { + sh_data->volume_velocity_sh = DRW_shader_create_with_lib( + datatoc_volume_velocity_vert_glsl, + NULL, + datatoc_gpu_shader_flat_color_frag_glsl, + datatoc_common_view_lib_glsl, + NULL); + } + return (use_needle) ? sh_data->volume_velocity_needle_sh : sh_data->volume_velocity_sh; +} + +GPUShader *OVERLAY_shader_wireframe_select(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->wireframe_select) { + sh_data->wireframe_select = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_common_globals_lib_glsl, + datatoc_gpu_shader_common_obinfos_lib_glsl, + datatoc_wireframe_vert_glsl, + NULL}, + .geom = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_wireframe_geom_glsl, + NULL}, + .frag = (const char *[]){datatoc_gpu_shader_depth_only_frag_glsl, NULL}, + .defs = (const char *[]){sh_cfg->def, "#define SELECT_EDGES\n", NULL}, + }); + } + return sh_data->wireframe_select; +} + +GPUShader *OVERLAY_shader_wireframe(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->wireframe) { + sh_data->wireframe = GPU_shader_create_from_arrays({ + .vert = (const char *[]){sh_cfg->lib, + datatoc_common_view_lib_glsl, + datatoc_common_globals_lib_glsl, + datatoc_gpu_shader_common_obinfos_lib_glsl, + datatoc_wireframe_vert_glsl, + NULL}, + .frag = (const char *[]){datatoc_common_view_lib_glsl, datatoc_wireframe_frag_glsl, NULL}, + /* Apple drivers does not support wide wires. Use geometry shader as a workaround. */ +#if USE_GEOM_SHADER_WORKAROUND + .geom = (const char *[]){sh_cfg->lib, + datatoc_common_globals_lib_glsl, + datatoc_wireframe_geom_glsl, + NULL}, + .defs = (const char *[]){sh_cfg->def, "#define USE_GEOM\n", NULL}, +#else + .defs = (const char *[]){sh_cfg->def, NULL}, +#endif + }); + } + return sh_data->wireframe; +} + +static OVERLAY_InstanceFormats g_formats = {NULL}; + +OVERLAY_InstanceFormats *OVERLAY_shader_instance_formats_get(void) +{ + DRW_shgroup_instance_format(g_formats.pos, + { + {"pos", DRW_ATTR_FLOAT, 3}, + }); + DRW_shgroup_instance_format(g_formats.pos_color, + { + {"pos", DRW_ATTR_FLOAT, 3}, + {"color", DRW_ATTR_FLOAT, 4}, + }); + DRW_shgroup_instance_format(g_formats.instance_pos, + { + {"inst_pos", DRW_ATTR_FLOAT, 3}, + }); + DRW_shgroup_instance_format(g_formats.instance_extra, + { + {"color", DRW_ATTR_FLOAT, 4}, + {"inst_obmat", DRW_ATTR_FLOAT, 16}, + }); + DRW_shgroup_instance_format(g_formats.wire_extra, + { + {"pos", DRW_ATTR_FLOAT, 3}, + {"colorid", DRW_ATTR_INT, 1}, + }); + DRW_shgroup_instance_format(g_formats.instance_bone, + { + {"inst_obmat", DRW_ATTR_FLOAT, 16}, + }); + DRW_shgroup_instance_format(g_formats.instance_bone_stick, + { + {"boneStart", DRW_ATTR_FLOAT, 3}, + {"boneEnd", DRW_ATTR_FLOAT, 3}, + {"wireColor", DRW_ATTR_FLOAT, 4}, /* TODO uchar color */ + {"boneColor", DRW_ATTR_FLOAT, 4}, + {"headColor", DRW_ATTR_FLOAT, 4}, + {"tailColor", DRW_ATTR_FLOAT, 4}, + }); + DRW_shgroup_instance_format(g_formats.instance_bone_envelope_outline, + { + {"headSphere", DRW_ATTR_FLOAT, 4}, + {"tailSphere", DRW_ATTR_FLOAT, 4}, + {"outlineColorSize", DRW_ATTR_FLOAT, 4}, + {"xAxis", DRW_ATTR_FLOAT, 3}, + }); + DRW_shgroup_instance_format(g_formats.instance_bone_envelope_distance, + { + {"headSphere", DRW_ATTR_FLOAT, 4}, + {"tailSphere", DRW_ATTR_FLOAT, 4}, + {"xAxis", DRW_ATTR_FLOAT, 3}, + }); + DRW_shgroup_instance_format(g_formats.instance_bone_envelope, + { + {"headSphere", DRW_ATTR_FLOAT, 4}, + {"tailSphere", DRW_ATTR_FLOAT, 4}, + {"boneColor", DRW_ATTR_FLOAT, 3}, + {"stateColor", DRW_ATTR_FLOAT, 3}, + {"xAxis", DRW_ATTR_FLOAT, 3}, + }); + + return &g_formats; +} + +void OVERLAY_shader_free(void) +{ + for (int sh_data_index = 0; sh_data_index < ARRAY_SIZE(e_data.sh_data); sh_data_index++) { + OVERLAY_Shaders *sh_data = &e_data.sh_data[sh_data_index]; + GPUShader **sh_data_as_array = (GPUShader **)sh_data; + for (int i = 0; i < (sizeof(OVERLAY_Shaders) / sizeof(GPUShader *)); i++) { + DRW_SHADER_FREE_SAFE(sh_data_as_array[i]); + } + } + struct GPUVertFormat **format = (struct GPUVertFormat **)&g_formats; + for (int i = 0; i < sizeof(g_formats) / sizeof(void *); i++, format++) { + MEM_SAFE_FREE(*format); + } +}
\ No newline at end of file diff --git a/source/blender/draw/engines/overlay/overlay_wireframe.c b/source/blender/draw/engines/overlay/overlay_wireframe.c new file mode 100644 index 00000000000..d86f524cb48 --- /dev/null +++ b/source/blender/draw/engines/overlay/overlay_wireframe.c @@ -0,0 +1,231 @@ +/* + * 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 "DNA_mesh_types.h" +#include "DNA_view3d_types.h" + +#include "BKE_editmesh.h" +#include "BKE_global.h" +#include "BKE_object.h" +#include "BKE_paint.h" + +#include "BLI_hash.h" + +#include "GPU_shader.h" +#include "DRW_render.h" + +#include "ED_view3d.h" + +#include "overlay_private.h" + +void OVERLAY_wireframe_init(OVERLAY_Data *vedata) +{ + OVERLAY_PrivateData *pd = vedata->stl->pd; + const DRWContextState *draw_ctx = DRW_context_state_get(); + pd->view_wires = DRW_view_create_with_zoffset(pd->view_default, draw_ctx->rv3d, 0.5f); +} + +void OVERLAY_wireframe_cache_init(OVERLAY_Data *vedata) +{ + OVERLAY_PassList *psl = vedata->psl; + OVERLAY_PrivateData *pd = vedata->stl->pd; + const DRWContextState *draw_ctx = DRW_context_state_get(); + DRWShadingGroup *grp = NULL; + + View3DShading *shading = &draw_ctx->v3d->shading; + + pd->shdata.wire_step_param = pd->overlay.wireframe_threshold - 254.0f / 255.0f; + + bool is_wire_shmode = (shading->type == OB_WIRE); + bool is_material_shmode = (shading->type > OB_SOLID); + bool is_object_color = is_wire_shmode && (shading->wire_color_type == V3D_SHADING_OBJECT_COLOR); + bool is_random_color = is_wire_shmode && (shading->wire_color_type == V3D_SHADING_RANDOM_COLOR); + + const bool use_select = (DRW_state_is_select() || DRW_state_is_depth()); + GPUShader *wires_sh = use_select ? OVERLAY_shader_wireframe_select() : + OVERLAY_shader_wireframe(); + + for (int xray = 0; xray < 2; xray++) { + /* Only do stencil test if stencil buffer is written by the render engine. */ + DRWState stencil_state = is_material_shmode ? 0 : DRW_STATE_STENCIL_EQUAL; + DRWState state = DRW_STATE_FIRST_VERTEX_CONVENTION | DRW_STATE_WRITE_COLOR | + DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_LESS_EQUAL; + DRWPass *pass; + uint stencil_mask; + + if (xray == 0) { + DRW_PASS_CREATE(psl->wireframe_ps, state | stencil_state | pd->clipping_state); + pass = psl->wireframe_ps; + stencil_mask = 0xFF; + } + else { + DRW_PASS_CREATE(psl->wireframe_xray_ps, state | pd->clipping_state); + pass = psl->wireframe_xray_ps; + stencil_mask = 0x00; + } + + for (int use_coloring = 0; use_coloring < 2; use_coloring++) { + pd->wires_grp[xray][use_coloring] = grp = DRW_shgroup_create(wires_sh, pass); + DRW_shgroup_uniform_block_persistent(grp, "globalsBlock", G_draw.block_ubo); + DRW_shgroup_uniform_float_copy(grp, "wireStepParam", pd->shdata.wire_step_param); + DRW_shgroup_uniform_bool_copy(grp, "useColoring", use_coloring); + DRW_shgroup_uniform_bool_copy(grp, "isTransform", (G.moving & G_TRANSFORM_OBJ) != 0); + DRW_shgroup_uniform_bool_copy(grp, "isObjectColor", is_object_color); + DRW_shgroup_uniform_bool_copy(grp, "isRandomColor", is_random_color); + DRW_shgroup_stencil_mask(grp, stencil_mask); + + pd->wires_all_grp[xray][use_coloring] = grp = DRW_shgroup_create(wires_sh, pass); + DRW_shgroup_uniform_float_copy(grp, "wireStepParam", 1.0f); + DRW_shgroup_stencil_mask(grp, stencil_mask); + } + + pd->wires_sculpt_grp[xray] = grp = DRW_shgroup_create(wires_sh, pass); + DRW_shgroup_uniform_float_copy(grp, "wireStepParam", 10.0f); + DRW_shgroup_uniform_bool_copy(grp, "useColoring", false); + DRW_shgroup_stencil_mask(grp, stencil_mask); + } +} + +void OVERLAY_wireframe_cache_populate(OVERLAY_Data *vedata, + Object *ob, + OVERLAY_DupliData *dupli, + bool init_dupli) +{ + OVERLAY_Data *data = vedata; + OVERLAY_PrivateData *pd = data->stl->pd; + const DRWContextState *draw_ctx = DRW_context_state_get(); + const bool all_wires = (ob->dtx & OB_DRAW_ALL_EDGES) != 0; + const bool is_xray = (ob->dtx & OB_DRAWXRAY) != 0; + const bool is_mesh = ob->type == OB_MESH; + const bool use_wire = (pd->overlay.flag & V3D_OVERLAY_WIREFRAMES) || (ob->dtx & OB_DRAWWIRE) || + (ob->dt == OB_WIRE); + + /* Fast path for duplis. */ + if (dupli && !init_dupli) { + if (dupli->wire_shgrp && dupli->wire_geom) { + if (dupli->base_flag == ob->base_flag) { + DRW_shgroup_call(dupli->wire_shgrp, dupli->wire_geom, ob); + return; + } + } + else { + /* Nothing to draw for this dupli. */ + return; + } + } + + const bool is_edit_mode = BKE_object_is_in_editmode(ob); + bool has_edit_mesh_cage = false; + if (is_mesh && is_edit_mode) { + /* TODO: Should be its own function. */ + Mesh *me = (Mesh *)ob->data; + BMEditMesh *embm = me->edit_mesh; + if (embm) { + has_edit_mesh_cage = embm->mesh_eval_cage && (embm->mesh_eval_cage != embm->mesh_eval_final); + } + } + + /* Don't do that in edit Mesh mode, unless there is a modifier preview. */ + if (use_wire && (!is_mesh || (!is_edit_mode || has_edit_mesh_cage))) { + const bool use_sculpt_pbvh = BKE_sculptsession_use_pbvh_draw(ob, draw_ctx->v3d) && + !DRW_state_is_image_render(); + const bool use_coloring = (use_wire && !is_edit_mode && !use_sculpt_pbvh && + !has_edit_mesh_cage); + DRWShadingGroup *shgrp = NULL; + struct GPUBatch *geom = DRW_cache_object_face_wireframe_get(ob); + + if (geom || use_sculpt_pbvh) { + if (use_sculpt_pbvh) { + shgrp = pd->wires_sculpt_grp[is_xray]; + } + else if (all_wires) { + shgrp = pd->wires_all_grp[is_xray][use_coloring]; + } + else { + shgrp = pd->wires_grp[is_xray][use_coloring]; + } + + if (use_sculpt_pbvh) { + DRW_shgroup_call_sculpt(shgrp, ob, true, false, false); + } + else { + DRW_shgroup_call(shgrp, geom, ob); + } + } + + if (dupli) { + dupli->wire_shgrp = shgrp; + dupli->wire_geom = geom; + } + } + else if (is_mesh && (!is_edit_mode || has_edit_mesh_cage)) { + OVERLAY_ExtraCallBuffers *cb = OVERLAY_extra_call_buffer_get(vedata, ob); + Mesh *me = ob->data; + float *color; + DRW_object_wire_theme_get(ob, draw_ctx->view_layer, &color); + + /* Draw loose geometry. */ + if ((me->totpoly == 0 && me->totedge > 0) || has_edit_mesh_cage) { + struct GPUBatch *geom = DRW_cache_mesh_loose_edges_get(ob); + if (geom) { + OVERLAY_extra_wire(cb, geom, ob->obmat, color); + } + } + else if (me->totedge == 0 && me->totvert > 0) { + struct GPUBatch *geom = DRW_cache_mesh_all_verts_get(ob); + if (geom) { + OVERLAY_extra_loose_points(cb, geom, ob->obmat, color); + } + } + } +} + +void OVERLAY_wireframe_draw(OVERLAY_Data *data) +{ + OVERLAY_FramebufferList *fbl = data->fbl; + OVERLAY_PassList *psl = data->psl; + OVERLAY_PrivateData *pd = data->stl->pd; + + if (pd->antialiasing.enabled) { + GPU_framebuffer_bind(fbl->overlay_line_fb); + } + + DRW_view_set_active(pd->view_wires); + DRW_draw_pass(psl->wireframe_ps); + + DRW_view_set_active(pd->view_default); + + if (pd->antialiasing.enabled) { + GPU_framebuffer_bind(fbl->overlay_default_fb); + } +} + +void OVERLAY_wireframe_in_front_draw(OVERLAY_Data *data) +{ + OVERLAY_PassList *psl = data->psl; + OVERLAY_PrivateData *pd = data->stl->pd; + + DRW_view_set_active(pd->view_wires); + DRW_draw_pass(psl->wireframe_xray_ps); + + DRW_view_set_active(pd->view_default); +} diff --git a/source/blender/draw/engines/overlay/shaders/antialiasing_frag.glsl b/source/blender/draw/engines/overlay/shaders/antialiasing_frag.glsl new file mode 100644 index 00000000000..98f69abe89f --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/antialiasing_frag.glsl @@ -0,0 +1,135 @@ + +uniform sampler2D colorTex; +uniform sampler2D depthTex; +uniform sampler2D lineTex; + +in vec2 uvs; + +out vec4 fragColor; + +#define M_1_SQRTPI 0.5641895835477563 /* 1/sqrt(pi) */ + +/** + * We want to know how much a pixel is covered by a line. + * We replace the square pixel with acircle of the same area and try to find the intersection area. + * The area we search is the circular segment. https://en.wikipedia.org/wiki/Circular_segment + * The formula for the area uses inverse trig function and is quite complexe. Instead, + * we approximate it by using the smoothstep function and a 1.05 factor to the disc radius. + */ +#define DISC_RADIUS (M_1_SQRTPI * 1.05) +#define LINE_SMOOTH_START (0.5 - DISC_RADIUS) +#define LINE_SMOOTH_END (0.5 + DISC_RADIUS) + +/** + * Returns coverage of a line onto a sample that is distance_to_line (in pixels) far from the line. + * line_kernel_size is the inner size of the line with 100% coverage. + */ +float line_coverage(float distance_to_line, float line_kernel_size) +{ + return smoothstep(LINE_SMOOTH_END, LINE_SMOOTH_START, abs(distance_to_line) - line_kernel_size); +} +vec4 line_coverage(vec4 distance_to_line, float line_kernel_size) +{ + return smoothstep(LINE_SMOOTH_END, LINE_SMOOTH_START, abs(distance_to_line) - line_kernel_size); +} + +vec2 decode_line_dir(vec2 dir) +{ + return dir * 2.0 - 1.0; +} + +float decode_line_dist(float dist) +{ + return (dist - 0.1) * 4.0 - 2.0; +} + +float neighbor_dist(vec3 line_dir_and_dist, vec2 ofs) +{ + float dist = decode_line_dist(line_dir_and_dist.z); + vec2 dir = decode_line_dir(line_dir_and_dist.xy); + + bool is_line = line_dir_and_dist.z != 0.0; + bool dir_horiz = abs(dir.x) > abs(dir.y); + bool ofs_horiz = (ofs.x != 0); + + if (!is_line || (ofs_horiz != dir_horiz)) { + dist += 1e10; /* No line. */ + } + else { + dist += dot(ofs, -dir); + } + return dist; +} + +void neighbor_blend( + float line_coverage, float line_depth, vec4 line_color, inout float frag_depth, inout vec4 col) +{ + line_color *= line_coverage; + if (line_coverage > 0.0 && line_depth < frag_depth) { + /* Alpha over. */ + col = col * (1.0 - line_color.a) + line_color; + frag_depth = line_depth; + } + else { + /* Alpha under. */ + col = col + line_color * (1.0 - col.a); + } +} + +void main() +{ + ivec2 center_texel = ivec2(gl_FragCoord.xy); + const float line_kernel = 0.0; + + fragColor = texelFetch(colorTex, center_texel, 0); + + float depth = texelFetch(depthTex, center_texel, 0).r; + + float dist_raw = texelFetch(lineTex, center_texel, 0).b; + float dist = decode_line_dist(dist_raw); + + /* TODO Opti: use textureGather */ + vec4 neightbor_col0 = texelFetchOffset(colorTex, center_texel, 0, ivec2(1, 0)); + vec4 neightbor_col1 = texelFetchOffset(colorTex, center_texel, 0, ivec2(-1, 0)); + vec4 neightbor_col2 = texelFetchOffset(colorTex, center_texel, 0, ivec2(0, 1)); + vec4 neightbor_col3 = texelFetchOffset(colorTex, center_texel, 0, ivec2(0, -1)); + + vec3 neightbor_line0 = texelFetchOffset(lineTex, center_texel, 0, ivec2(1, 0)).rgb; + vec3 neightbor_line1 = texelFetchOffset(lineTex, center_texel, 0, ivec2(-1, 0)).rgb; + vec3 neightbor_line2 = texelFetchOffset(lineTex, center_texel, 0, ivec2(0, 1)).rgb; + vec3 neightbor_line3 = texelFetchOffset(lineTex, center_texel, 0, ivec2(0, -1)).rgb; + + vec4 depths; + depths.x = texelFetchOffset(depthTex, center_texel, 0, ivec2(1, 0)).r; + depths.y = texelFetchOffset(depthTex, center_texel, 0, ivec2(-1, 0)).r; + depths.z = texelFetchOffset(depthTex, center_texel, 0, ivec2(0, 1)).r; + depths.w = texelFetchOffset(depthTex, center_texel, 0, ivec2(0, -1)).r; + + vec4 line_dists; + line_dists.x = neighbor_dist(neightbor_line0, vec2(1, 0)); + line_dists.y = neighbor_dist(neightbor_line1, vec2(-1, 0)); + line_dists.z = neighbor_dist(neightbor_line2, vec2(0, 1)); + line_dists.w = neighbor_dist(neightbor_line3, vec2(0, -1)); + + vec4 coverage = line_coverage(line_dists, line_kernel); + + if (dist_raw > 0.0) { + fragColor *= line_coverage(dist, line_kernel); + } + + /* We dont order fragments but use alpha over/alpha under based on current minimum frag depth. */ + neighbor_blend(coverage.x, depths.x, neightbor_col0, depth, fragColor); + neighbor_blend(coverage.y, depths.y, neightbor_col1, depth, fragColor); + neighbor_blend(coverage.z, depths.z, neightbor_col2, depth, fragColor); + neighbor_blend(coverage.w, depths.w, neightbor_col3, depth, fragColor); + +#if 1 + /* Fix aliasing issue with really dense meshes and 1 pixel sized lines. */ + if (dist_raw > 0.0 && line_kernel < 0.45) { + vec4 lines = vec4(neightbor_line0.z, neightbor_line1.z, neightbor_line2.z, neightbor_line3.z); + /* Count number of line neighbors. */ + float blend = dot(vec4(0.25), step(0.001, lines)); + fragColor = mix(fragColor, fragColor / fragColor.a, blend); + } +#endif +}
\ No newline at end of file diff --git a/source/blender/draw/engines/overlay/shaders/antialiasing_vert.glsl b/source/blender/draw/engines/overlay/shaders/antialiasing_vert.glsl new file mode 100644 index 00000000000..4f3c36c7bd7 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/antialiasing_vert.glsl @@ -0,0 +1,11 @@ + +out vec2 uvs; + +void main() +{ + int v = gl_VertexID % 3; + float x = float((v & 1) << 2); + float y = float((v & 2) << 1); + gl_Position = vec4(x - 1.0, y - 1.0, 1.0, 1.0); + uvs = vec2(x, y) * 0.5; +}
\ No newline at end of file diff --git a/source/blender/draw/engines/overlay/shaders/armature_dof_vert.glsl b/source/blender/draw/engines/overlay/shaders/armature_dof_vert.glsl new file mode 100644 index 00000000000..371229f23ab --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/armature_dof_vert.glsl @@ -0,0 +1,33 @@ + +/* ---- Instantiated Attrs ---- */ +in vec2 pos; + +/* ---- Per instance Attrs ---- */ +/* Assumed to be in world coordinate already. */ +in vec4 color; +in mat4 inst_obmat; + +flat out vec4 finalColor; + +vec3 sphere_project(float ax, float az) +{ + float sine = 1.0 - ax * ax - az * az; + float q3 = sqrt(max(0.0, sine)); + + return vec3(-az * q3, 0.5 - sine, ax * q3) * 2.0; +} + +void main() +{ + mat4 model_mat = inst_obmat; + model_mat[0][3] = model_mat[1][3] = model_mat[2][3] = 0.0; + model_mat[3][3] = 1.0; + + vec2 amin = vec2(inst_obmat[0][3], inst_obmat[1][3]); + vec2 amax = vec2(inst_obmat[2][3], inst_obmat[3][3]); + + vec3 final_pos = sphere_project(pos.x * abs((pos.x > 0.0) ? amax.x : amin.x), + pos.y * abs((pos.y > 0.0) ? amax.y : amin.y)); + gl_Position = ViewProjectionMatrix * (model_mat * vec4(final_pos, 1.0)); + finalColor = color; +} diff --git a/source/blender/draw/engines/overlay/shaders/armature_envelope_outline_vert.glsl b/source/blender/draw/engines/overlay/shaders/armature_envelope_outline_vert.glsl new file mode 100644 index 00000000000..e3cc8d582d8 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/armature_envelope_outline_vert.glsl @@ -0,0 +1,164 @@ + +/* ---- Instantiated Attrs ---- */ +in vec2 pos0; +in vec2 pos1; +in vec2 pos2; + +/* ---- Per instance Attrs ---- */ +/* Assumed to be in world coordinate already. */ +in vec4 headSphere; +in vec4 tailSphere; +in vec4 outlineColorSize; +in vec3 xAxis; + +flat out vec4 finalColor; + +/* project to screen space */ +vec2 proj(vec4 pos) +{ + return (0.5 * (pos.xy / pos.w) + 0.5) * sizeViewport.xy; +} + +vec2 compute_dir(vec2 v0, vec2 v1, vec2 v2) +{ + vec2 dir = normalize(v2 - v0); + dir = vec2(dir.y, -dir.x); + return dir; +} + +mat3 compute_mat(vec4 sphere, vec3 bone_vec, out float z_ofs) +{ + bool is_persp = (ProjectionMatrix[3][3] == 0.0); + vec3 cam_ray = (is_persp) ? sphere.xyz - ViewMatrixInverse[3].xyz : -ViewMatrixInverse[2].xyz; + + /* Sphere center distance from the camera (persp) in world space. */ + float cam_dist = length(cam_ray); + + /* Compute view aligned orthonormal space. */ + vec3 z_axis = cam_ray / cam_dist; + vec3 x_axis = normalize(cross(bone_vec, z_axis)); + vec3 y_axis = cross(z_axis, x_axis); + z_ofs = 0.0; + + if (is_persp) { + /* For perspective, the projected sphere radius + * can be bigger than the center disc. Compute the + * max angular size and compensate by sliding the disc + * towards the camera and scale it accordingly. */ + const float half_pi = 3.1415926 * 0.5; + float rad = sphere.w; + /* Let be : + * V the view vector origin. + * O the sphere origin. + * T the point on the target circle. + * We compute the angle between (OV) and (OT). */ + float a = half_pi - asin(rad / cam_dist); + float cos_b = cos(a); + float sin_b = sqrt(clamp(1.0 - cos_b * cos_b, 0.0, 1.0)); + + x_axis *= sin_b; + y_axis *= sin_b; + z_ofs = -rad * cos_b; + } + + return mat3(x_axis, y_axis, z_axis); +} + +struct Bone { + vec3 vec; + float sinb; +}; + +bool bone_blend_starts(vec3 p, Bone b) +{ + /* we just want to know when the head sphere starts interpolating. */ + return dot(p, b.vec) > -b.sinb; +} + +vec3 get_outline_point(vec2 pos, + vec4 sph_near, + vec4 sph_far, + mat3 mat_near, + mat3 mat_far, + float z_ofs_near, + float z_ofs_far, + Bone b) +{ + /* Compute outline position on the nearest sphere and check + * if it penetrates the capsule body. If it does, put this + * vertex on the farthest sphere. */ + vec3 wpos = mat_near * vec3(pos * sph_near.w, z_ofs_near); + if (bone_blend_starts(wpos, b)) { + wpos = sph_far.xyz + mat_far * vec3(pos * sph_far.w, z_ofs_far); + } + else { + wpos += sph_near.xyz; + } + return wpos; +} + +void main() +{ + float dst_head = distance(headSphere.xyz, ViewMatrixInverse[3].xyz); + float dst_tail = distance(tailSphere.xyz, ViewMatrixInverse[3].xyz); + // float dst_head = -dot(headSphere.xyz, ViewMatrix[2].xyz); + // float dst_tail = -dot(tailSphere.xyz, ViewMatrix[2].xyz); + + vec4 sph_near, sph_far; + if ((dst_head > dst_tail) && (ProjectionMatrix[3][3] == 0.0)) { + sph_near = tailSphere; + sph_far = headSphere; + } + else { + sph_near = headSphere; + sph_far = tailSphere; + } + + vec3 bone_vec = (sph_far.xyz - sph_near.xyz) + 1e-8; + + Bone b; + float bone_lenrcp = 1.0 / max(1e-8, sqrt(dot(bone_vec, bone_vec))); + b.sinb = (sph_far.w - sph_near.w) * bone_lenrcp * sph_near.w; + b.vec = bone_vec * bone_lenrcp; + + float z_ofs_near, z_ofs_far; + mat3 mat_near = compute_mat(sph_near, bone_vec, z_ofs_near); + mat3 mat_far = compute_mat(sph_far, bone_vec, z_ofs_far); + + vec3 wpos0 = get_outline_point( + pos0, sph_near, sph_far, mat_near, mat_far, z_ofs_near, z_ofs_far, b); + vec3 wpos1 = get_outline_point( + pos1, sph_near, sph_far, mat_near, mat_far, z_ofs_near, z_ofs_far, b); + vec3 wpos2 = get_outline_point( + pos2, sph_near, sph_far, mat_near, mat_far, z_ofs_near, z_ofs_far, b); + + vec4 pos_4d = vec4(wpos1, 1.0); +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(pos_4d.xyz); +#endif + + vec4 V = ViewMatrix * pos_4d; + float pres_fac = (ProjectionMatrix[3][3] == 0.0) ? abs(V.z) : 1.0; + + vec4 p0 = ViewProjectionMatrix * vec4(wpos0, 1.0); + vec4 p1 = ProjectionMatrix * V; + vec4 p2 = ViewProjectionMatrix * vec4(wpos2, 1.0); + + /* compute position from 3 vertex because the change in direction + * can happen very quicky and lead to very thin edges. */ + vec2 ss0 = proj(p0); + vec2 ss1 = proj(p1); + vec2 ss2 = proj(p2); + vec2 edge_dir = compute_dir(ss0, ss1, ss2); + + float line_thickness = 2.0 * sizePixel; + bool outer = ((gl_VertexID & 1) == 1); + vec2 t = outlineColorSize.w * line_thickness * sizeViewportInv.xy; + t *= pres_fac; + t = (outer) ? t : vec2(0.0); + + gl_Position = p1; + gl_Position.xy += t * edge_dir; + + finalColor = vec4(outlineColorSize.rgb, 1.0); +} diff --git a/source/blender/draw/engines/overlay/shaders/armature_envelope_solid_frag.glsl b/source/blender/draw/engines/overlay/shaders/armature_envelope_solid_frag.glsl new file mode 100644 index 00000000000..d0a8e48657e --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/armature_envelope_solid_frag.glsl @@ -0,0 +1,25 @@ + +uniform float alpha = 0.6; +uniform bool isDistance; + +flat in vec3 finalStateColor; +flat in vec3 finalBoneColor; +in vec3 normalView; + +out vec4 fragColor; + +void main() +{ + float n = normalize(normalView).z; + if (isDistance) { + n = 1.0 - clamp(-n, 0.0, 1.0); + fragColor = vec4(1.0, 1.0, 1.0, 0.2) * n; + } + else { + /* Smooth lighting factor. */ + const float s = 0.2; /* [0.0-0.5] range */ + float fac = clamp((n * (1.0 - s)) + s, 0.0, 1.0); + fragColor.rgb = mix(finalStateColor, finalBoneColor, fac); + fragColor.a = alpha; + } +} diff --git a/source/blender/draw/engines/overlay/shaders/armature_envelope_solid_vert.glsl b/source/blender/draw/engines/overlay/shaders/armature_envelope_solid_vert.glsl new file mode 100644 index 00000000000..620b3f2527c --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/armature_envelope_solid_vert.glsl @@ -0,0 +1,55 @@ + +/* ---- Instantiated Attrs ---- */ +in vec3 pos; + +/* ---- Per instance Attrs ---- */ +/* Assumed to be in world coordinate already. */ +in vec4 headSphere; +in vec4 tailSphere; +in vec3 xAxis; +in vec3 stateColor; +in vec3 boneColor; + +flat out vec3 finalStateColor; +flat out vec3 finalBoneColor; +out vec3 normalView; + +void main() +{ + vec3 bone_vec = tailSphere.xyz - headSphere.xyz; + float bone_len = max(1e-8, sqrt(dot(bone_vec, bone_vec))); + float bone_lenrcp = 1.0 / bone_len; +#ifdef SMOOTH_ENVELOPE + float sinb = (tailSphere.w - headSphere.w) * bone_lenrcp; +#else + const float sinb = 0.0; +#endif + + vec3 y_axis = bone_vec * bone_lenrcp; + vec3 z_axis = normalize(cross(xAxis, -y_axis)); + vec3 x_axis = cross(y_axis, z_axis); /* cannot trust xAxis to be orthogonal. */ + + vec3 sp, nor; + nor = sp = pos.xyz; + + /* In bone space */ + bool is_head = (pos.z < -sinb); + sp *= (is_head) ? headSphere.w : tailSphere.w; + sp.z += (is_head) ? 0.0 : bone_len; + + /* Convert to world space */ + mat3 bone_mat = mat3(x_axis, y_axis, z_axis); + sp = bone_mat * sp.xzy + headSphere.xyz; + nor = bone_mat * nor.xzy; + + normalView = mat3(ViewMatrix) * nor; + + finalStateColor = stateColor; + finalBoneColor = boneColor; + + vec4 pos_4d = vec4(sp, 1.0); + gl_Position = ViewProjectionMatrix * pos_4d; +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(pos_4d.xyz); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/armature_shape_outline_geom.glsl b/source/blender/draw/engines/overlay/shaders/armature_shape_outline_geom.glsl new file mode 100644 index 00000000000..bd05c7f3532 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/armature_shape_outline_geom.glsl @@ -0,0 +1,116 @@ + +layout(lines_adjacency) in; +layout(triangle_strip, max_vertices = 6) out; + +in vec4 pPos[]; +in vec3 vPos[]; +in vec2 ssPos[]; +in vec2 ssNor[]; +in vec4 vColSize[]; + +flat out vec4 finalColor; + +vec2 compute_dir(vec2 v0, vec2 v1) +{ + vec2 dir = normalize(v1 - v0); + dir = vec2(-dir.y, dir.x); + return dir; +} + +void emit_edge(vec2 edge_dir, vec2 hidden_dir, vec2 thick, bool is_persp) +{ + float fac = dot(-hidden_dir, edge_dir); + edge_dir *= (fac < 0.0) ? -1.0 : 1.0; + + vec2 t = thick * (is_persp ? abs(vPos[1].z) : 1.0); + gl_Position = pPos[1]; +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_set_clip_distance(gl_in[1].gl_ClipDistance); +#endif + EmitVertex(); + gl_Position.xy += t * edge_dir; +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_set_clip_distance(gl_in[1].gl_ClipDistance); +#endif + EmitVertex(); + + t = thick * (is_persp ? abs(vPos[2].z) : 1.0); + gl_Position = pPos[2]; +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_set_clip_distance(gl_in[2].gl_ClipDistance); +#endif + EmitVertex(); + gl_Position.xy += t * edge_dir; +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_set_clip_distance(gl_in[2].gl_ClipDistance); +#endif + EmitVertex(); +} + +void emit_corner(const int e, vec2 thick, bool is_persp) +{ + vec2 corner_dir = ssNor[e]; + vec2 t = thick * (is_persp ? abs(vPos[e].z) : 1.0); + + gl_Position = pPos[e] + vec4(t * corner_dir, 0.0, 0.0); +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_set_clip_distance(gl_in[e].gl_ClipDistance); +#endif + EmitVertex(); +} + +void main(void) +{ + finalColor = vec4(vColSize[0].rgb, 1.0); + + bool is_persp = (ProjectionMatrix[3][3] == 0.0); + + vec3 view_vec = (is_persp) ? normalize(vPos[1]) : vec3(0.0, 0.0, -1.0); + vec3 v10 = vPos[0] - vPos[1]; + vec3 v12 = vPos[2] - vPos[1]; + vec3 v13 = vPos[3] - vPos[1]; + + vec3 n0 = cross(v12, v10); + vec3 n3 = cross(v13, v12); + + float fac0 = dot(view_vec, n0); + float fac3 = dot(view_vec, n3); + + /* If one of the face is perpendicular to the view, + * consider it and outline edge. */ + if (abs(fac0) > 1e-5 && abs(fac3) > 1e-5) { + /* If both adjacent verts are facing the camera the same way, + * then it isn't an outline edge. */ + if (sign(fac0) == sign(fac3)) { + return; + } + } + + /* Don't outline if concave edge. */ + if (dot(n0, v13) > 0.0001) { + return; + } + + float line_thickness = 2.0 * sizePixel; + vec2 thick = vColSize[0].w * (line_thickness * sizeViewportInv.xy); + vec2 edge_dir = compute_dir(ssPos[1], ssPos[2]); + + vec2 hidden_point; + /* Take the farthest point to compute edge direction + * (avoid problems with point behind near plane). + * If the chosen point is parallel to the edge in screen space, + * choose the other point anyway. + * This fixes some issue with cubes in orthographic views.*/ + if (vPos[0].z < vPos[3].z) { + hidden_point = (abs(fac0) > 1e-5) ? ssPos[0] : ssPos[3]; + } + else { + hidden_point = (abs(fac3) > 1e-5) ? ssPos[3] : ssPos[0]; + } + vec2 hidden_dir = normalize(hidden_point - ssPos[1]); + + emit_corner(1, thick, is_persp); + emit_edge(edge_dir, hidden_dir, thick, is_persp); + emit_corner(2, thick, is_persp); + EndPrimitive(); +} diff --git a/source/blender/draw/engines/overlay/shaders/armature_shape_outline_vert.glsl b/source/blender/draw/engines/overlay/shaders/armature_shape_outline_vert.glsl new file mode 100644 index 00000000000..cd9368a997a --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/armature_shape_outline_vert.glsl @@ -0,0 +1,48 @@ + +/* ---- Instantiated Attrs ---- */ +in vec3 pos; +in vec3 snor; + +/* ---- Per instance Attrs ---- */ +in vec4 color; +in mat4 inst_obmat; + +out vec4 pPos; +out vec3 vPos; +out vec2 ssPos; +out vec2 ssNor; +out vec4 vColSize; + +/* project to screen space */ +vec2 proj(vec4 pos) +{ + return (0.5 * (pos.xy / pos.w) + 0.5) * sizeViewport.xy; +} + +void main() +{ + vec4 bone_color, state_color; + mat4 model_mat = extract_matrix_packed_data(inst_obmat, state_color, bone_color); + + vec4 worldPosition = model_mat * vec4(pos, 1.0); + vec4 viewpos = ViewMatrix * worldPosition; + + vPos = viewpos.xyz; + pPos = ProjectionMatrix * viewpos; + + /* This is slow and run per vertex, but it's still faster than + * doing it per instance on CPU and sending it on via instance attribute. */ + mat3 normal_mat = transpose(inverse(mat3(model_mat))); + /* TODO FIX: there is still a problem with this vector + * when the bone is scaled or in persp mode. But it's + * barelly visible at the outline corners. */ + ssNor = normalize(normal_world_to_view(normal_mat * snor).xy); + + ssPos = proj(pPos); + + vColSize = bone_color; + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(worldPosition.xyz); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/armature_shape_solid_frag.glsl b/source/blender/draw/engines/overlay/shaders/armature_shape_solid_frag.glsl new file mode 100644 index 00000000000..39963344dd8 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/armature_shape_solid_frag.glsl @@ -0,0 +1,11 @@ + +uniform float alpha = 0.6; + +in vec4 finalColor; + +out vec4 fragColor; + +void main() +{ + fragColor = vec4(finalColor.rgb, alpha); +} diff --git a/source/blender/draw/engines/overlay/shaders/armature_shape_solid_vert.glsl b/source/blender/draw/engines/overlay/shaders/armature_shape_solid_vert.glsl new file mode 100644 index 00000000000..8284bd43adc --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/armature_shape_solid_vert.glsl @@ -0,0 +1,37 @@ + +/* ---- Instantiated Attrs ---- */ +in vec3 pos; +in vec3 nor; + +/* ---- Per instance Attrs ---- */ +in mat4 inst_obmat; + +out vec4 finalColor; + +void main() +{ + vec4 bone_color, state_color; + mat4 model_mat = extract_matrix_packed_data(inst_obmat, state_color, bone_color); + + /* This is slow and run per vertex, but it's still faster than + * doing it per instance on CPU and sending it on via instance attribute. */ + mat3 normal_mat = transpose(inverse(mat3(model_mat))); + vec3 normal = normalize(normal_world_to_view(normal_mat * nor)); + + /* Do lighting at an angle to avoid flat shading on front facing bone. */ + const vec3 light = vec3(0.1, 0.1, 0.8); + float n = dot(normal, light); + + /* Smooth lighting factor. */ + const float s = 0.2; /* [0.0-0.5] range */ + float fac = clamp((n * (1.0 - s)) + s, 0.0, 1.0); + finalColor.rgb = mix(state_color.rgb, bone_color.rgb, fac); + finalColor.a = 1.0; + + vec4 worldPosition = model_mat * vec4(pos, 1.0); + gl_Position = ViewProjectionMatrix * worldPosition; + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(worldPosition.xyz); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/armature_sphere_outline_vert.glsl b/source/blender/draw/engines/overlay/shaders/armature_sphere_outline_vert.glsl new file mode 100644 index 00000000000..9dd4c444116 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/armature_sphere_outline_vert.glsl @@ -0,0 +1,104 @@ + +/* ---- Instantiated Attrs ---- */ +in vec2 pos0; +in vec2 pos1; + +/* ---- Per instance Attrs ---- */ +in mat4 inst_obmat; + +flat out vec4 finalColor; + +/* project to screen space */ +vec2 proj(vec4 pos) +{ + return (0.5 * (pos.xy / pos.w) + 0.5) * sizeViewport.xy; +} + +vec2 compute_dir(vec2 v0, vec2 v1, vec2 c) +{ + vec2 dir = normalize(v1 - v0); + dir = vec2(dir.y, -dir.x); + /* The model matrix can be scaled negativly. + * Use projected sphere center to determine + * the outline direction. */ + vec2 cv = c - v0; + dir = (dot(dir, cv) > 0.0) ? -dir : dir; + return dir; +} + +void main() +{ + vec4 bone_color, state_color; + mat4 model_mat = extract_matrix_packed_data(inst_obmat, state_color, bone_color); + + mat4 model_view_matrix = ViewMatrix * model_mat; + mat4 sphereMatrix = inverse(model_view_matrix); + + bool is_persp = (ProjectionMatrix[3][3] == 0.0); + + /* This is the local space camera ray (not normalize). + * In perspective mode it's also the viewspace position + * of the sphere center. */ + vec3 cam_ray = (is_persp) ? model_view_matrix[3].xyz : vec3(0.0, 0.0, -1.0); + cam_ray = mat3(sphereMatrix) * cam_ray; + + /* Sphere center distance from the camera (persp) in local space. */ + float cam_dist = length(cam_ray); + + /* Compute view aligned orthonormal space. */ + vec3 z_axis = cam_ray / cam_dist; + vec3 x_axis = normalize(cross(sphereMatrix[1].xyz, z_axis)); + vec3 y_axis = cross(z_axis, x_axis); + float z_ofs = 0.0; + + if (is_persp) { + /* For perspective, the projected sphere radius + * can be bigger than the center disc. Compute the + * max angular size and compensate by sliding the disc + * towards the camera and scale it accordingly. */ + const float half_pi = 3.1415926 * 0.5; + const float rad = 0.05; + /* Let be (in local space): + * V the view vector origin. + * O the sphere origin. + * T the point on the target circle. + * We compute the angle between (OV) and (OT). */ + float a = half_pi - asin(rad / cam_dist); + float cos_b = cos(a); + float sin_b = sqrt(clamp(1.0 - cos_b * cos_b, 0.0, 1.0)); + + x_axis *= sin_b; + y_axis *= sin_b; + z_ofs = -rad * cos_b; + } + + /* Camera oriented position (but still in local space) */ + vec3 cam_pos0 = x_axis * pos0.x + y_axis * pos0.y + z_axis * z_ofs; + vec3 cam_pos1 = x_axis * pos1.x + y_axis * pos1.y + z_axis * z_ofs; + + vec4 V = model_view_matrix * vec4(cam_pos0, 1.0); + vec4 p0 = ProjectionMatrix * V; + vec4 p1 = ProjectionMatrix * (model_view_matrix * vec4(cam_pos1, 1.0)); + vec4 c = ProjectionMatrix * vec4(model_view_matrix[3].xyz, 1.0); + + vec2 ssc = proj(c); + vec2 ss0 = proj(p0); + vec2 ss1 = proj(p1); + vec2 edge_dir = compute_dir(ss0, ss1, ssc); + + bool outer = ((gl_VertexID & 1) == 1); + + vec2 t = bone_color.w * (2.0 * sizeViewportInv.xy); + t *= (is_persp) ? abs(V.z) : 1.0; + t = (outer) ? t : vec2(0.0); + + gl_Position = p0; + gl_Position.xy += t * edge_dir; + + finalColor = vec4(bone_color.rgb, 1.0); + +#ifdef USE_WORLD_CLIP_PLANES + vec4 worldPosition = model_mat * vec4(cam_pos0, 1.0); + world_clip_planes_calc_clip_distance(worldPosition.xyz); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/armature_sphere_solid_frag.glsl b/source/blender/draw/engines/overlay/shaders/armature_sphere_solid_frag.glsl new file mode 100644 index 00000000000..94f339c4561 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/armature_sphere_solid_frag.glsl @@ -0,0 +1,76 @@ + +uniform float alpha = 0.4; + +flat in vec3 finalStateColor; +flat in vec3 finalBoneColor; +flat in mat4 sphereMatrix; +in vec3 viewPosition; + +#ifdef GL_ARB_conservative_depth +/* Saves a lot of overdraw! */ +layout(depth_greater) out float gl_FragDepth; +#endif + +out vec4 fragColor; + +#define cameraPos ViewMatrixInverse[3].xyz + +float get_depth_from_view_z(float z) +{ + if (ProjectionMatrix[3][3] == 0.0) { + z = (-ProjectionMatrix[3][2] / z) - ProjectionMatrix[2][2]; + } + else { + z = z * ProjectionMatrix[2][2] / (1.0 - ProjectionMatrix[3][2]); + } + return z * 0.5 + 0.5; +} + +void main() +{ + const float sphere_radius = 0.05; + + bool is_perp = (ProjectionMatrix[3][3] == 0.0); + vec3 ray_ori_view = (is_perp) ? vec3(0.0) : viewPosition.xyz; + vec3 ray_dir_view = (is_perp) ? viewPosition : vec3(0.0, 0.0, -1.0); + + /* Single matrix mul without branch. */ + vec4 mul_vec = (is_perp) ? vec4(ray_dir_view, 0.0) : vec4(ray_ori_view, 1.0); + vec3 mul_res = (sphereMatrix * mul_vec).xyz; + + /* Reminder : + * sphereMatrix[3] is the view space origin in sphere space (sph_ori -> view_ori). + * sphereMatrix[2] is the view space Z axis in sphere space. */ + + /* convert to sphere local space */ + vec3 ray_ori = (is_perp) ? sphereMatrix[3].xyz : mul_res; + vec3 ray_dir = (is_perp) ? mul_res : -sphereMatrix[2].xyz; + float ray_len = length(ray_dir); + ray_dir /= ray_len; + + /* Line to sphere intersect */ + const float sphere_radius_sqr = sphere_radius * sphere_radius; + float b = dot(ray_ori, ray_dir); + float c = dot(ray_ori, ray_ori) - sphere_radius_sqr; + float h = b * b - c; + float t = -sqrt(max(0.0, h)) - b; + + /* Compute dot product for lighting */ + vec3 p = ray_dir * t + ray_ori; /* Point on sphere */ + vec3 n = normalize(p); /* Normal is just the point in sphere space, normalized. */ + vec3 l = normalize(sphereMatrix[2].xyz); /* Just the view Z axis in the sphere space. */ + + /* Smooth lighting factor. */ + const float s = 0.2; /* [0.0-0.5] range */ + float fac = clamp((dot(n, l) * (1.0 - s)) + s, 0.0, 1.0); + fragColor.rgb = mix(finalStateColor, finalBoneColor, fac); + + /* 2x2 dither pattern to smooth the lighting. */ + float dither = (0.5 + dot(vec2(ivec2(gl_FragCoord.xy) & ivec2(1)), vec2(1.0, 2.0))) * 0.25; + dither *= (1.0 / 255.0); /* Assume 8bit per color buffer. */ + + fragColor = vec4(fragColor.rgb + dither, alpha); + + t /= ray_len; + gl_FragDepth = get_depth_from_view_z(ray_dir_view.z * t + ray_ori_view.z); +} diff --git a/source/blender/draw/engines/overlay/shaders/armature_sphere_solid_vert.glsl b/source/blender/draw/engines/overlay/shaders/armature_sphere_solid_vert.glsl new file mode 100644 index 00000000000..e6fa29ce851 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/armature_sphere_solid_vert.glsl @@ -0,0 +1,87 @@ + +/* ---- Instantiated Attrs ---- */ +in vec2 pos; + +/* ---- Per instance Attrs ---- */ +in vec4 color; +in mat4 inst_obmat; + +flat out vec3 finalStateColor; +flat out vec3 finalBoneColor; +flat out mat4 sphereMatrix; +out vec3 viewPosition; + +/* Sphere radius */ +const float rad = 0.05; + +void main() +{ + vec4 bone_color, state_color; + mat4 model_mat = extract_matrix_packed_data(inst_obmat, state_color, bone_color); + + mat4 model_view_matrix = ViewMatrix * model_mat; + sphereMatrix = inverse(model_view_matrix); + + bool is_persp = (ProjectionMatrix[3][3] == 0.0); + + /* This is the local space camera ray (not normalize). + * In perspective mode it's also the viewspace position + * of the sphere center. */ + vec3 cam_ray = (is_persp) ? model_view_matrix[3].xyz : vec3(0.0, 0.0, -1.0); + cam_ray = mat3(sphereMatrix) * cam_ray; + + /* Sphere center distance from the camera (persp) in local space. */ + float cam_dist = length(cam_ray); + + /* Compute view aligned orthonormal space. */ + vec3 z_axis = cam_ray / cam_dist; + vec3 x_axis = normalize(cross(sphereMatrix[1].xyz, z_axis)); + vec3 y_axis = cross(z_axis, x_axis); + + float z_ofs = -rad - 1e-8; /* offset to the front of the sphere */ + if (is_persp) { + /* For perspective, the projected sphere radius + * can be bigger than the center disc. Compute the + * max angular size and compensate by sliding the disc + * towards the camera and scale it accordingly. */ + const float half_pi = 3.1415926 * 0.5; + /* Let be (in local space): + * V the view vector origin. + * O the sphere origin. + * T the point on the target circle. + * We compute the angle between (OV) and (OT). */ + float a = half_pi - asin(rad / cam_dist); + float cos_b = cos(a); + float sin_b = sqrt(clamp(1.0 - cos_b * cos_b, 0.0, 1.0)); +#if 1 + /* Instead of choosing the biggest circle in screenspace, + * we choose the nearest with the same angular size. This + * permit us to leverage GL_ARB_conservative_depth in the + * fragment shader. */ + float minor = cam_dist - rad; + float major = cam_dist - cos_b * rad; + float fac = minor / major; + sin_b *= fac; +#else + z_ofs = -rad * cos_b; +#endif + x_axis *= sin_b; + y_axis *= sin_b; + } + + /* Camera oriented position (but still in local space) */ + vec3 cam_pos = x_axis * pos.x + y_axis * pos.y + z_axis * z_ofs; + + vec4 pos_4d = vec4(cam_pos, 1.0); + vec4 V = model_view_matrix * pos_4d; + gl_Position = ProjectionMatrix * V; + viewPosition = V.xyz; + + finalStateColor = state_color.xyz; + finalBoneColor = bone_color.xyz; + +#ifdef USE_WORLD_CLIP_PLANES + vec4 worldPosition = model_mat * pos_4d; + world_clip_planes_calc_clip_distance(worldPosition.xyz); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/armature_stick_frag.glsl b/source/blender/draw/engines/overlay/shaders/armature_stick_frag.glsl new file mode 100644 index 00000000000..ba89619e051 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/armature_stick_frag.glsl @@ -0,0 +1,13 @@ + +noperspective in float colorFac; +flat in vec4 finalWireColor; +flat in vec4 finalInnerColor; + +out vec4 fragColor; + +void main() +{ + float fac = smoothstep(1.0, 0.2, colorFac); + fragColor.rgb = mix(finalInnerColor.rgb, finalWireColor.rgb, fac); + fragColor.a = 1.0; +} diff --git a/source/blender/draw/engines/overlay/shaders/armature_stick_vert.glsl b/source/blender/draw/engines/overlay/shaders/armature_stick_vert.glsl new file mode 100644 index 00000000000..99bdbfea2be --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/armature_stick_vert.glsl @@ -0,0 +1,90 @@ + +/* ---- Instantiated Attrs ---- */ +in vec2 pos; /* bone aligned screen space */ +in uint flag; + +#define COL_WIRE 1u /* (1 << 0) */ +#define COL_HEAD 2u /* (1 << 1) */ +#define COL_TAIL 4u /* (1 << 2) */ +#define COL_BONE 8u /* (1 << 3) */ + +#define POS_HEAD 16u /* (1 << 4) */ +#define POS_TAIL 32u /* (1 << 5) */ /* UNUSED */ +#define POS_BONE 64u /* (1 << 6) */ + +/* ---- Per instance Attrs ---- */ +in vec3 boneStart; +in vec3 boneEnd; +in vec4 wireColor; /* alpha encode if we do wire. If 0.0 we dont. */ +in vec4 boneColor; /* alpha encode if we do bone. If 0.0 we dont. */ +in vec4 headColor; /* alpha encode if we do head. If 0.0 we dont. */ +in vec4 tailColor; /* alpha encode if we do tail. If 0.0 we dont. */ + +#define do_wire (wireColor.a > 0.0) +#define is_head bool(flag & POS_HEAD) +#define is_bone bool(flag & POS_BONE) + +noperspective out float colorFac; +flat out vec4 finalWireColor; +flat out vec4 finalInnerColor; + +/* project to screen space */ +vec2 proj(vec4 pos) +{ + return (0.5 * (pos.xy / pos.w) + 0.5) * sizeViewport.xy; +} + +void main() +{ + finalInnerColor = ((flag & COL_HEAD) != 0u) ? headColor : tailColor; + finalInnerColor = ((flag & COL_BONE) != 0u) ? boneColor : finalInnerColor; + finalWireColor = (do_wire) ? wireColor : finalInnerColor; + /* Make the color */ + colorFac = ((flag & COL_WIRE) == 0u) ? ((flag & COL_BONE) != 0u) ? 1.0 : 2.0 : 0.0; + + vec4 boneStart_4d = vec4(boneStart, 1.0); + vec4 boneEnd_4d = vec4(boneEnd, 1.0); + vec4 v0 = ViewMatrix * boneStart_4d; + vec4 v1 = ViewMatrix * boneEnd_4d; + + /* Clip the bone to the camera origin plane (not the clip plane) + * to avoid glitches if one end is behind the camera origin (in persp). */ + float clip_dist = (ProjectionMatrix[3][3] == 0.0) ? + -1e-7 : + 1e20; /* hardcoded, -1e-8 is giving gliches. */ + vec3 bvec = v1.xyz - v0.xyz; + vec3 clip_pt = v0.xyz + bvec * ((v0.z - clip_dist) / -bvec.z); + if (v0.z > clip_dist) { + v0.xyz = clip_pt; + } + else if (v1.z > clip_dist) { + v1.xyz = clip_pt; + } + + vec4 p0 = ProjectionMatrix * v0; + vec4 p1 = ProjectionMatrix * v1; + + float h = (is_head) ? p0.w : p1.w; + + vec2 x_screen_vec = normalize(proj(p1) - proj(p0) + 1e-8); + vec2 y_screen_vec = vec2(x_screen_vec.y, -x_screen_vec.x); + + /* 2D screen aligned pos at the point */ + vec2 vpos = pos.x * x_screen_vec + pos.y * y_screen_vec; + vpos *= (ProjectionMatrix[3][3] == 0.0) ? h : 1.0; + vpos *= (do_wire) ? 1.0 : 0.5; + + if (finalInnerColor.a > 0.0) { + float stick_size = sizePixel * 5.0; + gl_Position = (is_head) ? p0 : p1; + gl_Position.xy += stick_size * (vpos * sizeViewportInv.xy); + gl_Position.z += (is_bone) ? 0.0 : 1e-6; /* Avoid Z fighting of head/tails. */ + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance((is_head ? boneStart_4d : boneEnd_4d).xyz); +#endif + } + else { + gl_Position = vec4(0.0); + } +} diff --git a/source/blender/draw/engines/overlay/shaders/armature_wire_vert.glsl b/source/blender/draw/engines/overlay/shaders/armature_wire_vert.glsl new file mode 100644 index 00000000000..4e207b96016 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/armature_wire_vert.glsl @@ -0,0 +1,18 @@ + +in vec3 color; +in vec3 pos; + +flat out vec4 finalColor; + +void main() +{ + finalColor.rgb = color; + finalColor.a = 1.0; + + vec3 worldPosition = point_object_to_world(pos); + gl_Position = point_world_to_ndc(worldPosition); + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(worldPosition); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/depth_only_vert.glsl b/source/blender/draw/engines/overlay/shaders/depth_only_vert.glsl new file mode 100644 index 00000000000..7a3af0f3b61 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/depth_only_vert.glsl @@ -0,0 +1,14 @@ + +in vec3 pos; + +void main() +{ + GPU_INTEL_VERTEX_SHADER_WORKAROUND + + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/edit_curve_handle_geom.glsl b/source/blender/draw/engines/overlay/shaders/edit_curve_handle_geom.glsl new file mode 100644 index 00000000000..b6576ba7a21 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/edit_curve_handle_geom.glsl @@ -0,0 +1,114 @@ + +/* Keep the same value of `ACTIVE_NURB` in `draw_cache_imp_curve.c` */ +#define ACTIVE_NURB 1 << 2 +#define EVEN_U_BIT 1 << 3 + +layout(lines) in; +layout(triangle_strip, max_vertices = 10) out; + +uniform bool showCurveHandles; + +flat in int vertFlag[]; + +out vec4 finalColor; + +void output_line(vec2 offset, vec4 color) +{ + finalColor = color; + + gl_Position = gl_in[0].gl_Position; + gl_Position.xy += offset * gl_in[0].gl_Position.w; +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_set_clip_distance(gl_in[0].gl_ClipDistance); +#endif + EmitVertex(); + + gl_Position = gl_in[1].gl_Position; + gl_Position.xy += offset * gl_in[1].gl_Position.w; +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_set_clip_distance(gl_in[1].gl_ClipDistance); +#endif + EmitVertex(); +} + +void main() +{ + vec4 v1 = gl_in[0].gl_Position; + vec4 v2 = gl_in[1].gl_Position; + + int is_active_nurb = (vertFlag[1] & ACTIVE_NURB); + int color_id = (vertFlag[1] >> 4); + + /* Don't output any edges if we don't show handles */ + if (!showCurveHandles && (color_id < 5)) { + return; + } + + bool edge_selected = (((vertFlag[1] | vertFlag[0]) & VERT_SELECTED) != 0); + + vec4 inner_color; + if (color_id == 0) { + inner_color = (edge_selected) ? colorHandleSelFree : colorHandleFree; + } + else if (color_id == 1) { + inner_color = (edge_selected) ? colorHandleSelAuto : colorHandleAuto; + } + else if (color_id == 2) { + inner_color = (edge_selected) ? colorHandleSelVect : colorHandleVect; + } + else if (color_id == 3) { + inner_color = (edge_selected) ? colorHandleSelAlign : colorHandleAlign; + } + else if (color_id == 4) { + inner_color = (edge_selected) ? colorHandleSelAutoclamp : colorHandleAutoclamp; + } + else { + bool is_selected = (((vertFlag[1] & vertFlag[0]) & VERT_SELECTED) != 0); + bool is_u_segment = (((vertFlag[1] ^ vertFlag[0]) & EVEN_U_BIT) != 0); + if (is_u_segment) { + inner_color = (is_selected) ? colorNurbSelUline : colorNurbUline; + } + else { + inner_color = (is_selected) ? colorNurbSelVline : colorNurbVline; + } + } + + vec4 outer_color = (is_active_nurb != 0) ? + mix(colorActiveSpline, + inner_color, + 0.25) /* Minimize active color bleeding on inner_color. */ + : + vec4(inner_color.rgb, 0.0); + + vec2 v1_2 = (v2.xy / v2.w - v1.xy / v1.w); + vec2 offset = sizeEdge * 4.0 * sizeViewportInv.xy; /* 4.0 is eyeballed */ + + if (abs(v1_2.x * sizeViewport.x) < abs(v1_2.y * sizeViewport.y)) { + offset.y = 0.0; + } + else { + offset.x = 0.0; + } + + /* draw the transparent border (AA). */ + if (is_active_nurb != 0) { + offset *= 0.75; /* Don't make the active "halo" appear very thick. */ + output_line(offset * 2.0, vec4(colorActiveSpline.rgb, 0.0)); + } + + /* draw the outline. */ + output_line(offset, outer_color); + + /* draw the core of the line. */ + output_line(vec2(0.0), inner_color); + + /* draw the outline. */ + output_line(-offset, outer_color); + + /* draw the transparent border (AA). */ + if (is_active_nurb != 0) { + output_line(offset * -2.0, vec4(colorActiveSpline.rgb, 0.0)); + } + + EndPrimitive(); +} diff --git a/source/blender/draw/engines/overlay/shaders/edit_curve_handle_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_curve_handle_vert.glsl new file mode 100644 index 00000000000..a2b0d072719 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/edit_curve_handle_vert.glsl @@ -0,0 +1,18 @@ + +in vec3 pos; +in int data; + +flat out int vertFlag; + +void main() +{ + GPU_INTEL_VERTEX_SHADER_WORKAROUND + + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + vertFlag = data; + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/edit_curve_point_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_curve_point_vert.glsl new file mode 100644 index 00000000000..aca40bba171 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/edit_curve_point_vert.glsl @@ -0,0 +1,29 @@ + +in vec3 pos; +in int data; + +out vec4 finalColor; + +void main() +{ + GPU_INTEL_VERTEX_SHADER_WORKAROUND + + if ((data & VERT_SELECTED) != 0) { + if ((data & VERT_ACTIVE) != 0) { + finalColor = colorEditMeshActive; + } + else { + finalColor = colorVertexSelect; + } + } + else { + finalColor = colorVertex; + } + + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + gl_PointSize = sizeVertex * 2.0; +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/edit_curve_wire_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_curve_wire_vert.glsl new file mode 100644 index 00000000000..5dd8e579db3 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/edit_curve_wire_vert.glsl @@ -0,0 +1,31 @@ + +uniform float normalSize; + +in vec3 pos; +in vec3 nor; +in vec3 tan; +in float rad; + +flat out vec4 finalColor; + +void main() +{ + GPU_INTEL_VERTEX_SHADER_WORKAROUND + + vec3 final_pos = pos; + + float flip = (gl_InstanceID != 0) ? -1.0 : 1.0; + + if (gl_VertexID % 2 == 0) { + final_pos += normalSize * rad * (flip * nor - tan); + } + + vec3 world_pos = point_object_to_world(final_pos); + gl_Position = point_world_to_ndc(world_pos); + + finalColor = colorWireEdit; + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/edit_lattice_point_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_lattice_point_vert.glsl new file mode 100644 index 00000000000..06d34c22ba1 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/edit_lattice_point_vert.glsl @@ -0,0 +1,32 @@ + +in vec3 pos; +in int data; + +out vec4 finalColor; + +void main() +{ + GPU_INTEL_VERTEX_SHADER_WORKAROUND + + if ((data & VERT_SELECTED) != 0) { + finalColor = colorVertexSelect; + } + else if ((data & VERT_ACTIVE) != 0) { + finalColor = colorEditMeshActive; + } + else { + finalColor = colorVertex; + } + + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + + /* Small offset in Z */ + gl_Position.z -= 3e-4; + + gl_PointSize = sizeVertex * 2.0; + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/edit_lattice_wire_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_lattice_wire_vert.glsl new file mode 100644 index 00000000000..efa6ca72feb --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/edit_lattice_wire_vert.glsl @@ -0,0 +1,38 @@ + +uniform sampler1D weightTex; + +in vec3 pos; +in float weight; + +out vec4 finalColor; + +#define no_active_weight 666.0 + +vec3 weight_to_rgb(float t) +{ + if (t == no_active_weight) { + /* No weight. */ + return colorWire.rgb; + } + if (t > 1.0 || t < 0.0) { + /* Error color */ + return vec3(1.0, 0.0, 1.0); + } + else { + return texture(weightTex, t).rgb; + } +} + +void main() +{ + GPU_INTEL_VERTEX_SHADER_WORKAROUND + + finalColor = vec4(weight_to_rgb(weight), 1.0); + + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/edit_mesh_analysis_frag.glsl b/source/blender/draw/engines/overlay/shaders/edit_mesh_analysis_frag.glsl new file mode 100644 index 00000000000..8d96c0e418f --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/edit_mesh_analysis_frag.glsl @@ -0,0 +1,8 @@ +out vec4 fragColor; + +in vec4 weightColor; + +void main() +{ + fragColor = weightColor; +} diff --git a/source/blender/draw/engines/overlay/shaders/edit_mesh_analysis_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_mesh_analysis_vert.glsl new file mode 100644 index 00000000000..b89a3f407f9 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/edit_mesh_analysis_vert.glsl @@ -0,0 +1,35 @@ + +in vec3 pos; +in float weight; + +uniform sampler1D weightTex; + +out vec4 weightColor; + +vec3 weight_to_rgb(float t) +{ + if (t < 0.0) { + /* Minimum color, grey */ + return vec3(0.25, 0.25, 0.25); + } + 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); + weightColor = vec4(weight_to_rgb(weight), 1.0); + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/edit_mesh_common_lib.glsl b/source/blender/draw/engines/overlay/shaders/edit_mesh_common_lib.glsl new file mode 100644 index 00000000000..b79bae45f23 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/edit_mesh_common_lib.glsl @@ -0,0 +1,76 @@ + +uniform bool selectFaces = true; +uniform bool selectEdges = true; + +vec4 EDIT_MESH_edge_color_outer(int edge_flag, int face_flag, float crease, float bweight) +{ + vec4 color = vec4(0.0); + color = ((edge_flag & EDGE_FREESTYLE) != 0) ? colorEdgeFreestyle : color; + color = ((edge_flag & EDGE_SHARP) != 0) ? colorEdgeSharp : color; + color = (crease > 0.0) ? vec4(colorEdgeCrease.rgb, crease) : color; + color = (bweight > 0.0) ? vec4(colorEdgeBWeight.rgb, bweight) : color; + color = ((edge_flag & EDGE_SEAM) != 0) ? colorEdgeSeam : color; + return color; +} + +vec4 EDIT_MESH_edge_color_inner(int edge_flag) +{ + vec4 color = colorWireEdit; + vec4 color_select = (selectEdges) ? colorEdgeSelect : mix(colorEdgeSelect, colorWireEdit, .45); + color = ((edge_flag & EDGE_SELECTED) != 0) ? color_select : color; + color = ((edge_flag & EDGE_ACTIVE) != 0) ? colorEditMeshActive : color; + + color.a = (selectEdges || (edge_flag & (EDGE_SELECTED | EDGE_ACTIVE)) != 0) ? 1.0 : 0.4; + return color; +} + +vec4 EDIT_MESH_edge_vertex_color(int vertex_flag) +{ + vec4 color = colorWireEdit; + vec4 color_select = (selectEdges) ? colorEdgeSelect : mix(colorEdgeSelect, colorWireEdit, .45); + + bool edge_selected = (vertex_flag & (VERT_ACTIVE | VERT_SELECTED)) != 0; + color = (edge_selected) ? color_select : color; + + color.a = (selectEdges || edge_selected) ? 1.0 : 0.4; + return color; +} + +vec4 EDIT_MESH_vertex_color(int vertex_flag) +{ + if ((vertex_flag & VERT_ACTIVE) != 0) { + return vec4(colorEditMeshActive.xyz, 1.0); + } + else if ((vertex_flag & VERT_SELECTED) != 0) { + return colorVertexSelect; + } + else { + return colorVertex; + } +} + +vec4 EDIT_MESH_face_color(int face_flag) +{ + vec4 color = colorFace; + vec4 color_active = mix(colorFaceSelect, colorEditMeshActive, 0.5); + color = ((face_flag & FACE_FREESTYLE) != 0) ? colorFaceFreestyle : color; + color = ((face_flag & FACE_SELECTED) != 0) ? colorFaceSelect : color; + color = ((face_flag & FACE_ACTIVE) != 0) ? color_active : color; + color.a *= ((face_flag & (FACE_FREESTYLE | FACE_SELECTED | FACE_ACTIVE)) == 0 || selectFaces) ? + 1.0 : + 0.5; + return color; +} + +vec4 EDIT_MESH_facedot_color(float facedot_flag) +{ + if (facedot_flag < 0.0f) { + return vec4(colorEditMeshActive.xyz, 1.0); + } + else if (facedot_flag > 0.0f) { + return colorFaceDot; + } + else { + return colorVertex; + } +} diff --git a/source/blender/draw/engines/overlay/shaders/edit_mesh_facefill_frag.glsl b/source/blender/draw/engines/overlay/shaders/edit_mesh_facefill_frag.glsl new file mode 100644 index 00000000000..a8371958ec2 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/edit_mesh_facefill_frag.glsl @@ -0,0 +1,7 @@ +flat in vec4 faceColor; +out vec4 FragColor; + +void main() +{ + FragColor = faceColor; +} diff --git a/source/blender/draw/engines/overlay/shaders/edit_mesh_facefill_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_mesh_facefill_vert.glsl new file mode 100644 index 00000000000..df37c6fb0bc --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/edit_mesh_facefill_vert.glsl @@ -0,0 +1,23 @@ + +uniform ivec4 dataMask = ivec4(0xFF); + +in vec3 pos; +in ivec4 data; + +flat out vec4 faceColor; + +void main() +{ + GPU_INTEL_VERTEX_SHADER_WORKAROUND + + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + + ivec4 data_m = data & dataMask; + + faceColor = EDIT_MESH_face_color(data_m.x); + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/edit_mesh_frag.glsl b/source/blender/draw/engines/overlay/shaders/edit_mesh_frag.glsl new file mode 100644 index 00000000000..1fe20d1cb1f --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/edit_mesh_frag.glsl @@ -0,0 +1,46 @@ + +#define M_1_SQRTPI 0.5641895835477563 /* 1/sqrt(pi) */ + +/** + * We want to know how much a pixel is covered by a line. + * We replace the square pixel with acircle of the same area and try to find the intersection area. + * The area we search is the circular segment. https://en.wikipedia.org/wiki/Circular_segment + * The formula for the area uses inverse trig function and is quite complexe. Instead, + * we approximate it by using the smoothstep function and a 1.05 factor to the disc radius. + */ +#define DISC_RADIUS (M_1_SQRTPI * 1.05) +#define GRID_LINE_SMOOTH_START (0.5 - DISC_RADIUS) +#define GRID_LINE_SMOOTH_END (0.5 + DISC_RADIUS) + +uniform sampler2D depthTex; +uniform float alpha = 1.0; + +flat in vec4 finalColorOuter_f; +in vec4 finalColor_f; +noperspective in float edgeCoord_f; + +out vec4 FragColor; + +bool test_occlusion() +{ + return gl_FragCoord.z > texelFetch(depthTex, ivec2(gl_FragCoord.xy), 0).r; +} + +void main() +{ + float dist = abs(edgeCoord_f) - max(sizeEdge - 0.5, 0.0); + float dist_outer = dist - max(sizeEdge, 1.0); +#ifdef USE_SMOOTH_WIRE + float mix_w = smoothstep(GRID_LINE_SMOOTH_START, GRID_LINE_SMOOTH_END, dist); + float mix_w_outer = smoothstep(GRID_LINE_SMOOTH_START, GRID_LINE_SMOOTH_END, dist_outer); +#else + float mix_w = step(0.5, dist); + float mix_w_outer = step(0.5, dist_outer); +#endif + /* Line color & alpha. */ + FragColor = mix(finalColorOuter_f, finalColor_f, 1.0 - mix_w * finalColorOuter_f.a); + /* Line edges shape. */ + FragColor.a *= 1.0 - (finalColorOuter_f.a > 0.0 ? mix_w_outer : mix_w); + + FragColor.a *= test_occlusion() ? alpha : 1.0; +} diff --git a/source/blender/draw/engines/overlay/shaders/edit_mesh_geom.glsl b/source/blender/draw/engines/overlay/shaders/edit_mesh_geom.glsl new file mode 100644 index 00000000000..92252bbd223 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/edit_mesh_geom.glsl @@ -0,0 +1,88 @@ + +layout(lines) in; +layout(triangle_strip, max_vertices = 4) out; + +in vec4 finalColor[2]; +in vec4 finalColorOuter[2]; +in int selectOveride[2]; + +flat out vec4 finalColorOuter_f; +out vec4 finalColor_f; +noperspective out float edgeCoord_f; + +void do_vertex(vec4 color, vec4 pos, float coord, vec2 offset) +{ + finalColor_f = color; + edgeCoord_f = coord; + gl_Position = pos; + /* Multiply offset by 2 because gl_Position range is [-1..1]. */ + gl_Position.xy += offset * 2.0 * pos.w; + /* Correct but fails due to an AMD compiler bug, see: T62792. + * Do inline instead. */ +#if 0 + world_clip_planes_set_clip_distance(gl_in[i].gl_ClipDistance); +#endif + EmitVertex(); +} + +void main() +{ + vec2 ss_pos[2]; + + /* Clip line against near plane to avoid deformed lines. */ + vec4 pos0 = gl_in[0].gl_Position; + vec4 pos1 = gl_in[1].gl_Position; + vec2 pz_ndc = vec2(pos0.z / pos0.w, pos1.z / pos1.w); + bvec2 clipped = lessThan(pz_ndc, vec2(-1.0)); + if (all(clipped)) { + /* Totally clipped. */ + return; + } + + vec4 pos01 = pos0 - pos1; + float ofs = abs((pz_ndc.y + 1.0) / (pz_ndc.x - pz_ndc.y)); + if (clipped.y) { + pos1 += pos01 * ofs; + } + else if (clipped.x) { + pos0 -= pos01 * (1.0 - ofs); + } + + ss_pos[0] = pos0.xy / pos0.w; + ss_pos[1] = pos1.xy / pos1.w; + + vec2 line = ss_pos[0] - ss_pos[1]; + line = abs(line) * sizeViewport.xy; + + finalColorOuter_f = finalColorOuter[0]; + float half_size = sizeEdge; + /* Enlarge edge for flag display. */ + half_size += (finalColorOuter_f.a > 0.0) ? max(sizeEdge, 1.0) : 0.0; + +#ifdef USE_SMOOTH_WIRE + /* Add 1 px for AA */ + half_size += 0.5; +#endif + + vec3 edge_ofs = vec3(half_size * sizeViewportInv.xy, 0.0); + + bool horizontal = line.x > line.y; + edge_ofs = (horizontal) ? edge_ofs.zyz : edge_ofs.xzz; + +#ifdef USE_WORLD_CLIP_PLANES + /* Due to an AMD glitch, this line was moved out of the `do_vertex` + * function (see T62792). */ + world_clip_planes_set_clip_distance(gl_in[0].gl_ClipDistance); +#endif + do_vertex(finalColor[0], pos0, half_size, edge_ofs.xy); + do_vertex(finalColor[0], pos0, -half_size, -edge_ofs.xy); + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_set_clip_distance(gl_in[1].gl_ClipDistance); +#endif + vec4 final_color = (selectOveride[0] == 0) ? finalColor[1] : finalColor[0]; + do_vertex(final_color, pos1, half_size, edge_ofs.xy); + do_vertex(final_color, pos1, -half_size, -edge_ofs.xy); + + EndPrimitive(); +} diff --git a/source/blender/draw/engines/overlay/shaders/edit_mesh_normal_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_mesh_normal_vert.glsl new file mode 100644 index 00000000000..2a00160836b --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/edit_mesh_normal_vert.glsl @@ -0,0 +1,53 @@ + +uniform float normalSize; +uniform sampler2D depthTex; +uniform float alpha = 1.0; + +in vec3 pos; +in vec3 lnor; +in vec3 vnor; +in vec4 norAndFlag; + +flat out vec4 finalColor; + +bool test_occlusion() +{ + vec3 ndc = (gl_Position.xyz / gl_Position.w) * 0.5 + 0.5; + return (ndc.z - 0.00035) > texture(depthTex, ndc.xy).r; +} + +void main() +{ + GPU_INTEL_VERTEX_SHADER_WORKAROUND + + vec3 nor; + /* Select the right normal by cheking if the generic attrib is used. */ + if (!all(equal(lnor, vec3(0)))) { + nor = lnor; + finalColor = colorLNormal; + } + else if (!all(equal(vnor, vec3(0)))) { + nor = vnor; + finalColor = colorVNormal; + } + else { + nor = norAndFlag.xyz; + finalColor = colorNormal; + } + + vec3 n = normalize(normal_object_to_world(nor)); + + vec3 world_pos = point_object_to_world(pos); + + if (gl_VertexID == 0) { + world_pos += n * normalSize; + } + + gl_Position = point_world_to_ndc(world_pos); + + finalColor.a *= (test_occlusion()) ? alpha : 1.0; + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/edit_mesh_skin_root_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_mesh_skin_root_vert.glsl new file mode 100644 index 00000000000..944eb41058e --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/edit_mesh_skin_root_vert.glsl @@ -0,0 +1,25 @@ + +/* ---- Instantiated Attrs ---- */ +in vec3 pos; + +/* ---- Per instance Attrs ---- */ +in float size; +in vec3 local_pos; + +flat out vec4 finalColor; + +void main() +{ + mat3 imat = mat3(ModelMatrixInverse); + vec3 right = normalize(imat * screenVecs[0].xyz); + vec3 up = normalize(imat * screenVecs[1].xyz); + vec3 screen_pos = (right * pos.x + up * pos.z) * size; + vec4 pos_4d = ModelMatrix * vec4(local_pos + screen_pos, 1.0); + gl_Position = ViewProjectionMatrix * pos_4d; + /* Manual stipple: one segment out of 2 is transparent. */ + finalColor = ((gl_VertexID & 1) == 0) ? colorSkinRoot : vec4(0.0); + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(pos_4d.xyz); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/edit_mesh_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_mesh_vert.glsl new file mode 100644 index 00000000000..8759ef80888 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/edit_mesh_vert.glsl @@ -0,0 +1,97 @@ + +uniform sampler2D depthTex; +uniform float alpha = 1.0; +uniform ivec4 dataMask = ivec4(0xFF); + +in ivec4 data; +in vec3 pos; +#ifndef FACEDOT +in vec3 vnor; +#else +in vec4 norAndFlag; +# define vnor norAndFlag.xyz +#endif + +out vec4 finalColor; +#ifdef EDGE +out vec4 finalColorOuter; +#endif +#ifdef USE_GEOM_SHADER +out int selectOveride; +#endif + +bool test_occlusion() +{ + vec3 ndc = (gl_Position.xyz / gl_Position.w) * 0.5 + 0.5; + return ndc.z > texture(depthTex, ndc.xy).r; +} + +void main() +{ + GPU_INTEL_VERTEX_SHADER_WORKAROUND + + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + + ivec4 m_data = data & dataMask; + +#if defined(VERT) + finalColor = EDIT_MESH_vertex_color(m_data.y); + gl_PointSize = sizeVertex * 2.0; + /* Make selected and active vertex always on top. */ + if ((data.x & VERT_SELECTED) != 0) { + gl_Position.z -= 1e-7; + } + if ((data.x & VERT_ACTIVE) != 0) { + gl_Position.z -= 1e-7; + } + + bool occluded = test_occlusion(); + +#elif defined(EDGE) +# ifdef FLAT + finalColor = EDIT_MESH_edge_color_inner(m_data.y); + selectOveride = 1; +# else + finalColor = EDIT_MESH_edge_vertex_color(m_data.y); + selectOveride = (m_data.y & EDGE_SELECTED); +# endif + + float crease = float(m_data.z) / 255.0; + float bweight = float(m_data.w) / 255.0; + finalColorOuter = EDIT_MESH_edge_color_outer(m_data.y, m_data.x, crease, bweight); + + bool occluded = false; /* Done in fragment shader */ + +#elif defined(FACE) + finalColor = EDIT_MESH_face_color(m_data.x); + bool occluded = true; + +#elif defined(FACEDOT) + finalColor = EDIT_MESH_facedot_color(norAndFlag.w); + + /* Bias Facedot Z position in clipspace. */ + gl_Position.z -= 0.00035; + gl_PointSize = sizeFaceDot; + + bool occluded = test_occlusion(); + +#endif + + finalColor.a *= (occluded) ? alpha : 1.0; + +#if !defined(FACE) + /* Facing based color blend */ + vec3 vpos = point_world_to_view(world_pos); + vec3 view_normal = normalize(normal_object_to_view(vnor) + 1e-4); + vec3 view_vec = (ProjectionMatrix[3][3] == 0.0) ? normalize(vpos) : vec3(0.0, 0.0, 1.0); + float facing = dot(view_vec, view_normal); + facing = 1.0 - abs(facing) * 0.2; + + finalColor.rgb = mix(colorEditMeshMiddle.rgb, finalColor.rgb, facing); +#endif + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/edit_particle_point_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_particle_point_vert.glsl new file mode 100644 index 00000000000..86d5547225c --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/edit_particle_point_vert.glsl @@ -0,0 +1,19 @@ + +in vec3 pos; +in float color; + +out vec4 finalColor; + +void main() +{ + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + + finalColor = mix(colorWire, colorEdgeSelect, color); + + gl_PointSize = sizeVertex * 2.0; + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/edit_particle_strand_vert.glsl b/source/blender/draw/engines/overlay/shaders/edit_particle_strand_vert.glsl new file mode 100644 index 00000000000..1dde94f751c --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/edit_particle_strand_vert.glsl @@ -0,0 +1,42 @@ + +uniform sampler1D weightTex; +uniform bool useWeight; + +in vec3 pos; +in float color; + +out vec4 finalColor; + +#define no_active_weight 666.0 + +vec3 weight_to_rgb(float t) +{ + if (t == no_active_weight) { + /* No weight. */ + return colorWire.rgb; + } + if (t > 1.0 || t < 0.0) { + /* Error color */ + return vec3(1.0, 0.0, 1.0); + } + else { + return texture(weightTex, t).rgb; + } +} + +void main() +{ + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + + if (useWeight) { + finalColor = vec4(weight_to_rgb(color), 1.0); + } + else { + finalColor = mix(colorWire, colorEdgeSelect, color); + } + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/extra_frag.glsl b/source/blender/draw/engines/overlay/shaders/extra_frag.glsl new file mode 100644 index 00000000000..8a8ae8a9611 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/extra_frag.glsl @@ -0,0 +1,13 @@ + +noperspective in vec2 edgePos; +flat in vec2 edgeStart; +flat in vec4 finalColor; + +layout(location = 0) out vec4 fragColor; +layout(location = 1) out vec4 lineOutput; + +void main() +{ + fragColor = finalColor; + lineOutput = pack_line_data(gl_FragCoord.xy, edgeStart, edgePos); +}
\ No newline at end of file diff --git a/source/blender/draw/engines/overlay/shaders/extra_groundline_vert.glsl b/source/blender/draw/engines/overlay/shaders/extra_groundline_vert.glsl new file mode 100644 index 00000000000..4b08ea587d4 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/extra_groundline_vert.glsl @@ -0,0 +1,30 @@ + +in vec3 pos; + +/* Instance */ +in vec3 inst_pos; + +flat out vec4 finalColor; +flat out vec2 edgeStart; +noperspective out vec2 edgePos; + +void main() +{ + finalColor = colorLight; + + /* Relative to DPI scalling. Have constant screen size. */ + vec3 screen_pos = screenVecs[0].xyz * pos.x + screenVecs[1].xyz * pos.y; + vec3 p = inst_pos; + p.z *= (pos.z == 0.0) ? 0.0 : 1.0; + float screen_size = mul_project_m4_v3_zfac(p) * sizePixel; + vec3 world_pos = p + screen_pos * screen_size; + + gl_Position = point_world_to_ndc(world_pos); + + /* Convert to screen position [0..sizeVp]. */ + edgePos = edgeStart = ((gl_Position.xy / gl_Position.w) * 0.5 + 0.5) * sizeViewport.xy; + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +}
\ No newline at end of file diff --git a/source/blender/draw/engines/overlay/shaders/extra_loose_point_frag.glsl b/source/blender/draw/engines/overlay/shaders/extra_loose_point_frag.glsl new file mode 100644 index 00000000000..8784b6cd73a --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/extra_loose_point_frag.glsl @@ -0,0 +1,18 @@ + +in vec4 finalColor; + +out vec4 fragColor; + +void main() +{ + vec2 centered = abs(gl_PointCoord - vec2(0.5)); + float dist = max(centered.x, centered.y); + + float fac = dist * dist * 4.0; + fragColor = mix(colorEditMeshMiddle, finalColor, 0.45 + fac * 0.65); + + /* Make the effect more like a fresnel by offsetting + * the depth and creating mini-spheres. + * Disabled as it has performance impact. */ + // gl_FragDepth = gl_FragCoord.z + 1e-6 * fac; +}
\ No newline at end of file diff --git a/source/blender/draw/engines/overlay/shaders/extra_loose_point_vert.glsl b/source/blender/draw/engines/overlay/shaders/extra_loose_point_vert.glsl new file mode 100644 index 00000000000..76a2678a50e --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/extra_loose_point_vert.glsl @@ -0,0 +1,20 @@ + +in vec3 pos; + +out vec4 finalColor; + +void main() +{ + /* Extract data packed inside the unused mat4 members. */ + mat4 obmat = ModelMatrix; + finalColor = vec4(obmat[0][3], obmat[1][3], obmat[2][3], obmat[3][3]); + + vec3 world_pos = (ModelMatrix * vec4(pos, 1.0)).xyz; + gl_Position = point_world_to_ndc(world_pos); + + gl_PointSize = sizeVertex * 2.0; + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +}
\ No newline at end of file diff --git a/source/blender/draw/engines/overlay/shaders/extra_point_vert.glsl b/source/blender/draw/engines/overlay/shaders/extra_point_vert.glsl new file mode 100644 index 00000000000..14c03248981 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/extra_point_vert.glsl @@ -0,0 +1,30 @@ + +uniform vec4 color; + +in vec3 pos; + +out vec4 radii; +out vec4 fillColor; +out vec4 outlineColor; + +void main() +{ + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + + gl_PointSize = sizeObjectCenter; + float radius = 0.5 * sizeObjectCenter; + float outline_width = sizePixel; + radii[0] = radius; + radii[1] = radius - 1.0; + radii[2] = radius - outline_width; + radii[3] = radius - outline_width - 1.0; + radii /= sizeObjectCenter; + + fillColor = color; + outlineColor = colorOutline; + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +}
\ No newline at end of file diff --git a/source/blender/draw/engines/overlay/shaders/extra_vert.glsl b/source/blender/draw/engines/overlay/shaders/extra_vert.glsl new file mode 100644 index 00000000000..a72c5adb691 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/extra_vert.glsl @@ -0,0 +1,227 @@ + +in vec3 pos; +in int vclass; + +/* Instance */ +in mat4 inst_obmat; +in vec4 color; + +#define lamp_area_size inst_data.xy +#define lamp_clip_sta inst_data.z +#define lamp_clip_end inst_data.w + +#define lamp_spot_cosine inst_data.x +#define lamp_spot_blend inst_data.y + +#define camera_corner inst_data.xy +#define camera_center inst_data.zw +#define camera_dist inst_color_data +#define camera_dist_sta inst_data.z +#define camera_dist_end inst_data.w +#define camera_distance_color inst_data.x + +#define empty_size inst_data.xyz +#define empty_scale inst_data.w + +#define VCLASS_LIGHT_AREA_SHAPE (1 << 0) +#define VCLASS_LIGHT_SPOT_SHAPE (1 << 1) +#define VCLASS_LIGHT_SPOT_BLEND (1 << 2) +#define VCLASS_LIGHT_SPOT_CONE (1 << 3) +#define VCLASS_LIGHT_DIST (1 << 4) + +#define VCLASS_CAMERA_FRAME (1 << 5) +#define VCLASS_CAMERA_DIST (1 << 6) +#define VCLASS_CAMERA_VOLUME (1 << 7) + +#define VCLASS_SCREENSPACE (1 << 8) +#define VCLASS_SCREENALIGNED (1 << 9) + +#define VCLASS_EMPTY_SCALED (1 << 10) +#define VCLASS_EMPTY_AXES (1 << 11) +#define VCLASS_EMPTY_AXES_NAME (1 << 12) +#define VCLASS_EMPTY_AXES_SHADOW (1 << 13) +#define VCLASS_EMPTY_SIZE (1 << 14) + +flat out vec4 finalColor; +flat out vec2 edgeStart; +noperspective out vec2 edgePos; + +void main() +{ + /* Extract data packed inside the unused mat4 members. */ + vec4 inst_data = vec4(inst_obmat[0][3], inst_obmat[1][3], inst_obmat[2][3], inst_obmat[3][3]); + float inst_color_data = color.a; + mat4 obmat = inst_obmat; + obmat[0][3] = obmat[1][3] = obmat[2][3] = 0.0; + obmat[3][3] = 1.0; + + finalColor = color; + if (color.a < 0.0) { + finalColor.a = 1.0; + } + + float lamp_spot_sine; + vec3 vpos = pos; + vec3 vofs = vec3(0.0); + /* Lights */ + if ((vclass & VCLASS_LIGHT_AREA_SHAPE) != 0) { + /* HACK: use alpha color for spots to pass the area_size. */ + if (inst_color_data < 0.0) { + lamp_area_size.xy = vec2(-inst_color_data); + } + vpos.xy *= lamp_area_size.xy; + } + else if ((vclass & VCLASS_LIGHT_SPOT_SHAPE) != 0) { + lamp_spot_sine = sqrt(1.0 - lamp_spot_cosine * lamp_spot_cosine); + lamp_spot_sine *= ((vclass & VCLASS_LIGHT_SPOT_BLEND) != 0) ? lamp_spot_blend : 1.0; + vpos = vec3(pos.xy * lamp_spot_sine, -lamp_spot_cosine); + } + else if ((vclass & VCLASS_LIGHT_DIST) != 0) { + /* Meh nasty mess. Select one of the 6 axes to display on. (see light_distance_z_get()) */ + int dist_axis = int(pos.z); + float dist = pos.z - floor(pos.z) - 0.5; + float inv = sign(dist); + dist = (abs(dist) > 0.15) ? lamp_clip_end : lamp_clip_sta; + vofs[dist_axis] = inv * dist / length(obmat[dist_axis].xyz); + vpos.z = 0.0; + if (lamp_clip_end < 0.0) { + vpos = vofs = vec3(0.0); + } + } + /* Camera */ + else if ((vclass & VCLASS_CAMERA_FRAME) != 0) { + if ((vclass & VCLASS_CAMERA_VOLUME) != 0) { + vpos.z = mix(color.b, color.a, pos.z); + } + else if (camera_dist > 0.0) { + vpos.z = -abs(camera_dist); + } + else { + vpos.z *= -abs(camera_dist); + } + vpos.xy = (camera_center + camera_corner * vpos.xy) * abs(vpos.z); + } + else if ((vclass & VCLASS_CAMERA_DIST) != 0) { + vofs.xy = vec2(0.0); + vofs.z = -mix(camera_dist_sta, camera_dist_end, pos.z); + vpos.z = 0.0; + /* Distance line endpoints color */ + if (any(notEqual(pos.xy, vec2(0.0)))) { + /* Override color. */ + switch (int(camera_distance_color)) { + case 0: /* Mist */ + finalColor = vec4(0.5, 0.5, 0.5, 1.0); + break; + case 1: /* Mist Active */ + finalColor = vec4(1.0, 1.0, 1.0, 1.0); + break; + case 2: /* Clip */ + finalColor = vec4(0.5, 0.5, 0.25, 1.0); + break; + case 3: /* Clip Active */ + finalColor = vec4(1.0, 1.0, 0.5, 1.0); + break; + } + } + /* Focus cross */ + if (pos.z == 2.0) { + vofs.z = 0.0; + if (camera_dist < 0.0) { + vpos.z = -abs(camera_dist); + } + else { + /* Disabled */ + vpos = vec3(0.0); + } + } + } + /* Empties */ + else if ((vclass & VCLASS_EMPTY_SCALED) != 0) { + /* This is a bit silly but we avoid scalling the object matrix on CPU (saving a mat4 mul) */ + vpos *= empty_scale; + } + else if ((vclass & VCLASS_EMPTY_SIZE) != 0) { + /* This is a bit silly but we avoid scalling the object matrix on CPU (saving a mat4 mul) */ + vpos *= empty_size; + } + else if ((vclass & VCLASS_EMPTY_AXES) != 0) { + float axis = vpos.z; + vofs[int(axis)] = (1.0 + fract(axis)) * empty_scale; + /* Scale uniformly by axis length */ + vpos *= length(obmat[int(axis)].xyz) * empty_scale; + + vec3 axis_color = vec3(0.0); + axis_color[int(axis)] = 1.0; + finalColor.rgb = mix(axis_color + fract(axis), color.rgb, color.a); + finalColor.a = 1.0; + } + + /* Not exclusive with previous flags. */ + if ((vclass & VCLASS_CAMERA_VOLUME) != 0) { + /* Unpack final color. */ + int color_class = int(floor(color.r)); + float color_intensity = fract(color.r); + switch (color_class) { + case 0: /* No eye (convergence plane) */ + finalColor = vec4(1.0, 1.0, 1.0, 1.0); + break; + case 1: /* Left eye */ + finalColor = vec4(0.0, 1.0, 1.0, 1.0); + break; + case 2: /* Right eye */ + finalColor = vec4(1.0, 0.0, 0.0, 1.0); + break; + } + finalColor *= vec4(vec3(color_intensity), color.g); + } + + vec3 world_pos; + if ((vclass & VCLASS_SCREENSPACE) != 0) { + /* Relative to DPI scalling. Have constant screen size. */ + vec3 screen_pos = screenVecs[0].xyz * vpos.x + screenVecs[1].xyz * vpos.y; + vec3 p = (obmat * vec4(vofs, 1.0)).xyz; + float screen_size = mul_project_m4_v3_zfac(p) * sizePixel; + world_pos = p + screen_pos * screen_size; + } + else if ((vclass & VCLASS_SCREENALIGNED) != 0) { + /* World sized, camera facing geometry. */ + vec3 screen_pos = screenVecs[0].xyz * vpos.x + screenVecs[1].xyz * vpos.y; + world_pos = (obmat * vec4(vofs, 1.0)).xyz + screen_pos; + } + else { + world_pos = (obmat * vec4(vofs + vpos, 1.0)).xyz; + } + + if ((vclass & VCLASS_LIGHT_SPOT_CONE) != 0) { + /* Compute point on the cone before and after this one. */ + vec2 perp = vec2(pos.y, -pos.x); + const float incr_angle = 2.0 * 3.1415 / 32.0; + const vec2 slope = vec2(cos(incr_angle), sin(incr_angle)); + vec3 p0 = vec3((pos.xy * slope.x + perp * slope.y) * lamp_spot_sine, -lamp_spot_cosine); + vec3 p1 = vec3((pos.xy * slope.x - perp * slope.y) * lamp_spot_sine, -lamp_spot_cosine); + p0 = (obmat * vec4(p0, 1.0)).xyz; + p1 = (obmat * vec4(p1, 1.0)).xyz; + /* Compute normals of each side. */ + vec3 edge = obmat[3].xyz - world_pos; + vec3 n0 = normalize(cross(edge, p0 - world_pos)); + vec3 n1 = normalize(cross(edge, world_pos - p1)); + bool persp = (ProjectionMatrix[3][3] == 0.0); + vec3 V = (persp) ? normalize(ViewMatrixInverse[3].xyz - world_pos) : ViewMatrixInverse[2].xyz; + /* Discard non-silhouete edges. */ + bool facing0 = dot(n0, V) > 0.0; + bool facing1 = dot(n1, V) > 0.0; + if (facing0 == facing1) { + /* Hide line by making it cover 0 pixels. */ + world_pos = obmat[3].xyz; + } + } + + gl_Position = point_world_to_ndc(world_pos); + + /* Convert to screen position [0..sizeVp]. */ + edgePos = edgeStart = ((gl_Position.xy / gl_Position.w) * 0.5 + 0.5) * sizeViewport.xy; + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +}
\ No newline at end of file diff --git a/source/blender/draw/engines/overlay/shaders/extra_wire_frag.glsl b/source/blender/draw/engines/overlay/shaders/extra_wire_frag.glsl new file mode 100644 index 00000000000..7e469aee18d --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/extra_wire_frag.glsl @@ -0,0 +1,31 @@ + +noperspective in vec2 stipple_coord; +flat in vec2 stipple_start; +flat in vec4 finalColor; + +layout(location = 0) out vec4 fragColor; +layout(location = 1) out vec4 lineOutput; + +void main() +{ + fragColor = finalColor; + + /* Stipple */ + const float dash_width = 6.0; + const float dash_factor = 0.5; + + lineOutput = pack_line_data(gl_FragCoord.xy, stipple_start, stipple_coord); + + float dist = distance(stipple_start, stipple_coord); + + if (fragColor.a == 0.0) { + /* Disable stippling. */ + dist = 0.0; + } + + fragColor.a = 1.0; + + if (fract(dist / dash_width) > dash_factor) { + discard; + } +}
\ No newline at end of file diff --git a/source/blender/draw/engines/overlay/shaders/extra_wire_vert.glsl b/source/blender/draw/engines/overlay/shaders/extra_wire_vert.glsl new file mode 100644 index 00000000000..933b9d65a5f --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/extra_wire_vert.glsl @@ -0,0 +1,40 @@ + +in vec3 pos; +in vec4 color; +in int colorid; /* if equal 0 (i.e: Not specified) use color attrib and stippling. */ + +noperspective out vec2 stipple_coord; +flat out vec2 stipple_start; +flat out vec4 finalColor; + +vec2 screen_position(vec4 p) +{ + return ((p.xy / p.w) * 0.5 + 0.5) * sizeViewport.xy; +} + +void main() +{ + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + + stipple_coord = stipple_start = screen_position(gl_Position); + +#ifdef OBJECT_WIRE + /* Extract data packed inside the unused mat4 members. */ + finalColor = vec4(ModelMatrix[0][3], ModelMatrix[1][3], ModelMatrix[2][3], ModelMatrix[3][3]); +#else + + if (colorid == TH_CAMERA_PATH) { + finalColor = colorCameraPath; + finalColor.a = 0.0; /* No Stipple */ + } + else { + finalColor = color; + finalColor.a = 1.0; /* Stipple */ + } +#endif + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +}
\ No newline at end of file diff --git a/source/blender/draw/engines/overlay/shaders/facing_frag.glsl b/source/blender/draw/engines/overlay/shaders/facing_frag.glsl new file mode 100644 index 00000000000..1ed35b4a421 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/facing_frag.glsl @@ -0,0 +1,9 @@ +uniform vec3 color_towards = vec3(0.0, 0.0, 1.0); +uniform vec3 color_outwards = vec3(1.0, 0.0, 0.0); + +out vec4 fragColor; + +void main() +{ + fragColor = vec4(gl_FrontFacing ? color_towards : color_outwards, 0.7); +} diff --git a/source/blender/draw/engines/overlay/shaders/facing_vert.glsl b/source/blender/draw/engines/overlay/shaders/facing_vert.glsl new file mode 100644 index 00000000000..2dd84c0a060 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/facing_vert.glsl @@ -0,0 +1,12 @@ + +in vec3 pos; + +void main() +{ + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/grid_frag.glsl b/source/blender/draw/engines/overlay/shaders/grid_frag.glsl new file mode 100644 index 00000000000..db845c7f1dd --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/grid_frag.glsl @@ -0,0 +1,248 @@ + +/* Infinite grid + * Author: Clément Foucault */ + +/* We use the normalized local position to avoid precision + * loss during interpolation. */ +in vec3 local_pos; + +out vec4 FragColor; + +uniform vec3 planeAxes; +uniform float gridDistance; +uniform float meshSize; +uniform float lineKernel = 0.0; +uniform sampler2D depthBuffer; + +#define cameraPos (ViewMatrixInverse[3].xyz) + +uniform int gridFlag; + +#define STEPS_LEN 8 +uniform float gridSteps[STEPS_LEN] = float[](0.001, 0.01, 0.1, 1.0, 10.0, 100.0, 1000.0, 10000.0); + +#define AXIS_X (1 << 0) +#define AXIS_Y (1 << 1) +#define AXIS_Z (1 << 2) +#define GRID (1 << 3) +#define PLANE_XY (1 << 4) +#define PLANE_XZ (1 << 5) +#define PLANE_YZ (1 << 6) +#define GRID_BACK (1 << 9) /* grid is behind objects */ + +#define M_1_SQRTPI 0.5641895835477563 /* 1/sqrt(pi) */ + +/** + * We want to know how much a pixel is covered by a line. + * We replace the square pixel with acircle of the same area and try to find the intersection area. + * The area we search is the circular segment. https://en.wikipedia.org/wiki/Circular_segment + * The formula for the area uses inverse trig function and is quite complexe. Instead, + * we approximate it by using the smoothstep function and a 1.05 factor to the disc radius. + */ +#define DISC_RADIUS (M_1_SQRTPI * 1.05) +#define GRID_LINE_SMOOTH_START (0.5 - DISC_RADIUS) +#define GRID_LINE_SMOOTH_END (0.5 + DISC_RADIUS) + +float get_grid(vec2 co, vec2 fwidthCos, float grid_size) +{ + float half_size = grid_size / 2.0; + /* triangular wave pattern, amplitude is [0, half_size] */ + vec2 grid_domain = abs(mod(co + half_size, grid_size) - half_size); + /* modulate by the absolute rate of change of the coordinates + * (make lines have the same width under perspective) */ + grid_domain /= fwidthCos; + + /* collapse waves */ + float line_dist = min(grid_domain.x, grid_domain.y); + + return 1.0 - smoothstep(GRID_LINE_SMOOTH_START, GRID_LINE_SMOOTH_END, line_dist - lineKernel); +} + +vec3 get_axes(vec3 co, vec3 fwidthCos, float line_size) +{ + vec3 axes_domain = abs(co); + /* modulate by the absolute rate of change of the coordinates + * (make line have the same width under perspective) */ + axes_domain /= fwidthCos; + + return 1.0 - smoothstep(GRID_LINE_SMOOTH_START, + GRID_LINE_SMOOTH_END, + axes_domain - (line_size + lineKernel)); +} + +#define linearstep(p0, p1, v) (clamp(((v) - (p0)) / abs((p1) - (p0)), 0.0, 1.0)) + +void main() +{ + vec3 wPos = local_pos * meshSize; + vec3 dFdxPos = dFdx(wPos); + vec3 dFdyPos = dFdy(wPos); + vec3 fwidthPos = abs(dFdxPos) + abs(dFdyPos); + wPos += cameraPos * planeAxes; + + float dist, fade; + /* if persp */ + if (ProjectionMatrix[3][3] == 0.0) { + vec3 viewvec = cameraPos - wPos; + dist = length(viewvec); + viewvec /= dist; + + float angle; + if ((gridFlag & PLANE_XZ) != 0) { + angle = viewvec.y; + } + else if ((gridFlag & PLANE_YZ) != 0) { + angle = viewvec.x; + } + else { + angle = viewvec.z; + } + + angle = 1.0 - abs(angle); + angle *= angle; + fade = 1.0 - angle * angle; + fade *= 1.0 - smoothstep(0.0, gridDistance, dist - gridDistance); + } + else { + dist = abs(gl_FragCoord.z * 2.0 - 1.0); + fade = 1.0 - smoothstep(0.0, 0.5, dist - 0.5); + dist = 1.0; /* avoid branch after */ + + if ((gridFlag & PLANE_XY) != 0) { + float angle = 1.0 - abs(ViewMatrixInverse[2].z); + dist = 1.0 + angle * 2.0; + angle *= angle; + fade *= 1.0 - angle * angle; + } + } + + if ((gridFlag & GRID) != 0) { + /* Using `max(dot(dFdxPos, screenVecs[0]), dot(dFdyPos, screenVecs[1]))` + * would be more accurate, but not really necessary. */ + float grid_res = dot(dFdxPos, screenVecs[0].xyz); + + /* The gride begins to appear when it comprises 4 pixels */ + grid_res *= 4; + + /* from biggest to smallest */ + vec4 scale; +#if 0 + int step_id = 0; + scale[0] = 0.0; + scale[1] = gridSteps[0]; + while (scale[1] < grid_res && step_id != STEPS_LEN - 1) { + scale[0] = scale[1]; + scale[1] = gridSteps[++step_id]; + } + scale[2] = gridSteps[min(step_id + 1, STEPS_LEN - 1)]; + scale[3] = gridSteps[min(step_id + 2, STEPS_LEN - 1)]; +#else + /* For more efficiency, unroll the loop above. */ + if (gridSteps[0] > grid_res) { + scale = vec4(0.0, gridSteps[0], gridSteps[1], gridSteps[2]); + } + else if (gridSteps[1] > grid_res) { + scale = vec4(gridSteps[0], gridSteps[1], gridSteps[2], gridSteps[3]); + } + else if (gridSteps[2] > grid_res) { + scale = vec4(gridSteps[1], gridSteps[2], gridSteps[3], gridSteps[4]); + } + else if (gridSteps[3] > grid_res) { + scale = vec4(gridSteps[2], gridSteps[3], gridSteps[4], gridSteps[5]); + } + else if (gridSteps[4] > grid_res) { + scale = vec4(gridSteps[3], gridSteps[4], gridSteps[5], gridSteps[6]); + } + else if (gridSteps[5] > grid_res) { + scale = vec4(gridSteps[4], gridSteps[5], gridSteps[6], gridSteps[7]); + } + else if (gridSteps[6] > grid_res) { + scale = vec4(gridSteps[5], gridSteps[6], gridSteps[7], gridSteps[7]); + } + else { + scale = vec4(gridSteps[6], gridSteps[7], gridSteps[7], gridSteps[7]); + } +#endif + float blend = 1.0 - linearstep(scale[0], scale[1], grid_res); + blend = blend * blend * blend; + + vec2 grid_pos, grid_fwidth; + if ((gridFlag & PLANE_XZ) != 0) { + grid_pos = wPos.xz; + grid_fwidth = fwidthPos.xz; + } + else if ((gridFlag & PLANE_YZ) != 0) { + grid_pos = wPos.yz; + grid_fwidth = fwidthPos.yz; + } + else { + grid_pos = wPos.xy; + grid_fwidth = fwidthPos.xy; + } + + float gridA = get_grid(grid_pos, grid_fwidth, scale[1]); + float gridB = get_grid(grid_pos, grid_fwidth, scale[2]); + float gridC = get_grid(grid_pos, grid_fwidth, scale[3]); + + FragColor = colorGrid; + FragColor.a *= gridA * blend; + FragColor = mix(FragColor, mix(colorGrid, colorGridEmphasise, blend), gridB); + FragColor = mix(FragColor, colorGridEmphasise, gridC); + } + else { + FragColor = vec4(colorGrid.rgb, 0.0); + } + + if ((gridFlag & (AXIS_X | AXIS_Y | AXIS_Z)) != 0) { + /* Setup axes 'domains' */ + vec3 axes_dist, axes_fwidth; + + if ((gridFlag & AXIS_X) != 0) { + axes_dist.x = dot(wPos.yz, planeAxes.yz); + axes_fwidth.x = dot(fwidthPos.yz, planeAxes.yz); + } + if ((gridFlag & AXIS_Y) != 0) { + axes_dist.y = dot(wPos.xz, planeAxes.xz); + axes_fwidth.y = dot(fwidthPos.xz, planeAxes.xz); + } + if ((gridFlag & AXIS_Z) != 0) { + axes_dist.z = dot(wPos.xy, planeAxes.xy); + axes_fwidth.z = dot(fwidthPos.xy, planeAxes.xy); + } + + /* Computing all axes at once using vec3 */ + vec3 axes = get_axes(axes_dist, axes_fwidth, 0.1); + + if ((gridFlag & AXIS_X) != 0) { + FragColor.a = max(FragColor.a, axes.x); + FragColor.rgb = (axes.x < 1e-8) ? FragColor.rgb : colorGridAxisX.rgb; + } + if ((gridFlag & AXIS_Y) != 0) { + FragColor.a = max(FragColor.a, axes.y); + FragColor.rgb = (axes.y < 1e-8) ? FragColor.rgb : colorGridAxisY.rgb; + } + if ((gridFlag & AXIS_Z) != 0) { + FragColor.a = max(FragColor.a, axes.z); + FragColor.rgb = (axes.z < 1e-8) ? FragColor.rgb : colorGridAxisZ.rgb; + } + } + + /* Add a small bias so the grid will always + * be on top of a mesh with the same depth. */ + float grid_depth = gl_FragCoord.z - 6e-8 - fwidth(gl_FragCoord.z); + float scene_depth = texelFetch(depthBuffer, ivec2(gl_FragCoord.xy), 0).r; + if ((gridFlag & GRID_BACK) != 0) { + fade *= (scene_depth == 1.0) ? 1.0 : 0.0; + } + else { + /* Manual, non hard, depth test: + * Progressively fade the grid below occluders + * (avoids popping visuals due to depth buffer precision) */ + /* Harder settings tend to flicker more, + * but have less "see through" appearance. */ + const float test_hardness = 1e7; + fade *= 1.0 - clamp((grid_depth - scene_depth) * test_hardness, 0.0, 1.0); + } + + FragColor.a *= fade; +} diff --git a/source/blender/draw/engines/overlay/shaders/grid_vert.glsl b/source/blender/draw/engines/overlay/shaders/grid_vert.glsl new file mode 100644 index 00000000000..496bb011c74 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/grid_vert.glsl @@ -0,0 +1,52 @@ + +/* Infinite grid + * Clément Foucault */ + +uniform vec3 planeAxes; +uniform float meshSize; + +uniform int gridFlag; + +#define cameraPos (ViewMatrixInverse[3].xyz) + +#define PLANE_XY (1 << 4) +#define PLANE_XZ (1 << 5) +#define PLANE_YZ (1 << 6) +#define CLIP_Z_POS (1 << 7) +#define CLIP_Z_NEG (1 << 8) + +in vec3 pos; + +out vec3 local_pos; + +void main() +{ + vec3 vert_pos; + + /* Project camera pos to the needed plane */ + if ((gridFlag & PLANE_XY) != 0) { + vert_pos = vec3(pos.x, pos.y, 0.0); + } + else if ((gridFlag & PLANE_XZ) != 0) { + vert_pos = vec3(pos.x, 0.0, pos.y); + } + else { + vert_pos = vec3(0.0, pos.x, pos.y); + } + + local_pos = vert_pos; + + vec3 real_pos = cameraPos * planeAxes + vert_pos * meshSize; + + /* Used for additional Z axis */ + if ((gridFlag & CLIP_Z_POS) != 0) { + real_pos.z = clamp(real_pos.z, 0.0, 1e30); + local_pos.z = clamp(local_pos.z, 0.0, 1.0); + } + if ((gridFlag & CLIP_Z_NEG) != 0) { + real_pos.z = clamp(real_pos.z, -1e30, 0.0); + local_pos.z = clamp(local_pos.z, -1.0, 0.0); + } + + gl_Position = ViewProjectionMatrix * vec4(real_pos, 1.0); +} diff --git a/source/blender/draw/engines/overlay/shaders/image_frag.glsl b/source/blender/draw/engines/overlay/shaders/image_frag.glsl new file mode 100644 index 00000000000..0da7067851d --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/image_frag.glsl @@ -0,0 +1,34 @@ + +uniform sampler2D imgTexture; +uniform bool imgPremultiplied; +uniform bool imgAlphaBlend; +uniform bool imgLinear; +uniform vec4 color; + +in vec2 uvs; + +out vec4 fragColor; + +void main() +{ + vec2 uvs_clamped = clamp(uvs, 0.0, 1.0); + vec4 tex_color; + if (imgLinear) { + tex_color = texture_read_as_linearrgb(imgTexture, imgPremultiplied, uvs_clamped); + } + else { + tex_color = texture_read_as_srgb(imgTexture, imgPremultiplied, uvs_clamped); + } + fragColor = tex_color * color; + + if (!imgAlphaBlend) { + /* Arbitrary discard anything below 5% opacity. + * Note that this could be exposed to the User. */ + if (tex_color.a < 0.05) { + discard; + } + else { + fragColor.a = 1.0; + } + } +} diff --git a/source/blender/draw/engines/overlay/shaders/image_vert.glsl b/source/blender/draw/engines/overlay/shaders/image_vert.glsl new file mode 100644 index 00000000000..621e1d8068b --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/image_vert.glsl @@ -0,0 +1,21 @@ + +uniform bool depthSet; + +in vec3 pos; + +out vec2 uvs; + +void main() +{ + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + + if (depthSet) { + /* Result in a position at 1.0 (far plane). Small epsilon to avoid precision issue. + * This mimics the effect of infinite projection matrix + * (see http://www.terathon.com/gdc07_lengyel.pdf). */ + gl_Position.z = gl_Position.w - 2.4e-7; + } + + uvs = pos.xy * 0.5 + 0.5; +} diff --git a/source/blender/draw/engines/overlay/shaders/motion_path_line_geom.glsl b/source/blender/draw/engines/overlay/shaders/motion_path_line_geom.glsl new file mode 100644 index 00000000000..95e5b08049e --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/motion_path_line_geom.glsl @@ -0,0 +1,47 @@ + +layout(lines) in; +layout(triangle_strip, max_vertices = 4) out; + +uniform int lineThickness = 2; + +in vec4 finalColor_geom[]; +in vec2 ssPos[]; + +out vec4 finalColor; + +vec2 compute_dir(vec2 v0, vec2 v1) +{ + vec2 dir = normalize(v1 - v0 + 1e-8); + dir = vec2(-dir.y, dir.x); + return dir; +} + +void main(void) +{ + vec2 t; + vec2 edge_dir = compute_dir(ssPos[0], ssPos[1]) * sizeViewportInv.xy; + + bool is_persp = (ProjectionMatrix[3][3] == 0.0); + float line_size = float(lineThickness) * sizePixel; + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_set_clip_distance(gl_in[0].gl_ClipDistance); +#endif + finalColor = finalColor_geom[0]; + t = edge_dir * (line_size * (is_persp ? gl_in[0].gl_Position.w : 1.0)); + gl_Position = gl_in[0].gl_Position + vec4(t, 0.0, 0.0); + EmitVertex(); + gl_Position = gl_in[0].gl_Position - vec4(t, 0.0, 0.0); + EmitVertex(); + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_set_clip_distance(gl_in[1].gl_ClipDistance); +#endif + finalColor = finalColor_geom[1]; + t = edge_dir * (line_size * (is_persp ? gl_in[1].gl_Position.w : 1.0)); + gl_Position = gl_in[1].gl_Position + vec4(t, 0.0, 0.0); + EmitVertex(); + gl_Position = gl_in[1].gl_Position - vec4(t, 0.0, 0.0); + EmitVertex(); + EndPrimitive(); +} diff --git a/source/blender/draw/engines/overlay/shaders/motion_path_line_vert.glsl b/source/blender/draw/engines/overlay/shaders/motion_path_line_vert.glsl new file mode 100644 index 00000000000..6d7f673731e --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/motion_path_line_vert.glsl @@ -0,0 +1,96 @@ + +uniform ivec4 mpathLineSettings; +uniform bool selected; +uniform vec3 customColor; + +#define frameCurrent mpathLineSettings.x +#define frameStart mpathLineSettings.y +#define frameEnd mpathLineSettings.z +#define cacheStart mpathLineSettings.w + +in vec3 pos; + +out vec2 ssPos; +out vec4 finalColor_geom; + +/* project to screen space */ +vec2 proj(vec4 pos) +{ + return (0.5 * (pos.xy / pos.w) + 0.5) * sizeViewport.xy; +} + +#define SET_INTENSITY(A, B, C, min, max) \ + (((1.0 - (float(C - B) / float(C - A))) * (max - min)) + min) + +void main() +{ + gl_Position = ViewProjectionMatrix * vec4(pos, 1.0); + + ssPos = proj(gl_Position); + + int frame = gl_VertexID + cacheStart; + + float intensity; /* how faint */ + + vec3 blend_base = (abs(frame - frameCurrent) == 1) ? + colorCurrentFrame.rgb : + colorBackground.rgb; /* "bleed" cframe color to ease color blending */ + bool use_custom_color = customColor.x >= 0.0; + /* TODO: We might want something more consistent with custom color and standard colors. */ + if (frame < frameCurrent) { + if (use_custom_color) { + /* Custom color: previous frames color is darker than current frame */ + finalColor_geom.rgb = customColor * 0.25; + } + else { + /* black - before frameCurrent */ + if (selected) { + intensity = SET_INTENSITY(frameStart, frame, frameCurrent, 0.25, 0.75); + } + else { + intensity = SET_INTENSITY(frameStart, frame, frameCurrent, 0.68, 0.92); + } + finalColor_geom.rgb = mix(colorWire.rgb, blend_base, intensity); + } + } + else if (frame > frameCurrent) { + if (use_custom_color) { + /* Custom color: next frames color is equal to user selected color */ + finalColor_geom.rgb = customColor; + } + else { + /* blue - after frameCurrent */ + if (selected) { + intensity = SET_INTENSITY(frameCurrent, frame, frameEnd, 0.25, 0.75); + } + else { + intensity = SET_INTENSITY(frameCurrent, frame, frameEnd, 0.68, 0.92); + } + + finalColor_geom.rgb = mix(colorBonePose.rgb, blend_base, intensity); + } + } + else { + if (use_custom_color) { + /* Custom color: current frame color is slightly darker than user selected color */ + finalColor_geom.rgb = customColor * 0.5; + } + else { + /* green - on frameCurrent */ + if (selected) { + intensity = 0.5f; + } + else { + intensity = 0.99f; + } + finalColor_geom.rgb = clamp( + mix(colorCurrentFrame.rgb, colorBackground.rgb, intensity) - 0.1, 0.0, 0.1); + } + } + + finalColor_geom.a = 1.0; + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(pos); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/motion_path_point_vert.glsl b/source/blender/draw/engines/overlay/shaders/motion_path_point_vert.glsl new file mode 100644 index 00000000000..14335eb1b99 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/motion_path_point_vert.glsl @@ -0,0 +1,58 @@ + +uniform ivec4 mpathPointSettings; +uniform bool showKeyFrames = true; +uniform vec3 customColor; + +#define pointSize mpathPointSettings.x +#define frameCurrent mpathPointSettings.y +#define cacheStart mpathPointSettings.z +#define stepSize mpathPointSettings.w + +in vec3 pos; +in int flag; + +#define MOTIONPATH_VERT_SEL (1 << 0) +#define MOTIONPATH_VERT_KEY (1 << 1) + +out vec4 finalColor; + +void main() +{ + gl_Position = ViewProjectionMatrix * vec4(pos, 1.0); + gl_PointSize = float(pointSize + 2); + + int frame = gl_VertexID + cacheStart; + bool use_custom_color = customColor.x >= 0.0; + finalColor = (use_custom_color) ? vec4(customColor, 1.0) : vec4(1.0); + + /* Bias to reduce z fighting with the path */ + gl_Position.z -= 1e-4; + + if (gl_VertexID % stepSize == 0) { + gl_PointSize = float(pointSize) + 4; + } + + if (showKeyFrames) { + if ((flag & MOTIONPATH_VERT_KEY) != 0) { + gl_PointSize = float(pointSize + 5); + finalColor = colorVertexSelect; + /* Bias more to get these on top of regular points */ + gl_Position.z -= 1e-4; + } + /* Draw big green dot where the current frame is. + * NOTE: this is only done when keyframes are shown, since this adds similar types of clutter + */ + if (frame == frameCurrent) { + gl_PointSize = float(pointSize + 8); + finalColor = colorCurrentFrame; + /* Bias more to get these on top of keyframes */ + gl_Position.z -= 1e-4; + } + } + + gl_PointSize *= sizePixel; + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(pos); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl b/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl new file mode 100644 index 00000000000..79c970adfe0 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/outline_detect_frag.glsl @@ -0,0 +1,86 @@ + +in vec4 uvcoordsvar; + +out vec4 FragColor; + +uniform usampler2D outlineId; +uniform sampler2D outlineDepth; +uniform sampler2D sceneDepth; + +uniform float alphaOcclu; + +void main() +{ + ivec2 texel = ivec2(gl_FragCoord.xy); + +#ifdef GPU_ARB_texture_gather + vec2 texel_size = 1.0 / vec2(textureSize(outlineId, 0).xy); + vec2 uv = ceil(gl_FragCoord.xy) * texel_size; + + /* Samples order is CW starting from top left. */ + uvec4 tmp1 = textureGather(outlineId, uv - texel_size); + uvec4 tmp2 = textureGather(outlineId, uv); + + uint ref_id = tmp1.y; + uvec4 id = uvec4(tmp1.xz, tmp2.xz); +#else + uvec4 id; + uint ref_id = texelFetch(outlineId, texel, 0).r; + id.x = texelFetchOffset(outlineId, texel, 0, ivec2(-1, 0)).r; + id.y = texelFetchOffset(outlineId, texel, 0, ivec2(0, -1)).r; + id.z = texelFetchOffset(outlineId, texel, 0, ivec2(0, 1)).r; + id.w = texelFetchOffset(outlineId, texel, 0, ivec2(1, 0)).r; +#endif + +#ifdef WIRE + /* We want only 2px outlines. */ + /* TODO optimize, don't sample if we don't need to. */ + id.xy = uvec2(ref_id); +#endif + + bool outline = any(notEqual(id, uvec4(ref_id))); + + ivec2 depth_texel = texel; + /* If texel is an outline but has no valid id ... + * replace id and depth texel by a valid one. + * This keeps the outline thickness consistent everywhere. */ + if (ref_id == 0u && outline) { + depth_texel = (id.x != 0u) ? texel + ivec2(-1, 0) : depth_texel; + depth_texel = (id.y != 0u) ? texel + ivec2(0, -1) : depth_texel; + depth_texel = (id.z != 0u) ? texel + ivec2(0, 1) : depth_texel; + depth_texel = (id.w != 0u) ? texel + ivec2(1, 0) : depth_texel; + + ref_id = (id.x != 0u) ? id.x : ref_id; + ref_id = (id.y != 0u) ? id.y : ref_id; + ref_id = (id.z != 0u) ? id.z : ref_id; + ref_id = (id.w != 0u) ? id.w : ref_id; + } + + float ref_depth = texelFetch(outlineDepth, depth_texel, 0).r; + float scene_depth = texelFetch(sceneDepth, depth_texel, 0).r; + + /* Avoid bad cases of zfighting for occlusion only. */ + const float epsilon = 3.0 / 8388608.0; + bool occluded = (ref_depth > scene_depth + epsilon); + + /* WATCH: Keep in sync with outlineId of the prepass. */ + uint color_id = ref_id >> 14u; + if (ref_id == 0u) { + FragColor = vec4(0.0); + } + else if (color_id == 1u) { + FragColor = colorSelect; + } + else if (color_id == 2u) { + FragColor = colorDupliSelect; + } + else if (color_id == 3u) { + FragColor = colorActive; + } + else { + FragColor = colorTransform; + } + + FragColor.a *= (occluded) ? alphaOcclu : 1.0; + FragColor.a = (outline) ? FragColor.a : 0.0; +} diff --git a/source/blender/draw/engines/overlay/shaders/outline_expand_frag.glsl b/source/blender/draw/engines/overlay/shaders/outline_expand_frag.glsl new file mode 100644 index 00000000000..cb9fe0e7c36 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/outline_expand_frag.glsl @@ -0,0 +1,51 @@ + +in vec4 uvcoordsvar; + +out vec4 FragColor; + +uniform sampler2D outlineColor; + +uniform float alpha; +uniform bool doExpand; + +void main() +{ + ivec2 uv = ivec2(gl_FragCoord.xy); + FragColor = texelFetch(outlineColor, uv, 0).rgba; + + vec4 color[4]; + color[0] = texelFetchOffset(outlineColor, uv, 0, ivec2(1, 0)).rgba; + color[1] = texelFetchOffset(outlineColor, uv, 0, ivec2(0, 1)).rgba; + color[2] = texelFetchOffset(outlineColor, uv, 0, ivec2(-1, 0)).rgba; + color[3] = texelFetchOffset(outlineColor, uv, 0, ivec2(0, -1)).rgba; + + vec4 values = vec4(color[0].a, color[1].a, color[2].a, color[3].a); + + vec4 tests = step(vec4(1e-6), values); /* (color.a != 0.0) */ + bvec4 btests = equal(tests, vec4(1.0)); + + if (FragColor.a != 0.0) { + return; + } + +#ifdef LARGE_OUTLINE + if (!any(btests)) { + color[0] = texelFetchOffset(outlineColor, uv, 0, ivec2(2, 0)).rgba; + color[1] = texelFetchOffset(outlineColor, uv, 0, ivec2(0, 2)).rgba; + color[2] = texelFetchOffset(outlineColor, uv, 0, ivec2(-2, 0)).rgba; + color[3] = texelFetchOffset(outlineColor, uv, 0, ivec2(0, -2)).rgba; + + values = vec4(color[0].a, color[1].a, color[2].a, color[3].a); + + tests = step(vec4(1e-6), values); /* (color.a != 0.0) */ + btests = equal(tests, vec4(1.0)); + } +#endif + + FragColor = (btests.x) ? color[0] : FragColor; + FragColor = (btests.y) ? color[1] : FragColor; + FragColor = (btests.z) ? color[2] : FragColor; + FragColor = (btests.w) ? color[3] : FragColor; + + FragColor.a *= (!doExpand) ? 0.0 : 1.0; +} diff --git a/source/blender/draw/engines/overlay/shaders/outline_lightprobe_grid_vert.glsl b/source/blender/draw/engines/overlay/shaders/outline_lightprobe_grid_vert.glsl new file mode 100644 index 00000000000..144024a7d5d --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/outline_lightprobe_grid_vert.glsl @@ -0,0 +1,31 @@ + +uniform ivec3 grid_resolution; +uniform vec3 corner; +uniform vec3 increment_x; +uniform vec3 increment_y; +uniform vec3 increment_z; + +flat out int objectId; + +void main() +{ + vec3 ls_cell_location; + /* Keep in sync with update_irradiance_probe */ + ls_cell_location.z = float(gl_VertexID % grid_resolution.z); + ls_cell_location.y = float((gl_VertexID / grid_resolution.z) % grid_resolution.y); + ls_cell_location.x = float(gl_VertexID / (grid_resolution.z * grid_resolution.y)); + + vec3 ws_cell_location = corner + + (increment_x * ls_cell_location.x + increment_y * ls_cell_location.y + + increment_z * ls_cell_location.z); + + gl_Position = ViewProjectionMatrix * vec4(ws_cell_location, 1.0); + gl_PointSize = 2.0f; + + /* ID 0 is nothing (background) */ + objectId = resource_handle + 1; + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(ws_cell_location); +#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 new file mode 100644 index 00000000000..5d6c4881b5b --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/outline_prepass_frag.glsl @@ -0,0 +1,18 @@ + +/* Should be 2 bits only [0..3]. */ +uniform int outlineId; + +flat in int objectId; + +/* using uint because 16bit uint can contain more ids than int. */ +out uint outId; + +/* Replace top 2 bits (of the 16bit output) by outlineId. + * This leaves 16K different IDs to create outlines between objects. + * SHIFT = (32 - (16 - 2)) */ +#define SHIFT 18u + +void main() +{ + outId = (uint(outlineId) << 14u) | ((uint(objectId) << SHIFT) >> SHIFT); +} diff --git a/source/blender/draw/engines/overlay/shaders/outline_prepass_geom.glsl b/source/blender/draw/engines/overlay/shaders/outline_prepass_geom.glsl new file mode 100644 index 00000000000..b32913dcd60 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/outline_prepass_geom.glsl @@ -0,0 +1,54 @@ + +layout(lines_adjacency) in; +layout(line_strip, max_vertices = 2) out; + +in vec3 vPos[]; +in int objectId_g[]; + +flat out int objectId; + +void vert_from_gl_in(int v) +{ + gl_Position = gl_in[v].gl_Position; + objectId = objectId_g[v]; +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_set_clip_distance(gl_in[v].gl_ClipDistance); +#endif +} + +void main() +{ + bool is_persp = (ProjectionMatrix[3][3] == 0.0); + + vec3 view_vec = (is_persp) ? normalize(vPos[1]) : vec3(0.0, 0.0, -1.0); + + vec3 v10 = vPos[0] - vPos[1]; + vec3 v12 = vPos[2] - vPos[1]; + vec3 v13 = vPos[3] - vPos[1]; + + vec3 n0 = cross(v12, v10); + vec3 n3 = cross(v13, v12); + + float fac0 = dot(view_vec, n0); + float fac3 = dot(view_vec, n3); + + /* If both adjacent verts are facing the camera the same way, + * then it isn't an outline edge. */ + if (sign(fac0) == sign(fac3)) { + return; + } + + /* Don't outline if concave edge. */ + /* That would hide a lot of non useful edge but it flickers badly. + * TODO revisit later... */ + // if (dot(n0, v13) > 0.01) + // return; + + vert_from_gl_in(1); + EmitVertex(); + + vert_from_gl_in(2); + EmitVertex(); + + EndPrimitive(); +} diff --git a/source/blender/draw/engines/overlay/shaders/outline_prepass_vert.glsl b/source/blender/draw/engines/overlay/shaders/outline_prepass_vert.glsl new file mode 100644 index 00000000000..7740f9a4af2 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/outline_prepass_vert.glsl @@ -0,0 +1,29 @@ + +in vec3 pos; + +#ifdef USE_GEOM +out vec3 vPos; +out int objectId_g; +# define objectId objectId_g +#else + +flat out int objectId; +#endif + +void main() +{ + vec3 world_pos = point_object_to_world(pos); +#ifdef USE_GEOM + vPos = point_world_to_view(world_pos); +#endif + gl_Position = point_world_to_ndc(world_pos); + /* Small bias to always be on top of the geom. */ + gl_Position.z -= 1e-3; + + /* ID 0 is nothing (background) */ + objectId = resource_handle + 1; + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/outline_resolve_frag.glsl b/source/blender/draw/engines/overlay/shaders/outline_resolve_frag.glsl new file mode 100644 index 00000000000..ba5d073a9c2 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/outline_resolve_frag.glsl @@ -0,0 +1,21 @@ + +in vec4 uvcoordsvar; + +out vec4 FragColor; + +uniform sampler2D outlineBluredColor; +uniform vec2 rcpDimensions; + +void main() +{ +#ifdef USE_FXAA + float aa_alpha = + FxaaPixelShader(uvcoordsvar.st, outlineBluredColor, rcpDimensions, 1.0, 0.166, 0.0833).r; +#endif + + FragColor = texture(outlineBluredColor, uvcoordsvar.st).rgba; + +#ifdef USE_FXAA + FragColor.a = aa_alpha; +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/paint_face_vert.glsl b/source/blender/draw/engines/overlay/shaders/paint_face_vert.glsl new file mode 100644 index 00000000000..2b5d586bdd1 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/paint_face_vert.glsl @@ -0,0 +1,24 @@ + +in vec3 pos; +in vec4 nor; /* select flag on the 4th component */ + +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_select = (nor.w > 0.0); + bool is_hidden = (nor.w < 0.0); + + /* Don't draw faces that are selected. */ + if (is_hidden || is_select) { + gl_Position = vec4(-2.0, -2.0, -2.0, 1.0); + } + else { +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif + } +} diff --git a/source/blender/draw/engines/overlay/shaders/paint_point_vert.glsl b/source/blender/draw/engines/overlay/shaders/paint_point_vert.glsl new file mode 100644 index 00000000000..9d102bd4295 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/paint_point_vert.glsl @@ -0,0 +1,32 @@ + +in vec3 pos; +in vec4 nor; /* select flag on the 4th component */ + +out vec4 finalColor; + +void main() +{ + GPU_INTEL_VERTEX_SHADER_WORKAROUND + + bool is_select = (nor.w > 0.0); + bool is_hidden = (nor.w < 0.0); + + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + /* Add offset in Z to avoid zfighting and render selected wires on top. */ + /* TODO scale this bias using znear and zfar range. */ + gl_Position.z -= (is_select ? 2e-4 : 1e-4); + + if (is_hidden) { + gl_Position = vec4(-2.0, -2.0, -2.0, 1.0); + } + + finalColor = (is_select) ? vec4(1.0) : colorWire; + finalColor.a = nor.w; + + gl_PointSize = sizeVertex * 2.0; + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/paint_texture_frag.glsl b/source/blender/draw/engines/overlay/shaders/paint_texture_frag.glsl new file mode 100644 index 00000000000..4d0692039a4 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/paint_texture_frag.glsl @@ -0,0 +1,23 @@ +in vec2 uv_interp; + +out vec4 fragColor; + +uniform float opacity = 1.0; + +uniform sampler2D maskImage; +uniform bool maskImagePremultiplied; +uniform vec3 maskColor; +uniform bool maskInvertStencil; + +void main() +{ + vec4 mask = vec4(texture_read_as_srgb(maskImage, maskImagePremultiplied, uv_interp).rgb, 1.0); + if (maskInvertStencil) { + mask.rgb = 1.0 - mask.rgb; + } + float mask_step = smoothstep(0, 3.0, mask.r + mask.g + mask.b); + mask.rgb *= maskColor; + mask.a = mask_step * opacity; + + fragColor = mask; +} diff --git a/source/blender/draw/engines/overlay/shaders/paint_texture_vert.glsl b/source/blender/draw/engines/overlay/shaders/paint_texture_vert.glsl new file mode 100644 index 00000000000..cb29fefb7ef --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/paint_texture_vert.glsl @@ -0,0 +1,19 @@ + +in vec3 pos; +in vec2 mu; /* masking uv map */ + +out vec2 uv_interp; + +void main() +{ + GPU_INTEL_VERTEX_SHADER_WORKAROUND + + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + + uv_interp = mu; + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/paint_vertcol_frag.glsl b/source/blender/draw/engines/overlay/shaders/paint_vertcol_frag.glsl new file mode 100644 index 00000000000..2500ff83abe --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/paint_vertcol_frag.glsl @@ -0,0 +1,28 @@ + +in vec3 finalColor; + +out vec4 fragColor; + +uniform float opacity = 1.0; +uniform bool useAlphaBlend = false; + +vec3 linear_to_srgb_attr(vec3 c) +{ + c = max(c, vec3(0.0)); + vec3 c1 = c * 12.92; + vec3 c2 = 1.055 * pow(c, vec3(1.0 / 2.4)) - 0.055; + return mix(c1, c2, step(vec3(0.0031308), c)); +} + +void main() +{ + vec3 color = linear_to_srgb_attr(finalColor); + + if (useAlphaBlend) { + fragColor = vec4(color, opacity); + } + else { + /* mix with 1.0 -> is like opacity when using multiply blend mode */ + fragColor = vec4(mix(vec3(1.0), color, opacity), 1.0); + } +} diff --git a/source/blender/draw/engines/overlay/shaders/paint_vertcol_vert.glsl b/source/blender/draw/engines/overlay/shaders/paint_vertcol_vert.glsl new file mode 100644 index 00000000000..e060e33deba --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/paint_vertcol_vert.glsl @@ -0,0 +1,27 @@ + +in vec3 pos; +in vec3 ac; /* active color */ + +out vec3 finalColor; + +vec3 srgb_to_linear_attr(vec3 c) +{ + c = max(c, vec3(0.0)); + vec3 c1 = c * (1.0 / 12.92); + vec3 c2 = pow((c + 0.055) * (1.0 / 1.055), vec3(2.4)); + return mix(c1, c2, step(vec3(0.04045), c)); +} + +void main() +{ + GPU_INTEL_VERTEX_SHADER_WORKAROUND + + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + + finalColor = srgb_to_linear_attr(ac); + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/paint_weight_frag.glsl b/source/blender/draw/engines/overlay/shaders/paint_weight_frag.glsl new file mode 100644 index 00000000000..0020d76ed6a --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/paint_weight_frag.glsl @@ -0,0 +1,106 @@ + +in vec2 weight_interp; /* (weight, alert) */ + +out vec4 fragColor; + +uniform float opacity = 1.0; +uniform sampler1D colorramp; + +uniform bool useAlphaBlend = false; +uniform bool drawContours = false; + +float contours(float value, float steps, float width_px, float max_rel_width, float gradient) +{ + /* Minimum visible and minimum full strength line width in screen space for fade out. */ + const float min_width_px = 1.3, fade_width_px = 2.3; + /* Line is thinner towards the increase in the weight gradient by this factor. */ + const float hi_bias = 2.0; + + /* Don't draw lines at 0 or 1. */ + float rel_value = value * steps; + + if (rel_value < 0.5 || rel_value > steps - 0.5) { + return 0.0; + } + + /* Check if completely invisible due to fade out. */ + float rel_gradient = gradient * steps; + float rel_min_width = min_width_px * rel_gradient; + + if (max_rel_width <= rel_min_width) { + return 0.0; + } + + /* Main shape of the line, accounting for width bias and maximum weight space width. */ + float rel_width = width_px * rel_gradient; + + float offset = fract(rel_value + 0.5) - 0.5; + + float base_alpha = 1.0 - max(offset * hi_bias, -offset) / min(max_rel_width, rel_width); + + /* Line fadeout when too thin in screen space. */ + float rel_fade_width = fade_width_px * rel_gradient; + + float fade_alpha = (max_rel_width - rel_min_width) / (rel_fade_width - rel_min_width); + + return clamp(base_alpha, 0.0, 1.0) * clamp(fade_alpha, 0.0, 1.0); +} + +vec4 contour_grid(float weight, float weight_gradient) +{ + /* Fade away when the gradient is too low to avoid big fills and noise. */ + float flt_eps = max(1e-8, 1e-6 * weight); + + if (weight_gradient <= flt_eps) { + return vec4(0.0); + } + + /* Three levels of grid lines */ + float grid10 = contours(weight, 10.0, 5.0, 0.3, weight_gradient); + float grid100 = contours(weight, 100.0, 3.5, 0.35, weight_gradient) * 0.6; + float grid1000 = contours(weight, 1000.0, 2.5, 0.4, weight_gradient) * 0.25; + + /* White lines for 0.1 and 0.01, and black for 0.001 */ + vec4 grid = vec4(1.0) * max(grid10, grid100); + + grid.a = max(grid.a, grid1000); + + return grid * clamp((weight_gradient - flt_eps) / flt_eps, 0.0, 1.0); +} + +void main() +{ + float alert = weight_interp.y; + vec4 color; + + /* Missing vertex group alert color. Uniform in practice. */ + if (alert > 1.1) { + color = colorVertexMissingData; + } + /* Weights are available */ + else { + float weight = weight_interp.x; + vec4 weight_color = texture(colorramp, weight, 0); + + /* Contour display */ + if (drawContours) { + /* This must be executed uniformly for all fragments */ + float weight_gradient = length(vec2(dFdx(weight), dFdy(weight))); + + vec4 grid = contour_grid(weight, weight_gradient); + + weight_color = grid + weight_color * (1 - grid.a); + } + + /* Zero weight alert color. Nonlinear blend to reduce impact. */ + color = mix(weight_color, colorVertexUnreferenced, alert * alert); + } + + if (useAlphaBlend) { + fragColor = vec4(color.rgb, opacity); + } + else { + /* mix with 1.0 -> is like opacity when using multiply blend mode */ + fragColor = vec4(mix(vec3(1.0), color.rgb, opacity), 1.0); + } +} diff --git a/source/blender/draw/engines/overlay/shaders/paint_weight_vert.glsl b/source/blender/draw/engines/overlay/shaders/paint_weight_vert.glsl new file mode 100644 index 00000000000..b3baa8c7b07 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/paint_weight_vert.glsl @@ -0,0 +1,20 @@ + +in float weight; +in vec3 pos; + +out vec2 weight_interp; /* (weight, alert) */ + +void main() +{ + GPU_INTEL_VERTEX_SHADER_WORKAROUND + + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + + /* Separate actual weight and alerts for independent interpolation */ + weight_interp = max(vec2(weight, -weight), 0.0); + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/paint_wire_vert.glsl b/source/blender/draw/engines/overlay/shaders/paint_wire_vert.glsl new file mode 100644 index 00000000000..d5a42d2d309 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/paint_wire_vert.glsl @@ -0,0 +1,38 @@ + +uniform bool useSelect; + +in vec3 pos; +in vec4 nor; /* flag stored in w */ + +flat out vec4 finalColor; + +void main() +{ + GPU_INTEL_VERTEX_SHADER_WORKAROUND + + bool is_select = (nor.w > 0.0) && useSelect; + bool is_hidden = (nor.w < 0.0) && useSelect; + + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + /* Add offset in Z to avoid zfighting and render selected wires on top. */ + /* TODO scale this bias using znear and zfar range. */ + gl_Position.z -= (is_select ? 2e-4 : 1e-4); + + if (is_hidden) { + gl_Position = vec4(-2.0, -2.0, -2.0, 1.0); + } + + const vec4 colSel = vec4(1.0); + + finalColor = (is_select) ? colSel : colorWire; + + /* Weight paint needs a light color to contrasts with dark weights. */ + if (!useSelect) { + finalColor = vec4(1.0, 1.0, 1.0, 0.3); + } + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/particle_frag.glsl b/source/blender/draw/engines/overlay/shaders/particle_frag.glsl new file mode 100644 index 00000000000..36928d0c776 --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/particle_frag.glsl @@ -0,0 +1,16 @@ + +in vec4 finalColor; + +out vec4 fragColor; + +void main() +{ + float dist = length(gl_PointCoord - vec2(0.5)); + + if (dist > 0.5) { + discard; + } + /* Nice sphere falloff. */ + float intensity = sqrt(1.0 - dist * 2.0) * 0.5 + 0.5; + fragColor = finalColor * vec4(intensity, intensity, intensity, 1.0); +} diff --git a/source/blender/draw/engines/overlay/shaders/particle_vert.glsl b/source/blender/draw/engines/overlay/shaders/particle_vert.glsl new file mode 100644 index 00000000000..9ff2c42921d --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/particle_vert.glsl @@ -0,0 +1,68 @@ + +uniform sampler1D weightTex; +uniform vec4 color; /* Drawsize packed in alpha */ + +/* ---- Instantiated Attrs ---- */ +in vec3 pos; +in int vclass; + +/* ---- Per instance Attrs ---- */ +in vec3 part_pos; +in vec4 part_rot; +in float part_val; + +#ifdef USE_DOTS +out vec4 finalColor; +#else +flat out vec4 finalColor; +#endif + +#define VCLASS_SCREENALIGNED (1 << 9) + +#define VCLASS_EMPTY_AXES (1 << 11) + +vec3 rotate(vec3 vec, vec4 quat) +{ + /* The quaternion representation here stores the w component in the first index */ + return vec + 2.0 * cross(quat.yzw, cross(quat.yzw, vec) + quat.x * vec); +} + +void main() +{ + float draw_size = color.a; + + vec3 world_pos = part_pos; + +#ifdef USE_DOTS + gl_Position = point_world_to_ndc(world_pos); + /* World sized points. */ + gl_PointSize = sizePixel * draw_size * ProjectionMatrix[1][1] * sizeViewport.y / gl_Position.w; +#else + + if ((vclass & VCLASS_SCREENALIGNED) != 0) { + /* World sized, camera facing geometry. */ + world_pos += (screenVecs[0].xyz * pos.x + screenVecs[1].xyz * pos.y) * draw_size; + } + else { + world_pos += rotate(pos, part_rot) * draw_size; + } + + gl_Position = point_world_to_ndc(world_pos); +#endif + + /* Coloring */ + if ((vclass & VCLASS_EMPTY_AXES) != 0) { + /* see VBO construction for explanation. */ + finalColor = vec4(clamp(pos * 10000.0, 0.0, 1.0), 1.0); + } + else if (part_val < 0.0) { + finalColor = vec4(color.rgb, 1.0); + } + else { + finalColor = vec4(texture(weightTex, part_val).rgb, 1.0); + } + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/sculpt_mask_vert.glsl b/source/blender/draw/engines/overlay/shaders/sculpt_mask_vert.glsl new file mode 100644 index 00000000000..18a096da61b --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/sculpt_mask_vert.glsl @@ -0,0 +1,20 @@ + +uniform float maskOpacity; + +in vec3 pos; +in float msk; + +out vec4 finalColor; + +void main() +{ + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); + + float mask = 1.0 - (msk * maskOpacity); + finalColor = vec4(0.0, 0.0, 0.0, mask); + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(world_pos); +#endif +} diff --git a/source/blender/draw/engines/overlay/shaders/volume_velocity_vert.glsl b/source/blender/draw/engines/overlay/shaders/volume_velocity_vert.glsl new file mode 100644 index 00000000000..64f88bd74fa --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/volume_velocity_vert.glsl @@ -0,0 +1,117 @@ + +uniform sampler3D velocityX; +uniform sampler3D velocityY; +uniform sampler3D velocityZ; +uniform float displaySize = 1.0; +uniform float slicePosition; +uniform int sliceAxis; /* -1 is no slice, 0 is X, 1 is Y, 2 is Z. */ + +/* SmokeDomainSettings.cell_size */ +uniform vec3 cellSize; +/* SmokeDomainSettings.p0 */ +uniform vec3 domainOriginOffset; +/* SmokeDomainSettings.res_min */ +uniform ivec3 adaptiveCellOffset; + +flat out vec4 finalColor; + +const vec3 corners[4] = vec3[4](vec3(0.0, 0.2, -0.5), + vec3(-0.2 * 0.866, -0.2 * 0.5, -0.5), + vec3(0.2 * 0.866, -0.2 * 0.5, -0.5), + vec3(0.0, 0.0, 0.5)); + +const int indices[12] = int[12](0, 1, 1, 2, 2, 0, 0, 3, 1, 3, 2, 3); + +/* Straight Port from BKE_defvert_weight_to_rgb() + * TODO port this to a color ramp. */ +vec3 weight_to_color(float weight) +{ + vec3 r_rgb = vec3(0.0); + float blend = ((weight / 2.0) + 0.5); + + if (weight <= 0.25) { /* blue->cyan */ + r_rgb.g = blend * weight * 4.0; + r_rgb.b = blend; + } + else if (weight <= 0.50) { /* cyan->green */ + r_rgb.g = blend; + r_rgb.b = blend * (1.0 - ((weight - 0.25) * 4.0)); + } + else if (weight <= 0.75) { /* green->yellow */ + r_rgb.r = blend * ((weight - 0.50) * 4.0); + r_rgb.g = blend; + } + else if (weight <= 1.0) { /* yellow->red */ + r_rgb.r = blend; + r_rgb.g = blend * (1.0 - ((weight - 0.75) * 4.0)); + } + else { + /* exceptional value, unclamped or nan, + * avoid uninitialized memory use */ + r_rgb = vec3(1.0, 0.0, 1.0); + } + + return r_rgb; +} + +mat3 rotation_from_vector(vec3 v) +{ + /* Add epsilon to avoid NaN. */ + vec3 N = normalize(v + 1e-8); + vec3 UpVector = abs(N.z) < 0.99999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); + vec3 T = normalize(cross(UpVector, N)); + vec3 B = cross(N, T); + return mat3(T, B, N); +} + +void main() +{ +#ifdef USE_NEEDLE + int cell = gl_VertexID / 12; +#else + int cell = gl_VertexID / 2; +#endif + + ivec3 volume_size = textureSize(velocityX, 0); + + ivec3 cell_ofs = ivec3(0); + ivec3 cell_div = volume_size; + if (sliceAxis == 0) { + cell_ofs.x = int(slicePosition * float(volume_size.x)); + cell_div.x = 1; + } + else if (sliceAxis == 1) { + cell_ofs.y = int(slicePosition * float(volume_size.y)); + cell_div.y = 1; + } + else if (sliceAxis == 2) { + cell_ofs.z = int(slicePosition * float(volume_size.z)); + cell_div.z = 1; + } + + ivec3 cell_co; + cell_co.x = cell % cell_div.x; + cell_co.y = (cell / cell_div.x) % cell_div.y; + cell_co.z = cell / (cell_div.x * cell_div.y); + cell_co += cell_ofs; + + vec3 pos = domainOriginOffset + cellSize * (vec3(cell_co + adaptiveCellOffset) + 0.5); + + vec3 velocity; + velocity.x = texelFetch(velocityX, cell_co, 0).r; + velocity.y = texelFetch(velocityY, cell_co, 0).r; + velocity.z = texelFetch(velocityZ, cell_co, 0).r; + + finalColor = vec4(weight_to_color(length(velocity)), 1.0); + +#ifdef USE_NEEDLE + mat3 rot_mat = rotation_from_vector(velocity); + vec3 rotated_pos = rot_mat * corners[indices[gl_VertexID % 12]]; + pos += rotated_pos * length(velocity) * displaySize * cellSize; +#else + pos += ((gl_VertexID % 2) == 1) ? velocity * displaySize * cellSize : vec3(0.0); +#endif + + vec3 world_pos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(world_pos); +} diff --git a/source/blender/draw/engines/overlay/shaders/wireframe_frag.glsl b/source/blender/draw/engines/overlay/shaders/wireframe_frag.glsl new file mode 100644 index 00000000000..39d0012574c --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/wireframe_frag.glsl @@ -0,0 +1,20 @@ + +in vec3 finalColor; +flat in float edgeSharpness; + +flat in vec2 edgeStart; +noperspective in vec2 edgePos; + +layout(location = 0) out vec4 fragColor; +layout(location = 1) out vec4 lineOutput; + +void main() +{ + if (edgeSharpness < 0.0) { + discard; + } + + lineOutput = pack_line_data(gl_FragCoord.xy, edgeStart, edgePos); + fragColor.rgb = finalColor; + fragColor.a = 1.0; +} diff --git a/source/blender/draw/engines/overlay/shaders/wireframe_geom.glsl b/source/blender/draw/engines/overlay/shaders/wireframe_geom.glsl new file mode 100644 index 00000000000..346c9d83a9e --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/wireframe_geom.glsl @@ -0,0 +1,61 @@ + +/* This shader is only used for edge selection and OSX workaround for large wires. */ + +layout(lines) in; +layout(triangle_strip, max_vertices = 4) out; + +in vec3 finalColor_g[]; +in float edgeSharpness_g[]; + +#ifndef SELECT_EDGES +out vec3 finalColor; +flat out float edgeSharpness; + +flat out vec2 edgeStart; +noperspective out vec2 edgePos; +#endif + +void do_vertex(const int i, float coord, vec2 offset) +{ +#ifndef SELECT_EDGES + /* TODO */ + edgePos = edgeStart = vec2(0); + edgeSharpness = edgeSharpness_g[i]; + finalColor = finalColor_g[i]; +#endif + gl_Position = gl_in[i].gl_Position; + /* Multiply offset by 2 because gl_Position range is [-1..1]. */ + gl_Position.xy += offset * 2.0 * gl_Position.w; +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_set_clip_distance(gl_in[i].gl_ClipDistance); +#endif + EmitVertex(); +} + +void main() +{ + vec2 ss_pos[2]; + ss_pos[0] = gl_in[0].gl_Position.xy / gl_in[0].gl_Position.w; + ss_pos[1] = gl_in[1].gl_Position.xy / gl_in[1].gl_Position.w; + + vec2 line = ss_pos[0] - ss_pos[1]; + line = abs(line) * sizeViewport.xy; + + float half_size = sizePixel * 0.5; + + vec3 edge_ofs = vec3(half_size * sizeViewportInv.xy, 0.0); + + bool horizontal = line.x > line.y; + edge_ofs = (horizontal) ? edge_ofs.zyz : edge_ofs.xzz; + + if (edgeSharpness_g[0] < 0.0) { + return; + } + + do_vertex(0, half_size, edge_ofs.xy); + do_vertex(0, -half_size, -edge_ofs.xy); + do_vertex(1, half_size, edge_ofs.xy); + do_vertex(1, -half_size, -edge_ofs.xy); + + EndPrimitive(); +} diff --git a/source/blender/draw/engines/overlay/shaders/wireframe_vert.glsl b/source/blender/draw/engines/overlay/shaders/wireframe_vert.glsl new file mode 100644 index 00000000000..78ce8fd8a8f --- /dev/null +++ b/source/blender/draw/engines/overlay/shaders/wireframe_vert.glsl @@ -0,0 +1,144 @@ + +uniform float wireStepParam; + +in vec3 pos; +in vec3 nor; +in float wd; /* wiredata */ + +float get_edge_sharpness(float wd) +{ + return ((wd == 0.0) ? -1.5 : wd) + wireStepParam; +} + +/* Geometry shader version */ +#if defined(SELECT_EDGES) || defined(USE_GEOM) +out vec3 finalColor_g; +out float edgeSharpness_g; + +#else /* USE_GEOM */ + +flat out vec2 edgeStart; +noperspective out vec2 edgePos; + +out vec3 finalColor; +flat out float edgeSharpness; +# define finalColor_g finalColor +# define edgeSharpness_g edgeSharpness + +#endif /* SELECT_EDGES */ + +uniform bool useColoring; +uniform bool isTransform; +uniform bool isObjectColor; +uniform bool isRandomColor; + +void wire_color_get(out vec3 rim_col, out vec3 wire_col) +{ + int flag = int(abs(ObjectInfo.w)); + bool is_selected = (flag & DRW_BASE_SELECTED) != 0; + bool is_from_dupli = (flag & DRW_BASE_FROM_DUPLI) != 0; + bool is_from_set = (flag & DRW_BASE_FROM_SET) != 0; + bool is_active = (flag & DRW_BASE_ACTIVE) != 0; + + if (is_from_set) { + rim_col = colorDupli.rgb; + wire_col = colorDupli.rgb; + } + else if (is_from_dupli) { + if (is_selected) { + if (isTransform) { + rim_col = colorTransform.rgb; + } + else { + rim_col = colorDupliSelect.rgb; + } + } + else { + rim_col = colorDupli.rgb; + } + wire_col = colorDupli.rgb; + } + else if (is_selected && useColoring) { + if (isTransform) { + rim_col = colorTransform.rgb; + } + else if (is_active) { + rim_col = colorActive.rgb; + } + else { + rim_col = colorSelect.rgb; + } + wire_col = colorWire.rgb; + } + else { + rim_col = colorWire.rgb; + wire_col = colorBackground.rgb; + } +} + +vec3 hsv_to_rgb(vec3 hsv) +{ + vec3 nrgb = abs(hsv.x * 6.0 - vec3(3.0, 2.0, 4.0)) * vec3(1, -1, -1) + vec3(-1, 2, 2); + nrgb = clamp(nrgb, 0.0, 1.0); + return ((nrgb - 1.0) * hsv.y + 1.0) * hsv.z; +} + +void wire_object_color_get(out vec3 rim_col, out vec3 wire_col) +{ + int flag = int(abs(ObjectInfo.w)); + bool is_selected = (flag & DRW_BASE_SELECTED) != 0; + + if (isObjectColor) { + rim_col = wire_col = ObjectColor.rgb * 0.5; + } + else { + float hue = ObjectInfo.z; + vec3 hsv = vec3(hue, 0.75, 0.8); + rim_col = wire_col = hsv_to_rgb(hsv); + } + + if (is_selected && useColoring) { + /* "Normalize" color. */ + wire_col += 1e-4; /* Avoid division by 0. */ + float brightness = max(wire_col.x, max(wire_col.y, wire_col.z)); + wire_col *= 0.5 / brightness; + rim_col += 0.75; + } + else { + rim_col *= 0.5; + wire_col += 0.5; + } +} + +void main() +{ + vec3 wpos = point_object_to_world(pos); + gl_Position = point_world_to_ndc(wpos); + +#if !(defined(SELECT_EDGES) || defined(USE_GEOM)) + /* Convert to screen position [0..sizeVp]. */ + edgePos = edgeStart = ((gl_Position.xy / gl_Position.w) * 0.5 + 0.5) * sizeViewport.xy; +#endif + + edgeSharpness_g = get_edge_sharpness(wd); + + vec3 rim_col, wire_col; + if (isObjectColor || isRandomColor) { + wire_object_color_get(rim_col, wire_col); + } + else { + wire_color_get(rim_col, wire_col); + } + + vec3 wnor = normalize(normal_object_to_world(nor)); + float facing = dot(wnor, ViewMatrixInverse[2].xyz); + facing = clamp(abs(facing), 0.0, 1.0); + + vec3 final_front_col = mix(rim_col, wire_col, 0.4); + vec3 final_rim_col = mix(rim_col, wire_col, 0.1); + finalColor_g = mix(final_rim_col, final_front_col, facing); + +#ifdef USE_WORLD_CLIP_PLANES + world_clip_planes_calc_clip_distance(wpos); +#endif +} diff --git a/source/blender/draw/engines/select/select_engine.c b/source/blender/draw/engines/select/select_engine.c index abfa57dd218..f6e54eef4ed 100644 --- a/source/blender/draw/engines/select/select_engine.c +++ b/source/blender/draw/engines/select/select_engine.c @@ -147,8 +147,8 @@ static void select_engine_init(void *vedata) /* Create view with depth offset */ stl->g_data->view_faces = (DRWView *)view_default; - stl->g_data->view_edges = DRW_view_create_with_zoffset(draw_ctx->rv3d, 1.0f); - stl->g_data->view_verts = DRW_view_create_with_zoffset(draw_ctx->rv3d, 1.1f); + stl->g_data->view_edges = DRW_view_create_with_zoffset(view_default, draw_ctx->rv3d, 1.0f); + stl->g_data->view_verts = DRW_view_create_with_zoffset(view_default, draw_ctx->rv3d, 1.1f); } } diff --git a/source/blender/draw/engines/workbench/shaders/workbench_effect_fxaa_frag.glsl b/source/blender/draw/engines/workbench/shaders/workbench_effect_fxaa_frag.glsl index 092878e43aa..95ca2c0c297 100644 --- a/source/blender/draw/engines/workbench/shaders/workbench_effect_fxaa_frag.glsl +++ b/source/blender/draw/engines/workbench/shaders/workbench_effect_fxaa_frag.glsl @@ -9,8 +9,6 @@ uniform vec2 invertedViewportSize; void main() { ivec2 texel = ivec2(gl_FragCoord.xy); - float alpha = texelFetch(colorBuffer, texel, 0).a; FragColor = FxaaPixelShader( uvcoordsvar.st, colorBuffer, invertedViewportSize, 1.0, 0.166, 0.0833); - FragColor.a = alpha; } diff --git a/source/blender/draw/engines/workbench/workbench_deferred.c b/source/blender/draw/engines/workbench/workbench_deferred.c index f84bd34545c..e5872dbac50 100644 --- a/source/blender/draw/engines/workbench/workbench_deferred.c +++ b/source/blender/draw/engines/workbench/workbench_deferred.c @@ -74,7 +74,6 @@ static struct { /* TODO(fclem) move everything below to wpd and custom viewlayer data. */ struct GPUTexture *oit_accum_tx; /* ref only, not alloced */ struct GPUTexture *oit_revealage_tx; /* ref only, not alloced */ - struct GPUTexture *ghost_depth_tx; /* ref only, not alloced */ struct GPUTexture *object_id_tx; /* ref only, not alloced */ struct GPUTexture *color_buffer_tx; /* ref only, not alloced */ struct GPUTexture *cavity_buffer_tx; /* ref only, not alloced */ @@ -582,7 +581,7 @@ void workbench_deferred_engine_init(WORKBENCH_Data *vedata) psl->ghost_resolve_pass = DRW_pass_create("Resolve Ghost Depth", DRW_STATE_WRITE_DEPTH | DRW_STATE_DEPTH_ALWAYS); grp = DRW_shgroup_create(e_data.ghost_resolve_sh, psl->ghost_resolve_pass); - DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &e_data.ghost_depth_tx); + DRW_shgroup_uniform_texture_ref(grp, "depthBuffer", &dtxl->depth_in_front); DRW_shgroup_call(grp, DRW_cache_fullscreen_quad_get(), NULL); } @@ -621,23 +620,6 @@ void workbench_deferred_engine_init(WORKBENCH_Data *vedata) } } -static void workbench_setup_ghost_framebuffer(WORKBENCH_FramebufferList *fbl) -{ - const float *viewport_size = DRW_viewport_size_get(); - const int size[2] = {(int)viewport_size[0], (int)viewport_size[1]}; - - e_data.ghost_depth_tx = DRW_texture_pool_query_2d( - size[0], size[1], GPU_DEPTH_COMPONENT24, &draw_engine_workbench_solid); - - GPU_framebuffer_ensure_config(&fbl->ghost_prepass_fb, - { - GPU_ATTACHMENT_TEXTURE(e_data.ghost_depth_tx), - GPU_ATTACHMENT_TEXTURE(e_data.color_buffer_tx), - GPU_ATTACHMENT_TEXTURE(e_data.object_id_tx), - GPU_ATTACHMENT_TEXTURE(e_data.normal_buffer_tx), - }); -} - void workbench_deferred_engine_free(void) { for (int sh_data_index = 0; sh_data_index < ARRAY_SIZE(e_data.sh_data); sh_data_index++) { @@ -1217,8 +1199,33 @@ void workbench_deferred_solid_cache_populate(WORKBENCH_Data *vedata, Object *ob) } } -void workbench_deferred_cache_finish(WORKBENCH_Data *UNUSED(vedata)) +void workbench_deferred_cache_finish(WORKBENCH_Data *vedata) { + WORKBENCH_PassList *psl = vedata->psl; + WORKBENCH_FramebufferList *fbl = vedata->fbl; + + if (GHOST_ENABLED(psl)) { + /* HACK we allocate the infront depth here to avoid the overhead when if is not needed. */ + DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); + + DRW_texture_ensure_fullscreen_2d(&dtxl->depth_in_front, GPU_DEPTH24_STENCIL8, 0); + + GPU_framebuffer_ensure_config( + &dfbl->default_fb, + {GPU_ATTACHMENT_TEXTURE(dtxl->depth), GPU_ATTACHMENT_TEXTURE(dtxl->color)}); + GPU_framebuffer_ensure_config( + &dfbl->in_front_fb, + {GPU_ATTACHMENT_TEXTURE(dtxl->depth_in_front), GPU_ATTACHMENT_TEXTURE(dtxl->color)}); + + GPU_framebuffer_ensure_config(&fbl->ghost_prepass_fb, + { + GPU_ATTACHMENT_TEXTURE(dtxl->depth_in_front), + GPU_ATTACHMENT_TEXTURE(e_data.color_buffer_tx), + GPU_ATTACHMENT_TEXTURE(e_data.object_id_tx), + GPU_ATTACHMENT_TEXTURE(e_data.normal_buffer_tx), + }); + } } void workbench_deferred_draw_background(WORKBENCH_Data *vedata) @@ -1252,6 +1259,7 @@ void workbench_deferred_draw_scene(WORKBENCH_Data *vedata) WORKBENCH_FramebufferList *fbl = vedata->fbl; WORKBENCH_PrivateData *wpd = stl->g_data; DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get(); + DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); if (workbench_is_taa_enabled(wpd)) { workbench_taa_draw_scene_start(vedata); @@ -1262,12 +1270,17 @@ void workbench_deferred_draw_scene(WORKBENCH_Data *vedata) DRW_draw_pass(psl->prepass_pass); DRW_draw_pass(psl->prepass_hair_pass); - if (GHOST_ENABLED(psl)) { - /* meh, late init to not request a depth buffer we won't use. */ - workbench_setup_ghost_framebuffer(fbl); - + if (fbl->ghost_prepass_fb) { GPU_framebuffer_bind(fbl->ghost_prepass_fb); GPU_framebuffer_clear_depth(fbl->ghost_prepass_fb, 1.0f); + } + else if (dtxl->depth_in_front) { + /* TODO(fclem) This clear should be done in a global place. */ + GPU_framebuffer_bind(dfbl->in_front_fb); + GPU_framebuffer_clear_depth(dfbl->in_front_fb, 1.0f); + } + + if (GHOST_ENABLED(psl)) { DRW_draw_pass(psl->ghost_prepass_pass); DRW_draw_pass(psl->ghost_prepass_hair_pass); @@ -1318,6 +1331,7 @@ void workbench_deferred_draw_scene(WORKBENCH_Data *vedata) /* In order to not draw on top of ghost objects, we clear the stencil * to 0xFF and the ghost object to 0x00 and only draw overlays on top if * stencil is not 0. */ + /* TODO(fclem) Remove this hack. */ GPU_framebuffer_bind(dfbl->depth_only_fb); GPU_framebuffer_clear_stencil(dfbl->depth_only_fb, 0xFF); @@ -1335,7 +1349,6 @@ void workbench_deferred_draw_scene(WORKBENCH_Data *vedata) DRW_draw_pass(psl->background_pass); if (OIT_ENABLED(wpd) && !DRW_pass_is_empty(psl->transparent_accum_pass)) { - DefaultTextureList *dtxl = DRW_viewport_texture_list_get(); /* meh, late init to not request buffers we won't use. */ workbench_init_oit_framebuffer(fbl, dtxl); |