This caused a glitch with COW, where forcing edge selection caused the evaluated scene to enable this afterwards. Now pass the selection mode as an argument to the draw function.
1078 lines
28 KiB
C
1078 lines
28 KiB
C
/*
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
*
|
|
* 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.
|
|
*
|
|
* The Original Code is Copyright (C) 2008 Blender Foundation.
|
|
* All rights reserved.
|
|
*
|
|
*
|
|
* Contributor(s): Blender Foundation
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
/** \file blender/editors/space_view3d/view3d_draw_legacy.c
|
|
* \ingroup spview3d
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
|
|
#include "DNA_armature_types.h"
|
|
#include "DNA_camera_types.h"
|
|
#include "DNA_customdata_types.h"
|
|
#include "DNA_object_types.h"
|
|
#include "DNA_group_types.h"
|
|
#include "DNA_mesh_types.h"
|
|
#include "DNA_key_types.h"
|
|
#include "DNA_lamp_types.h"
|
|
#include "DNA_scene_types.h"
|
|
#include "DNA_world_types.h"
|
|
#include "DNA_brush_types.h"
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_math.h"
|
|
#include "BLI_jitter_2d.h"
|
|
#include "BLI_utildefines.h"
|
|
#include "BLI_endian_switch.h"
|
|
#include "BLI_threads.h"
|
|
|
|
#include "BKE_anim.h"
|
|
#include "BKE_camera.h"
|
|
#include "BKE_context.h"
|
|
#include "BKE_customdata.h"
|
|
#include "BKE_DerivedMesh.h"
|
|
#include "BKE_image.h"
|
|
#include "BKE_key.h"
|
|
#include "BKE_layer.h"
|
|
#include "BKE_main.h"
|
|
#include "BKE_object.h"
|
|
#include "BKE_global.h"
|
|
#include "BKE_paint.h"
|
|
#include "BKE_scene.h"
|
|
#include "BKE_screen.h"
|
|
#include "BKE_unit.h"
|
|
#include "BKE_movieclip.h"
|
|
|
|
#include "DEG_depsgraph.h"
|
|
#include "DEG_depsgraph_query.h"
|
|
|
|
#include "IMB_imbuf_types.h"
|
|
#include "IMB_imbuf.h"
|
|
#include "IMB_colormanagement.h"
|
|
|
|
#include "BIF_glutil.h"
|
|
|
|
#include "WM_api.h"
|
|
#include "WM_types.h"
|
|
|
|
#include "BLF_api.h"
|
|
#include "BLT_translation.h"
|
|
|
|
#include "ED_armature.h"
|
|
#include "ED_keyframing.h"
|
|
#include "ED_gpencil.h"
|
|
#include "ED_screen.h"
|
|
#include "ED_space_api.h"
|
|
#include "ED_screen_types.h"
|
|
#include "ED_transform.h"
|
|
|
|
#include "UI_interface.h"
|
|
#include "UI_interface_icons.h"
|
|
#include "UI_resources.h"
|
|
|
|
#include "GPU_draw.h"
|
|
#include "GPU_framebuffer.h"
|
|
#include "GPU_material.h"
|
|
#include "GPU_extensions.h"
|
|
#include "GPU_immediate.h"
|
|
#include "GPU_immediate_util.h"
|
|
#include "GPU_select.h"
|
|
#include "GPU_matrix.h"
|
|
|
|
#include "RE_engine.h"
|
|
|
|
#include "DRW_engine.h"
|
|
|
|
#include "view3d_intern.h" /* own include */
|
|
|
|
/* ********* custom clipping *********** */
|
|
|
|
void ED_view3d_clipping_set(RegionView3D *rv3d)
|
|
{
|
|
double plane[4];
|
|
const unsigned int tot = (rv3d->viewlock & RV3D_BOXCLIP) ? 4 : 6;
|
|
|
|
for (unsigned a = 0; a < tot; a++) {
|
|
copy_v4db_v4fl(plane, rv3d->clip[a]);
|
|
glClipPlane(GL_CLIP_PLANE0 + a, plane);
|
|
glEnable(GL_CLIP_PLANE0 + a);
|
|
}
|
|
}
|
|
|
|
/* use these to temp disable/enable clipping when 'rv3d->rflag & RV3D_CLIPPING' is set */
|
|
void ED_view3d_clipping_disable(void)
|
|
{
|
|
for (unsigned a = 0; a < 6; a++) {
|
|
glDisable(GL_CLIP_PLANE0 + a);
|
|
}
|
|
}
|
|
void ED_view3d_clipping_enable(void)
|
|
{
|
|
for (unsigned a = 0; a < 6; a++) {
|
|
glEnable(GL_CLIP_PLANE0 + a);
|
|
}
|
|
}
|
|
|
|
static bool view3d_clipping_test(const float co[3], const float clip[6][4])
|
|
{
|
|
if (plane_point_side_v3(clip[0], co) > 0.0f)
|
|
if (plane_point_side_v3(clip[1], co) > 0.0f)
|
|
if (plane_point_side_v3(clip[2], co) > 0.0f)
|
|
if (plane_point_side_v3(clip[3], co) > 0.0f)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
/* for 'local' ED_view3d_clipping_local must run first
|
|
* then all comparisons can be done in localspace */
|
|
bool ED_view3d_clipping_test(const RegionView3D *rv3d, const float co[3], const bool is_local)
|
|
{
|
|
return view3d_clipping_test(co, is_local ? rv3d->clip_local : rv3d->clip);
|
|
}
|
|
|
|
/* *********************** backdraw for selection *************** */
|
|
|
|
static void backdrawview3d(
|
|
struct Depsgraph *depsgraph, Scene *scene,
|
|
ARegion *ar, View3D *v3d,
|
|
Object *obact, Object *obedit,
|
|
short select_mode)
|
|
{
|
|
RegionView3D *rv3d = ar->regiondata;
|
|
Scene *scene_eval = (Scene *)DEG_get_evaluated_id(depsgraph, &scene->id);
|
|
Object *obact_eval = DEG_get_evaluated_object(depsgraph, obact);
|
|
|
|
BLI_assert(ar->regiontype == RGN_TYPE_WINDOW);
|
|
|
|
if (obact_eval && (obact_eval->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT) ||
|
|
BKE_paint_select_face_test(obact_eval)))
|
|
{
|
|
/* do nothing */
|
|
}
|
|
/* texture paint mode sampling */
|
|
else if (obact_eval && (obact_eval->mode & OB_MODE_TEXTURE_PAINT) &&
|
|
(v3d->drawtype > OB_WIRE))
|
|
{
|
|
/* do nothing */
|
|
}
|
|
else if ((obact_eval && (obact_eval->mode & OB_MODE_PARTICLE_EDIT)) &&
|
|
V3D_IS_ZBUF(v3d))
|
|
{
|
|
/* do nothing */
|
|
}
|
|
else if ((obedit && (obedit->mode & OB_MODE_EDIT)) &&
|
|
V3D_IS_ZBUF(v3d))
|
|
{
|
|
/* do nothing */
|
|
}
|
|
else {
|
|
v3d->flag &= ~V3D_INVALID_BACKBUF;
|
|
return;
|
|
}
|
|
|
|
if (!(v3d->flag & V3D_INVALID_BACKBUF))
|
|
return;
|
|
|
|
#if 0
|
|
if (test) {
|
|
if (qtest()) {
|
|
addafterqueue(ar->win, BACKBUFDRAW, 1);
|
|
return;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (v3d->drawtype > OB_WIRE) v3d->zbuf = true;
|
|
|
|
/* dithering and AA break color coding, so disable */
|
|
glDisable(GL_DITHER);
|
|
|
|
if (false) {
|
|
/* for multisample we use an offscreen FBO. multisample drawing can fail
|
|
* with color coded selection drawing, and reading back depths from such
|
|
* a buffer can also cause a few seconds freeze on OS X / NVidia.
|
|
*
|
|
* NOTE: code is no longer used now, but offscreen drawing is likely
|
|
* what we will always want to do for the new viewport. */
|
|
int w = BLI_rcti_size_x(&ar->winrct);
|
|
int h = BLI_rcti_size_y(&ar->winrct);
|
|
char error[256];
|
|
|
|
if (rv3d->gpuoffscreen) {
|
|
if (GPU_offscreen_width(rv3d->gpuoffscreen) != w ||
|
|
GPU_offscreen_height(rv3d->gpuoffscreen) != h)
|
|
{
|
|
GPU_offscreen_free(rv3d->gpuoffscreen);
|
|
rv3d->gpuoffscreen = NULL;
|
|
}
|
|
}
|
|
|
|
if (!rv3d->gpuoffscreen) {
|
|
rv3d->gpuoffscreen = GPU_offscreen_create(w, h, 0, true, false, error);
|
|
|
|
if (!rv3d->gpuoffscreen)
|
|
fprintf(stderr, "Failed to create offscreen selection buffer for multisample: %s\n", error);
|
|
}
|
|
}
|
|
|
|
if (rv3d->gpuoffscreen)
|
|
GPU_offscreen_bind(rv3d->gpuoffscreen, true);
|
|
else
|
|
glScissor(ar->winrct.xmin, ar->winrct.ymin, BLI_rcti_size_x(&ar->winrct), BLI_rcti_size_y(&ar->winrct));
|
|
|
|
glClearColor(0.0, 0.0, 0.0, 0.0);
|
|
if (v3d->zbuf) {
|
|
glEnable(GL_DEPTH_TEST);
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
}
|
|
else {
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
glDisable(GL_DEPTH_TEST);
|
|
}
|
|
|
|
if (rv3d->rflag & RV3D_CLIPPING)
|
|
ED_view3d_clipping_set(rv3d);
|
|
|
|
G.f |= G_BACKBUFSEL;
|
|
|
|
if (obact_eval && ((obact_eval->base_flag & BASE_VISIBLED) != 0)) {
|
|
draw_object_backbufsel(depsgraph, scene_eval, v3d, rv3d, obact_eval, select_mode);
|
|
}
|
|
|
|
if (rv3d->gpuoffscreen)
|
|
GPU_offscreen_unbind(rv3d->gpuoffscreen, true);
|
|
|
|
v3d->flag &= ~V3D_INVALID_BACKBUF;
|
|
|
|
G.f &= ~G_BACKBUFSEL;
|
|
v3d->zbuf = false;
|
|
glDisable(GL_DEPTH_TEST);
|
|
glEnable(GL_DITHER);
|
|
|
|
if (rv3d->rflag & RV3D_CLIPPING)
|
|
ED_view3d_clipping_disable();
|
|
}
|
|
|
|
void view3d_opengl_read_pixels(ARegion *ar, int x, int y, int w, int h, int format, int type, void *data)
|
|
{
|
|
RegionView3D *rv3d = ar->regiondata;
|
|
|
|
if (rv3d->gpuoffscreen) {
|
|
GPU_offscreen_bind(rv3d->gpuoffscreen, true);
|
|
glReadBuffer(GL_COLOR_ATTACHMENT0);
|
|
glReadPixels(x, y, w, h, format, type, data);
|
|
GPU_offscreen_unbind(rv3d->gpuoffscreen, true);
|
|
}
|
|
else {
|
|
glReadPixels(ar->winrct.xmin + x, ar->winrct.ymin + y, w, h, format, type, data);
|
|
}
|
|
}
|
|
|
|
/* XXX depth reading exception, for code not using gpu offscreen */
|
|
static void view3d_opengl_read_Z_pixels(ARegion *ar, int x, int y, int w, int h, int format, int type, void *data)
|
|
{
|
|
glReadPixels(ar->winrct.xmin + x, ar->winrct.ymin + y, w, h, format, type, data);
|
|
}
|
|
|
|
void ED_view3d_backbuf_validate_with_select_mode(ViewContext *vc, short select_mode)
|
|
{
|
|
if (vc->v3d->flag & V3D_INVALID_BACKBUF) {
|
|
backdrawview3d(vc->depsgraph, vc->scene, vc->ar, vc->v3d, vc->obact, vc->obedit, select_mode);
|
|
}
|
|
}
|
|
|
|
void ED_view3d_backbuf_validate(ViewContext *vc)
|
|
{
|
|
ED_view3d_backbuf_validate_with_select_mode(vc, -1);
|
|
}
|
|
|
|
/**
|
|
* allow for small values [0.5 - 2.5],
|
|
* and large values, FLT_MAX by clamping by the area size
|
|
*/
|
|
int ED_view3d_backbuf_sample_size_clamp(ARegion *ar, const float dist)
|
|
{
|
|
return (int)min_ff(ceilf(dist), (float)max_ii(ar->winx, ar->winx));
|
|
}
|
|
|
|
/* samples a single pixel (copied from vpaint) */
|
|
unsigned int ED_view3d_backbuf_sample(
|
|
ViewContext *vc, int x, int y)
|
|
{
|
|
if (x >= vc->ar->winx || y >= vc->ar->winy) {
|
|
return 0;
|
|
}
|
|
|
|
ED_view3d_backbuf_validate(vc);
|
|
|
|
unsigned int col;
|
|
view3d_opengl_read_pixels(vc->ar, x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &col);
|
|
glReadBuffer(GL_BACK);
|
|
|
|
if (ENDIAN_ORDER == B_ENDIAN) {
|
|
BLI_endian_switch_uint32(&col);
|
|
}
|
|
|
|
return GPU_select_to_index(col);
|
|
}
|
|
|
|
/* reads full rect, converts indices */
|
|
ImBuf *ED_view3d_backbuf_read(
|
|
ViewContext *vc, int xmin, int ymin, int xmax, int ymax)
|
|
{
|
|
/* clip */
|
|
const rcti clip = {
|
|
max_ii(xmin, 0), min_ii(xmax, vc->ar->winx - 1),
|
|
max_ii(ymin, 0), min_ii(ymax, vc->ar->winy - 1)};
|
|
const int size_clip[2] = {
|
|
BLI_rcti_size_x(&clip) + 1,
|
|
BLI_rcti_size_y(&clip) + 1};
|
|
|
|
if (UNLIKELY((clip.xmin > clip.xmax) ||
|
|
(clip.ymin > clip.ymax)))
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
ImBuf *ibuf_clip = IMB_allocImBuf(size_clip[0], size_clip[1], 32, IB_rect);
|
|
|
|
ED_view3d_backbuf_validate(vc);
|
|
|
|
view3d_opengl_read_pixels(vc->ar, clip.xmin, clip.ymin, size_clip[0], size_clip[1], GL_RGBA, GL_UNSIGNED_BYTE, ibuf_clip->rect);
|
|
|
|
glReadBuffer(GL_BACK);
|
|
|
|
if (ENDIAN_ORDER == B_ENDIAN) {
|
|
IMB_convert_rgba_to_abgr(ibuf_clip);
|
|
}
|
|
|
|
GPU_select_to_index_array(ibuf_clip->rect, size_clip[0] * size_clip[1]);
|
|
|
|
if ((clip.xmin == xmin) &&
|
|
(clip.xmax == xmax) &&
|
|
(clip.ymin == ymin) &&
|
|
(clip.ymax == ymax))
|
|
{
|
|
return ibuf_clip;
|
|
}
|
|
else {
|
|
/* put clipped result into a non-clipped buffer */
|
|
const int size[2] = {
|
|
(xmax - xmin + 1),
|
|
(ymax - ymin + 1)};
|
|
|
|
ImBuf *ibuf_full = IMB_allocImBuf(size[0], size[1], 32, IB_rect);
|
|
|
|
IMB_rectcpy(
|
|
ibuf_full, ibuf_clip,
|
|
clip.xmin - xmin, clip.ymin - ymin,
|
|
0, 0,
|
|
size_clip[0], size_clip[1]);
|
|
IMB_freeImBuf(ibuf_clip);
|
|
return ibuf_full;
|
|
}
|
|
}
|
|
|
|
/* smart function to sample a rect spiralling outside, nice for backbuf selection */
|
|
unsigned int ED_view3d_backbuf_sample_rect(
|
|
ViewContext *vc, const int mval[2], int size,
|
|
unsigned int min, unsigned int max, float *r_dist)
|
|
{
|
|
int dirvec[4][2];
|
|
|
|
const int amount = (size - 1) / 2;
|
|
|
|
const int minx = mval[0] - (amount + 1);
|
|
const int miny = mval[1] - (amount + 1);
|
|
ImBuf *buf = ED_view3d_backbuf_read(vc, minx, miny, minx + size - 1, miny + size - 1);
|
|
if (!buf) return 0;
|
|
|
|
unsigned index = 0;
|
|
int rc = 0;
|
|
|
|
dirvec[0][0] = 1; dirvec[0][1] = 0;
|
|
dirvec[1][0] = 0; dirvec[1][1] = -size;
|
|
dirvec[2][0] = -1; dirvec[2][1] = 0;
|
|
dirvec[3][0] = 0; dirvec[3][1] = size;
|
|
|
|
const unsigned *bufmin = buf->rect;
|
|
const unsigned *tbuf = buf->rect;
|
|
const unsigned *bufmax = buf->rect + size * size;
|
|
tbuf += amount * size + amount;
|
|
|
|
for (int nr = 1; nr <= size; nr++) {
|
|
for (int a = 0; a < 2; a++) {
|
|
for (int b = 0; b < nr; b++) {
|
|
if (*tbuf && *tbuf >= min && *tbuf < max) {
|
|
/* we got a hit */
|
|
|
|
/* get x,y pixel coords from the offset
|
|
* (manhatten distance in keeping with other screen-based selection) */
|
|
*r_dist = (float)(
|
|
abs(((int)(tbuf - buf->rect) % size) - (size / 2)) +
|
|
abs(((int)(tbuf - buf->rect) / size) - (size / 2)));
|
|
|
|
/* indices start at 1 here */
|
|
index = (*tbuf - min) + 1;
|
|
goto exit;
|
|
}
|
|
|
|
tbuf += (dirvec[rc][0] + dirvec[rc][1]);
|
|
|
|
if (tbuf < bufmin || tbuf >= bufmax) {
|
|
goto exit;
|
|
}
|
|
}
|
|
rc++;
|
|
rc &= 3;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
IMB_freeImBuf(buf);
|
|
return index;
|
|
}
|
|
|
|
|
|
/* ************************************************************* */
|
|
|
|
static void view3d_stereo_bgpic_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 void view3d_draw_bgpic(Scene *scene, Depsgraph *depsgraph,
|
|
ARegion *ar, View3D *v3d,
|
|
const bool do_foreground, const bool do_camera_frame)
|
|
{
|
|
RegionView3D *rv3d = ar->regiondata;
|
|
int fg_flag = do_foreground ? CAM_BGIMG_FLAG_FOREGROUND : 0;
|
|
if (v3d->camera == NULL || v3d->camera->type != OB_CAMERA) {
|
|
return;
|
|
}
|
|
Camera *cam = v3d->camera->data;
|
|
|
|
for (CameraBGImage *bgpic = cam->bg_images.first; bgpic; bgpic = bgpic->next) {
|
|
bgpic->iuser.scene = scene; /* Needed for render results. */
|
|
|
|
if ((bgpic->flag & CAM_BGIMG_FLAG_FOREGROUND) != fg_flag)
|
|
continue;
|
|
|
|
{
|
|
float image_aspect[2];
|
|
float x1, y1, x2, y2, centx, centy;
|
|
|
|
void *lock;
|
|
|
|
Image *ima = NULL;
|
|
|
|
/* disable individual images */
|
|
if ((bgpic->flag & CAM_BGIMG_FLAG_DISABLED))
|
|
continue;
|
|
|
|
ImBuf *ibuf = NULL;
|
|
ImBuf *freeibuf = NULL;
|
|
ImBuf *releaseibuf = NULL;
|
|
if (bgpic->source == CAM_BGIMG_SOURCE_IMAGE) {
|
|
ima = bgpic->ima;
|
|
if (ima == NULL)
|
|
continue;
|
|
BKE_image_user_frame_calc(&bgpic->iuser, CFRA, 0);
|
|
if (ima->source == IMA_SRC_SEQUENCE && !(bgpic->iuser.flag & IMA_USER_FRAME_IN_RANGE)) {
|
|
ibuf = NULL; /* frame is out of range, dont show */
|
|
}
|
|
else {
|
|
view3d_stereo_bgpic_setup(scene, v3d, ima, &bgpic->iuser);
|
|
ibuf = BKE_image_acquire_ibuf(ima, &bgpic->iuser, &lock);
|
|
releaseibuf = ibuf;
|
|
}
|
|
|
|
image_aspect[0] = ima->aspx;
|
|
image_aspect[1] = ima->aspy;
|
|
}
|
|
else if (bgpic->source == CAM_BGIMG_SOURCE_MOVIE) {
|
|
/* TODO: skip drawing when out of frame range (as image sequences do above) */
|
|
MovieClip *clip = NULL;
|
|
|
|
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)
|
|
continue;
|
|
|
|
BKE_movieclip_user_set_frame(&bgpic->cuser, CFRA);
|
|
ibuf = BKE_movieclip_get_ibuf(clip, &bgpic->cuser);
|
|
|
|
image_aspect[0] = clip->aspx;
|
|
image_aspect[1] = clip->aspy;
|
|
|
|
/* working with ibuf from image and clip has got different workflow now.
|
|
* ibuf acquired from clip is referenced by cache system and should
|
|
* be dereferenced after usage. */
|
|
freeibuf = ibuf;
|
|
}
|
|
else {
|
|
/* perhaps when loading future files... */
|
|
BLI_assert(0);
|
|
copy_v2_fl(image_aspect, 1.0f);
|
|
}
|
|
|
|
if (ibuf == NULL)
|
|
continue;
|
|
|
|
if ((ibuf->rect == NULL && ibuf->rect_float == NULL) || ibuf->channels != 4) { /* invalid image format */
|
|
if (freeibuf)
|
|
IMB_freeImBuf(freeibuf);
|
|
if (releaseibuf)
|
|
BKE_image_release_ibuf(ima, releaseibuf, lock);
|
|
|
|
continue;
|
|
}
|
|
|
|
if (ibuf->rect == NULL)
|
|
IMB_rect_from_float(ibuf);
|
|
|
|
BLI_assert(rv3d->persp == RV3D_CAMOB);
|
|
{
|
|
if (do_camera_frame) {
|
|
rctf vb;
|
|
ED_view3d_calc_camera_border(scene, depsgraph, ar, v3d, rv3d, &vb, false);
|
|
x1 = vb.xmin;
|
|
y1 = vb.ymin;
|
|
x2 = vb.xmax;
|
|
y2 = vb.ymax;
|
|
}
|
|
else {
|
|
x1 = ar->winrct.xmin;
|
|
y1 = ar->winrct.ymin;
|
|
x2 = ar->winrct.xmax;
|
|
y2 = ar->winrct.ymax;
|
|
}
|
|
|
|
/* apply offset last - camera offset is different to offset in blender units */
|
|
/* so this has some sane way of working - this matches camera's shift _exactly_ */
|
|
{
|
|
const float max_dim = max_ff(x2 - x1, y2 - y1);
|
|
const float xof_scale = bgpic->offset[0] * max_dim;
|
|
const float yof_scale = bgpic->offset[1] * max_dim;
|
|
|
|
x1 += xof_scale;
|
|
y1 += yof_scale;
|
|
x2 += xof_scale;
|
|
y2 += yof_scale;
|
|
}
|
|
|
|
centx = (x1 + x2) * 0.5f;
|
|
centy = (y1 + y2) * 0.5f;
|
|
|
|
/* aspect correction */
|
|
if (bgpic->flag & CAM_BGIMG_FLAG_CAMERA_ASPECT) {
|
|
/* apply aspect from clip */
|
|
const float w_src = ibuf->x * image_aspect[0];
|
|
const float h_src = ibuf->y * image_aspect[1];
|
|
|
|
/* destination aspect is already applied from the camera frame */
|
|
const float w_dst = x1 - x2;
|
|
const float h_dst = y1 - y2;
|
|
|
|
const float asp_src = w_src / h_src;
|
|
const float asp_dst = w_dst / h_dst;
|
|
|
|
if (fabsf(asp_src - asp_dst) >= FLT_EPSILON) {
|
|
if ((asp_src > asp_dst) == ((bgpic->flag & CAM_BGIMG_FLAG_CAMERA_CROP) != 0)) {
|
|
/* fit X */
|
|
const float div = asp_src / asp_dst;
|
|
x1 = ((x1 - centx) * div) + centx;
|
|
x2 = ((x2 - centx) * div) + centx;
|
|
}
|
|
else {
|
|
/* fit Y */
|
|
const float div = asp_dst / asp_src;
|
|
y1 = ((y1 - centy) * div) + centy;
|
|
y2 = ((y2 - centy) * div) + centy;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* complete clip? */
|
|
rctf clip_rect;
|
|
BLI_rctf_init(&clip_rect, x1, x2, y1, y2);
|
|
if (bgpic->rotation) {
|
|
BLI_rctf_rotate_expand(&clip_rect, &clip_rect, bgpic->rotation);
|
|
}
|
|
|
|
if (clip_rect.xmax < 0 || clip_rect.ymax < 0 || clip_rect.xmin > ar->winx || clip_rect.ymin > ar->winy) {
|
|
if (freeibuf)
|
|
IMB_freeImBuf(freeibuf);
|
|
if (releaseibuf)
|
|
BKE_image_release_ibuf(ima, releaseibuf, lock);
|
|
|
|
continue;
|
|
}
|
|
|
|
float zoomx = (x2 - x1) / ibuf->x;
|
|
float zoomy = (y2 - y1) / ibuf->y;
|
|
|
|
/* for some reason; zoomlevels down refuses to use GL_ALPHA_SCALE */
|
|
if (zoomx < 1.0f || zoomy < 1.0f) {
|
|
float tzoom = min_ff(zoomx, zoomy);
|
|
int mip = 0;
|
|
|
|
if ((ibuf->userflags & IB_MIPMAP_INVALID) != 0) {
|
|
IMB_remakemipmap(ibuf, 0);
|
|
ibuf->userflags &= ~IB_MIPMAP_INVALID;
|
|
}
|
|
else if (ibuf->mipmap[0] == NULL)
|
|
IMB_makemipmap(ibuf, 0);
|
|
|
|
while (tzoom < 1.0f && mip < 8 && ibuf->mipmap[mip]) {
|
|
tzoom *= 2.0f;
|
|
zoomx *= 2.0f;
|
|
zoomy *= 2.0f;
|
|
mip++;
|
|
}
|
|
if (mip > 0)
|
|
ibuf = ibuf->mipmap[mip - 1];
|
|
}
|
|
|
|
if (v3d->zbuf) glDisable(GL_DEPTH_TEST);
|
|
glDepthMask(GL_FALSE);
|
|
|
|
glEnable(GL_BLEND);
|
|
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
gpuPushProjectionMatrix();
|
|
gpuPushMatrix();
|
|
ED_region_pixelspace(ar);
|
|
|
|
gpuTranslate2f(centx, centy);
|
|
gpuScaleUniform(bgpic->scale);
|
|
gpuRotate2D(RAD2DEGF(-bgpic->rotation));
|
|
|
|
if (bgpic->flag & CAM_BGIMG_FLAG_FLIP_X) {
|
|
zoomx *= -1.0f;
|
|
x1 = x2;
|
|
}
|
|
if (bgpic->flag & CAM_BGIMG_FLAG_FLIP_Y) {
|
|
zoomy *= -1.0f;
|
|
y1 = y2;
|
|
}
|
|
|
|
float col[4] = {1.0f, 1.0f, 1.0f, bgpic->alpha};
|
|
IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_COLOR);
|
|
immDrawPixelsTex(&state, x1 - centx, y1 - centy, ibuf->x, ibuf->y, GL_RGBA, GL_UNSIGNED_BYTE, GL_LINEAR, ibuf->rect,
|
|
zoomx, zoomy, col);
|
|
|
|
gpuPopProjectionMatrix();
|
|
gpuPopMatrix();
|
|
|
|
glDisable(GL_BLEND);
|
|
|
|
glDepthMask(GL_TRUE);
|
|
if (v3d->zbuf) glEnable(GL_DEPTH_TEST);
|
|
|
|
if (freeibuf)
|
|
IMB_freeImBuf(freeibuf);
|
|
if (releaseibuf)
|
|
BKE_image_release_ibuf(ima, releaseibuf, lock);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ED_view3d_draw_bgpic_test(
|
|
Scene *scene, Depsgraph *depsgraph,
|
|
ARegion *ar, View3D *v3d,
|
|
const bool do_foreground, const bool do_camera_frame)
|
|
{
|
|
RegionView3D *rv3d = ar->regiondata;
|
|
|
|
if ((rv3d->persp == RV3D_CAMOB) && v3d->camera && (v3d->camera->type == OB_CAMERA)) {
|
|
Camera *cam = v3d->camera->data;
|
|
if ((cam->flag & CAM_SHOW_BG_IMAGE) == 0) {
|
|
return;
|
|
}
|
|
}
|
|
else {
|
|
return;
|
|
}
|
|
|
|
/* disabled - mango request, since footage /w only render is quite useful
|
|
* and this option is easy to disable all background images at once */
|
|
#if 0
|
|
if (v3d->flag2 & V3D_RENDER_OVERRIDE)
|
|
return;
|
|
#endif
|
|
|
|
if ((rv3d->view == RV3D_VIEW_USER) || (rv3d->persp != RV3D_ORTHO)) {
|
|
if (rv3d->persp == RV3D_CAMOB) {
|
|
view3d_draw_bgpic(scene, depsgraph, ar, v3d, do_foreground, do_camera_frame);
|
|
}
|
|
}
|
|
else {
|
|
view3d_draw_bgpic(scene, depsgraph, ar, v3d, do_foreground, do_camera_frame);
|
|
}
|
|
}
|
|
|
|
/* *********************** */
|
|
|
|
/* XXX warning, not using gpu offscreen here */
|
|
void view3d_update_depths_rect(ARegion *ar, ViewDepths *d, rcti *rect)
|
|
{
|
|
/* clamp rect by region */
|
|
rcti r = {
|
|
.xmin = 0,
|
|
.xmax = ar->winx - 1,
|
|
.ymin = 0,
|
|
.ymax = ar->winy - 1
|
|
};
|
|
|
|
/* Constrain rect to depth bounds */
|
|
BLI_rcti_isect(&r, rect, rect);
|
|
|
|
/* assign values to compare with the ViewDepths */
|
|
int x = rect->xmin;
|
|
int y = rect->ymin;
|
|
|
|
int w = BLI_rcti_size_x(rect);
|
|
int h = BLI_rcti_size_y(rect);
|
|
|
|
if (w <= 0 || h <= 0) {
|
|
if (d->depths)
|
|
MEM_freeN(d->depths);
|
|
d->depths = NULL;
|
|
|
|
d->damaged = false;
|
|
}
|
|
else if (d->w != w ||
|
|
d->h != h ||
|
|
d->x != x ||
|
|
d->y != y ||
|
|
d->depths == NULL
|
|
)
|
|
{
|
|
d->x = x;
|
|
d->y = y;
|
|
d->w = w;
|
|
d->h = h;
|
|
|
|
if (d->depths)
|
|
MEM_freeN(d->depths);
|
|
|
|
d->depths = MEM_mallocN(sizeof(float) * d->w * d->h, "View depths Subset");
|
|
|
|
d->damaged = true;
|
|
}
|
|
|
|
if (d->damaged) {
|
|
/* XXX using special function here, it doesn't use the gpu offscreen system */
|
|
view3d_opengl_read_Z_pixels(ar, d->x, d->y, d->w, d->h, GL_DEPTH_COMPONENT, GL_FLOAT, d->depths);
|
|
glGetDoublev(GL_DEPTH_RANGE, d->depth_range);
|
|
d->damaged = false;
|
|
}
|
|
}
|
|
|
|
/* note, with nouveau drivers the glReadPixels() is very slow. [#24339] */
|
|
void ED_view3d_depth_update(ARegion *ar)
|
|
{
|
|
RegionView3D *rv3d = ar->regiondata;
|
|
|
|
/* Create storage for, and, if necessary, copy depth buffer */
|
|
if (!rv3d->depths) rv3d->depths = MEM_callocN(sizeof(ViewDepths), "ViewDepths");
|
|
if (rv3d->depths) {
|
|
ViewDepths *d = rv3d->depths;
|
|
if (d->w != ar->winx ||
|
|
d->h != ar->winy ||
|
|
!d->depths)
|
|
{
|
|
d->w = ar->winx;
|
|
d->h = ar->winy;
|
|
if (d->depths)
|
|
MEM_freeN(d->depths);
|
|
d->depths = MEM_mallocN(sizeof(float) * d->w * d->h, "View depths");
|
|
d->damaged = true;
|
|
}
|
|
|
|
if (d->damaged) {
|
|
view3d_opengl_read_pixels(ar, 0, 0, d->w, d->h, GL_DEPTH_COMPONENT, GL_FLOAT, d->depths);
|
|
glGetDoublev(GL_DEPTH_RANGE, d->depth_range);
|
|
|
|
d->damaged = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* utility function to find the closest Z value, use for autodepth */
|
|
float view3d_depth_near(ViewDepths *d)
|
|
{
|
|
/* convert to float for comparisons */
|
|
const float near = (float)d->depth_range[0];
|
|
const float far_real = (float)d->depth_range[1];
|
|
float far = far_real;
|
|
|
|
const float *depths = d->depths;
|
|
float depth = FLT_MAX;
|
|
int i = (int)d->w * (int)d->h; /* cast to avoid short overflow */
|
|
|
|
/* far is both the starting 'far' value
|
|
* and the closest value found. */
|
|
while (i--) {
|
|
depth = *depths++;
|
|
if ((depth < far) && (depth > near)) {
|
|
far = depth;
|
|
}
|
|
}
|
|
|
|
return far == far_real ? FLT_MAX : far;
|
|
}
|
|
|
|
void ED_view3d_draw_depth_gpencil(
|
|
Depsgraph *depsgraph, Scene *scene, ARegion *ar, View3D *v3d)
|
|
{
|
|
ViewLayer *view_layer = DEG_get_evaluated_view_layer(depsgraph);
|
|
bool zbuf = v3d->zbuf;
|
|
|
|
/* Setup view matrix. */
|
|
ED_view3d_draw_setup_view(NULL, depsgraph, scene, ar, v3d, NULL, NULL, NULL);
|
|
|
|
glClear(GL_DEPTH_BUFFER_BIT);
|
|
|
|
v3d->zbuf = true;
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
if (v3d->flag2 & V3D_SHOW_GPENCIL) {
|
|
ED_gpencil_draw_view3d(NULL, scene, view_layer, depsgraph, v3d, ar, true);
|
|
}
|
|
|
|
v3d->zbuf = zbuf;
|
|
if (!zbuf) glDisable(GL_DEPTH_TEST);
|
|
}
|
|
|
|
/* *********************** customdata **************** */
|
|
|
|
CustomDataMask ED_view3d_datamask(const Scene *UNUSED(scene), const View3D *v3d)
|
|
{
|
|
CustomDataMask mask = 0;
|
|
const int drawtype = view3d_effective_drawtype(v3d);
|
|
|
|
if (ELEM(drawtype, OB_TEXTURE, OB_MATERIAL) ||
|
|
((drawtype == OB_SOLID) && (v3d->flag2 & V3D_SOLID_TEX)))
|
|
{
|
|
mask |= CD_MASK_MLOOPUV | CD_MASK_MLOOPCOL;
|
|
|
|
if (drawtype == OB_MATERIAL)
|
|
mask |= CD_MASK_ORCO;
|
|
}
|
|
|
|
return mask;
|
|
}
|
|
|
|
/* goes over all modes and view3d settings */
|
|
CustomDataMask ED_view3d_screen_datamask(const Scene *scene, const bScreen *screen)
|
|
{
|
|
CustomDataMask mask = CD_MASK_BAREMESH;
|
|
|
|
/* check if we need tfaces & mcols due to view mode */
|
|
for (const ScrArea *sa = screen->areabase.first; sa; sa = sa->next) {
|
|
if (sa->spacetype == SPACE_VIEW3D) {
|
|
mask |= ED_view3d_datamask(scene, sa->spacedata.first);
|
|
}
|
|
}
|
|
|
|
return mask;
|
|
}
|
|
|
|
/**
|
|
* Store values from #RegionView3D, set when drawing.
|
|
* This is needed when we draw with to a viewport using a different matrix (offscreen drawing for example).
|
|
*
|
|
* Values set by #ED_view3d_update_viewmat should be handled here.
|
|
*/
|
|
struct RV3DMatrixStore {
|
|
float winmat[4][4];
|
|
float viewmat[4][4];
|
|
float viewinv[4][4];
|
|
float persmat[4][4];
|
|
float persinv[4][4];
|
|
float viewcamtexcofac[4];
|
|
float pixsize;
|
|
};
|
|
|
|
struct RV3DMatrixStore *ED_view3d_mats_rv3d_backup(struct RegionView3D *rv3d)
|
|
{
|
|
struct RV3DMatrixStore *rv3dmat = MEM_mallocN(sizeof(*rv3dmat), __func__);
|
|
copy_m4_m4(rv3dmat->winmat, rv3d->winmat);
|
|
copy_m4_m4(rv3dmat->viewmat, rv3d->viewmat);
|
|
copy_m4_m4(rv3dmat->persmat, rv3d->persmat);
|
|
copy_m4_m4(rv3dmat->persinv, rv3d->persinv);
|
|
copy_m4_m4(rv3dmat->viewinv, rv3d->viewinv);
|
|
copy_v4_v4(rv3dmat->viewcamtexcofac, rv3d->viewcamtexcofac);
|
|
rv3dmat->pixsize = rv3d->pixsize;
|
|
return (void *)rv3dmat;
|
|
}
|
|
|
|
void ED_view3d_mats_rv3d_restore(struct RegionView3D *rv3d, struct RV3DMatrixStore *rv3dmat_pt)
|
|
{
|
|
struct RV3DMatrixStore *rv3dmat = rv3dmat_pt;
|
|
copy_m4_m4(rv3d->winmat, rv3dmat->winmat);
|
|
copy_m4_m4(rv3d->viewmat, rv3dmat->viewmat);
|
|
copy_m4_m4(rv3d->persmat, rv3dmat->persmat);
|
|
copy_m4_m4(rv3d->persinv, rv3dmat->persinv);
|
|
copy_m4_m4(rv3d->viewinv, rv3dmat->viewinv);
|
|
copy_v4_v4(rv3d->viewcamtexcofac, rv3dmat->viewcamtexcofac);
|
|
rv3d->pixsize = rv3dmat->pixsize;
|
|
}
|
|
|
|
/**
|
|
* \note The info that this uses is updated in #ED_refresh_viewport_fps,
|
|
* which currently gets called during #SCREEN_OT_animation_step.
|
|
*/
|
|
void ED_scene_draw_fps(Scene *scene, const rcti *rect)
|
|
{
|
|
ScreenFrameRateInfo *fpsi = scene->fps_info;
|
|
char printable[16];
|
|
|
|
if (!fpsi || !fpsi->lredrawtime || !fpsi->redrawtime)
|
|
return;
|
|
|
|
printable[0] = '\0';
|
|
|
|
#if 0
|
|
/* this is too simple, better do an average */
|
|
fps = (float)(1.0 / (fpsi->lredrawtime - fpsi->redrawtime))
|
|
#else
|
|
fpsi->redrawtimes_fps[fpsi->redrawtime_index] = (float)(1.0 / (fpsi->lredrawtime - fpsi->redrawtime));
|
|
|
|
float fps = 0.0f;
|
|
int tot = 0;
|
|
for (int i = 0; i < REDRAW_FRAME_AVERAGE; i++) {
|
|
if (fpsi->redrawtimes_fps[i]) {
|
|
fps += fpsi->redrawtimes_fps[i];
|
|
tot++;
|
|
}
|
|
}
|
|
if (tot) {
|
|
fpsi->redrawtime_index = (fpsi->redrawtime_index + 1) % REDRAW_FRAME_AVERAGE;
|
|
|
|
//fpsi->redrawtime_index++;
|
|
//if (fpsi->redrawtime >= REDRAW_FRAME_AVERAGE)
|
|
// fpsi->redrawtime = 0;
|
|
|
|
fps = fps / tot;
|
|
}
|
|
#endif
|
|
|
|
const int font_id = BLF_default();
|
|
|
|
/* is this more than half a frame behind? */
|
|
if (fps + 0.5f < (float)(FPS)) {
|
|
UI_FontThemeColor(font_id, TH_REDALERT);
|
|
BLI_snprintf(printable, sizeof(printable), IFACE_("fps: %.2f"), fps);
|
|
}
|
|
else {
|
|
UI_FontThemeColor(font_id, TH_TEXT_HI);
|
|
BLI_snprintf(printable, sizeof(printable), IFACE_("fps: %i"), (int)(fps + 0.5f));
|
|
}
|
|
|
|
#ifdef WITH_INTERNATIONAL
|
|
BLF_draw_default(rect->xmin + U.widget_unit, rect->ymax - U.widget_unit, 0.0f, printable, sizeof(printable));
|
|
#else
|
|
BLF_draw_default_ascii(rect->xmin + U.widget_unit, rect->ymax - U.widget_unit, 0.0f, printable, sizeof(printable));
|
|
#endif
|
|
}
|
|
|
|
static bool view3d_main_region_do_render_draw(const Scene *scene)
|
|
{
|
|
RenderEngineType *type = RE_engines_find(scene->r.engine);
|
|
return (type && type->view_update && type->render_to_view);
|
|
}
|
|
|
|
bool ED_view3d_calc_render_border(const Scene *scene, Depsgraph *depsgraph, View3D *v3d, ARegion *ar, rcti *rect)
|
|
{
|
|
RegionView3D *rv3d = ar->regiondata;
|
|
bool use_border;
|
|
|
|
/* test if there is a 3d view rendering */
|
|
if (v3d->drawtype != OB_RENDER || !view3d_main_region_do_render_draw(scene))
|
|
return false;
|
|
|
|
/* test if there is a border render */
|
|
if (rv3d->persp == RV3D_CAMOB)
|
|
use_border = (scene->r.mode & R_BORDER) != 0;
|
|
else
|
|
use_border = (v3d->flag2 & V3D_RENDER_BORDER) != 0;
|
|
|
|
if (!use_border)
|
|
return false;
|
|
|
|
/* compute border */
|
|
if (rv3d->persp == RV3D_CAMOB) {
|
|
rctf viewborder;
|
|
ED_view3d_calc_camera_border(scene, depsgraph, ar, v3d, rv3d, &viewborder, false);
|
|
|
|
rect->xmin = viewborder.xmin + scene->r.border.xmin * BLI_rctf_size_x(&viewborder);
|
|
rect->ymin = viewborder.ymin + scene->r.border.ymin * BLI_rctf_size_y(&viewborder);
|
|
rect->xmax = viewborder.xmin + scene->r.border.xmax * BLI_rctf_size_x(&viewborder);
|
|
rect->ymax = viewborder.ymin + scene->r.border.ymax * BLI_rctf_size_y(&viewborder);
|
|
}
|
|
else {
|
|
rect->xmin = v3d->render_border.xmin * ar->winx;
|
|
rect->xmax = v3d->render_border.xmax * ar->winx;
|
|
rect->ymin = v3d->render_border.ymin * ar->winy;
|
|
rect->ymax = v3d->render_border.ymax * ar->winy;
|
|
}
|
|
|
|
BLI_rcti_translate(rect, ar->winrct.xmin, ar->winrct.ymin);
|
|
BLI_rcti_isect(&ar->winrct, rect, rect);
|
|
|
|
return true;
|
|
}
|