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/overlay/overlay_image.c | |
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/overlay/overlay_image.c')
-rw-r--r-- | source/blender/draw/engines/overlay/overlay_image.c | 470 |
1 files changed, 470 insertions, 0 deletions
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); +} |