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.cc
Sergey Sharybin f17fbf8065 Refactor: Rename Object->obmat to Object->object_to_world
Motivation is to disambiguate on the naming level what the matrix
actually means. It is very easy to understand the meaning backwards,
especially since in Python the name goes the opposite way (it is
called `world_matrix` in the Python API).

It is important to disambiguate the naming without making developers
to look into the comment in the header file (which is also not super
clear either). Additionally, more clear naming facilitates the unit
verification (or, in this case, space validation) when reading an
expression.

This patch calls the matrix `object_to_world` which makes it clear
from the local code what is it exactly going on. This is only done
on DNA level, and a lot of local variables still follow the old
naming.

A DNA rename is setup in a way that there is no change on the file
level, so there should be no regressions at all.

The possibility is to add `_matrix` or `_mat` suffix to the name
to make it explicit that it is a matrix. Although, not sure if it
really helps the readability, or is it something redundant.

Differential Revision: https://developer.blender.org/D16328
2022-11-01 10:48:18 +01:00

501 lines
17 KiB
C++

/* SPDX-License-Identifier: GPL-2.0-or-later
* 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.hh"
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 eStereoViews(v3d->stereo3d_camera);
}
return eStereoViews(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 = nullptr;
GPUTexture *tex = nullptr;
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 == nullptr) {
return nullptr;
}
*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, don't show. */
return nullptr;
}
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 == nullptr) {
BKE_image_release_ibuf(image, ibuf, lock);
iuser->scene = nullptr;
return nullptr;
}
width = ibuf->x;
height = ibuf->y;
tex = BKE_image_get_gpu_texture(image, iuser, ibuf);
BKE_image_release_ibuf(image, ibuf, lock);
iuser->scene = nullptr;
if (tex == nullptr) {
return nullptr;
}
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 == nullptr) {
return nullptr;
}
BKE_movieclip_user_set_frame(&bgpic->cuser, ctime);
tex = BKE_movieclip_get_gpu_texture(clip, &bgpic->cuser);
if (tex == nullptr) {
return nullptr;
}
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 nullptr;
}
*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 = static_cast<LinkData *>(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 = static_cast<Camera *>(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);
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, std::min(bgpic->alpha, 0.999999f)};
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, "isCameraBackground", true);
DRW_shgroup_uniform_bool_copy(grp, "depthSet", true);
DRW_shgroup_uniform_vec4_copy(grp, "ucolor", 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 = nullptr;
Image *ima = static_cast<Image *>(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 != nullptr) {
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, nullptr);
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(ima, size, image_aspect);
copy_m4_m4(mat, ob->object_to_world);
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 = nullptr;
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, "isCameraBackground", false);
DRW_shgroup_uniform_bool_copy(grp, "depthSet", depth_mode != OB_EMPTY_IMAGE_DEPTH_DEFAULT);
DRW_shgroup_uniform_vec4_copy(grp, "ucolor", 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);
}
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(nullptr);
}
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(nullptr);
OVERLAY_image_free_movieclips_textures(vedata);
}