This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/draw/engines/overlay/overlay_image.c
2020-11-12 11:35:31 +11:00

515 lines
17 KiB
C

/*
* 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 "BLI_listbase.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;
DRWView *default_view = (DRWView *)DRW_view_default_get();
pd->view_reference_images = DRW_view_create_with_zoffset(default_view, 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_PREMUL;
DRW_PASS_CREATE(psl->image_background_ps, state);
state = DRW_STATE_WRITE_COLOR | DRW_STATE_BLEND_ALPHA_UNDER_PREMUL;
DRW_PASS_CREATE(psl->image_background_scene_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_PREMUL;
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_PREMUL;
DRW_PASS_CREATE(psl->image_empties_front_ps, state);
DRW_PASS_CREATE(psl->image_foreground_ps, state);
DRW_PASS_CREATE(psl->image_foreground_scene_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 eStereoViews camera_background_images_stereo_eye(const Scene *scene, const View3D *v3d)
{
if ((scene->r.scemode & R_MULTIVIEW) == 0) {
return STEREO_LEFT_ID;
}
if (v3d->stereo3d_camera != STEREO_3D_ID) {
/* show only left or right camera */
return v3d->stereo3d_camera;
}
return v3d->multiview_eye;
}
static void camera_background_images_stereo_setup(const Scene *scene,
const View3D *v3d,
Image *ima,
ImageUser *iuser)
{
if (BKE_image_is_stereo(ima)) {
iuser->flag |= IMA_SHOW_STEREO;
iuser->multiview_eye = camera_background_images_stereo_eye(scene, v3d);
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,
bool *r_use_view_transform)
{
void *lock;
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;
*r_use_view_transform = false;
switch (bgpic->source) {
case CAM_BGIMG_SOURCE_IMAGE:
if (image == NULL) {
return NULL;
}
*r_use_alpha_premult = (image->alpha_mode == IMA_ALPHA_PREMUL);
*r_use_view_transform = (image->flag & IMA_VIEW_AS_RENDER) != 0;
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;
}
camera_background_images_stereo_setup(scene, draw_ctx->v3d, image, iuser);
iuser->scene = draw_ctx->scene;
ImBuf *ibuf = BKE_image_acquire_ibuf(image, iuser, &lock);
if (ibuf == NULL) {
BKE_image_release_ibuf(image, ibuf, lock);
iuser->scene = NULL;
return NULL;
}
width = ibuf->x;
height = ibuf->y;
tex = BKE_image_get_gpu_texture(image, iuser, ibuf);
BKE_image_release_ibuf(image, ibuf, lock);
iuser->scene = NULL;
if (tex == NULL) {
return NULL;
}
aspect_x = bgpic->ima->aspx;
aspect_y = bgpic->ima->aspy;
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 = BKE_movieclip_get_gpu_texture(clip, &bgpic->cuser);
if (tex == NULL) {
return NULL;
}
aspect_x = clip->aspx;
aspect_y = clip->aspy;
*r_use_view_transform = true;
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;
BKE_movieclip_free_gputexture(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];
if (cam->type == CAM_ORTHO) {
mul_v2_fl(translate[3], cam->ortho_scale);
}
/* 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();
const View3D *v3d = draw_ctx->v3d;
const Scene *scene = draw_ctx->scene;
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;
}
const bool stereo_eye = camera_background_images_stereo_eye(scene, v3d) == STEREO_LEFT_ID;
const char *viewname = (stereo_eye == STEREO_LEFT_ID) ? STEREO_RIGHT_NAME : STEREO_LEFT_NAME;
float modelmat[4][4];
BKE_camera_multiview_model_matrix(&scene->r, ob, viewname, modelmat);
LISTBASE_FOREACH (CameraBGImage *, bgpic, &cam->bg_images) {
if (bgpic->flag & CAM_BGIMG_FLAG_DISABLED) {
continue;
}
float aspect = 1.0;
bool use_alpha_premult;
bool use_view_transform = false;
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, &use_view_transform);
if (tex) {
image_camera_background_matrix_get(cam, bgpic, draw_ctx, aspect, mat);
mul_m4_m4m4(mat, modelmat, mat);
const bool is_foreground = (bgpic->flag & CAM_BGIMG_FLAG_FOREGROUND) != 0;
/* Alpha is clamped just below 1.0 to fix background images to interfere with foreground
* images. Without this a background image with 1.0 will be rendered on top of a transparent
* foreground image due to the different blending modes they use. */
const float color_premult_alpha[4] = {1.0f, 1.0f, 1.0f, MIN2(bgpic->alpha, 0.999999)};
DRWPass *pass = is_foreground ? (use_view_transform ? psl->image_foreground_scene_ps :
psl->image_foreground_ps) :
(use_view_transform ? psl->image_background_scene_ps :
psl->image_background_ps);
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", true);
DRW_shgroup_uniform_bool_copy(grp, "depthSet", true);
DRW_shgroup_uniform_vec4_copy(grp, "color", color_premult_alpha);
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) {
ImageUser iuser = *ob->iuser;
camera_background_images_stereo_setup(draw_ctx->scene, draw_ctx->v3d, ima, &iuser);
tex = BKE_image_get_gpu_texture(ima, &iuser, NULL);
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;
if ((ob->dtx & OB_DRAW_IN_FRONT) != 0) {
/* Object In Front overrides image empty depth mode. */
pass = psl->image_empties_front_ps;
}
else {
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, "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_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);
}
/* This function draws images that needs the view transform applied.
* It draws these images directly into the scene color buffer. */
void OVERLAY_image_scene_background_draw(OVERLAY_Data *vedata)
{
OVERLAY_PassList *psl = vedata->psl;
if (DRW_state_is_fbo() && (!DRW_pass_is_empty(psl->image_background_scene_ps) ||
!DRW_pass_is_empty(psl->image_foreground_scene_ps))) {
const DefaultFramebufferList *dfbl = DRW_viewport_framebuffer_list_get();
GPU_framebuffer_bind(dfbl->default_fb);
DRW_draw_pass(psl->image_background_scene_ps);
DRW_draw_pass(psl->image_foreground_scene_ps);
}
}
void OVERLAY_image_background_draw(OVERLAY_Data *vedata)
{
OVERLAY_PassList *psl = vedata->psl;
DRW_draw_pass(psl->image_background_ps);
DRW_draw_pass(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_empties_ps);
DRW_draw_pass(psl->image_empties_blend_ps);
DRW_view_set_active(NULL);
}
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(NULL);
OVERLAY_image_free_movieclips_textures(vedata);
}