1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2015 by Blender Foundation
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup wm
22 */
23
24 #include <stdlib.h>
25 #include <string.h>
26
27 #include "DNA_listBase.h"
28
29 #include "RNA_access.h"
30
31 #include "MEM_guardedalloc.h"
32
33 #include "BLI_utildefines.h"
34
35 #include "BKE_context.h"
36 #include "BKE_global.h"
37 #include "BKE_report.h"
38
39 #include "GHOST_C-api.h"
40
41 #include "ED_screen.h"
42
43 #include "GPU_capabilities.h"
44 #include "GPU_immediate.h"
45 #include "GPU_texture.h"
46 #include "GPU_viewport.h"
47
48 #include "WM_api.h"
49 #include "WM_types.h"
50 #include "wm.h"
51 #include "wm_draw.h"
52 #include "wm_window.h"
53
54 #include "UI_interface.h"
55 #include "UI_resources.h"
56
wm_stereo3d_draw_sidebyside(wmWindow * win,int view)57 void wm_stereo3d_draw_sidebyside(wmWindow *win, int view)
58 {
59 bool cross_eyed = (win->stereo3d_format->flag & S3D_SIDEBYSIDE_CROSSEYED) != 0;
60
61 GPUVertFormat *format = immVertexFormat();
62 uint texcoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
63 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
64
65 immBindBuiltinProgram(GPU_SHADER_2D_IMAGE);
66
67 int soffx = WM_window_pixels_x(win) * 0.5f;
68 if (view == STEREO_LEFT_ID) {
69 if (!cross_eyed) {
70 soffx = 0;
71 }
72 }
73 else { /* #RIGHT_LEFT_ID */
74 if (cross_eyed) {
75 soffx = 0;
76 }
77 }
78
79 const int sizex = WM_window_pixels_x(win);
80 const int sizey = WM_window_pixels_y(win);
81
82 /* wmOrtho for the screen has this same offset */
83 const float halfx = GLA_PIXEL_OFS / sizex;
84 const float halfy = GLA_PIXEL_OFS / sizex;
85
86 immUniform1i("image", 0); /* texture is already bound to GL_TEXTURE0 unit */
87
88 immBegin(GPU_PRIM_TRI_FAN, 4);
89
90 immAttr2f(texcoord, halfx, halfy);
91 immVertex2f(pos, soffx, 0.0f);
92
93 immAttr2f(texcoord, 1.0f + halfx, halfy);
94 immVertex2f(pos, soffx + (sizex * 0.5f), 0.0f);
95
96 immAttr2f(texcoord, 1.0f + halfx, 1.0f + halfy);
97 immVertex2f(pos, soffx + (sizex * 0.5f), sizey);
98
99 immAttr2f(texcoord, halfx, 1.0f + halfy);
100 immVertex2f(pos, soffx, sizey);
101
102 immEnd();
103
104 immUnbindProgram();
105 }
106
wm_stereo3d_draw_topbottom(wmWindow * win,int view)107 void wm_stereo3d_draw_topbottom(wmWindow *win, int view)
108 {
109 GPUVertFormat *format = immVertexFormat();
110 uint texcoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
111 uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
112
113 immBindBuiltinProgram(GPU_SHADER_2D_IMAGE);
114
115 int soffy;
116 if (view == STEREO_LEFT_ID) {
117 soffy = WM_window_pixels_y(win) * 0.5f;
118 }
119 else { /* STEREO_RIGHT_ID */
120 soffy = 0;
121 }
122
123 const int sizex = WM_window_pixels_x(win);
124 const int sizey = WM_window_pixels_y(win);
125
126 /* wmOrtho for the screen has this same offset */
127 const float halfx = GLA_PIXEL_OFS / sizex;
128 const float halfy = GLA_PIXEL_OFS / sizex;
129
130 immUniform1i("image", 0); /* texture is already bound to GL_TEXTURE0 unit */
131
132 immBegin(GPU_PRIM_TRI_FAN, 4);
133
134 immAttr2f(texcoord, halfx, halfy);
135 immVertex2f(pos, 0.0f, soffy);
136
137 immAttr2f(texcoord, 1.0f + halfx, halfy);
138 immVertex2f(pos, sizex, soffy);
139
140 immAttr2f(texcoord, 1.0f + halfx, 1.0f + halfy);
141 immVertex2f(pos, sizex, soffy + (sizey * 0.5f));
142
143 immAttr2f(texcoord, halfx, 1.0f + halfy);
144 immVertex2f(pos, 0.0f, soffy + (sizey * 0.5f));
145
146 immEnd();
147
148 immUnbindProgram();
149 }
150
wm_stereo3d_is_fullscreen_required(eStereoDisplayMode stereo_display)151 static bool wm_stereo3d_is_fullscreen_required(eStereoDisplayMode stereo_display)
152 {
153 return ELEM(stereo_display, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM);
154 }
155
WM_stereo3d_enabled(wmWindow * win,bool skip_stereo3d_check)156 bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check)
157 {
158 const bScreen *screen = WM_window_get_active_screen(win);
159 const Scene *scene = WM_window_get_active_scene(win);
160
161 /* some 3d methods change the window arrangement, thus they shouldn't
162 * toggle on/off just because there is no 3d elements being drawn */
163 if (wm_stereo3d_is_fullscreen_required(win->stereo3d_format->display_mode)) {
164 return GHOST_GetWindowState(win->ghostwin) == GHOST_kWindowStateFullScreen;
165 }
166
167 if ((skip_stereo3d_check == false) && (ED_screen_stereo3d_required(screen, scene) == false)) {
168 return false;
169 }
170
171 /* some 3d methods change the window arrangement, thus they shouldn't
172 * toggle on/off just because there is no 3d elements being drawn */
173 if (wm_stereo3d_is_fullscreen_required(win->stereo3d_format->display_mode)) {
174 return GHOST_GetWindowState(win->ghostwin) == GHOST_kWindowStateFullScreen;
175 }
176
177 return true;
178 }
179
180 /**
181 * If needed, adjust \a r_mouse_xy
182 * so that drawn cursor and handled mouse position are matching visually.
183 */
wm_stereo3d_mouse_offset_apply(wmWindow * win,int * r_mouse_xy)184 void wm_stereo3d_mouse_offset_apply(wmWindow *win, int *r_mouse_xy)
185 {
186 if (!WM_stereo3d_enabled(win, false)) {
187 return;
188 }
189
190 if (win->stereo3d_format->display_mode == S3D_DISPLAY_SIDEBYSIDE) {
191 const int half_x = win->sizex / 2;
192 /* right half of the screen */
193 if (r_mouse_xy[0] > half_x) {
194 r_mouse_xy[0] -= half_x;
195 }
196 r_mouse_xy[0] *= 2;
197 }
198 else if (win->stereo3d_format->display_mode == S3D_DISPLAY_TOPBOTTOM) {
199 const int half_y = win->sizey / 2;
200 /* upper half of the screen */
201 if (r_mouse_xy[1] > half_y) {
202 r_mouse_xy[1] -= half_y;
203 }
204 r_mouse_xy[1] *= 2;
205 }
206 }
207
208 /************************** Stereo 3D operator **********************************/
209 typedef struct Stereo3dData {
210 Stereo3dFormat stereo3d_format;
211 } Stereo3dData;
212
wm_stereo3d_set_properties(bContext * UNUSED (C),wmOperator * op)213 static bool wm_stereo3d_set_properties(bContext *UNUSED(C), wmOperator *op)
214 {
215 Stereo3dData *s3dd = op->customdata;
216 Stereo3dFormat *s3d = &s3dd->stereo3d_format;
217 PropertyRNA *prop;
218 bool is_set = false;
219
220 prop = RNA_struct_find_property(op->ptr, "display_mode");
221 if (RNA_property_is_set(op->ptr, prop)) {
222 s3d->display_mode = RNA_property_enum_get(op->ptr, prop);
223 is_set = true;
224 }
225
226 prop = RNA_struct_find_property(op->ptr, "anaglyph_type");
227 if (RNA_property_is_set(op->ptr, prop)) {
228 s3d->anaglyph_type = RNA_property_enum_get(op->ptr, prop);
229 is_set = true;
230 }
231
232 prop = RNA_struct_find_property(op->ptr, "interlace_type");
233 if (RNA_property_is_set(op->ptr, prop)) {
234 s3d->interlace_type = RNA_property_enum_get(op->ptr, prop);
235 is_set = true;
236 }
237
238 prop = RNA_struct_find_property(op->ptr, "use_interlace_swap");
239 if (RNA_property_is_set(op->ptr, prop)) {
240 if (RNA_property_boolean_get(op->ptr, prop)) {
241 s3d->flag |= S3D_INTERLACE_SWAP;
242 }
243 else {
244 s3d->flag &= ~S3D_INTERLACE_SWAP;
245 }
246 is_set = true;
247 }
248
249 prop = RNA_struct_find_property(op->ptr, "use_sidebyside_crosseyed");
250 if (RNA_property_is_set(op->ptr, prop)) {
251 if (RNA_property_boolean_get(op->ptr, prop)) {
252 s3d->flag |= S3D_SIDEBYSIDE_CROSSEYED;
253 }
254 else {
255 s3d->flag &= ~S3D_SIDEBYSIDE_CROSSEYED;
256 }
257 is_set = true;
258 }
259
260 return is_set;
261 }
262
wm_stereo3d_set_init(bContext * C,wmOperator * op)263 static void wm_stereo3d_set_init(bContext *C, wmOperator *op)
264 {
265 wmWindow *win = CTX_wm_window(C);
266
267 Stereo3dData *s3dd = MEM_callocN(sizeof(Stereo3dData), __func__);
268 op->customdata = s3dd;
269
270 /* store the original win stereo 3d settings in case of cancel */
271 s3dd->stereo3d_format = *win->stereo3d_format;
272 }
273
wm_stereo3d_set_exec(bContext * C,wmOperator * op)274 int wm_stereo3d_set_exec(bContext *C, wmOperator *op)
275 {
276 wmWindowManager *wm = CTX_wm_manager(C);
277 wmWindow *win_src = CTX_wm_window(C);
278 wmWindow *win_dst = NULL;
279 const bool is_fullscreen = WM_window_is_fullscreen(win_src);
280 char prev_display_mode = win_src->stereo3d_format->display_mode;
281 bool ok = true;
282
283 if (G.background) {
284 return OPERATOR_CANCELLED;
285 }
286
287 if (op->customdata == NULL) {
288 /* no invoke means we need to set the operator properties here */
289 wm_stereo3d_set_init(C, op);
290 wm_stereo3d_set_properties(C, op);
291 }
292
293 Stereo3dData *s3dd = op->customdata;
294 *win_src->stereo3d_format = s3dd->stereo3d_format;
295
296 if (prev_display_mode == S3D_DISPLAY_PAGEFLIP &&
297 prev_display_mode != win_src->stereo3d_format->display_mode) {
298 /* in case the hardware supports pageflip but not the display */
299 if ((win_dst = wm_window_copy_test(C, win_src, false, false))) {
300 /* pass */
301 }
302 else {
303 BKE_report(
304 op->reports,
305 RPT_ERROR,
306 "Failed to create a window without quad-buffer support, you may experience flickering");
307 ok = false;
308 }
309 }
310 else if (win_src->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) {
311 const bScreen *screen = WM_window_get_active_screen(win_src);
312
313 /* ED_workspace_layout_duplicate() can't handle other cases yet T44688 */
314 if (screen->state != SCREENNORMAL) {
315 BKE_report(
316 op->reports, RPT_ERROR, "Failed to switch to Time Sequential mode when in fullscreen");
317 ok = false;
318 }
319 /* pageflip requires a new window to be created with the proper OS flags */
320 else if ((win_dst = wm_window_copy_test(C, win_src, false, false))) {
321 if (GPU_stereo_quadbuffer_support()) {
322 BKE_report(op->reports, RPT_INFO, "Quad-buffer window successfully created");
323 }
324 else {
325 wm_window_close(C, wm, win_dst);
326 win_dst = NULL;
327 BKE_report(op->reports, RPT_ERROR, "Quad-buffer not supported by the system");
328 ok = false;
329 }
330 }
331 else {
332 BKE_report(op->reports,
333 RPT_ERROR,
334 "Failed to create a window compatible with the time sequential display method");
335 ok = false;
336 }
337 }
338
339 if (wm_stereo3d_is_fullscreen_required(s3dd->stereo3d_format.display_mode)) {
340 if (!is_fullscreen) {
341 BKE_report(op->reports, RPT_INFO, "Stereo 3D Mode requires the window to be fullscreen");
342 }
343 }
344
345 MEM_freeN(op->customdata);
346
347 if (ok) {
348 if (win_dst) {
349 wm_window_close(C, wm, win_src);
350 }
351
352 WM_event_add_notifier(C, NC_WINDOW, NULL);
353 return OPERATOR_FINISHED;
354 }
355
356 /* without this, the popup won't be freed freed properly T44688 */
357 CTX_wm_window_set(C, win_src);
358 win_src->stereo3d_format->display_mode = prev_display_mode;
359 return OPERATOR_CANCELLED;
360 }
361
wm_stereo3d_set_invoke(bContext * C,wmOperator * op,const wmEvent * UNUSED (event))362 int wm_stereo3d_set_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
363 {
364 wm_stereo3d_set_init(C, op);
365
366 if (wm_stereo3d_set_properties(C, op)) {
367 return wm_stereo3d_set_exec(C, op);
368 }
369 return WM_operator_props_dialog_popup(C, op, 300);
370 }
371
wm_stereo3d_set_draw(bContext * UNUSED (C),wmOperator * op)372 void wm_stereo3d_set_draw(bContext *UNUSED(C), wmOperator *op)
373 {
374 Stereo3dData *s3dd = op->customdata;
375 PointerRNA stereo3d_format_ptr;
376 uiLayout *layout = op->layout;
377 uiLayout *col;
378
379 RNA_pointer_create(NULL, &RNA_Stereo3dDisplay, &s3dd->stereo3d_format, &stereo3d_format_ptr);
380
381 uiLayoutSetPropSep(layout, true);
382 uiLayoutSetPropDecorate(layout, false);
383
384 col = uiLayoutColumn(layout, false);
385 uiItemR(col, &stereo3d_format_ptr, "display_mode", 0, NULL, ICON_NONE);
386
387 switch (s3dd->stereo3d_format.display_mode) {
388 case S3D_DISPLAY_ANAGLYPH: {
389 uiItemR(col, &stereo3d_format_ptr, "anaglyph_type", 0, NULL, ICON_NONE);
390 break;
391 }
392 case S3D_DISPLAY_INTERLACE: {
393 uiItemR(col, &stereo3d_format_ptr, "interlace_type", 0, NULL, ICON_NONE);
394 uiItemR(col, &stereo3d_format_ptr, "use_interlace_swap", 0, NULL, ICON_NONE);
395 break;
396 }
397 case S3D_DISPLAY_SIDEBYSIDE: {
398 uiItemR(col, &stereo3d_format_ptr, "use_sidebyside_crosseyed", 0, NULL, ICON_NONE);
399 /* fall-through */
400 }
401 case S3D_DISPLAY_PAGEFLIP:
402 case S3D_DISPLAY_TOPBOTTOM:
403 default: {
404 break;
405 }
406 }
407 }
408
wm_stereo3d_set_check(bContext * UNUSED (C),wmOperator * UNUSED (op))409 bool wm_stereo3d_set_check(bContext *UNUSED(C), wmOperator *UNUSED(op))
410 {
411 /* the check function guarantees that the menu is updated to show the
412 * sub-options when an enum change (e.g., it shows the anaglyph options
413 * when anaglyph is on, and the interlace options when this is on */
414 return true;
415 }
416
wm_stereo3d_set_cancel(bContext * UNUSED (C),wmOperator * op)417 void wm_stereo3d_set_cancel(bContext *UNUSED(C), wmOperator *op)
418 {
419 MEM_freeN(op->customdata);
420 op->customdata = NULL;
421 }
422