There are a couple of functions that create rna pointers. For example `RNA_main_pointer_create` and `RNA_pointer_create`. Currently, those take an output parameter `r_ptr` as last argument. This patch changes it so that the functions actually return a` PointerRNA` instead of using the output parameters. This has a few benefits: * Output parameters should only be used when there is an actual benefit. Otherwise, one should default to returning the value. * It's simpler to use the API in the large majority of cases (note that this patch reduces the number of lines of code). * It allows the `PointerRNA` to be const on the call-site, if that is desired. No performance regression has been measured in production files. If one of these functions happened to be called in a hot loop where there is a regression, the solution should be to use an inline function there which allows the compiler to optimize it even better. Pull Request: https://projects.blender.org/blender/blender/pulls/111976
408 lines
12 KiB
C++
408 lines
12 KiB
C++
/* SPDX-FileCopyrightText: 2015 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
/** \file
|
|
* \ingroup wm
|
|
*/
|
|
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
|
|
#include "DNA_listBase.h"
|
|
|
|
#include "RNA_access.hh"
|
|
#include "RNA_prototypes.h"
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_utildefines.h"
|
|
|
|
#include "BKE_context.h"
|
|
#include "BKE_global.h"
|
|
#include "BKE_report.h"
|
|
|
|
#include "GHOST_C-api.h"
|
|
|
|
#include "ED_screen.hh"
|
|
|
|
#include "GPU_capabilities.h"
|
|
#include "GPU_immediate.h"
|
|
#include "GPU_texture.h"
|
|
#include "GPU_viewport.h"
|
|
|
|
#include "WM_api.hh"
|
|
#include "WM_types.hh"
|
|
#include "wm.hh"
|
|
#include "wm_draw.hh"
|
|
#include "wm_window.hh"
|
|
|
|
#include "UI_interface.hh"
|
|
#include "UI_resources.hh"
|
|
|
|
void wm_stereo3d_draw_sidebyside(wmWindow *win, int view)
|
|
{
|
|
bool cross_eyed = (win->stereo3d_format->flag & S3D_SIDEBYSIDE_CROSSEYED) != 0;
|
|
|
|
GPUVertFormat *format = immVertexFormat();
|
|
uint texcoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
|
|
immBindBuiltinProgram(GPU_SHADER_3D_IMAGE);
|
|
|
|
int soffx = WM_window_pixels_x(win) * 0.5f;
|
|
if (view == STEREO_LEFT_ID) {
|
|
if (!cross_eyed) {
|
|
soffx = 0;
|
|
}
|
|
}
|
|
else { /* #RIGHT_LEFT_ID */
|
|
if (cross_eyed) {
|
|
soffx = 0;
|
|
}
|
|
}
|
|
|
|
const int sizex = WM_window_pixels_x(win);
|
|
const int sizey = WM_window_pixels_y(win);
|
|
|
|
/* wmOrtho for the screen has this same offset */
|
|
const float halfx = GLA_PIXEL_OFS / sizex;
|
|
const float halfy = GLA_PIXEL_OFS / sizex;
|
|
|
|
/* Texture is already bound to GL_TEXTURE0 unit. */
|
|
|
|
immBegin(GPU_PRIM_TRI_FAN, 4);
|
|
|
|
immAttr2f(texcoord, halfx, halfy);
|
|
immVertex2f(pos, soffx, 0.0f);
|
|
|
|
immAttr2f(texcoord, 1.0f + halfx, halfy);
|
|
immVertex2f(pos, soffx + (sizex * 0.5f), 0.0f);
|
|
|
|
immAttr2f(texcoord, 1.0f + halfx, 1.0f + halfy);
|
|
immVertex2f(pos, soffx + (sizex * 0.5f), sizey);
|
|
|
|
immAttr2f(texcoord, halfx, 1.0f + halfy);
|
|
immVertex2f(pos, soffx, sizey);
|
|
|
|
immEnd();
|
|
|
|
immUnbindProgram();
|
|
}
|
|
|
|
void wm_stereo3d_draw_topbottom(wmWindow *win, int view)
|
|
{
|
|
GPUVertFormat *format = immVertexFormat();
|
|
uint texcoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
|
|
|
|
immBindBuiltinProgram(GPU_SHADER_3D_IMAGE);
|
|
|
|
int soffy;
|
|
if (view == STEREO_LEFT_ID) {
|
|
soffy = WM_window_pixels_y(win) * 0.5f;
|
|
}
|
|
else { /* STEREO_RIGHT_ID */
|
|
soffy = 0;
|
|
}
|
|
|
|
const int sizex = WM_window_pixels_x(win);
|
|
const int sizey = WM_window_pixels_y(win);
|
|
|
|
/* wmOrtho for the screen has this same offset */
|
|
const float halfx = GLA_PIXEL_OFS / sizex;
|
|
const float halfy = GLA_PIXEL_OFS / sizex;
|
|
|
|
/* Texture is already bound to GL_TEXTURE0 unit. */
|
|
|
|
immBegin(GPU_PRIM_TRI_FAN, 4);
|
|
|
|
immAttr2f(texcoord, halfx, halfy);
|
|
immVertex2f(pos, 0.0f, soffy);
|
|
|
|
immAttr2f(texcoord, 1.0f + halfx, halfy);
|
|
immVertex2f(pos, sizex, soffy);
|
|
|
|
immAttr2f(texcoord, 1.0f + halfx, 1.0f + halfy);
|
|
immVertex2f(pos, sizex, soffy + (sizey * 0.5f));
|
|
|
|
immAttr2f(texcoord, halfx, 1.0f + halfy);
|
|
immVertex2f(pos, 0.0f, soffy + (sizey * 0.5f));
|
|
|
|
immEnd();
|
|
|
|
immUnbindProgram();
|
|
}
|
|
|
|
static bool wm_stereo3d_is_fullscreen_required(eStereoDisplayMode stereo_display)
|
|
{
|
|
return ELEM(stereo_display, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM);
|
|
}
|
|
|
|
bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check)
|
|
{
|
|
const bScreen *screen = WM_window_get_active_screen(win);
|
|
const Scene *scene = WM_window_get_active_scene(win);
|
|
|
|
/* some 3d methods change the window arrangement, thus they shouldn't
|
|
* toggle on/off just because there is no 3d elements being drawn */
|
|
if (wm_stereo3d_is_fullscreen_required(eStereoDisplayMode(win->stereo3d_format->display_mode))) {
|
|
return GHOST_GetWindowState(static_cast<GHOST_WindowHandle>(win->ghostwin)) ==
|
|
GHOST_kWindowStateFullScreen;
|
|
}
|
|
|
|
if ((skip_stereo3d_check == false) && (ED_screen_stereo3d_required(screen, scene) == false)) {
|
|
return false;
|
|
}
|
|
|
|
/* some 3d methods change the window arrangement, thus they shouldn't
|
|
* toggle on/off just because there is no 3d elements being drawn */
|
|
if (wm_stereo3d_is_fullscreen_required(eStereoDisplayMode(win->stereo3d_format->display_mode))) {
|
|
return GHOST_GetWindowState(static_cast<GHOST_WindowHandle>(win->ghostwin)) ==
|
|
GHOST_kWindowStateFullScreen;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void wm_stereo3d_mouse_offset_apply(wmWindow *win, int r_mouse_xy[2])
|
|
{
|
|
if (!WM_stereo3d_enabled(win, false)) {
|
|
return;
|
|
}
|
|
|
|
if (win->stereo3d_format->display_mode == S3D_DISPLAY_SIDEBYSIDE) {
|
|
const int half_x = WM_window_pixels_x(win) / 2;
|
|
/* right half of the screen */
|
|
if (r_mouse_xy[0] > half_x) {
|
|
r_mouse_xy[0] -= half_x;
|
|
}
|
|
r_mouse_xy[0] *= 2;
|
|
}
|
|
else if (win->stereo3d_format->display_mode == S3D_DISPLAY_TOPBOTTOM) {
|
|
const int half_y = WM_window_pixels_y(win) / 2;
|
|
/* upper half of the screen */
|
|
if (r_mouse_xy[1] > half_y) {
|
|
r_mouse_xy[1] -= half_y;
|
|
}
|
|
r_mouse_xy[1] *= 2;
|
|
}
|
|
}
|
|
|
|
/************************** Stereo 3D operator **********************************/
|
|
struct Stereo3dData {
|
|
Stereo3dFormat stereo3d_format;
|
|
};
|
|
|
|
static bool wm_stereo3d_set_properties(bContext * /*C*/, wmOperator *op)
|
|
{
|
|
Stereo3dData *s3dd = static_cast<Stereo3dData *>(op->customdata);
|
|
Stereo3dFormat *s3d = &s3dd->stereo3d_format;
|
|
PropertyRNA *prop;
|
|
bool is_set = false;
|
|
|
|
prop = RNA_struct_find_property(op->ptr, "display_mode");
|
|
if (RNA_property_is_set(op->ptr, prop)) {
|
|
s3d->display_mode = RNA_property_enum_get(op->ptr, prop);
|
|
is_set = true;
|
|
}
|
|
|
|
prop = RNA_struct_find_property(op->ptr, "anaglyph_type");
|
|
if (RNA_property_is_set(op->ptr, prop)) {
|
|
s3d->anaglyph_type = RNA_property_enum_get(op->ptr, prop);
|
|
is_set = true;
|
|
}
|
|
|
|
prop = RNA_struct_find_property(op->ptr, "interlace_type");
|
|
if (RNA_property_is_set(op->ptr, prop)) {
|
|
s3d->interlace_type = RNA_property_enum_get(op->ptr, prop);
|
|
is_set = true;
|
|
}
|
|
|
|
prop = RNA_struct_find_property(op->ptr, "use_interlace_swap");
|
|
if (RNA_property_is_set(op->ptr, prop)) {
|
|
if (RNA_property_boolean_get(op->ptr, prop)) {
|
|
s3d->flag |= S3D_INTERLACE_SWAP;
|
|
}
|
|
else {
|
|
s3d->flag &= ~S3D_INTERLACE_SWAP;
|
|
}
|
|
is_set = true;
|
|
}
|
|
|
|
prop = RNA_struct_find_property(op->ptr, "use_sidebyside_crosseyed");
|
|
if (RNA_property_is_set(op->ptr, prop)) {
|
|
if (RNA_property_boolean_get(op->ptr, prop)) {
|
|
s3d->flag |= S3D_SIDEBYSIDE_CROSSEYED;
|
|
}
|
|
else {
|
|
s3d->flag &= ~S3D_SIDEBYSIDE_CROSSEYED;
|
|
}
|
|
is_set = true;
|
|
}
|
|
|
|
return is_set;
|
|
}
|
|
|
|
static void wm_stereo3d_set_init(bContext *C, wmOperator *op)
|
|
{
|
|
wmWindow *win = CTX_wm_window(C);
|
|
|
|
Stereo3dData *s3dd = static_cast<Stereo3dData *>(MEM_callocN(sizeof(Stereo3dData), __func__));
|
|
op->customdata = s3dd;
|
|
|
|
/* store the original win stereo 3d settings in case of cancel */
|
|
s3dd->stereo3d_format = *win->stereo3d_format;
|
|
}
|
|
|
|
int wm_stereo3d_set_exec(bContext *C, wmOperator *op)
|
|
{
|
|
wmWindowManager *wm = CTX_wm_manager(C);
|
|
wmWindow *win_src = CTX_wm_window(C);
|
|
wmWindow *win_dst = nullptr;
|
|
const bool is_fullscreen = WM_window_is_fullscreen(win_src);
|
|
char prev_display_mode = win_src->stereo3d_format->display_mode;
|
|
bool ok = true;
|
|
|
|
if (G.background) {
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
if (op->customdata == nullptr) {
|
|
/* no invoke means we need to set the operator properties here */
|
|
wm_stereo3d_set_init(C, op);
|
|
wm_stereo3d_set_properties(C, op);
|
|
}
|
|
|
|
Stereo3dData *s3dd = static_cast<Stereo3dData *>(op->customdata);
|
|
*win_src->stereo3d_format = s3dd->stereo3d_format;
|
|
|
|
if (prev_display_mode == S3D_DISPLAY_PAGEFLIP &&
|
|
prev_display_mode != win_src->stereo3d_format->display_mode)
|
|
{
|
|
/* In case the hardware supports page-flip but not the display. */
|
|
if ((win_dst = wm_window_copy_test(C, win_src, false, false))) {
|
|
/* pass */
|
|
}
|
|
else {
|
|
BKE_report(
|
|
op->reports,
|
|
RPT_ERROR,
|
|
"Failed to create a window without quad-buffer support, you may experience flickering");
|
|
ok = false;
|
|
}
|
|
}
|
|
else if (win_src->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) {
|
|
const bScreen *screen = WM_window_get_active_screen(win_src);
|
|
|
|
/* #ED_workspace_layout_duplicate() can't handle other cases yet #44688 */
|
|
if (screen->state != SCREENNORMAL) {
|
|
BKE_report(
|
|
op->reports, RPT_ERROR, "Failed to switch to Time Sequential mode when in fullscreen");
|
|
ok = false;
|
|
}
|
|
/* Page-flip requires a new window to be created with the proper OS flags. */
|
|
else if ((win_dst = wm_window_copy_test(C, win_src, false, false))) {
|
|
if (GPU_stereo_quadbuffer_support()) {
|
|
BKE_report(op->reports, RPT_INFO, "Quad-buffer window successfully created");
|
|
}
|
|
else {
|
|
wm_window_close(C, wm, win_dst);
|
|
win_dst = nullptr;
|
|
BKE_report(op->reports, RPT_ERROR, "Quad-buffer not supported by the system");
|
|
ok = false;
|
|
}
|
|
}
|
|
else {
|
|
BKE_report(op->reports,
|
|
RPT_ERROR,
|
|
"Failed to create a window compatible with the time sequential display method");
|
|
ok = false;
|
|
}
|
|
}
|
|
|
|
if (wm_stereo3d_is_fullscreen_required(eStereoDisplayMode(s3dd->stereo3d_format.display_mode))) {
|
|
if (!is_fullscreen) {
|
|
BKE_report(op->reports, RPT_INFO, "Stereo 3D Mode requires the window to be fullscreen");
|
|
}
|
|
}
|
|
|
|
MEM_freeN(op->customdata);
|
|
|
|
if (ok) {
|
|
if (win_dst) {
|
|
wm_window_close(C, wm, win_src);
|
|
}
|
|
|
|
WM_event_add_notifier(C, NC_WINDOW, nullptr);
|
|
return OPERATOR_FINISHED;
|
|
}
|
|
|
|
/* Without this, the popup won't be freed properly, see #44688. */
|
|
CTX_wm_window_set(C, win_src);
|
|
win_src->stereo3d_format->display_mode = prev_display_mode;
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
int wm_stereo3d_set_invoke(bContext *C, wmOperator *op, const wmEvent * /*event*/)
|
|
{
|
|
wm_stereo3d_set_init(C, op);
|
|
|
|
if (wm_stereo3d_set_properties(C, op)) {
|
|
return wm_stereo3d_set_exec(C, op);
|
|
}
|
|
return WM_operator_props_dialog_popup(C, op, 300);
|
|
}
|
|
|
|
void wm_stereo3d_set_draw(bContext * /*C*/, wmOperator *op)
|
|
{
|
|
Stereo3dData *s3dd = static_cast<Stereo3dData *>(op->customdata);
|
|
uiLayout *layout = op->layout;
|
|
uiLayout *col;
|
|
|
|
PointerRNA stereo3d_format_ptr = RNA_pointer_create(
|
|
nullptr, &RNA_Stereo3dDisplay, &s3dd->stereo3d_format);
|
|
|
|
uiLayoutSetPropSep(layout, true);
|
|
uiLayoutSetPropDecorate(layout, false);
|
|
|
|
col = uiLayoutColumn(layout, false);
|
|
uiItemR(col, &stereo3d_format_ptr, "display_mode", UI_ITEM_NONE, nullptr, ICON_NONE);
|
|
|
|
switch (s3dd->stereo3d_format.display_mode) {
|
|
case S3D_DISPLAY_ANAGLYPH: {
|
|
uiItemR(col, &stereo3d_format_ptr, "anaglyph_type", UI_ITEM_NONE, nullptr, ICON_NONE);
|
|
break;
|
|
}
|
|
case S3D_DISPLAY_INTERLACE: {
|
|
uiItemR(col, &stereo3d_format_ptr, "interlace_type", UI_ITEM_NONE, nullptr, ICON_NONE);
|
|
uiItemR(col, &stereo3d_format_ptr, "use_interlace_swap", UI_ITEM_NONE, nullptr, ICON_NONE);
|
|
break;
|
|
}
|
|
case S3D_DISPLAY_SIDEBYSIDE: {
|
|
uiItemR(
|
|
col, &stereo3d_format_ptr, "use_sidebyside_crosseyed", UI_ITEM_NONE, nullptr, ICON_NONE);
|
|
/* fall-through */
|
|
}
|
|
case S3D_DISPLAY_PAGEFLIP:
|
|
case S3D_DISPLAY_TOPBOTTOM:
|
|
default: {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool wm_stereo3d_set_check(bContext * /*C*/, wmOperator * /*op*/)
|
|
{
|
|
/* the check function guarantees that the menu is updated to show the
|
|
* sub-options when an enum change (e.g., it shows the anaglyph options
|
|
* when anaglyph is on, and the interlace options when this is on */
|
|
return true;
|
|
}
|
|
|
|
void wm_stereo3d_set_cancel(bContext * /*C*/, wmOperator *op)
|
|
{
|
|
MEM_freeN(op->customdata);
|
|
op->customdata = nullptr;
|
|
}
|