Files
test/source/blender/editors/space_view3d/view3d_draw.cc
Thomas Dinges 6b8bb26c45 EEVEE: Port existing EEVEE shaders and generated materials to use GPUShaderCreateInfo.
Required by Metal backend for efficient shader compilation. EEVEE material
resource binding permutations now controlled via CreateInfo and selected
based on material options. Other existing CreateInfo's also modified to
ensure explicitness for depth-writing mode. Other missing bindings also
addressed to ensure full compliance with the Metal backend.

Authored by Apple: Michael Parkin-White

Ref T96261

Reviewed By: fclem

Differential Revision: https://developer.blender.org/D16243
2022-12-08 21:12:19 +01:00

2618 lines
81 KiB
C++

/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2008 Blender Foundation. All rights reserved. */
/** \file
* \ingroup spview3d
*/
#include <cmath>
#include "BLI_jitter_2d.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_math_vector.hh"
#include "BLI_rect.h"
#include "BLI_string.h"
#include "BLI_string_utils.h"
#include "BLI_threads.h"
#include "BKE_armature.h"
#include "BKE_camera.h"
#include "BKE_collection.h"
#include "BKE_context.h"
#include "BKE_customdata.h"
#include "BKE_global.h"
#include "BKE_image.h"
#include "BKE_key.h"
#include "BKE_layer.h"
#include "BKE_main.h"
#include "BKE_object.h"
#include "BKE_paint.h"
#include "BKE_scene.h"
#include "BKE_studiolight.h"
#include "BKE_unit.h"
#include "BLF_api.h"
#include "BLT_translation.h"
#include "DNA_armature_types.h"
#include "DNA_brush_types.h"
#include "DNA_camera_types.h"
#include "DNA_key_types.h"
#include "DNA_mesh_types.h"
#include "DNA_object_types.h"
#include "DNA_view3d_types.h"
#include "DNA_windowmanager_types.h"
#include "DRW_engine.h"
#include "DRW_select_buffer.h"
#include "ED_gpencil.h"
#include "ED_info.h"
#include "ED_keyframing.h"
#include "ED_screen.h"
#include "ED_screen_types.h"
#include "ED_transform.h"
#include "ED_view3d_offscreen.h"
#include "ED_viewer_path.hh"
#include "DEG_depsgraph_query.h"
#include "GPU_batch.h"
#include "GPU_batch_presets.h"
#include "GPU_framebuffer.h"
#include "GPU_immediate.h"
#include "GPU_immediate_util.h"
#include "GPU_material.h"
#include "GPU_matrix.h"
#include "GPU_state.h"
#include "GPU_viewport.h"
#include "MEM_guardedalloc.h"
#include "UI_interface.h"
#include "UI_resources.h"
#include "RE_engine.h"
#include "WM_api.h"
#include "WM_types.h"
#include "RNA_access.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
#include "view3d_intern.h" /* own include */
using blender::float4;
#define M_GOLDEN_RATIO_CONJUGATE 0.618033988749895f
#define VIEW3D_OVERLAY_LINEHEIGHT (0.9f * U.widget_unit)
/* -------------------------------------------------------------------- */
/** \name General Functions
* \{ */
void ED_view3d_update_viewmat(Depsgraph *depsgraph,
const Scene *scene,
View3D *v3d,
ARegion *region,
const float viewmat[4][4],
const float winmat[4][4],
const rcti *rect,
bool offscreen)
{
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
/* setup window matrices */
if (winmat) {
copy_m4_m4(rv3d->winmat, winmat);
}
else {
view3d_winmatrix_set(depsgraph, region, v3d, rect);
}
/* setup view matrix */
if (viewmat) {
copy_m4_m4(rv3d->viewmat, viewmat);
}
else {
float rect_scale[2];
if (rect) {
rect_scale[0] = float(BLI_rcti_size_x(rect)) / float(region->winx);
rect_scale[1] = float(BLI_rcti_size_y(rect)) / float(region->winy);
}
/* NOTE: calls BKE_object_where_is_calc for camera... */
view3d_viewmatrix_set(depsgraph, scene, v3d, rv3d, rect ? rect_scale : nullptr);
}
/* update utility matrices */
mul_m4_m4m4(rv3d->persmat, rv3d->winmat, rv3d->viewmat);
invert_m4_m4(rv3d->persinv, rv3d->persmat);
invert_m4_m4(rv3d->viewinv, rv3d->viewmat);
/* calculate GLSL view dependent values */
/* store window coordinates scaling/offset */
if (!offscreen && rv3d->persp == RV3D_CAMOB && v3d->camera) {
rctf cameraborder;
ED_view3d_calc_camera_border(scene, depsgraph, region, v3d, rv3d, &cameraborder, false);
rv3d->viewcamtexcofac[0] = float(region->winx) / BLI_rctf_size_x(&cameraborder);
rv3d->viewcamtexcofac[1] = float(region->winy) / BLI_rctf_size_y(&cameraborder);
rv3d->viewcamtexcofac[2] = -rv3d->viewcamtexcofac[0] * cameraborder.xmin / float(region->winx);
rv3d->viewcamtexcofac[3] = -rv3d->viewcamtexcofac[1] * cameraborder.ymin / float(region->winy);
}
else {
rv3d->viewcamtexcofac[0] = rv3d->viewcamtexcofac[1] = 1.0f;
rv3d->viewcamtexcofac[2] = rv3d->viewcamtexcofac[3] = 0.0f;
}
/* Calculate pixel-size factor once, this is used for lights and object-centers. */
{
/* NOTE: '1.0f / len_v3(v1)' replaced 'len_v3(rv3d->viewmat[0])'
* because of float point precision problems at large values T23908. */
float v1[3], v2[3];
float len_px, len_sc;
v1[0] = rv3d->persmat[0][0];
v1[1] = rv3d->persmat[1][0];
v1[2] = rv3d->persmat[2][0];
v2[0] = rv3d->persmat[0][1];
v2[1] = rv3d->persmat[1][1];
v2[2] = rv3d->persmat[2][1];
len_px = 2.0f / sqrtf(min_ff(len_squared_v3(v1), len_squared_v3(v2)));
if (rect) {
len_sc = float(max_ii(BLI_rcti_size_x(rect), BLI_rcti_size_y(rect)));
}
else {
len_sc = float(MAX2(region->winx, region->winy));
}
rv3d->pixsize = len_px / len_sc;
}
}
static void view3d_main_region_setup_view(Depsgraph *depsgraph,
Scene *scene,
View3D *v3d,
ARegion *region,
const float viewmat[4][4],
const float winmat[4][4],
const rcti *rect)
{
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
ED_view3d_update_viewmat(depsgraph, scene, v3d, region, viewmat, winmat, rect, false);
/* set for opengl */
GPU_matrix_projection_set(rv3d->winmat);
GPU_matrix_set(rv3d->viewmat);
}
static void view3d_main_region_setup_offscreen(Depsgraph *depsgraph,
const Scene *scene,
View3D *v3d,
ARegion *region,
const float viewmat[4][4],
const float winmat[4][4])
{
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
ED_view3d_update_viewmat(depsgraph, scene, v3d, region, viewmat, winmat, nullptr, true);
/* set for opengl */
GPU_matrix_projection_set(rv3d->winmat);
GPU_matrix_set(rv3d->viewmat);
}
static bool view3d_stereo3d_active(wmWindow *win,
const Scene *scene,
View3D *v3d,
RegionView3D *rv3d)
{
if ((scene->r.scemode & R_MULTIVIEW) == 0) {
return false;
}
if ((v3d->camera == nullptr) || (v3d->camera->type != OB_CAMERA) || rv3d->persp != RV3D_CAMOB) {
return false;
}
switch (v3d->stereo3d_camera) {
case STEREO_MONO_ID:
return false;
break;
case STEREO_3D_ID:
/* win will be nullptr when calling this from the selection or draw loop. */
if ((win == nullptr) || (WM_stereo3d_enabled(win, true) == false)) {
return false;
}
if (((scene->r.views_format & SCE_VIEWS_FORMAT_MULTIVIEW) != 0) &&
!BKE_scene_multiview_is_stereo3d(&scene->r)) {
return false;
}
break;
/* We always need the stereo calculation for left and right cameras. */
case STEREO_LEFT_ID:
case STEREO_RIGHT_ID:
default:
break;
}
return true;
}
/* setup the view and win matrices for the multiview cameras
*
* unlike view3d_stereo3d_setup_offscreen, when view3d_stereo3d_setup is called
* we have no winmatrix (i.e., projection matrix) defined at that time.
* Since the camera and the camera shift are needed for the winmat calculation
* we do a small hack to replace it temporarily so we don't need to change the
* view3d)main_region_setup_view() code to account for that.
*/
static void view3d_stereo3d_setup(
Depsgraph *depsgraph, Scene *scene, View3D *v3d, ARegion *region, const rcti *rect)
{
bool is_left;
const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME};
const char *viewname;
/* show only left or right camera */
if (v3d->stereo3d_camera != STEREO_3D_ID) {
v3d->multiview_eye = v3d->stereo3d_camera;
}
is_left = v3d->multiview_eye == STEREO_LEFT_ID;
viewname = names[is_left ? STEREO_LEFT_ID : STEREO_RIGHT_ID];
/* update the viewport matrices with the new camera */
if (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D) {
Camera *data, *data_eval;
float viewmat[4][4];
float shiftx;
data = (Camera *)v3d->camera->data;
data_eval = (Camera *)DEG_get_evaluated_id(depsgraph, &data->id);
shiftx = data_eval->shiftx;
BLI_thread_lock(LOCK_VIEW3D);
data_eval->shiftx = BKE_camera_multiview_shift_x(&scene->r, v3d->camera, viewname);
BKE_camera_multiview_view_matrix(&scene->r, v3d->camera, is_left, viewmat);
view3d_main_region_setup_view(depsgraph, scene, v3d, region, viewmat, nullptr, rect);
data_eval->shiftx = shiftx;
BLI_thread_unlock(LOCK_VIEW3D);
}
else { /* SCE_VIEWS_FORMAT_MULTIVIEW */
float viewmat[4][4];
Object *view_ob = v3d->camera;
Object *camera = BKE_camera_multiview_render(scene, v3d->camera, viewname);
BLI_thread_lock(LOCK_VIEW3D);
v3d->camera = camera;
BKE_camera_multiview_view_matrix(&scene->r, camera, false, viewmat);
view3d_main_region_setup_view(depsgraph, scene, v3d, region, viewmat, nullptr, rect);
v3d->camera = view_ob;
BLI_thread_unlock(LOCK_VIEW3D);
}
}
#ifdef WITH_XR_OPENXR
static void view3d_xr_mirror_setup(const wmWindowManager *wm,
Depsgraph *depsgraph,
Scene *scene,
View3D *v3d,
ARegion *region,
const rcti *rect)
{
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
float viewmat[4][4];
const float lens_old = v3d->lens;
if (!WM_xr_session_state_viewer_pose_matrix_info_get(&wm->xr, viewmat, &v3d->lens)) {
/* Can't get info from XR session, use fallback values. */
copy_m4_m4(viewmat, rv3d->viewmat);
v3d->lens = lens_old;
}
view3d_main_region_setup_view(depsgraph, scene, v3d, region, viewmat, nullptr, rect);
/* Set draw flags. */
SET_FLAG_FROM_TEST(v3d->flag2,
(wm->xr.session_settings.draw_flags & V3D_OFSDRAW_XR_SHOW_CONTROLLERS) != 0,
V3D_XR_SHOW_CONTROLLERS);
SET_FLAG_FROM_TEST(v3d->flag2,
(wm->xr.session_settings.draw_flags & V3D_OFSDRAW_XR_SHOW_CUSTOM_OVERLAYS) !=
0,
V3D_XR_SHOW_CUSTOM_OVERLAYS);
/* Hide navigation gizmo since it gets distorted if the view matrix has a scale factor. */
v3d->gizmo_flag |= V3D_GIZMO_HIDE_NAVIGATE;
/* Reset overridden View3D data. */
v3d->lens = lens_old;
}
#endif /* WITH_XR_OPENXR */
void ED_view3d_draw_setup_view(const wmWindowManager *wm,
wmWindow *win,
Depsgraph *depsgraph,
Scene *scene,
ARegion *region,
View3D *v3d,
const float viewmat[4][4],
const float winmat[4][4],
const rcti *rect)
{
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
#ifdef WITH_XR_OPENXR
/* Setup the view matrix. */
if (ED_view3d_is_region_xr_mirror_active(wm, v3d, region)) {
view3d_xr_mirror_setup(wm, depsgraph, scene, v3d, region, rect);
}
else
#endif
if (view3d_stereo3d_active(win, scene, v3d, rv3d)) {
view3d_stereo3d_setup(depsgraph, scene, v3d, region, rect);
}
else {
view3d_main_region_setup_view(depsgraph, scene, v3d, region, viewmat, winmat, rect);
}
#ifndef WITH_XR_OPENXR
UNUSED_VARS(wm);
#endif
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Draw View Border
* \{ */
static void view3d_camera_border(const Scene *scene,
struct Depsgraph *depsgraph,
const ARegion *region,
const View3D *v3d,
const RegionView3D *rv3d,
rctf *r_viewborder,
const bool no_shift,
const bool no_zoom)
{
CameraParams params;
rctf rect_view, rect_camera;
Object *camera_eval = DEG_get_evaluated_object(depsgraph, v3d->camera);
/* get viewport viewplane */
BKE_camera_params_init(&params);
BKE_camera_params_from_view3d(&params, depsgraph, v3d, rv3d);
if (no_zoom) {
params.zoom = 1.0f;
}
BKE_camera_params_compute_viewplane(&params, region->winx, region->winy, 1.0f, 1.0f);
rect_view = params.viewplane;
/* get camera viewplane */
BKE_camera_params_init(&params);
/* fallback for non camera objects */
params.clip_start = v3d->clip_start;
params.clip_end = v3d->clip_end;
BKE_camera_params_from_object(&params, camera_eval);
if (no_shift) {
params.shiftx = 0.0f;
params.shifty = 0.0f;
}
BKE_camera_params_compute_viewplane(
&params, scene->r.xsch, scene->r.ysch, scene->r.xasp, scene->r.yasp);
rect_camera = params.viewplane;
/* get camera border within viewport */
r_viewborder->xmin = ((rect_camera.xmin - rect_view.xmin) / BLI_rctf_size_x(&rect_view)) *
region->winx;
r_viewborder->xmax = ((rect_camera.xmax - rect_view.xmin) / BLI_rctf_size_x(&rect_view)) *
region->winx;
r_viewborder->ymin = ((rect_camera.ymin - rect_view.ymin) / BLI_rctf_size_y(&rect_view)) *
region->winy;
r_viewborder->ymax = ((rect_camera.ymax - rect_view.ymin) / BLI_rctf_size_y(&rect_view)) *
region->winy;
}
void ED_view3d_calc_camera_border_size(const Scene *scene,
Depsgraph *depsgraph,
const ARegion *region,
const View3D *v3d,
const RegionView3D *rv3d,
float r_size[2])
{
rctf viewborder;
view3d_camera_border(scene, depsgraph, region, v3d, rv3d, &viewborder, true, true);
r_size[0] = BLI_rctf_size_x(&viewborder);
r_size[1] = BLI_rctf_size_y(&viewborder);
}
void ED_view3d_calc_camera_border(const Scene *scene,
Depsgraph *depsgraph,
const ARegion *region,
const View3D *v3d,
const RegionView3D *rv3d,
rctf *r_viewborder,
const bool no_shift)
{
view3d_camera_border(scene, depsgraph, region, v3d, rv3d, r_viewborder, no_shift, false);
}
static void drawviewborder_grid3(uint shdr_pos, float x1, float x2, float y1, float y2, float fac)
{
float x3, y3, x4, y4;
x3 = x1 + fac * (x2 - x1);
y3 = y1 + fac * (y2 - y1);
x4 = x1 + (1.0f - fac) * (x2 - x1);
y4 = y1 + (1.0f - fac) * (y2 - y1);
immBegin(GPU_PRIM_LINES, 8);
immVertex2f(shdr_pos, x1, y3);
immVertex2f(shdr_pos, x2, y3);
immVertex2f(shdr_pos, x1, y4);
immVertex2f(shdr_pos, x2, y4);
immVertex2f(shdr_pos, x3, y1);
immVertex2f(shdr_pos, x3, y2);
immVertex2f(shdr_pos, x4, y1);
immVertex2f(shdr_pos, x4, y2);
immEnd();
}
/* harmonious triangle */
static void drawviewborder_triangle(
uint shdr_pos, float x1, float x2, float y1, float y2, const char golden, const char dir)
{
float ofs;
float w = x2 - x1;
float h = y2 - y1;
immBegin(GPU_PRIM_LINES, 6);
if (w > h) {
if (golden) {
ofs = w * (1.0f - M_GOLDEN_RATIO_CONJUGATE);
}
else {
ofs = h * (h / w);
}
if (dir == 'B') {
SWAP(float, y1, y2);
}
immVertex2f(shdr_pos, x1, y1);
immVertex2f(shdr_pos, x2, y2);
immVertex2f(shdr_pos, x2, y1);
immVertex2f(shdr_pos, x1 + (w - ofs), y2);
immVertex2f(shdr_pos, x1, y2);
immVertex2f(shdr_pos, x1 + ofs, y1);
}
else {
if (golden) {
ofs = h * (1.0f - M_GOLDEN_RATIO_CONJUGATE);
}
else {
ofs = w * (w / h);
}
if (dir == 'B') {
SWAP(float, x1, x2);
}
immVertex2f(shdr_pos, x1, y1);
immVertex2f(shdr_pos, x2, y2);
immVertex2f(shdr_pos, x2, y1);
immVertex2f(shdr_pos, x1, y1 + ofs);
immVertex2f(shdr_pos, x1, y2);
immVertex2f(shdr_pos, x2, y1 + (h - ofs));
}
immEnd();
}
static void drawviewborder(Scene *scene, Depsgraph *depsgraph, ARegion *region, View3D *v3d)
{
float x1, x2, y1, y2;
float x1i, x2i, y1i, y2i;
rctf viewborder;
Camera *ca = nullptr;
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
if (v3d->camera == nullptr) {
return;
}
if (v3d->camera->type == OB_CAMERA) {
ca = static_cast<Camera *>(v3d->camera->data);
}
ED_view3d_calc_camera_border(scene, depsgraph, region, v3d, rv3d, &viewborder, false);
/* the offsets */
x1 = viewborder.xmin;
y1 = viewborder.ymin;
x2 = viewborder.xmax;
y2 = viewborder.ymax;
GPU_line_width(1.0f);
/* apply offsets so the real 3D camera shows through */
/* NOTE: quite un-scientific but without this bit extra
* 0.0001 on the lower left the 2D border sometimes
* obscures the 3D camera border */
/* NOTE: with VIEW3D_CAMERA_BORDER_HACK defined this error isn't noticeable
* but keep it here in case we need to remove the workaround */
x1i = int(x1 - 1.0001f);
y1i = int(y1 - 1.0001f);
x2i = int(x2 + (1.0f - 0.0001f));
y2i = int(y2 + (1.0f - 0.0001f));
uint shdr_pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
/* First, solid lines. */
{
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
/* passepartout, specified in camera edit buttons */
if (ca && (ca->flag & CAM_SHOWPASSEPARTOUT) && ca->passepartalpha > 0.000001f) {
const float winx = (region->winx + 1);
const float winy = (region->winy + 1);
float alpha = 1.0f;
if (ca->passepartalpha != 1.0f) {
GPU_blend(GPU_BLEND_ALPHA);
alpha = ca->passepartalpha;
}
immUniformColor4f(0.0f, 0.0f, 0.0f, alpha);
if (x1i > 0.0f) {
immRectf(shdr_pos, 0.0f, winy, x1i, 0.0f);
}
if (x2i < winx) {
immRectf(shdr_pos, x2i, winy, winx, 0.0f);
}
if (y2i < winy) {
immRectf(shdr_pos, x1i, winy, x2i, y2i);
}
if (y2i > 0.0f) {
immRectf(shdr_pos, x1i, y1i, x2i, 0.0f);
}
GPU_blend(GPU_BLEND_NONE);
immUniformThemeColor3(TH_BACK);
imm_draw_box_wire_2d(shdr_pos, x1i, y1i, x2i, y2i);
}
#ifdef VIEW3D_CAMERA_BORDER_HACK
if (view3d_camera_border_hack_test == true) {
immUniformColor3ubv(view3d_camera_border_hack_col);
imm_draw_box_wire_2d(shdr_pos, x1i + 1, y1i + 1, x2i - 1, y2i - 1);
view3d_camera_border_hack_test = false;
}
#endif
immUnbindProgram();
}
/* When overlays are disabled, only show camera outline & passepartout. */
if (v3d->flag2 & V3D_HIDE_OVERLAYS) {
return;
}
/* And now, the dashed lines! */
immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
{
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
immUniform1i("colors_len", 0); /* "simple" mode */
immUniform1f("dash_width", 6.0f);
immUniform1f("udash_factor", 0.5f);
/* outer line not to confuse with object selection */
if (v3d->flag2 & V3D_LOCK_CAMERA) {
immUniformThemeColor(TH_REDALERT);
imm_draw_box_wire_2d(shdr_pos, x1i - 1, y1i - 1, x2i + 1, y2i + 1);
}
immUniformThemeColor3(TH_VIEW_OVERLAY);
imm_draw_box_wire_2d(shdr_pos, x1i, y1i, x2i, y2i);
}
/* Render Border. */
if (scene->r.mode & R_BORDER) {
float x3, y3, x4, y4;
x3 = floorf(x1 + (scene->r.border.xmin * (x2 - x1))) - 1;
y3 = floorf(y1 + (scene->r.border.ymin * (y2 - y1))) - 1;
x4 = floorf(x1 + (scene->r.border.xmax * (x2 - x1))) + (U.pixelsize - 1);
y4 = floorf(y1 + (scene->r.border.ymax * (y2 - y1))) + (U.pixelsize - 1);
immUniformColor3f(1.0f, 0.25f, 0.25f);
imm_draw_box_wire_2d(shdr_pos, x3, y3, x4, y4);
}
/* safety border */
if (ca) {
GPU_blend(GPU_BLEND_ALPHA);
immUniformThemeColorAlpha(TH_VIEW_OVERLAY, 0.75f);
if (ca->dtx & CAM_DTX_CENTER) {
float x3, y3;
x3 = x1 + 0.5f * (x2 - x1);
y3 = y1 + 0.5f * (y2 - y1);
immBegin(GPU_PRIM_LINES, 4);
immVertex2f(shdr_pos, x1, y3);
immVertex2f(shdr_pos, x2, y3);
immVertex2f(shdr_pos, x3, y1);
immVertex2f(shdr_pos, x3, y2);
immEnd();
}
if (ca->dtx & CAM_DTX_CENTER_DIAG) {
immBegin(GPU_PRIM_LINES, 4);
immVertex2f(shdr_pos, x1, y1);
immVertex2f(shdr_pos, x2, y2);
immVertex2f(shdr_pos, x1, y2);
immVertex2f(shdr_pos, x2, y1);
immEnd();
}
if (ca->dtx & CAM_DTX_THIRDS) {
drawviewborder_grid3(shdr_pos, x1, x2, y1, y2, 1.0f / 3.0f);
}
if (ca->dtx & CAM_DTX_GOLDEN) {
drawviewborder_grid3(shdr_pos, x1, x2, y1, y2, 1.0f - M_GOLDEN_RATIO_CONJUGATE);
}
if (ca->dtx & CAM_DTX_GOLDEN_TRI_A) {
drawviewborder_triangle(shdr_pos, x1, x2, y1, y2, 0, 'A');
}
if (ca->dtx & CAM_DTX_GOLDEN_TRI_B) {
drawviewborder_triangle(shdr_pos, x1, x2, y1, y2, 0, 'B');
}
if (ca->dtx & CAM_DTX_HARMONY_TRI_A) {
drawviewborder_triangle(shdr_pos, x1, x2, y1, y2, 1, 'A');
}
if (ca->dtx & CAM_DTX_HARMONY_TRI_B) {
drawviewborder_triangle(shdr_pos, x1, x2, y1, y2, 1, 'B');
}
if (ca->flag & CAM_SHOW_SAFE_MARGINS) {
rctf margins_rect{};
margins_rect.xmin = x1;
margins_rect.xmax = x2;
margins_rect.ymin = y1;
margins_rect.ymax = y2;
UI_draw_safe_areas(
shdr_pos, &margins_rect, scene->safe_areas.title, scene->safe_areas.action);
if (ca->flag & CAM_SHOW_SAFE_CENTER) {
rctf center_rect{};
center_rect.xmin = x1;
center_rect.xmax = x2;
center_rect.ymin = y1;
center_rect.ymax = y2;
UI_draw_safe_areas(shdr_pos,
&center_rect,
scene->safe_areas.title_center,
scene->safe_areas.action_center);
}
}
if (ca->flag & CAM_SHOWSENSOR) {
/* determine sensor fit, and get sensor x/y, for auto fit we
* assume and square sensor and only use sensor_x */
float sizex = scene->r.xsch * scene->r.xasp;
float sizey = scene->r.ysch * scene->r.yasp;
int sensor_fit = BKE_camera_sensor_fit(ca->sensor_fit, sizex, sizey);
float sensor_x = ca->sensor_x;
float sensor_y = (ca->sensor_fit == CAMERA_SENSOR_FIT_AUTO) ? ca->sensor_x : ca->sensor_y;
/* determine sensor plane */
rctf rect;
if (sensor_fit == CAMERA_SENSOR_FIT_HOR) {
float sensor_scale = (x2i - x1i) / sensor_x;
float sensor_height = sensor_scale * sensor_y;
rect.xmin = x1i;
rect.xmax = x2i;
rect.ymin = (y1i + y2i) * 0.5f - sensor_height * 0.5f;
rect.ymax = rect.ymin + sensor_height;
}
else {
float sensor_scale = (y2i - y1i) / sensor_y;
float sensor_width = sensor_scale * sensor_x;
rect.xmin = (x1i + x2i) * 0.5f - sensor_width * 0.5f;
rect.xmax = rect.xmin + sensor_width;
rect.ymin = y1i;
rect.ymax = y2i;
}
/* draw */
immUniformThemeColorShadeAlpha(TH_VIEW_OVERLAY, 100, 255);
/* TODO: Was using:
* `UI_draw_roundbox_4fv(false, rect.xmin, rect.ymin, rect.xmax, rect.ymax, 2.0f, color);`
* We'll probably need a new imm_draw_line_roundbox_dashed or that - though in practice the
* 2.0f round corner effect was nearly not visible anyway. */
imm_draw_box_wire_2d(shdr_pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
}
GPU_blend(GPU_BLEND_NONE);
}
immUnbindProgram();
/* end dashed lines */
/* camera name - draw in highlighted text color */
if (ca && ((v3d->overlay.flag & V3D_OVERLAY_HIDE_TEXT) == 0) && (ca->flag & CAM_SHOWNAME)) {
UI_FontThemeColor(BLF_default(), TH_TEXT_HI);
BLF_draw_default(x1i,
y1i - (0.7f * U.widget_unit),
0.0f,
v3d->camera->id.name + 2,
sizeof(v3d->camera->id.name) - 2);
}
}
static void drawrenderborder(ARegion *region, View3D *v3d)
{
/* use the same program for everything */
uint shdr_pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
GPU_line_width(1.0f);
immBindBuiltinProgram(GPU_SHADER_3D_LINE_DASHED_UNIFORM_COLOR);
float viewport_size[4];
GPU_viewport_size_get_f(viewport_size);
immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
immUniform1i("colors_len", 0); /* "simple" mode */
immUniform4f("color", 1.0f, 0.25f, 0.25f, 1.0f);
immUniform1f("dash_width", 6.0f);
immUniform1f("udash_factor", 0.5f);
imm_draw_box_wire_2d(shdr_pos,
v3d->render_border.xmin * region->winx,
v3d->render_border.ymin * region->winy,
v3d->render_border.xmax * region->winx,
v3d->render_border.ymax * region->winy);
immUnbindProgram();
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Other Elements
* \{ */
float ED_scene_grid_scale(const Scene *scene, const char **r_grid_unit)
{
/* apply units */
if (scene->unit.system) {
const void *usys;
int len;
BKE_unit_system_get(scene->unit.system, B_UNIT_LENGTH, &usys, &len);
if (usys) {
int i = BKE_unit_base_get(usys);
if (r_grid_unit) {
*r_grid_unit = BKE_unit_display_name_get(usys, i);
}
return float(BKE_unit_scalar_get(usys, i)) / scene->unit.scale_length;
}
}
return 1.0f;
}
float ED_view3d_grid_scale(const Scene *scene, View3D *v3d, const char **r_grid_unit)
{
return v3d->grid * ED_scene_grid_scale(scene, r_grid_unit);
}
#define STEPS_LEN 8
void ED_view3d_grid_steps(const Scene *scene,
View3D *v3d,
RegionView3D *rv3d,
float r_grid_steps[STEPS_LEN])
{
const void *usys;
int len;
BKE_unit_system_get(scene->unit.system, B_UNIT_LENGTH, &usys, &len);
float grid_scale = v3d->grid;
BLI_assert(STEPS_LEN >= len);
if (usys) {
if (rv3d->view == RV3D_VIEW_USER) {
/* Skip steps */
len = BKE_unit_base_get(usys) + 1;
}
grid_scale /= scene->unit.scale_length;
int i;
for (i = 0; i < len; i++) {
r_grid_steps[i] = float(BKE_unit_scalar_get(usys, len - 1 - i)) * grid_scale;
}
for (; i < STEPS_LEN; i++) {
/* Fill last slots */
r_grid_steps[i] = 10.0f * r_grid_steps[i - 1];
}
}
else {
if (rv3d->view != RV3D_VIEW_USER) {
/* Allow 3 more subdivisions. */
grid_scale /= powf(v3d->gridsubdiv, 3);
}
int subdiv = 1;
for (int i = 0;; i++) {
r_grid_steps[i] = grid_scale * subdiv;
if (i == STEPS_LEN - 1) {
break;
}
subdiv *= v3d->gridsubdiv;
}
}
}
float ED_view3d_grid_view_scale(Scene *scene,
View3D *v3d,
ARegion *region,
const char **r_grid_unit)
{
float grid_scale;
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
if (!rv3d->is_persp && RV3D_VIEW_IS_AXIS(rv3d->view)) {
/* Decrease the distance between grid snap points depending on zoom. */
float dist = 12.0f / (region->sizex * rv3d->winmat[0][0]);
float grid_steps[STEPS_LEN];
ED_view3d_grid_steps(scene, v3d, rv3d, grid_steps);
/* Skip last item, in case the 'mid_dist' is greater than the largest unit. */
int i;
for (i = 0; i < ARRAY_SIZE(grid_steps) - 1; i++) {
grid_scale = grid_steps[i];
if (grid_scale > dist) {
break;
}
}
if (r_grid_unit) {
const void *usys;
int len;
BKE_unit_system_get(scene->unit.system, B_UNIT_LENGTH, &usys, &len);
if (usys) {
*r_grid_unit = BKE_unit_display_name_get(usys, len - i - 1);
}
}
}
else {
grid_scale = ED_view3d_grid_scale(scene, v3d, r_grid_unit);
}
return grid_scale;
}
#undef STEPS_LEN
static void draw_view_axis(RegionView3D *rv3d, const rcti *rect)
{
const float k = U.rvisize * U.pixelsize; /* axis size */
/* axis alpha offset (rvibright has range 0-10) */
const int bright = -20 * (10 - U.rvibright);
/* Axis center in screen coordinates.
*
* - Unit size offset so small text doesn't draw outside the screen
* - Extra X offset because of the panel expander.
*/
const float startx = rect->xmax - (k + UI_UNIT_X * 1.5);
const float starty = rect->ymax - (k + UI_UNIT_Y);
float axis_pos[3][2];
uchar axis_col[3][4];
int axis_order[3] = {0, 1, 2};
axis_sort_v3(rv3d->viewinv[2], axis_order);
for (int axis_i = 0; axis_i < 3; axis_i++) {
int i = axis_order[axis_i];
/* get position of each axis tip on screen */
float vec[3] = {0.0f};
vec[i] = 1.0f;
mul_qt_v3(rv3d->viewquat, vec);
axis_pos[i][0] = startx + vec[0] * k;
axis_pos[i][1] = starty + vec[1] * k;
/* get color of each axis */
UI_GetThemeColorShade3ubv(TH_AXIS_X + i, bright, axis_col[i]); /* rgb */
axis_col[i][3] = 255 * hypotf(vec[0], vec[1]); /* alpha */
}
/* draw axis lines */
GPU_line_width(2.0f);
GPU_line_smooth(true);
GPU_blend(GPU_BLEND_ALPHA);
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
immBindBuiltinProgram(GPU_SHADER_3D_FLAT_COLOR);
immBegin(GPU_PRIM_LINES, 6);
for (int axis_i = 0; axis_i < 3; axis_i++) {
int i = axis_order[axis_i];
immAttr4ubv(col, axis_col[i]);
immVertex2f(pos, startx, starty);
immAttr4ubv(col, axis_col[i]);
immVertex2fv(pos, axis_pos[i]);
}
immEnd();
immUnbindProgram();
GPU_line_smooth(false);
/* draw axis names */
for (int axis_i = 0; axis_i < 3; axis_i++) {
int i = axis_order[axis_i];
const char axis_text[2] = {char('x' + i), '\0'};
BLF_color4ubv(BLF_default(), axis_col[i]);
BLF_draw_default(axis_pos[i][0] + 2, axis_pos[i][1] + 2, 0.0f, axis_text, 1);
}
}
#ifdef WITH_INPUT_NDOF
/* draw center and axis of rotation for ongoing 3D mouse navigation */
static void draw_rotation_guide(const RegionView3D *rv3d)
{
float o[3]; /* center of rotation */
float end[3]; /* endpoints for drawing */
uchar color[4] = {0, 108, 255, 255}; /* bright blue so it matches device LEDs */
negate_v3_v3(o, rv3d->ofs);
GPU_blend(GPU_BLEND_ALPHA);
GPU_depth_mask(false); /* Don't overwrite the Z-buffer. */
GPUVertFormat *format = immVertexFormat();
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
uint col = GPU_vertformat_attr_add(format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
immBindBuiltinProgram(GPU_SHADER_3D_SMOOTH_COLOR);
if (rv3d->rot_angle != 0.0f) {
/* -- draw rotation axis -- */
float scaled_axis[3];
const float scale = rv3d->dist;
mul_v3_v3fl(scaled_axis, rv3d->rot_axis, scale);
immBegin(GPU_PRIM_LINE_STRIP, 3);
color[3] = 0; /* more transparent toward the ends */
immAttr4ubv(col, color);
add_v3_v3v3(end, o, scaled_axis);
immVertex3fv(pos, end);
# if 0
color[3] = 0.2f + fabsf(rv3d->rot_angle); /* modulate opacity with angle */
/* ^^ neat idea, but angle is frame-rate dependent, so it's usually close to 0.2 */
# endif
color[3] = 127; /* more opaque toward the center */
immAttr4ubv(col, color);
immVertex3fv(pos, o);
color[3] = 0;
immAttr4ubv(col, color);
sub_v3_v3v3(end, o, scaled_axis);
immVertex3fv(pos, end);
immEnd();
/* -- draw ring around rotation center -- */
{
# define ROT_AXIS_DETAIL 13
const float s = 0.05f * scale;
const float step = 2.0f * float(M_PI / ROT_AXIS_DETAIL);
float q[4]; /* rotate ring so it's perpendicular to axis */
const int upright = fabsf(rv3d->rot_axis[2]) >= 0.95f;
if (!upright) {
const float up[3] = {0.0f, 0.0f, 1.0f};
float vis_angle, vis_axis[3];
cross_v3_v3v3(vis_axis, up, rv3d->rot_axis);
vis_angle = acosf(dot_v3v3(up, rv3d->rot_axis));
axis_angle_to_quat(q, vis_axis, vis_angle);
}
immBegin(GPU_PRIM_LINE_LOOP, ROT_AXIS_DETAIL);
color[3] = 63; /* somewhat faint */
immAttr4ubv(col, color);
float angle = 0.0f;
for (int i = 0; i < ROT_AXIS_DETAIL; i++, angle += step) {
float p[3] = {s * cosf(angle), s * sinf(angle), 0.0f};
if (!upright) {
mul_qt_v3(q, p);
}
add_v3_v3(p, o);
immVertex3fv(pos, p);
}
immEnd();
# undef ROT_AXIS_DETAIL
}
color[3] = 255; /* solid dot */
}
else {
color[3] = 127; /* see-through dot */
}
immUnbindProgram();
/* -- draw rotation center -- */
immBindBuiltinProgram(GPU_SHADER_3D_POINT_FIXED_SIZE_VARYING_COLOR);
GPU_point_size(5.0f);
immBegin(GPU_PRIM_POINTS, 1);
immAttr4ubv(col, color);
immVertex3fv(pos, o);
immEnd();
immUnbindProgram();
GPU_blend(GPU_BLEND_NONE);
GPU_depth_mask(true);
}
#endif /* WITH_INPUT_NDOF */
/**
* Render and camera border
*/
static void view3d_draw_border(const bContext *C, ARegion *region)
{
Scene *scene = CTX_data_scene(C);
Depsgraph *depsgraph = CTX_data_expect_evaluated_depsgraph(C);
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
View3D *v3d = CTX_wm_view3d(C);
if (rv3d->persp == RV3D_CAMOB) {
drawviewborder(scene, depsgraph, region, v3d);
}
else if (v3d->flag2 & V3D_RENDER_BORDER) {
drawrenderborder(region, v3d);
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Draw Text & Info
* \{ */
/**
* Draw Info
*/
static void view3d_draw_grease_pencil(const bContext * /*C*/)
{
/* TODO: viewport. */
}
/**
* Viewport Name
*/
static const char *view3d_get_name(View3D *v3d, RegionView3D *rv3d)
{
const char *name = nullptr;
switch (rv3d->view) {
case RV3D_VIEW_FRONT:
if (rv3d->persp == RV3D_ORTHO) {
name = IFACE_("Front Orthographic");
}
else {
name = IFACE_("Front Perspective");
}
break;
case RV3D_VIEW_BACK:
if (rv3d->persp == RV3D_ORTHO) {
name = IFACE_("Back Orthographic");
}
else {
name = IFACE_("Back Perspective");
}
break;
case RV3D_VIEW_TOP:
if (rv3d->persp == RV3D_ORTHO) {
name = IFACE_("Top Orthographic");
}
else {
name = IFACE_("Top Perspective");
}
break;
case RV3D_VIEW_BOTTOM:
if (rv3d->persp == RV3D_ORTHO) {
name = IFACE_("Bottom Orthographic");
}
else {
name = IFACE_("Bottom Perspective");
}
break;
case RV3D_VIEW_RIGHT:
if (rv3d->persp == RV3D_ORTHO) {
name = IFACE_("Right Orthographic");
}
else {
name = IFACE_("Right Perspective");
}
break;
case RV3D_VIEW_LEFT:
if (rv3d->persp == RV3D_ORTHO) {
name = IFACE_("Left Orthographic");
}
else {
name = IFACE_("Left Perspective");
}
break;
default:
if (rv3d->persp == RV3D_CAMOB) {
if ((v3d->camera) && (v3d->camera->type == OB_CAMERA)) {
Camera *cam;
cam = static_cast<Camera *>(v3d->camera->data);
if (cam->type == CAM_PERSP) {
name = IFACE_("Camera Perspective");
}
else if (cam->type == CAM_ORTHO) {
name = IFACE_("Camera Orthographic");
}
else {
BLI_assert(cam->type == CAM_PANO);
name = IFACE_("Camera Panoramic");
}
}
else {
name = IFACE_("Object as Camera");
}
}
else {
name = (rv3d->persp == RV3D_ORTHO) ? IFACE_("User Orthographic") :
IFACE_("User Perspective");
}
}
return name;
}
static void draw_viewport_name(ARegion *region, View3D *v3d, int xoffset, int *yoffset)
{
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
const char *name = view3d_get_name(v3d, rv3d);
const char *name_array[3] = {name, nullptr, nullptr};
int name_array_len = 1;
const int font_id = BLF_default();
/* 6 is the maximum size of the axis roll text. */
/* increase size for unicode languages (Chinese in utf-8...) */
char tmpstr[96 + 6];
BLF_enable(font_id, BLF_SHADOW);
BLF_shadow(font_id, 5, float4{0.0f, 0.0f, 0.0f, 1.0f});
BLF_shadow_offset(font_id, 1, -1);
if (RV3D_VIEW_IS_AXIS(rv3d->view) && (rv3d->view_axis_roll != RV3D_VIEW_AXIS_ROLL_0)) {
const char *axis_roll;
switch (rv3d->view_axis_roll) {
case RV3D_VIEW_AXIS_ROLL_90:
axis_roll = " 90\xC2\xB0";
break;
case RV3D_VIEW_AXIS_ROLL_180:
axis_roll = " 180\xC2\xB0";
break;
default:
axis_roll = " -90\xC2\xB0";
break;
}
name_array[name_array_len++] = axis_roll;
}
if (v3d->localvd) {
name_array[name_array_len++] = IFACE_(" (Local)");
}
/* Indicate that clipping region is enabled. */
if (rv3d->rflag & RV3D_CLIPPING) {
name_array[name_array_len++] = IFACE_(" (Clipped)");
}
if (name_array_len > 1) {
BLI_string_join_array(tmpstr, sizeof(tmpstr), name_array, name_array_len);
name = tmpstr;
}
UI_FontThemeColor(BLF_default(), TH_TEXT_HI);
*yoffset -= VIEW3D_OVERLAY_LINEHEIGHT;
BLF_draw_default(xoffset, *yoffset, 0.0f, name, sizeof(tmpstr));
BLF_disable(font_id, BLF_SHADOW);
}
/**
* Draw info beside axes in top-left corner:
* frame-number, collection, object name, bone name (if available), marker name (if available).
*/
static void draw_selected_name(
const View3D *v3d, Scene *scene, ViewLayer *view_layer, Object *ob, int xoffset, int *yoffset)
{
const int cfra = scene->r.cfra;
const char *msg_pin = " (Pinned)";
const char *msg_sep = " : ";
const int font_id = BLF_default();
char info[300];
char *s = info;
s += BLI_sprintf(s, "(%d)", cfra);
if ((ob == nullptr) || (ob->mode == OB_MODE_OBJECT)) {
BKE_view_layer_synced_ensure(scene, view_layer);
LayerCollection *layer_collection = BKE_view_layer_active_collection_get(view_layer);
s += BLI_sprintf(s,
" %s%s",
BKE_collection_ui_name_get(layer_collection->collection),
(ob == nullptr) ? "" : " |");
}
/* Info can contain:
* - A frame `(7 + 2)`.
* - A collection name `(MAX_NAME + 3)`.
* - 3 object names `(MAX_NAME)`.
* - 2 BREAD_CRUMB_SEPARATOR(s) `(6)`.
* - A SHAPE_KEY_PINNED marker and a trailing '\0' `(9+1)` - translated, so give some room!
* - A marker name `(MAX_NAME + 3)`.
*/
/* get name of marker on current frame (if available) */
const char *markern = BKE_scene_find_marker_name(scene, cfra);
/* check if there is an object */
if (ob) {
*s++ = ' ';
s += BLI_strcpy_rlen(s, ob->id.name + 2);
/* name(s) to display depends on type of object */
if (ob->type == OB_ARMATURE) {
bArmature *arm = static_cast<bArmature *>(ob->data);
/* show name of active bone too (if possible) */
if (arm->edbo) {
if (arm->act_edbone) {
s += BLI_strcpy_rlen(s, msg_sep);
s += BLI_strcpy_rlen(s, arm->act_edbone->name);
}
}
else if (ob->mode & OB_MODE_POSE) {
if (arm->act_bone) {
if (arm->act_bone->layer & arm->layer) {
s += BLI_strcpy_rlen(s, msg_sep);
s += BLI_strcpy_rlen(s, arm->act_bone->name);
}
}
}
}
else if (ELEM(ob->type, OB_MESH, OB_LATTICE, OB_CURVES_LEGACY)) {
/* Try to display active bone and active shape-key too (if they exist). */
if (ob->type == OB_MESH && ob->mode & OB_MODE_WEIGHT_PAINT) {
Object *armobj = BKE_object_pose_armature_get(ob);
if (armobj && armobj->mode & OB_MODE_POSE) {
bArmature *arm = static_cast<bArmature *>(armobj->data);
if (arm->act_bone) {
if (arm->act_bone->layer & arm->layer) {
s += BLI_strcpy_rlen(s, msg_sep);
s += BLI_strcpy_rlen(s, arm->act_bone->name);
}
}
}
}
Key *key = BKE_key_from_object(ob);
if (key) {
KeyBlock *kb = static_cast<KeyBlock *>(BLI_findlink(&key->block, ob->shapenr - 1));
if (kb) {
s += BLI_strcpy_rlen(s, msg_sep);
s += BLI_strcpy_rlen(s, kb->name);
if (ob->shapeflag & OB_SHAPE_LOCK) {
s += BLI_strcpy_rlen(s, IFACE_(msg_pin));
}
}
}
}
/* color depends on whether there is a keyframe */
if (id_frame_has_keyframe(
(ID *)ob, /* BKE_scene_ctime_get(scene) */ float(cfra), ANIMFILTER_KEYS_LOCAL)) {
UI_FontThemeColor(font_id, TH_TIME_KEYFRAME);
}
else if (ED_gpencil_has_keyframe_v3d(scene, ob, cfra)) {
UI_FontThemeColor(font_id, TH_TIME_GP_KEYFRAME);
}
else {
UI_FontThemeColor(font_id, TH_TEXT_HI);
}
}
else {
/* no object */
if (ED_gpencil_has_keyframe_v3d(scene, nullptr, cfra)) {
UI_FontThemeColor(font_id, TH_TIME_GP_KEYFRAME);
}
else {
UI_FontThemeColor(font_id, TH_TEXT_HI);
}
}
if (markern) {
s += BLI_sprintf(s, " <%s>", markern);
}
if (v3d->flag2 & V3D_SHOW_VIEWER) {
if (!BLI_listbase_is_empty(&v3d->viewer_path.path)) {
s += BLI_sprintf(s, "%s", IFACE_(" (Viewer)"));
}
}
BLF_enable(font_id, BLF_SHADOW);
BLF_shadow(font_id, 5, float4{0.0f, 0.0f, 0.0f, 1.0f});
BLF_shadow_offset(font_id, 1, -1);
*yoffset -= VIEW3D_OVERLAY_LINEHEIGHT;
BLF_draw_default(xoffset, *yoffset, 0.0f, info, sizeof(info));
BLF_disable(font_id, BLF_SHADOW);
}
static void draw_grid_unit_name(
Scene *scene, ARegion *region, View3D *v3d, int xoffset, int *yoffset)
{
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
if (!rv3d->is_persp && RV3D_VIEW_IS_AXIS(rv3d->view)) {
const char *grid_unit = nullptr;
int font_id = BLF_default();
ED_view3d_grid_view_scale(scene, v3d, region, &grid_unit);
if (grid_unit) {
char numstr[32] = "";
UI_FontThemeColor(font_id, TH_TEXT_HI);
if (v3d->grid != 1.0f) {
BLI_snprintf(numstr, sizeof(numstr), "%s x %.4g", grid_unit, v3d->grid);
}
*yoffset -= VIEW3D_OVERLAY_LINEHEIGHT;
BLF_enable(font_id, BLF_SHADOW);
BLF_shadow(font_id, 5, float4{0.0f, 0.0f, 0.0f, 1.0f});
BLF_shadow_offset(font_id, 1, -1);
BLF_draw_default(xoffset, *yoffset, 0.0f, numstr[0] ? numstr : grid_unit, sizeof(numstr));
BLF_disable(font_id, BLF_SHADOW);
}
}
}
void view3d_draw_region_info(const bContext *C, ARegion *region)
{
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
View3D *v3d = CTX_wm_view3d(C);
Scene *scene = CTX_data_scene(C);
wmWindowManager *wm = CTX_wm_manager(C);
Main *bmain = CTX_data_main(C);
ViewLayer *view_layer = CTX_data_view_layer(C);
#ifdef WITH_INPUT_NDOF
if ((U.ndof_flag & NDOF_SHOW_GUIDE) && ((RV3D_LOCK_FLAGS(rv3d) & RV3D_LOCK_ROTATION) == 0) &&
(rv3d->persp != RV3D_CAMOB)) {
/* TODO: draw something else (but not this) during fly mode */
draw_rotation_guide(rv3d);
}
#endif
/* correct projection matrix */
ED_region_pixelspace(region);
/* local coordinate visible rect inside region, to accommodate overlapping ui */
const rcti *rect = ED_region_visible_rect(region);
view3d_draw_border(C, region);
view3d_draw_grease_pencil(C);
BLF_batch_draw_begin();
if (v3d->gizmo_flag & (V3D_GIZMO_HIDE | V3D_GIZMO_HIDE_NAVIGATE)) {
/* pass */
}
else {
switch ((eUserpref_MiniAxisType)U.mini_axis_type) {
case USER_MINI_AXIS_TYPE_GIZMO:
/* The gizmo handles its own drawing. */
break;
case USER_MINI_AXIS_TYPE_MINIMAL:
draw_view_axis(rv3d, rect);
case USER_MINI_AXIS_TYPE_NONE:
break;
}
}
int xoffset = rect->xmin + (0.5f * U.widget_unit);
int yoffset = rect->ymax - (0.1f * U.widget_unit);
if ((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0 && (v3d->overlay.flag & V3D_OVERLAY_HIDE_TEXT) == 0) {
if ((U.uiflag & USER_SHOW_FPS) && ED_screen_animation_no_scrub(wm)) {
ED_scene_draw_fps(scene, xoffset, &yoffset);
}
else if (U.uiflag & USER_SHOW_VIEWPORTNAME) {
draw_viewport_name(region, v3d, xoffset, &yoffset);
}
if (U.uiflag & USER_DRAWVIEWINFO) {
BKE_view_layer_synced_ensure(scene, view_layer);
Object *ob = BKE_view_layer_active_object_get(view_layer);
draw_selected_name(v3d, scene, view_layer, ob, xoffset, &yoffset);
}
if (v3d->gridflag & (V3D_SHOW_FLOOR | V3D_SHOW_X | V3D_SHOW_Y | V3D_SHOW_Z)) {
/* draw below the viewport name */
draw_grid_unit_name(scene, region, v3d, xoffset, &yoffset);
}
DRW_draw_region_engine_info(xoffset, &yoffset, VIEW3D_OVERLAY_LINEHEIGHT);
}
if ((v3d->flag2 & V3D_HIDE_OVERLAYS) == 0 && (v3d->overlay.flag & V3D_OVERLAY_STATS)) {
View3D *v3d_local = v3d->localvd ? v3d : nullptr;
ED_info_draw_stats(
bmain, scene, view_layer, v3d_local, xoffset, &yoffset, VIEW3D_OVERLAY_LINEHEIGHT);
}
BLF_batch_draw_end();
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Draw Viewport Contents
* \{ */
static void view3d_draw_view(const bContext *C, ARegion *region)
{
ED_view3d_draw_setup_view(CTX_wm_manager(C),
CTX_wm_window(C),
CTX_data_expect_evaluated_depsgraph(C),
CTX_data_scene(C),
region,
CTX_wm_view3d(C),
nullptr,
nullptr,
nullptr);
/* Only 100% compliant on new spec goes below */
DRW_draw_view(C);
}
RenderEngineType *ED_view3d_engine_type(const Scene *scene, int drawtype)
{
/*
* Temporary viewport draw modes until we have a proper system.
* all modes are done in the draw manager, except external render
* engines like Cycles.
*/
RenderEngineType *type = RE_engines_find(scene->r.engine);
if (drawtype == OB_MATERIAL && (type->flag & RE_USE_EEVEE_VIEWPORT)) {
return RE_engines_find(RE_engine_id_BLENDER_EEVEE);
}
return type;
}
static void view3d_update_viewer_path(const bContext *C)
{
View3D *v3d = CTX_wm_view3d(C);
WorkSpace *workspace = CTX_wm_workspace(C);
/* Always use viewer path from workspace, pinning is not supported currently. */
if (!BKE_viewer_path_equal(&v3d->viewer_path, &workspace->viewer_path)) {
BKE_viewer_path_clear(&v3d->viewer_path);
BKE_viewer_path_copy(&v3d->viewer_path, &workspace->viewer_path);
}
}
void view3d_main_region_draw(const bContext *C, ARegion *region)
{
Main *bmain = CTX_data_main(C);
View3D *v3d = CTX_wm_view3d(C);
view3d_update_viewer_path(C);
view3d_draw_view(C, region);
DRW_cache_free_old_subdiv();
DRW_cache_free_old_batches(bmain);
BKE_image_free_old_gputextures(bmain);
GPU_pass_cache_garbage_collect();
/* No depth test for drawing action zones afterwards. */
GPU_depth_test(GPU_DEPTH_NONE);
v3d->runtime.flag &= ~V3D_RUNTIME_DEPTHBUF_OVERRIDDEN;
/* TODO: Clear cache? */
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Off-screen Drawing
* \{ */
static void view3d_stereo3d_setup_offscreen(Depsgraph *depsgraph,
const Scene *scene,
View3D *v3d,
ARegion *region,
const float winmat[4][4],
const char *viewname)
{
/* update the viewport matrices with the new camera */
if (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D) {
float viewmat[4][4];
const bool is_left = STREQ(viewname, STEREO_LEFT_NAME);
BKE_camera_multiview_view_matrix(&scene->r, v3d->camera, is_left, viewmat);
view3d_main_region_setup_offscreen(depsgraph, scene, v3d, region, viewmat, winmat);
}
else { /* SCE_VIEWS_FORMAT_MULTIVIEW */
float viewmat[4][4];
Object *camera = BKE_camera_multiview_render(scene, v3d->camera, viewname);
BKE_camera_multiview_view_matrix(&scene->r, camera, false, viewmat);
view3d_main_region_setup_offscreen(depsgraph, scene, v3d, region, viewmat, winmat);
}
}
void ED_view3d_draw_offscreen(Depsgraph *depsgraph,
const Scene *scene,
eDrawType drawtype,
View3D *v3d,
ARegion *region,
int winx,
int winy,
const float viewmat[4][4],
const float winmat[4][4],
bool is_image_render,
bool draw_background,
const char *viewname,
const bool do_color_management,
const bool restore_rv3d_mats,
GPUOffScreen *ofs,
GPUViewport *viewport)
{
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
RenderEngineType *engine_type = ED_view3d_engine_type(scene, drawtype);
/* Store `orig` variables. */
struct {
struct bThemeState theme_state;
/* #View3D */
eDrawType v3d_shading_type;
/* #Region */
int region_winx, region_winy;
rcti region_winrct;
/* #RegionView3D */
/**
* Needed so the value won't be left overwritten,
* Without this the #wmPaintCursor can't use the pixel size & view matrices for drawing.
*/
struct RV3DMatrixStore *rv3d_mats;
} orig{};
orig.v3d_shading_type = eDrawType(v3d->shading.type);
orig.region_winx = region->winx;
orig.region_winy = region->winy;
orig.region_winrct = region->winrct;
orig.rv3d_mats = ED_view3d_mats_rv3d_backup(static_cast<RegionView3D *>(region->regiondata));
UI_Theme_Store(&orig.theme_state);
UI_SetTheme(SPACE_VIEW3D, RGN_TYPE_WINDOW);
/* Set temporary new size. */
region->winx = winx;
region->winy = winy;
region->winrct.xmin = 0;
region->winrct.ymin = 0;
region->winrct.xmax = winx;
region->winrct.ymax = winy;
/* There are too many functions inside the draw manager that check the shading type,
* so use a temporary override instead. */
v3d->shading.type = drawtype;
/* Set flags. */
G.f |= G_FLAG_RENDER_VIEWPORT;
{
/* Free images which can have changed on frame-change.
* WARNING(@campbellbarton): can be slow so only free animated images. */
BKE_image_free_anim_gputextures(G.main);
}
GPU_matrix_push_projection();
GPU_matrix_identity_set();
GPU_matrix_push();
GPU_matrix_identity_set();
if ((viewname != nullptr && viewname[0] != '\0') && (viewmat == nullptr) &&
rv3d->persp == RV3D_CAMOB && v3d->camera) {
view3d_stereo3d_setup_offscreen(depsgraph, scene, v3d, region, winmat, viewname);
}
else {
view3d_main_region_setup_offscreen(depsgraph, scene, v3d, region, viewmat, winmat);
}
/* main drawing call */
DRW_draw_render_loop_offscreen(depsgraph,
engine_type,
region,
v3d,
is_image_render,
draw_background,
do_color_management,
ofs,
viewport);
DRW_cache_free_old_subdiv();
GPU_matrix_pop_projection();
GPU_matrix_pop();
/* Restore all `orig` members. */
region->winx = orig.region_winx;
region->winy = orig.region_winy;
region->winrct = orig.region_winrct;
/* Optionally do _not_ restore rv3d matrices (e.g. they are used/stored in the ImBuff for
* reprojection, see texture_paint_image_from_view_exec(). */
if (restore_rv3d_mats) {
ED_view3d_mats_rv3d_restore(static_cast<RegionView3D *>(region->regiondata), orig.rv3d_mats);
}
MEM_freeN(orig.rv3d_mats);
UI_Theme_Restore(&orig.theme_state);
v3d->shading.type = orig.v3d_shading_type;
G.f &= ~G_FLAG_RENDER_VIEWPORT;
}
void ED_view3d_draw_offscreen_simple(Depsgraph *depsgraph,
Scene *scene,
View3DShading *shading_override,
eDrawType drawtype,
int object_type_exclude_viewport_override,
int object_type_exclude_select_override,
int winx,
int winy,
uint draw_flags,
const float viewmat[4][4],
const float winmat[4][4],
float clip_start,
float clip_end,
bool is_xr_surface,
bool is_image_render,
bool draw_background,
const char *viewname,
const bool do_color_management,
GPUOffScreen *ofs,
GPUViewport *viewport)
{
View3D v3d = blender::dna::shallow_zero_initialize();
ARegion ar = {nullptr};
RegionView3D rv3d = {{{0}}};
v3d.regionbase.first = v3d.regionbase.last = &ar;
ar.regiondata = &rv3d;
ar.regiontype = RGN_TYPE_WINDOW;
View3DShading *source_shading_settings = &scene->display.shading;
if (draw_flags & V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS && shading_override != nullptr) {
source_shading_settings = shading_override;
}
memcpy(&v3d.shading, source_shading_settings, sizeof(View3DShading));
v3d.shading.type = drawtype;
if (shading_override) {
/* Pass. */
}
else if (drawtype == OB_MATERIAL) {
v3d.shading.flag = V3D_SHADING_SCENE_WORLD | V3D_SHADING_SCENE_LIGHTS;
}
if ((draw_flags & ~V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS) == V3D_OFSDRAW_NONE) {
v3d.flag2 = V3D_HIDE_OVERLAYS;
}
else {
if (draw_flags & V3D_OFSDRAW_SHOW_ANNOTATION) {
v3d.flag2 |= V3D_SHOW_ANNOTATION;
}
if (draw_flags & V3D_OFSDRAW_SHOW_GRIDFLOOR) {
v3d.gridflag |= V3D_SHOW_FLOOR | V3D_SHOW_X | V3D_SHOW_Y;
v3d.grid = 1.0f;
v3d.gridlines = 16;
v3d.gridsubdiv = 10;
}
if (draw_flags & V3D_OFSDRAW_SHOW_SELECTION) {
v3d.flag |= V3D_SELECT_OUTLINE;
}
if (draw_flags & V3D_OFSDRAW_XR_SHOW_CONTROLLERS) {
v3d.flag2 |= V3D_XR_SHOW_CONTROLLERS;
}
if (draw_flags & V3D_OFSDRAW_XR_SHOW_CUSTOM_OVERLAYS) {
v3d.flag2 |= V3D_XR_SHOW_CUSTOM_OVERLAYS;
}
/* Disable other overlays (set all available _HIDE_ flags). */
v3d.overlay.flag |= V3D_OVERLAY_HIDE_CURSOR | V3D_OVERLAY_HIDE_TEXT |
V3D_OVERLAY_HIDE_MOTION_PATHS | V3D_OVERLAY_HIDE_OBJECT_ORIGINS;
if ((draw_flags & V3D_OFSDRAW_SHOW_OBJECT_EXTRAS) == 0) {
v3d.overlay.flag |= V3D_OVERLAY_HIDE_OBJECT_XTRAS;
}
if ((object_type_exclude_viewport_override & (1 << OB_ARMATURE)) != 0) {
v3d.overlay.flag |= V3D_OVERLAY_HIDE_BONES;
}
v3d.flag |= V3D_HIDE_HELPLINES;
}
if (is_xr_surface) {
v3d.flag |= V3D_XR_SESSION_SURFACE;
}
v3d.object_type_exclude_viewport = object_type_exclude_viewport_override;
v3d.object_type_exclude_select = object_type_exclude_select_override;
rv3d.persp = RV3D_PERSP;
v3d.clip_start = clip_start;
v3d.clip_end = clip_end;
/* Actually not used since we pass in the projection matrix. */
v3d.lens = 0;
ED_view3d_draw_offscreen(depsgraph,
scene,
drawtype,
&v3d,
&ar,
winx,
winy,
viewmat,
winmat,
is_image_render,
draw_background,
viewname,
do_color_management,
true,
ofs,
viewport);
}
ImBuf *ED_view3d_draw_offscreen_imbuf(Depsgraph *depsgraph,
Scene *scene,
eDrawType drawtype,
View3D *v3d,
ARegion *region,
int sizex,
int sizey,
eImBufFlags imbuf_flag,
int alpha_mode,
const char *viewname,
const bool restore_rv3d_mats,
/* output vars */
GPUOffScreen *ofs,
char err_out[256])
{
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
const bool draw_sky = (alpha_mode == R_ADDSKY);
/* view state */
bool is_ortho = false;
float winmat[4][4];
if (ofs && ((GPU_offscreen_width(ofs) != sizex) || (GPU_offscreen_height(ofs) != sizey))) {
/* sizes differ, can't reuse */
ofs = nullptr;
}
GPUFrameBuffer *old_fb = GPU_framebuffer_active_get();
if (old_fb) {
GPU_framebuffer_restore();
}
const bool own_ofs = (ofs == nullptr);
DRW_opengl_context_enable();
if (own_ofs) {
/* bind */
ofs = GPU_offscreen_create(sizex, sizey, true, GPU_RGBA8, err_out);
if (ofs == nullptr) {
DRW_opengl_context_disable();
return nullptr;
}
}
GPU_offscreen_bind(ofs, true);
/* read in pixels & stamp */
ImBuf *ibuf = IMB_allocImBuf(sizex, sizey, 32, imbuf_flag);
/* render 3d view */
if (rv3d->persp == RV3D_CAMOB && v3d->camera) {
CameraParams params;
Object *camera = BKE_camera_multiview_render(scene, v3d->camera, viewname);
const Object *camera_eval = DEG_get_evaluated_object(depsgraph, camera);
BKE_camera_params_init(&params);
/* fallback for non camera objects */
params.clip_start = v3d->clip_start;
params.clip_end = v3d->clip_end;
BKE_camera_params_from_object(&params, camera_eval);
BKE_camera_multiview_params(&scene->r, &params, camera_eval, viewname);
BKE_camera_params_compute_viewplane(&params, sizex, sizey, scene->r.xasp, scene->r.yasp);
BKE_camera_params_compute_matrix(&params);
is_ortho = params.is_ortho;
copy_m4_m4(winmat, params.winmat);
}
else {
rctf viewplane;
float clip_start, clipend;
is_ortho = ED_view3d_viewplane_get(
depsgraph, v3d, rv3d, sizex, sizey, &viewplane, &clip_start, &clipend, nullptr);
if (is_ortho) {
orthographic_m4(winmat,
viewplane.xmin,
viewplane.xmax,
viewplane.ymin,
viewplane.ymax,
-clipend,
clipend);
}
else {
perspective_m4(winmat,
viewplane.xmin,
viewplane.xmax,
viewplane.ymin,
viewplane.ymax,
clip_start,
clipend);
}
}
/* XXX(jbakker): `do_color_management` should be controlled by the caller. Currently when doing a
* viewport render animation and saving to an 8bit file format, color management would be applied
* twice. Once here, and once when saving the saving to disk. In this case the Save As Render
* option cannot be controlled either. But when doing an off-screen render you want to do the
* color management here.
*
* This option was added here to increase the performance for quick view-port preview renders.
* When using workbench the color differences haven't been reported as a bug. But users also use
* the viewport rendering to render Eevee scenes. In the later situation the saved colors are
* totally wrong. */
const bool do_color_management = (ibuf->rect_float == nullptr);
ED_view3d_draw_offscreen(depsgraph,
scene,
drawtype,
v3d,
region,
sizex,
sizey,
nullptr,
winmat,
true,
draw_sky,
viewname,
do_color_management,
restore_rv3d_mats,
ofs,
nullptr);
if (ibuf->rect_float) {
GPU_offscreen_read_pixels(ofs, GPU_DATA_FLOAT, ibuf->rect_float);
}
else if (ibuf->rect) {
GPU_offscreen_read_pixels(ofs, GPU_DATA_UBYTE, ibuf->rect);
}
/* unbind */
GPU_offscreen_unbind(ofs, true);
if (own_ofs) {
GPU_offscreen_free(ofs);
}
DRW_opengl_context_disable();
if (old_fb) {
GPU_framebuffer_bind(old_fb);
}
if (ibuf->rect_float && ibuf->rect) {
IMB_rect_from_float(ibuf);
}
return ibuf;
}
ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Depsgraph *depsgraph,
Scene *scene,
View3DShading *shading_override,
eDrawType drawtype,
Object *camera,
int width,
int height,
eImBufFlags imbuf_flag,
eV3DOffscreenDrawFlag draw_flags,
int alpha_mode,
const char *viewname,
GPUOffScreen *ofs,
char err_out[256])
{
View3D v3d = blender::dna::shallow_zero_initialize();
ARegion region = {nullptr};
RegionView3D rv3d = {{{0}}};
/* connect data */
v3d.regionbase.first = v3d.regionbase.last = &region;
region.regiondata = &rv3d;
region.regiontype = RGN_TYPE_WINDOW;
v3d.camera = camera;
View3DShading *source_shading_settings = &scene->display.shading;
if (draw_flags & V3D_OFSDRAW_OVERRIDE_SCENE_SETTINGS && shading_override != nullptr) {
source_shading_settings = shading_override;
}
memcpy(&v3d.shading, source_shading_settings, sizeof(View3DShading));
if (drawtype == OB_RENDER) {
/* Don't use external engines for preview. Fall back to solid instead of Eevee as rendering
* with Eevee is potentially slow due to compiling shaders and loading textures, and the
* depsgraph may not have been updated to have all the right geometry attributes. */
if (!(BKE_scene_uses_blender_eevee(scene) || BKE_scene_uses_blender_workbench(scene))) {
drawtype = OB_SOLID;
}
}
if (drawtype == OB_MATERIAL) {
v3d.shading.flag = V3D_SHADING_SCENE_WORLD | V3D_SHADING_SCENE_LIGHTS;
v3d.shading.render_pass = SCE_PASS_COMBINED;
}
else if (drawtype == OB_RENDER) {
v3d.shading.flag = V3D_SHADING_SCENE_WORLD_RENDER | V3D_SHADING_SCENE_LIGHTS_RENDER;
v3d.shading.render_pass = SCE_PASS_COMBINED;
}
else if (drawtype == OB_TEXTURE) {
drawtype = OB_SOLID;
v3d.shading.light = V3D_LIGHTING_STUDIO;
v3d.shading.color_type = V3D_SHADING_TEXTURE_COLOR;
}
v3d.shading.type = drawtype;
v3d.flag2 = V3D_HIDE_OVERLAYS;
/* HACK: When rendering gpencil objects this opacity is used to mix vertex colors in when not in
* render mode. */
v3d.overlay.gpencil_vertex_paint_opacity = 1.0f;
if (draw_flags & V3D_OFSDRAW_SHOW_ANNOTATION) {
v3d.flag2 |= V3D_SHOW_ANNOTATION;
}
if (draw_flags & V3D_OFSDRAW_SHOW_GRIDFLOOR) {
v3d.gridflag |= V3D_SHOW_FLOOR | V3D_SHOW_X | V3D_SHOW_Y;
}
v3d.shading.background_type = V3D_SHADING_BACKGROUND_WORLD;
rv3d.persp = RV3D_CAMOB;
copy_m4_m4(rv3d.viewinv, v3d.camera->object_to_world);
normalize_m4(rv3d.viewinv);
invert_m4_m4(rv3d.viewmat, rv3d.viewinv);
{
CameraParams params;
const Object *view_camera_eval = DEG_get_evaluated_object(
depsgraph, BKE_camera_multiview_render(scene, v3d.camera, viewname));
BKE_camera_params_init(&params);
BKE_camera_params_from_object(&params, view_camera_eval);
BKE_camera_multiview_params(&scene->r, &params, view_camera_eval, viewname);
BKE_camera_params_compute_viewplane(&params, width, height, scene->r.xasp, scene->r.yasp);
BKE_camera_params_compute_matrix(&params);
copy_m4_m4(rv3d.winmat, params.winmat);
v3d.clip_start = params.clip_start;
v3d.clip_end = params.clip_end;
v3d.lens = params.lens;
}
mul_m4_m4m4(rv3d.persmat, rv3d.winmat, rv3d.viewmat);
invert_m4_m4(rv3d.persinv, rv3d.viewinv);
return ED_view3d_draw_offscreen_imbuf(depsgraph,
scene,
eDrawType(v3d.shading.type),
&v3d,
&region,
width,
height,
imbuf_flag,
alpha_mode,
viewname,
true,
ofs,
err_out);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Viewport Clipping
* \{ */
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;
}
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);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Back-Draw for Selection
* \{ */
/**
* \note Only use in object mode.
*/
static void validate_object_select_id(struct Depsgraph *depsgraph,
const Scene *scene,
ViewLayer *view_layer,
ARegion *region,
View3D *v3d,
Object *obact)
{
/* TODO: Use a flag in the selection engine itself. */
if (v3d->runtime.flag & V3D_RUNTIME_DEPTHBUF_OVERRIDDEN) {
return;
}
Object *obact_eval = DEG_get_evaluated_object(depsgraph, obact);
BLI_assert(region->regiontype == RGN_TYPE_WINDOW);
UNUSED_VARS_NDEBUG(region);
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->shading.type > OB_WIRE)) {
/* do nothing */
}
else if ((obact_eval && (obact_eval->mode & OB_MODE_PARTICLE_EDIT)) && !XRAY_ENABLED(v3d)) {
/* do nothing */
}
else {
v3d->runtime.flag |= V3D_RUNTIME_DEPTHBUF_OVERRIDDEN;
return;
}
if (obact_eval && ((obact_eval->base_flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) != 0)) {
BKE_view_layer_synced_ensure(scene, view_layer);
Base *base = BKE_view_layer_base_find(view_layer, obact);
DRW_select_buffer_context_create(&base, 1, -1);
}
v3d->runtime.flag |= V3D_RUNTIME_DEPTHBUF_OVERRIDDEN;
}
/* TODO: Creating, attaching texture, and destroying a framebuffer is quite slow.
* Calling this function should be avoided during interactive drawing. */
static void view3d_opengl_read_Z_pixels(GPUViewport *viewport, rcti *rect, void *data)
{
GPUTexture *depth_tx = GPU_viewport_depth_texture(viewport);
GPUFrameBuffer *depth_read_fb = nullptr;
GPU_framebuffer_ensure_config(&depth_read_fb,
{
GPU_ATTACHMENT_TEXTURE(depth_tx),
GPU_ATTACHMENT_NONE,
});
GPU_framebuffer_bind(depth_read_fb);
GPU_framebuffer_read_depth(depth_read_fb,
rect->xmin,
rect->ymin,
BLI_rcti_size_x(rect),
BLI_rcti_size_y(rect),
GPU_DATA_FLOAT,
data);
GPU_framebuffer_restore();
GPU_framebuffer_free(depth_read_fb);
}
void ED_view3d_select_id_validate(ViewContext *vc)
{
validate_object_select_id(
vc->depsgraph, vc->scene, vc->view_layer, vc->region, vc->v3d, vc->obact);
}
int ED_view3d_backbuf_sample_size_clamp(ARegion *region, const float dist)
{
return int(min_ff(ceilf(dist), float(max_ii(region->winx, region->winx))));
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Z-Depth Utilities
* \{ */
void view3d_depths_rect_create(ARegion *region, rcti *rect, ViewDepths *r_d)
{
/* clamp rect by region */
rcti r{};
r.xmin = 0;
r.xmax = region->winx - 1;
r.ymin = 0;
r.ymax = region->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) {
r_d->depths = nullptr;
return;
}
r_d->x = x;
r_d->y = y;
r_d->w = w;
r_d->h = h;
r_d->depths = static_cast<float *>(MEM_mallocN(sizeof(float) * w * h, "View depths Subset"));
{
GPUViewport *viewport = WM_draw_region_get_viewport(region);
view3d_opengl_read_Z_pixels(viewport, rect, r_d->depths);
/* Range is assumed to be this as they are never changed. */
r_d->depth_range[0] = 0.0;
r_d->depth_range[1] = 1.0;
}
}
/* NOTE: with NOUVEAU drivers the #glReadPixels() is very slow. T24339. */
static ViewDepths *view3d_depths_create(ARegion *region)
{
ViewDepths *d = MEM_cnew<ViewDepths>("ViewDepths");
{
GPUViewport *viewport = WM_draw_region_get_viewport(region);
GPUTexture *depth_tx = GPU_viewport_depth_texture(viewport);
uint32_t *int_depths = static_cast<uint32_t *>(
GPU_texture_read(depth_tx, GPU_DATA_UINT_24_8, 0));
d->w = GPU_texture_width(depth_tx);
d->h = GPU_texture_height(depth_tx);
d->depths = (float *)int_depths;
/* Convert in-place. */
int pixel_count = d->w * d->h;
for (int i = 0; i < pixel_count; i++) {
d->depths[i] = (int_depths[i] >> 8u) / float(0xFFFFFF);
}
/* Assumed to be this as they are never changed. */
d->depth_range[0] = 0.0;
d->depth_range[1] = 1.0;
}
return d;
}
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_depth_override(Depsgraph *depsgraph,
ARegion *region,
View3D *v3d,
Object *obact,
eV3DDepthOverrideMode mode,
ViewDepths **r_depths)
{
if (v3d->runtime.flag & V3D_RUNTIME_DEPTHBUF_OVERRIDDEN) {
/* Force redraw if `r_depths` is required. */
if (!r_depths || *r_depths != nullptr) {
return;
}
}
struct bThemeState theme_state;
Scene *scene = DEG_get_evaluated_scene(depsgraph);
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
short flag = v3d->flag;
/* temp set drawtype to solid */
/* Setting these temporarily is not nice */
v3d->flag &= ~V3D_SELECT_OUTLINE;
/* Tools may request depth outside of regular drawing code. */
UI_Theme_Store(&theme_state);
UI_SetTheme(SPACE_VIEW3D, RGN_TYPE_WINDOW);
ED_view3d_draw_setup_view(static_cast<wmWindowManager *>(G_MAIN->wm.first),
nullptr,
depsgraph,
scene,
region,
v3d,
nullptr,
nullptr,
nullptr);
/* get surface depth without bias */
rv3d->rflag |= RV3D_ZOFFSET_DISABLED;
/* Needed in cases the 3D Viewport isn't already setup. */
WM_draw_region_viewport_ensure(region, SPACE_VIEW3D);
WM_draw_region_viewport_bind(region);
GPUViewport *viewport = WM_draw_region_get_viewport(region);
/* When Blender is starting, a click event can trigger a depth test while the viewport is not
* yet available. */
if (viewport != nullptr) {
switch (mode) {
case V3D_DEPTH_NO_GPENCIL:
DRW_draw_depth_loop(
depsgraph, region, v3d, viewport, false, true, (v3d->flag2 & V3D_HIDE_OVERLAYS) == 0);
break;
case V3D_DEPTH_GPENCIL_ONLY:
DRW_draw_depth_loop(depsgraph, region, v3d, viewport, true, false, false);
break;
case V3D_DEPTH_OBJECT_ONLY:
DRW_draw_depth_object(
scene, region, v3d, viewport, DEG_get_evaluated_object(depsgraph, obact));
break;
}
if (r_depths) {
if (*r_depths) {
ED_view3d_depths_free(*r_depths);
}
*r_depths = view3d_depths_create(region);
}
}
WM_draw_region_viewport_unbind(region);
rv3d->rflag &= ~RV3D_ZOFFSET_DISABLED;
v3d->flag = flag;
v3d->runtime.flag |= V3D_RUNTIME_DEPTHBUF_OVERRIDDEN;
UI_Theme_Restore(&theme_state);
}
void ED_view3d_depths_free(ViewDepths *depths)
{
if (depths->depths) {
MEM_freeN(depths->depths);
}
MEM_freeN(depths);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Custom-data Utilities
* \{ */
void ED_view3d_datamask(const bContext *C,
const Scene * /*scene*/,
const View3D *v3d,
CustomData_MeshMasks *r_cddata_masks)
{
if (ELEM(v3d->shading.type, OB_TEXTURE, OB_MATERIAL, OB_RENDER)) {
r_cddata_masks->lmask |= CD_MASK_MLOOPUV | CD_MASK_PROP_BYTE_COLOR;
r_cddata_masks->vmask |= CD_MASK_ORCO | CD_MASK_PROP_COLOR;
}
else if (v3d->shading.type == OB_SOLID) {
if (v3d->shading.color_type == V3D_SHADING_TEXTURE_COLOR) {
r_cddata_masks->lmask |= CD_MASK_MLOOPUV;
}
if (v3d->shading.color_type == V3D_SHADING_VERTEX_COLOR) {
r_cddata_masks->lmask |= CD_MASK_PROP_BYTE_COLOR;
r_cddata_masks->vmask |= CD_MASK_ORCO | CD_MASK_PROP_COLOR;
}
}
if ((CTX_data_mode_enum(C) == CTX_MODE_EDIT_MESH) &&
(v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_WEIGHT)) {
r_cddata_masks->vmask |= CD_MASK_MDEFORMVERT;
}
if (CTX_data_mode_enum(C) == CTX_MODE_SCULPT) {
r_cddata_masks->vmask |= CD_MASK_PAINT_MASK;
}
}
void ED_view3d_screen_datamask(const bContext *C,
const Scene *scene,
const bScreen *screen,
CustomData_MeshMasks *r_cddata_masks)
{
CustomData_MeshMasks_update(r_cddata_masks, &CD_MASK_BAREMESH);
/* Check if we need tfaces & mcols due to view mode. */
LISTBASE_FOREACH (const ScrArea *, area, &screen->areabase) {
if (area->spacetype == SPACE_VIEW3D) {
ED_view3d_datamask(C, scene, static_cast<View3D *>(area->spacedata.first), r_cddata_masks);
}
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Region View Matrix Backup/Restore
* \{ */
/**
* 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 = static_cast<RV3DMatrixStore *>(
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 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;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name FPS Drawing
* \{ */
void ED_scene_draw_fps(const Scene *scene, int xoffset, int *yoffset)
{
ScreenFrameRateInfo *fpsi = static_cast<ScreenFrameRateInfo *>(scene->fps_info);
char printable[16];
if (!fpsi || !fpsi->lredrawtime || !fpsi->redrawtime) {
return;
}
printable[0] = '\0';
/* Doing an average for a more robust calculation. */
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;
fps = fps / tot;
}
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));
}
BLF_enable(font_id, BLF_SHADOW);
BLF_shadow(font_id, 5, float4{0.0f, 0.0f, 0.0f, 1.0f});
BLF_shadow_offset(font_id, 1, -1);
*yoffset -= VIEW3D_OVERLAY_LINEHEIGHT;
BLF_draw_default(xoffset, *yoffset, 0.0f, printable, sizeof(printable));
BLF_disable(font_id, BLF_SHADOW);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Calculate Render Border
* \{ */
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->view_draw);
}
bool ED_view3d_calc_render_border(
const Scene *scene, Depsgraph *depsgraph, View3D *v3d, ARegion *region, rcti *rect)
{
RegionView3D *rv3d = static_cast<RegionView3D *>(region->regiondata);
bool use_border;
/* Test if there is a 3d view rendering. */
if (v3d->shading.type != 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, region, 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 * region->winx;
rect->xmax = v3d->render_border.xmax * region->winx;
rect->ymin = v3d->render_border.ymin * region->winy;
rect->ymax = v3d->render_border.ymax * region->winy;
}
BLI_rcti_translate(rect, region->winrct.xmin, region->winrct.ymin);
BLI_rcti_isect(&region->winrct, rect, rect);
return true;
}
/** \} */