Blender V5.0
wm_stereo.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2015 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <cstdlib>
10#include <cstring>
11
12#include "RNA_access.hh"
13#include "RNA_prototypes.hh"
14
15#include "MEM_guardedalloc.h"
16
17#include "BLI_utildefines.h"
18
19#include "BKE_context.hh"
20#include "BKE_global.hh"
21#include "BKE_report.hh"
22
23#include "BLT_translation.hh"
24
25#include "GHOST_C-api.h"
26
27#include "ED_screen.hh"
28
29#include "GPU_capabilities.hh"
30#include "GPU_immediate.hh"
31#include "GPU_viewport.hh"
32
33#include "WM_api.hh"
34#include "WM_types.hh"
35#include "wm.hh"
36#include "wm_window.hh"
37
39#include "UI_resources.hh"
40
42{
43 bool cross_eyed = (win->stereo3d_format->flag & S3D_SIDEBYSIDE_CROSSEYED) != 0;
44
47 format, "texCoord", blender::gpu::VertAttrType::SFLOAT_32_32);
48 uint pos = GPU_vertformat_attr_add(format, "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
49
51
52 const blender::int2 win_size = WM_window_native_pixel_size(win);
53
54 int soffx = win_size[0] / 2;
55 if (view == STEREO_LEFT_ID) {
56 if (!cross_eyed) {
57 soffx = 0;
58 }
59 }
60 else { /* #RIGHT_LEFT_ID. */
61 if (cross_eyed) {
62 soffx = 0;
63 }
64 }
65
66 /* `wmOrtho` for the screen has this same offset. */
67 const float halfx = GLA_PIXEL_OFS / win_size[0];
68 const float halfy = GLA_PIXEL_OFS / win_size[1];
69
70 /* Texture is already bound to GL_TEXTURE0 unit. */
71
73
74 immAttr2f(texcoord, halfx, halfy);
75 immVertex2f(pos, soffx, 0.0f);
76
77 immAttr2f(texcoord, 1.0f + halfx, halfy);
78 immVertex2f(pos, soffx + (win_size[0] * 0.5f), 0.0f);
79
80 immAttr2f(texcoord, 1.0f + halfx, 1.0f + halfy);
81 immVertex2f(pos, soffx + (win_size[0] * 0.5f), win_size[1]);
82
83 immAttr2f(texcoord, halfx, 1.0f + halfy);
84 immVertex2f(pos, soffx, win_size[1]);
85
86 immEnd();
87
89}
90
92{
95 format, "texCoord", blender::gpu::VertAttrType::SFLOAT_32_32);
96 uint pos = GPU_vertformat_attr_add(format, "pos", blender::gpu::VertAttrType::SFLOAT_32_32);
97
99
100 const blender::int2 win_size = WM_window_native_pixel_size(win);
101
102 int soffy;
103 if (view == STEREO_LEFT_ID) {
104 soffy = win_size[1] * 0.5f;
105 }
106 else { /* #STEREO_RIGHT_ID. */
107 soffy = 0;
108 }
109
110 /* `wmOrtho` for the screen has this same offset. */
111 const float halfx = GLA_PIXEL_OFS / win_size[0];
112 const float halfy = GLA_PIXEL_OFS / win_size[1];
113
114 /* Texture is already bound to GL_TEXTURE0 unit. */
115
117
118 immAttr2f(texcoord, halfx, halfy);
119 immVertex2f(pos, 0.0f, soffy);
120
121 immAttr2f(texcoord, 1.0f + halfx, halfy);
122 immVertex2f(pos, win_size[0], soffy);
123
124 immAttr2f(texcoord, 1.0f + halfx, 1.0f + halfy);
125 immVertex2f(pos, win_size[0], soffy + (win_size[1] * 0.5f));
126
127 immAttr2f(texcoord, halfx, 1.0f + halfy);
128 immVertex2f(pos, 0.0f, soffy + (win_size[1] * 0.5f));
129
130 immEnd();
131
133}
134
136{
137 return ELEM(stereo_display, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM);
138}
139
140bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check)
141{
142 const bScreen *screen = WM_window_get_active_screen(win);
143 const Scene *scene = WM_window_get_active_scene(win);
144
145 /* Some 3d methods change the window arrangement, thus they shouldn't
146 * toggle on/off just because there is no 3d elements being drawn. */
148 return GHOST_GetWindowState(static_cast<GHOST_WindowHandle>(win->ghostwin)) ==
150 }
151
152 if ((skip_stereo3d_check == false) && (ED_screen_stereo3d_required(screen, scene) == false)) {
153 return false;
154 }
155
156 /* Some 3d methods change the window arrangement, thus they shouldn't
157 * toggle on/off just because there is no 3d elements being drawn. */
159 return GHOST_GetWindowState(static_cast<GHOST_WindowHandle>(win->ghostwin)) ==
161 }
162
163 return true;
164}
165
166void wm_stereo3d_mouse_offset_apply(wmWindow *win, int r_mouse_xy[2])
167{
168 if (!WM_stereo3d_enabled(win, false)) {
169 return;
170 }
171
173 const int half_x = WM_window_native_pixel_x(win) / 2;
174 /* Right half of the screen. */
175 if (r_mouse_xy[0] > half_x) {
176 r_mouse_xy[0] -= half_x;
177 }
178 r_mouse_xy[0] *= 2;
179 }
181 const int half_y = WM_window_native_pixel_y(win) / 2;
182 /* Upper half of the screen. */
183 if (r_mouse_xy[1] > half_y) {
184 r_mouse_xy[1] -= half_y;
185 }
186 r_mouse_xy[1] *= 2;
187 }
188}
189
190/************************** Stereo 3D operator **********************************/
194
196{
197 Stereo3dData *s3dd = static_cast<Stereo3dData *>(op->customdata);
198 Stereo3dFormat *s3d = &s3dd->stereo3d_format;
199 PropertyRNA *prop;
200 bool is_set = false;
201
202 prop = RNA_struct_find_property(op->ptr, "display_mode");
203 if (RNA_property_is_set(op->ptr, prop)) {
204 s3d->display_mode = RNA_property_enum_get(op->ptr, prop);
205 is_set = true;
206 }
207
208 prop = RNA_struct_find_property(op->ptr, "anaglyph_type");
209 if (RNA_property_is_set(op->ptr, prop)) {
210 s3d->anaglyph_type = RNA_property_enum_get(op->ptr, prop);
211 is_set = true;
212 }
213
214 prop = RNA_struct_find_property(op->ptr, "interlace_type");
215 if (RNA_property_is_set(op->ptr, prop)) {
216 s3d->interlace_type = RNA_property_enum_get(op->ptr, prop);
217 is_set = true;
218 }
219
220 prop = RNA_struct_find_property(op->ptr, "use_interlace_swap");
221 if (RNA_property_is_set(op->ptr, prop)) {
222 if (RNA_property_boolean_get(op->ptr, prop)) {
223 s3d->flag |= S3D_INTERLACE_SWAP;
224 }
225 else {
227 }
228 is_set = true;
229 }
230
231 prop = RNA_struct_find_property(op->ptr, "use_sidebyside_crosseyed");
232 if (RNA_property_is_set(op->ptr, prop)) {
233 if (RNA_property_boolean_get(op->ptr, prop)) {
235 }
236 else {
238 }
239 is_set = true;
240 }
241
242 return is_set;
243}
244
246{
247 wmWindow *win = CTX_wm_window(C);
248
249 Stereo3dData *s3dd = MEM_callocN<Stereo3dData>(__func__);
250 op->customdata = s3dd;
251
252 /* Store the original win stereo 3d settings in case of cancel. */
253 s3dd->stereo3d_format = *win->stereo3d_format;
254}
255
257{
259 wmWindow *win_src = CTX_wm_window(C);
260 wmWindow *win_dst = nullptr;
261 const bool is_fullscreen = WM_window_is_fullscreen(win_src);
262 char prev_display_mode = win_src->stereo3d_format->display_mode;
263 bool ok = true;
264
265 if (G.background) {
266 return OPERATOR_CANCELLED;
267 }
268
269 if (op->customdata == nullptr) {
270 /* No invoke means we need to set the operator properties here. */
273 }
274
275 Stereo3dData *s3dd = static_cast<Stereo3dData *>(op->customdata);
276 *win_src->stereo3d_format = s3dd->stereo3d_format;
277
278 if (prev_display_mode == S3D_DISPLAY_PAGEFLIP &&
279 prev_display_mode != win_src->stereo3d_format->display_mode)
280 {
281 /* In case the hardware supports page-flip but not the display. */
282 if ((win_dst = wm_window_copy_test(C, win_src, false, false))) {
283 /* Pass. */
284 }
285 else {
287 op->reports,
288 RPT_ERROR,
289 "Failed to create a window without quad-buffer support, you may experience flickering");
290 ok = false;
291 }
292 }
293 else if (win_src->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) {
294 const bScreen *screen = WM_window_get_active_screen(win_src);
295
296 /* #ED_workspace_layout_duplicate() can't handle other cases yet #44688 */
297 if (screen->state != SCREENNORMAL) {
299 op->reports, RPT_ERROR, "Failed to switch to Time Sequential mode when in fullscreen");
300 ok = false;
301 }
302 /* Page-flip requires a new window to be created with the proper OS flags. */
303 else if ((win_dst = wm_window_copy_test(C, win_src, false, false))) {
305 BKE_report(op->reports, RPT_INFO, "Quad-buffer window successfully created");
306 }
307 else {
308 wm_window_close(C, wm, win_dst);
309 win_dst = nullptr;
310 BKE_report(op->reports, RPT_ERROR, "Quad-buffer not supported by the system");
311 ok = false;
312 }
313 }
314 else {
316 RPT_ERROR,
317 "Failed to create a window compatible with the time sequential display method");
318 ok = false;
319 }
320 }
321
323 if (!is_fullscreen) {
324 BKE_report(op->reports, RPT_INFO, "Stereo 3D Mode requires the window to be fullscreen");
325 }
326 }
327
328 MEM_freeN(s3dd);
329 op->customdata = nullptr;
330
331 if (ok) {
332 if (win_dst) {
333 wm_window_close(C, wm, win_src);
334 }
335
337 return OPERATOR_FINISHED;
338 }
339
340 /* Without this, the popup won't be freed properly, see #44688. */
341 CTX_wm_window_set(C, win_src);
342 win_src->stereo3d_format->display_mode = prev_display_mode;
343 return OPERATOR_CANCELLED;
344}
345
347{
349
350 if (wm_stereo3d_set_properties(C, op)) {
351 return wm_stereo3d_set_exec(C, op);
352 }
353 return WM_operator_props_dialog_popup(C, op, 300, IFACE_("Set Stereo 3D"), IFACE_("Set"));
354}
355
357{
358 Stereo3dData *s3dd = static_cast<Stereo3dData *>(op->customdata);
359 uiLayout *layout = op->layout;
360 uiLayout *col;
361
362 PointerRNA stereo3d_format_ptr = RNA_pointer_create_discrete(
363 nullptr, &RNA_Stereo3dDisplay, &s3dd->stereo3d_format);
364
365 layout->use_property_split_set(true);
366 layout->use_property_decorate_set(false);
367
368 col = &layout->column(false);
369 col->prop(&stereo3d_format_ptr, "display_mode", UI_ITEM_NONE, std::nullopt, ICON_NONE);
370
371 switch (s3dd->stereo3d_format.display_mode) {
373 col->prop(&stereo3d_format_ptr, "anaglyph_type", UI_ITEM_NONE, std::nullopt, ICON_NONE);
374 break;
375 }
377 col->prop(&stereo3d_format_ptr, "interlace_type", UI_ITEM_NONE, std::nullopt, ICON_NONE);
378 col->prop(&stereo3d_format_ptr, "use_interlace_swap", UI_ITEM_NONE, std::nullopt, ICON_NONE);
379 break;
380 }
382 col->prop(
383 &stereo3d_format_ptr, "use_sidebyside_crosseyed", UI_ITEM_NONE, std::nullopt, ICON_NONE);
384 /* Fall-through. */
385 }
388 default: {
389 break;
390 }
391 }
392}
393
395{
396 /* The check function guarantees that the menu is updated to show the
397 * sub-options when an enum change (e.g., it shows the anaglyph options
398 * when anaglyph is on, and the interlace options when this is on. */
399 return true;
400}
401
403{
404 Stereo3dData *s3dd = static_cast<Stereo3dData *>(op->customdata);
405 MEM_freeN(s3dd);
406 op->customdata = nullptr;
407}
wmWindow * CTX_wm_window(const bContext *C)
void CTX_wm_window_set(bContext *C, wmWindow *win)
wmWindowManager * CTX_wm_manager(const bContext *C)
@ RPT_INFO
Definition BKE_report.hh:35
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
unsigned int uint
#define ELEM(...)
#define IFACE_(msgid)
@ S3D_INTERLACE_SWAP
@ S3D_SIDEBYSIDE_CROSSEYED
eStereoDisplayMode
@ S3D_DISPLAY_ANAGLYPH
@ S3D_DISPLAY_INTERLACE
@ S3D_DISPLAY_TOPBOTTOM
@ S3D_DISPLAY_SIDEBYSIDE
@ S3D_DISPLAY_PAGEFLIP
@ STEREO_LEFT_ID
@ SCREENNORMAL
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
bool ED_screen_stereo3d_required(const bScreen *screen, const Scene *scene)
static AppView * view
GHOST C-API function and type declarations.
GHOST_TWindowState GHOST_GetWindowState(GHOST_WindowHandle windowhandle)
@ GHOST_kWindowStateFullScreen
bool GPU_stereo_quadbuffer_support()
void immEnd()
void immUnbindProgram()
void immBindBuiltinProgram(GPUBuiltinShader shader_id)
void immVertex2f(uint attr_id, float x, float y)
GPUVertFormat * immVertexFormat()
void immAttr2f(uint attr_id, float x, float y)
void immBegin(GPUPrimType, uint vertex_len)
@ GPU_PRIM_TRI_FAN
@ GPU_SHADER_3D_IMAGE
uint GPU_vertformat_attr_add(GPUVertFormat *format, blender::StringRef name, blender::gpu::VertAttrType type)
#define GLA_PIXEL_OFS
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
#define UI_ITEM_NONE
#define NC_WINDOW
Definition WM_types.hh:375
uint pos
uint col
format
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
#define G(x, y, z)
VecBase< int32_t, 2 > int2
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
bool RNA_property_boolean_get(PointerRNA *ptr, PropertyRNA *prop)
int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop)
PointerRNA RNA_pointer_create_discrete(ID *id, StructRNA *type, void *data)
Stereo3dFormat stereo3d_format
Definition wm_stereo.cc:192
void use_property_decorate_set(bool is_sep)
uiLayout & column(bool align)
void use_property_split_set(bool value)
struct ReportList * reports
struct uiLayout * layout
struct PointerRNA * ptr
struct Stereo3dFormat * stereo3d_format
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorStatus WM_operator_props_dialog_popup(bContext *C, wmOperator *op, int width, std::optional< std::string > title, std::optional< std::string > confirm_text, const bool cancel_default, std::optional< std::string > message)
void wm_stereo3d_set_cancel(bContext *, wmOperator *op)
Definition wm_stereo.cc:402
void wm_stereo3d_draw_sidebyside(wmWindow *win, int view)
Definition wm_stereo.cc:41
bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check)
Definition wm_stereo.cc:140
wmOperatorStatus wm_stereo3d_set_invoke(bContext *C, wmOperator *op, const wmEvent *)
Definition wm_stereo.cc:346
void wm_stereo3d_draw_topbottom(wmWindow *win, int view)
Definition wm_stereo.cc:91
wmOperatorStatus wm_stereo3d_set_exec(bContext *C, wmOperator *op)
Definition wm_stereo.cc:256
static bool wm_stereo3d_is_fullscreen_required(eStereoDisplayMode stereo_display)
Definition wm_stereo.cc:135
bool wm_stereo3d_set_check(bContext *, wmOperator *)
Definition wm_stereo.cc:394
void wm_stereo3d_set_draw(bContext *, wmOperator *op)
Definition wm_stereo.cc:356
static void wm_stereo3d_set_init(bContext *C, wmOperator *op)
Definition wm_stereo.cc:245
void wm_stereo3d_mouse_offset_apply(wmWindow *win, int r_mouse_xy[2])
Definition wm_stereo.cc:166
static bool wm_stereo3d_set_properties(bContext *, wmOperator *op)
Definition wm_stereo.cc:195
blender::int2 WM_window_native_pixel_size(const wmWindow *win)
bool WM_window_is_fullscreen(const wmWindow *win)
void wm_window_close(bContext *C, wmWindowManager *wm, wmWindow *win)
Definition wm_window.cc:463
int WM_window_native_pixel_y(const wmWindow *win)
int WM_window_native_pixel_x(const wmWindow *win)
wmWindow * wm_window_copy_test(bContext *C, wmWindow *win_src, const bool duplicate_layout, const bool child)
Definition wm_window.cc:365
Scene * WM_window_get_active_scene(const wmWindow *win)
bScreen * WM_window_get_active_screen(const wmWindow *win)