The zblur plugin (aka as DoF) integrated in render. Compared to patch
[blender.git] / source / blender / src / renderwin.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * The Original Code is: all of this file.
27  *
28  * Contributor(s): none yet.
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31  */
32  
33 #ifdef WIN32
34 /* for the multimedia timer */
35 #include <windows.h>
36 #include <mmsystem.h>
37 #endif
38
39 #include <string.h>
40 #include <stdarg.h>
41 #include <math.h>
42
43 #ifdef HAVE_CONFIG_H
44 #include <config.h>
45 #endif
46
47 #ifdef WIN32
48 #include "BLI_winstuff.h"
49 #else
50  /* for signal callback, not (fully) supported at windows */
51 #include <sys/time.h>
52 #include <signal.h>
53
54 #endif
55
56 #include <limits.h>
57
58 #include "BLI_blenlib.h"
59
60 #include "MEM_guardedalloc.h"
61
62 #include "BMF_Api.h"
63
64 #include "DNA_view3d_types.h"
65 #include "DNA_screen_types.h"
66 #include "DNA_vec_types.h"
67
68 #include "BKE_global.h"
69 #include "BKE_utildefines.h"
70
71 #include "BIF_gl.h"
72 #include "BIF_glutil.h"
73 #include "BIF_graphics.h"
74 #include "BIF_screen.h"
75 #include "BIF_space.h"
76 #include "BIF_mywindow.h"
77 #include "BIF_previewrender.h"
78 #include "BIF_renderwin.h"
79 #include "BIF_resources.h"
80 #include "BIF_toets.h"
81
82 #include "BDR_editobject.h"
83
84 #include "BSE_view.h"
85 #include "BSE_drawview.h"
86 #include "BSE_filesel.h"
87 #include "BSE_headerbuttons.h"
88
89 #include "blendef.h"
90 #include "mydevice.h"
91 #include "winlay.h"
92 #include "render.h"
93
94 /* ------------ renderwin struct, to prevent too much global vars --------- */
95 /* ------------ only used for display in a 2nd window  --------- */
96
97
98 /* flags escape presses during event handling
99 * so we can test for user break later.
100 */
101 #define RW_FLAGS_ESCAPE         (1<<0)
102 /* old zoom style (2x, locked to mouse, exits
103 * when mouse leaves window), to be removed
104 * at some point.
105 */
106 #define RW_FLAGS_OLDZOOM                (1<<1)
107 /* on when image is being panned with middlemouse
108 */
109 #define RW_FLAGS_PANNING                (1<<2)
110 /* on when the mouse is dragging over the image
111 * to examine pixel values.
112 */
113 #define RW_FLAGS_PIXEL_EXAMINING        (1<<3)
114
115 /* forces draw of alpha */
116 #define RW_FLAGS_ALPHA          (1<<4)
117
118 /* space for info text */
119 #define RW_HEADERY              18
120
121 typedef struct {
122         Window *win;
123
124         float zoom, zoomofs[2];
125         int active;
126         
127         int mbut[3];
128         int lmouse[2];
129         
130         unsigned int flags;
131         
132         float pan_mouse_start[2], pan_ofs_start[2];
133
134         char *info_text;
135         char *render_text, *render_text_spare;
136         
137 } RenderWin;
138
139 static RenderWin *render_win= NULL;
140
141 /* --------------- help functions for RenderWin struct ---------------------------- */
142
143
144 /* only called in function open_renderwin */
145 static RenderWin *renderwin_alloc(Window *win)
146 {
147         RenderWin *rw= MEM_mallocN(sizeof(*rw), "RenderWin");
148         rw->win= win;
149         rw->zoom= 1.0;
150         rw->active= 0;
151         rw->flags= 0;
152         rw->zoomofs[0]= rw->zoomofs[1]= 0;
153         rw->info_text= NULL;
154         rw->render_text= rw->render_text_spare= NULL;
155
156         rw->lmouse[0]= rw->lmouse[1]= 0;
157         rw->mbut[0]= rw->mbut[1]= rw->mbut[2]= 0;
158
159         return rw;
160 }
161
162
163 static void renderwin_queue_redraw(RenderWin *rw)
164 {
165         window_queue_redraw(rw->win); // to ghost
166 }
167
168 static void renderwin_reshape(RenderWin *rw)
169 {
170         ;
171 }
172
173 static void renderwin_get_disprect(RenderWin *rw, float disprect_r[2][2])
174 {
175         float display_w, display_h;
176         float cent_x, cent_y;
177         int w, h;
178
179         window_get_size(rw->win, &w, &h);
180         h-= RW_HEADERY;
181
182         display_w= R.rectx*rw->zoom;
183         display_h= R.recty*rw->zoom;
184         cent_x= (rw->zoomofs[0] + R.rectx/2)*rw->zoom;
185         cent_y= (rw->zoomofs[1] + R.recty/2)*rw->zoom;
186         
187         disprect_r[0][0]= w/2 - cent_x;
188         disprect_r[0][1]= h/2 - cent_y;
189         disprect_r[1][0]= disprect_r[0][0] + display_w;
190         disprect_r[1][1]= disprect_r[0][1] + display_h;
191 }
192
193         /** 
194          * Project window coordinate to image pixel coordinate.
195          * Returns true if resulting coordinate is within image.
196          */
197 static int renderwin_win_to_image_co(RenderWin *rw, int winco[2], int imgco_r[2])
198 {
199         float disprect[2][2];
200         
201         renderwin_get_disprect(rw, disprect);
202         
203         imgco_r[0]= (int) ((winco[0]-disprect[0][0])/rw->zoom);
204         imgco_r[1]= (int) ((winco[1]-disprect[0][1])/rw->zoom);
205         
206         return (imgco_r[0]>=0 && imgco_r[1]>=0 && imgco_r[0]<R.rectx && imgco_r[1]<R.recty);
207 }
208
209         /**
210          * Project window coordinates to normalized device coordinates
211          * Returns true if resulting coordinate is within window.
212          */
213 static int renderwin_win_to_ndc(RenderWin *rw, int win_co[2], float ndc_r[2])
214 {
215         int w, h;
216
217         window_get_size(rw->win, &w, &h);
218         h-= RW_HEADERY;
219
220         ndc_r[0]=  ((float)(win_co[0]*2)/(w-1) - 1.0);
221         ndc_r[1]=  ((float)(win_co[1]*2)/(h-1) - 1.0);
222
223         return (fabs(ndc_r[0])<=1.0 && fabs(ndc_r[1])<=1.0);
224 }
225
226 static void renderwin_set_infotext(RenderWin *rw, char *info_text)
227 {
228         if (rw->info_text) MEM_freeN(rw->info_text);
229         rw->info_text= info_text?BLI_strdup(info_text):NULL;
230 }
231
232 static void renderwin_reset_view(RenderWin *rw)
233 {
234         int w, h, rectx, recty;
235
236         if (rw->info_text) renderwin_set_infotext(rw, NULL);
237
238         /* now calculate a zoom for when image is larger than window */
239         window_get_size(rw->win, &w, &h);
240         h-= RW_HEADERY;
241         
242         /* at this point the r.rectx/y values are not correct yet */
243         rectx= (G.scene->r.size*G.scene->r.xsch)/100;
244         recty= (G.scene->r.size*G.scene->r.ysch)/100;
245         
246         /* crop option makes image smaller */
247         if ((G.scene->r.mode & R_BORDER) && (G.scene->r.mode & R_MOVIECROP)) { 
248                 if(!(G.scene->r.scemode & R_OGL)) {
249                         rectx*= (G.scene->r.border.xmax-G.scene->r.border.xmin);
250                         recty*= (G.scene->r.border.ymax-G.scene->r.border.ymin);
251                 }
252         }
253         
254         if(rectx>w || recty>h) {
255                 if(rectx-w > recty-h) rw->zoom= ((float)w)/((float)rectx);
256                 else rw->zoom= ((float)h)/((float)recty);
257         }
258         else rw->zoom= 1.0;
259
260         rw->zoomofs[0]= rw->zoomofs[1]= 0;
261         renderwin_queue_redraw(rw);
262 }
263
264 static void renderwin_draw_render_info(RenderWin *rw)
265 {
266         /* render text is added to top */
267         if(RW_HEADERY) {
268                 float colf[3];
269                 rcti rect;
270                 
271                 window_get_size(rw->win, &rect.xmax, &rect.ymax);
272                 rect.xmin= 0;
273                 rect.ymin= rect.ymax-RW_HEADERY;
274                 glEnable(GL_SCISSOR_TEST);
275                 glaDefine2DArea(&rect);
276                 
277                 /* clear header rect */
278                 BIF_SetTheme(NULL);     // sets view3d theme by default
279                 BIF_GetThemeColor3fv(TH_HEADER, colf);
280                 glClearColor(colf[0], colf[1], colf[2], 1.0); 
281                 glClear(GL_COLOR_BUFFER_BIT);
282                 
283                 if(rw->render_text) {
284                         BIF_ThemeColor(TH_TEXT);
285                         glRasterPos2i(12, 5);
286                         BMF_DrawString(G.fonts, rw->render_text);
287                 }
288         }       
289         
290 }
291
292 static void renderwin_draw(RenderWin *rw, int just_clear)
293 {
294         float disprect[2][2];
295         int set_back_mainwindow;
296         rcti rect;
297         
298         /* since renderwin uses callbacks (controlled by ghost) it can
299            mess up active window output with redraw events after a render. 
300            this is patchy, still WIP */
301         set_back_mainwindow = (winlay_get_active_window() != rw->win);
302         window_make_active(rw->win);
303         
304         rect.xmin= rect.ymin= 0;
305         window_get_size(rw->win, &rect.xmax, &rect.ymax);
306         rect.ymax-= RW_HEADERY;
307         
308         renderwin_get_disprect(rw, disprect);
309         
310         /* do this first, so window ends with correct scissor */
311         renderwin_draw_render_info(rw);
312         
313         glEnable(GL_SCISSOR_TEST);
314         glaDefine2DArea(&rect);
315         
316         glClearColor(.1875, .1875, .1875, 1.0); 
317         glClear(GL_COLOR_BUFFER_BIT);
318
319         if (just_clear || !R.rectot) {
320                 glColor3ub(0, 0, 0);
321                 glRectfv(disprect[0], disprect[1]);
322         } else {
323                 glPixelZoom(rw->zoom, rw->zoom);
324                 if(rw->flags & RW_FLAGS_ALPHA) {
325                         char *rect= (char *)R.rectot;
326                         
327                         glColorMask(1, 0, 0, 0);
328                         glaDrawPixelsSafe(disprect[0][0], disprect[0][1], R.rectx, R.recty, rect+3);
329                         glColorMask(0, 1, 0, 0);
330                         glaDrawPixelsSafe(disprect[0][0], disprect[0][1], R.rectx, R.recty, rect+2);
331                         glColorMask(0, 0, 1, 0);
332                         glaDrawPixelsSafe(disprect[0][0], disprect[0][1], R.rectx, R.recty, rect+1);
333                         glColorMask(1, 1, 1, 1);
334                         
335                 }
336                 else {
337                         glaDrawPixelsSafe(disprect[0][0], disprect[0][1], R.rectx, R.recty, R.rectot);
338                 }
339                 glPixelZoom(1.0, 1.0);
340         }
341         
342         /* info text is overlayed on bottom */
343         if (rw->info_text) {
344                 float w;
345                 glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
346                 glEnable(GL_BLEND);
347                 w=186.0*strlen(rw->info_text)/30;
348                 glColor4f(.5,.5,.5,.25);
349                 glRectf(0.0,0.0,w,30.0);
350                 glDisable(GL_BLEND);
351                 glColor3ub(255, 255, 255);
352                 glRasterPos2i(10, 10);
353                 BMF_DrawString(G.font, rw->info_text);
354         }
355         
356         window_swap_buffers(rw->win);
357         
358         if (set_back_mainwindow) mainwindow_make_active();      
359 }
360
361 /* ------ interactivity calls for RenderWin ------------- */
362
363 static void renderwin_mouse_moved(RenderWin *rw)
364 {
365         if (rw->flags & RW_FLAGS_PIXEL_EXAMINING) {
366                 int imgco[2];
367                 char buf[64];
368                 int *pxlz;      // zbuffer is signed 
369                 char *pxl;
370
371                 if (R.rectot && renderwin_win_to_image_co(rw, rw->lmouse, imgco)) {
372                         pxl= (char*) &R.rectot[R.rectx*imgco[1] + imgco[0]];
373                         
374                         if (R.rectz) {
375                                 pxlz= &R.rectz[R.rectx*imgco[1] + imgco[0]];                    
376                                 sprintf(buf, "R: %d, G: %d, B: %d, A: %d, Z: %f", pxl[0], pxl[1], pxl[2], pxl[3], 0.5+0.5*( ((float)*pxlz)/(float)INT_MAX) );
377                         }
378                         else {
379                                 sprintf(buf, "R: %d, G: %d, B: %d, A: %d", pxl[0], pxl[1], pxl[2], pxl[3]);                     
380                         }
381
382                         renderwin_set_infotext(rw, buf);
383                         renderwin_queue_redraw(rw);
384                 } else {
385                         renderwin_set_infotext(rw, NULL);
386                         renderwin_queue_redraw(rw);
387                 }
388         } 
389         else if (rw->flags & RW_FLAGS_PANNING) {
390                 int delta_x= rw->lmouse[0] - rw->pan_mouse_start[0];
391                 int delta_y= rw->lmouse[1] - rw->pan_mouse_start[1];
392         
393                 rw->zoomofs[0]= rw->pan_ofs_start[0] - delta_x/rw->zoom;
394                 rw->zoomofs[1]= rw->pan_ofs_start[1] - delta_y/rw->zoom;
395                 rw->zoomofs[0]= CLAMPIS(rw->zoomofs[0], -R.rectx/2, R.rectx/2);
396                 rw->zoomofs[1]= CLAMPIS(rw->zoomofs[1], -R.recty/2, R.recty/2);
397
398                 renderwin_queue_redraw(rw);
399         } 
400         else if (rw->flags & RW_FLAGS_OLDZOOM) {
401                 float ndc[2];
402                 int w, h;
403
404                 window_get_size(rw->win, &w, &h);
405                 h-= RW_HEADERY;
406                 renderwin_win_to_ndc(rw, rw->lmouse, ndc);
407
408                 rw->zoomofs[0]= -0.5*ndc[0]*(w-R.rectx*rw->zoom)/rw->zoom;
409                 rw->zoomofs[1]= -0.5*ndc[1]*(h-R.recty*rw->zoom)/rw->zoom;
410
411                 renderwin_queue_redraw(rw);
412         }
413 }
414
415 static void renderwin_mousebut_changed(RenderWin *rw)
416 {
417         if (rw->mbut[0]) {
418                 rw->flags|= RW_FLAGS_PIXEL_EXAMINING;
419         } 
420         else if (rw->mbut[1]) {
421                 rw->flags|= RW_FLAGS_PANNING;
422                 rw->pan_mouse_start[0]= rw->lmouse[0];
423                 rw->pan_mouse_start[1]= rw->lmouse[1];
424                 rw->pan_ofs_start[0]= rw->zoomofs[0];
425                 rw->pan_ofs_start[1]= rw->zoomofs[1];
426         } 
427         else {
428                 if (rw->flags & RW_FLAGS_PANNING) {
429                         rw->flags &= ~RW_FLAGS_PANNING;
430                         renderwin_queue_redraw(rw);
431                 }
432                 if (rw->flags & RW_FLAGS_PIXEL_EXAMINING) {
433                         rw->flags&= ~RW_FLAGS_PIXEL_EXAMINING;
434                         renderwin_set_infotext(rw, NULL);
435                         renderwin_queue_redraw(rw);
436                 }
437         }
438 }
439
440
441 /* handler for renderwin, passed on to Ghost */
442 static void renderwin_handler(Window *win, void *user_data, short evt, short val, char ascii)
443 {
444         RenderWin *rw= user_data;
445
446         // added this for safety, while render it's just creating bezerk results
447         if(R.flag & R_RENDERING) {
448                 if(evt==ESCKEY && val) 
449                         rw->flags|= RW_FLAGS_ESCAPE;
450                 return;
451         }
452         
453         if (evt==RESHAPE) {
454                 renderwin_reshape(rw);
455         } 
456         else if (evt==REDRAW) {
457                 renderwin_draw(rw, 0);
458         } 
459         else if (evt==WINCLOSE) {
460                 BIF_close_render_display();
461         } 
462         else if (evt==INPUTCHANGE) {
463                 rw->active= val;
464
465                 if (!val && (rw->flags&RW_FLAGS_OLDZOOM)) {
466                         rw->flags&= ~RW_FLAGS_OLDZOOM;
467                         renderwin_reset_view(rw);
468                 }
469         } 
470         else if (ELEM(evt, MOUSEX, MOUSEY)) {
471                 rw->lmouse[evt==MOUSEY]= val;
472                 renderwin_mouse_moved(rw);
473         } 
474         else if (ELEM3(evt, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) {
475                 int which= (evt==LEFTMOUSE)?0:(evt==MIDDLEMOUSE)?1:2;
476                 rw->mbut[which]= val;
477                 renderwin_mousebut_changed(rw);
478         } 
479         else if (val) {
480                 if (evt==ESCKEY) {
481                         if (rw->flags&RW_FLAGS_OLDZOOM) {
482                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
483                                 renderwin_reset_view(rw);
484                         } 
485                         else {
486                                 rw->flags|= RW_FLAGS_ESCAPE;
487                                 mainwindow_raise();
488                                 mainwindow_make_active();
489                                 rw->active= 0;
490                         }
491                 } 
492                 else if( evt==AKEY) {
493                         rw->flags ^= RW_FLAGS_ALPHA;
494                         renderwin_queue_redraw(render_win);
495                 }
496                 else if (evt==JKEY) {
497                         if(R.flag==0) BIF_swap_render_rects();
498                 } 
499                 else if (evt==ZKEY) {
500                         if (rw->flags&RW_FLAGS_OLDZOOM) {
501                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
502                                 renderwin_reset_view(rw);
503                         } else {
504                                 rw->zoom= 2.0;
505                                 rw->flags|= RW_FLAGS_OLDZOOM;
506                                 renderwin_mouse_moved(rw);
507                         }
508                 } 
509                 else if (evt==PADPLUSKEY) {
510                         if (rw->zoom<15.9) {
511                                 if(rw->zoom>0.5 && rw->zoom<1.0) rw->zoom= 1.0;
512                                 else rw->zoom*= 2.0;
513                                 renderwin_queue_redraw(rw);
514                         }
515                 } 
516                 else if (evt==PADMINUS) {
517                         if (rw->zoom>0.26) {
518                                 if(rw->zoom>1.0 && rw->zoom<2.0) rw->zoom= 1.0;
519                                 else rw->zoom*= 0.5;
520                                 renderwin_queue_redraw(rw);
521                         }
522                 } 
523                 else if (evt==PADENTER || evt==HOMEKEY) {
524                         if (rw->flags&RW_FLAGS_OLDZOOM) {
525                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
526                         }
527                         renderwin_reset_view(rw);
528                 } 
529                 else if (evt==F3KEY) {
530                         if(R.flag==0) {
531                                 mainwindow_raise();
532                                 mainwindow_make_active();
533                                 rw->active= 0;
534                                 areawinset(find_biggest_area()->win);
535                                 BIF_save_rendered_image();
536                         }
537                 } 
538                 else if (evt==F11KEY) {
539                         BIF_toggle_render_display();
540                 } 
541                 else if (evt==F12KEY) {
542                         /* if it's rendering, this flag is set */
543                         if(R.flag==0) BIF_do_render(0);
544                 }
545         }
546 }
547
548 static char *renderwin_get_title(int doswap)
549 {
550         static int swap= 0;
551         char *title="";
552         
553         swap+= doswap;
554         
555         if(swap & 1) {
556                 if (G.scene->r.renderer==R_YAFRAY) title = "YafRay:Render (spare)";
557                 else title = "Blender:Render (spare)";
558         }
559         else {
560                 if (G.scene->r.renderer==R_YAFRAY) title = "YafRay:Render";
561                 else title = "Blender:Render";
562         }
563
564         return title;
565 }
566
567 /* opens window and allocs struct */
568 static void open_renderwin(int winpos[2], int winsize[2])
569 {
570         extern void mywindow_build_and_set_renderwin( int orx, int ory, int sizex, int sizey); // mywindow.c
571         Window *win;
572         char *title;
573         
574         title= renderwin_get_title(0);  /* 0 = no swap */
575         win= window_open(title, winpos[0], winpos[1], winsize[0], winsize[1]+RW_HEADERY, 0);
576
577         render_win= renderwin_alloc(win);
578
579         /* Ghost calls handler */
580         window_set_handler(win, renderwin_handler, render_win);
581
582         winlay_process_events(0);
583         window_make_active(render_win->win);
584         winlay_process_events(0);
585         
586         /* mywindow has to know about it too */
587         mywindow_build_and_set_renderwin(winpos[0], winpos[1], winsize[0], winsize[1]+RW_HEADERY);
588         /* and we should be able to draw 3d in it */
589         init_gl_stuff();
590         
591         renderwin_draw(render_win, 1);
592         renderwin_draw(render_win, 1);
593 }
594
595 /* -------------- callbacks for render loop: Window (RenderWin) ----------------------- */
596
597 /* calculations for window size and position */
598 void calc_renderwin_rectangle(int posmask, int renderpos_r[2], int rendersize_r[2]) 
599 {
600         int scr_w, scr_h, x, y, div= 0;
601         float ndc_x= 0.0, ndc_y= 0.0;
602
603                 /* XXX, we temporarily hack the screen size and position so
604                  * the window is always 60 pixels away from a side, really need
605                  * a GHOST_GetMaxWindowBounds or so - zr
606                  */
607         winlay_get_screensize(&scr_w, &scr_h);
608         
609         rendersize_r[0]= (G.scene->r.size*G.scene->r.xsch)/100;
610         rendersize_r[1]= (G.scene->r.size*G.scene->r.ysch)/100;
611         
612         /* crop option makes image smaller */
613         if ((G.scene->r.mode & R_BORDER) && (G.scene->r.mode & R_MOVIECROP)) { 
614                 rendersize_r[0]*= (G.scene->r.border.xmax-G.scene->r.border.xmin);
615                 rendersize_r[1]*= (G.scene->r.border.ymax-G.scene->r.border.ymin);
616         }
617         
618         if(G.scene->r.mode & R_PANORAMA) {
619                 rendersize_r[0]*= G.scene->r.xparts;
620                 rendersize_r[1]*= G.scene->r.yparts;
621         }
622         /* increased size of clipping for OSX, should become an option instead */
623         rendersize_r[0]= CLAMPIS(rendersize_r[0], 100, scr_w-120);
624         rendersize_r[1]= CLAMPIS(rendersize_r[1], 100, scr_h-120);
625
626         for (y=-1; y<=1; y++) {
627                 for (x=-1; x<=1; x++) {
628                         if (posmask & (1<<((y+1)*3 + (x+1)))) {
629                                 ndc_x+= x;
630                                 ndc_y+= y;
631                                 div++;
632                         }
633                 }
634         }
635
636         if (div) {
637                 ndc_x/= div;
638                 ndc_y/= div;
639         }
640                 
641         renderpos_r[0]= 60 + (scr_w-90-rendersize_r[0])*(ndc_x*0.5 + 0.5);
642         renderpos_r[1]= 30 + (scr_h-90-rendersize_r[1])*(ndc_y*0.5 + 0.5);
643 }
644         
645 /* init renderwin, alloc/open/resize */
646 static void renderwin_init_display_cb(void) 
647 {
648         if (G.afbreek == 0) {
649                 int rendersize[2], renderpos[2];
650
651                 calc_renderwin_rectangle(G.winpos, renderpos, rendersize);
652
653                 if (!render_win) {
654                         open_renderwin(renderpos, rendersize);
655                         renderwin_reset_view(render_win); // incl. autozoom for large images
656                 } else {
657                         int win_x, win_y;
658                         int win_w, win_h;
659
660                         window_get_position(render_win->win, &win_x, &win_y);
661                         window_get_size(render_win->win, &win_w, &win_h);
662                         win_h-= RW_HEADERY;
663
664                                 /* XXX, this is nasty and I guess bound to cause problems,
665                                  * but to ensure the window is at the user specified position
666                                  * and size we reopen the window all the time... we need
667                                  * a ghost _set_position to fix this -zr
668                                  */
669                                  
670                                  /* XXX, well... it is nasty yes, and reopens windows each time on
671                                     subsequent renders. Better rule is to make it reopen only only
672                                     size change, and use the preferred position only on open_renderwin
673                                         cases (ton)
674                                  */
675                         if(rendersize[0]!= win_w || rendersize[1]!= win_h) {
676                                 BIF_close_render_display();
677                                 open_renderwin(renderpos, rendersize);
678                         }
679                         else {
680                                 window_raise(render_win->win);
681                                 window_make_active(render_win->win);
682                                 mywinset(2);    // to assign scissor/viewport again in mywindow.c. is hackish yes
683                         }
684
685                         renderwin_reset_view(render_win);
686                         render_win->flags&= ~RW_FLAGS_ESCAPE;
687                         render_win->active= 1;
688                 }
689                 /* make sure we are in normal draw again */
690                 render_win->flags &= ~RW_FLAGS_ALPHA;
691         }
692 }
693
694 /* callback for redraw render win */
695 static void renderwin_clear_display_cb(short ignore) 
696 {
697         if (render_win) {
698                 window_make_active(render_win->win);    
699                 renderwin_draw(render_win, 1);
700         }
701 }
702
703 /* XXX, this is not good, we do this without any regard to state
704 * ... better is to make this an optimization of a more clear
705 * implementation. the bug shows up when you do something like
706 * open the window, then draw part of the progress, then get
707 * a redraw event. whatever can go wrong will. -zr
708 *
709 * Note: blocked queue handling while rendering to prevent that (ton)
710 */
711
712 /* in render window; display a couple of scanlines of rendered image (see callback below) */
713 static void renderwin_progress(RenderWin *rw, int start_y, int nlines, int rect_w, int rect_h, unsigned char *rect)
714 {
715         float disprect[2][2];
716         rcti win_rct;
717
718         win_rct.xmin= win_rct.ymin= 0;
719         window_get_size(rw->win, &win_rct.xmax, &win_rct.ymax);
720         win_rct.ymax-= RW_HEADERY;
721         
722         renderwin_get_disprect(rw, disprect);
723         
724         /* for efficiency & speed; not drawing in Blender UI while rendering */
725         //window_make_active(rw->win);
726
727         glEnable(GL_SCISSOR_TEST);
728         glaDefine2DArea(&win_rct);
729
730         glDrawBuffer(GL_FRONT);
731         glPixelZoom(rw->zoom, rw->zoom);
732         glaDrawPixelsSafe(disprect[0][0], disprect[0][1] + start_y*rw->zoom, rect_w, nlines, &rect[start_y*rect_w*4]);
733         glPixelZoom(1.0, 1.0);
734         glFlush();
735         glDrawBuffer(GL_BACK);
736 }
737
738
739 /* in render window; display a couple of scanlines of rendered image */
740 static void renderwin_progress_display_cb(int y1, int y2, int w, int h, unsigned int *rect)
741 {
742         if (render_win) {
743                 renderwin_progress(render_win, y1, y2-y1+1, w, h, (unsigned char*) rect);
744         }
745 }
746
747
748 /* -------------- callbacks for render loop: in View3D ----------------------- */
749
750
751 static View3D *render_view3d = NULL;
752
753 /* init Render view callback */
754 static void renderview_init_display_cb(void)
755 {
756         ScrArea *sa;
757
758                 /* Choose the first view with a persp camera,
759                  * if one doesn't exist we will get the first
760                  * View3D window.
761                  */ 
762         render_view3d= NULL;
763         for (sa= G.curscreen->areabase.first; sa; sa= sa->next) {
764                 if (sa->win && sa->spacetype==SPACE_VIEW3D) {
765                         View3D *vd= sa->spacedata.first;
766                         
767                         if (vd->persp==2 && vd->camera==G.scene->camera) {
768                                 render_view3d= vd;
769                                 break;
770                         } else if (!render_view3d) {
771                                 render_view3d= vd;
772                         }
773                 }
774         }
775 }
776
777
778 /* in 3d view; display a couple of scanlines of rendered image */
779 static void renderview_progress_display_cb(int y1, int y2, int w, int h, unsigned int *rect)
780 {
781         if (render_view3d) {
782                 View3D *v3d= render_view3d;
783                 int nlines= y2-y1+1;
784                 float sx, sy, facx, facy;
785                 rcti win_rct, vb;
786
787                 calc_viewborder(v3d, &vb);
788                 
789                 /* if border render  */
790                 if(G.scene->r.mode & R_BORDER) { 
791                         
792                         /* but, if image is full (at end of border render, without crop) we don't */
793                         if(R.rectx != (G.scene->r.size*G.scene->r.xsch)/100 ||
794                            R.recty != (G.scene->r.size*G.scene->r.ysch)/100 ) {
795                         
796                                 facx= (float) (vb.xmax-vb.xmin);
797                                 facy= (float) (vb.ymax-vb.ymin);
798                                 
799                                 vb.xmax= vb.xmin + facx*G.scene->r.border.xmax;
800                                 vb.ymax= vb.ymin + facy*G.scene->r.border.ymax;
801                                 vb.xmin+= facx*G.scene->r.border.xmin;
802                                 vb.ymin+= facy*G.scene->r.border.ymin;
803                         }
804                 }
805                         
806                 facx= (float) (vb.xmax-vb.xmin)/R.rectx;
807                 facy= (float) (vb.ymax-vb.ymin)/R.recty;
808
809                 bwin_get_rect(v3d->area->win, &win_rct);
810
811                 glaDefine2DArea(&win_rct);
812         
813                 glDrawBuffer(GL_FRONT);
814                 
815                 sx= vb.xmin;
816                 sy= vb.ymin + facy*y1;
817
818                 glPixelZoom(facx, facy);
819                 glaDrawPixelsSafe(sx, sy, w, nlines, rect+w*y1);
820                 glPixelZoom(1.0, 1.0);
821
822                 glFlush();
823                 glDrawBuffer(GL_BACK);
824                 v3d->flag |= V3D_DISPIMAGE;
825                 v3d->area->win_swap= WIN_FRONT_OK;
826                 
827         }
828 }
829
830 /* in 3d view; display stats of rendered image */
831 static void renderview_draw_render_info(char *str)
832 {
833         if (render_view3d) {
834                 View3D *v3d= render_view3d;
835                 rcti vb, win_rct;
836                 
837                 calc_viewborder(v3d, &vb);
838                 
839                 bwin_get_rect(v3d->area->win, &win_rct);
840                 glaDefine2DArea(&win_rct);
841                 
842                 glDrawBuffer(GL_FRONT);
843                 
844                 /* clear header rect */
845                 BIF_ThemeColor(TH_HEADER);
846                 glRecti(vb.xmin, vb.ymax, vb.xmax, vb.ymax+RW_HEADERY);
847                 
848                 if(str) {
849                         BIF_ThemeColor(TH_TEXT);
850                         glRasterPos2i(vb.xmin+12, vb.ymax+5);
851                         BMF_DrawString(G.fonts, str);
852                 }
853                 
854                 glFlush();
855                 glDrawBuffer(GL_BACK);
856
857                 v3d->area->win_swap= WIN_FRONT_OK;
858                 
859         }
860 }
861
862
863 /* -------------- callbacks for render loop: interactivity ----------------------- */
864
865
866 /* callback for print info in top header of renderwin */
867 /* time is only not zero on last call, we then don't update the other stats */ 
868 static void printrenderinfo_cb(double time, int sample)
869 {
870         extern int mem_in_use;
871         static int totvert=0, totvlak=0, tothalo=0, totlamp=0;
872         static float megs_used_memory=0.0;
873         char str[300], *spos= str;
874                 
875         if(time==0.0) {
876                 megs_used_memory= mem_in_use/(1024.0*1024.0);
877                 totvert= R.totvert;
878                 totvlak= R.totvlak;
879                 totlamp= R.totlamp;
880                 tothalo= R.tothalo;
881         }
882         
883         if(tothalo)
884                 spos+= sprintf(spos, "Fra:%d  Ve:%d Fa:%d Ha:%d La:%d Mem:%.2fM", (G.scene->r.cfra), totvert, totvlak, tothalo, totlamp, megs_used_memory);
885         else 
886                 spos+= sprintf(spos, "Fra:%d  Ve:%d Fa:%d La:%d Mem:%.2fM", (G.scene->r.cfra), totvert, totvlak, totlamp, megs_used_memory);
887
888         if(time==0.0) {
889                 if (R.r.mode & R_FIELDS) {
890                         spos+= sprintf(spos, "Field %c ", (R.flag&R_SEC_FIELD)?'B':'A');
891                 }
892                 if (sample!=-1) {
893                         spos+= sprintf(spos, "Sample: %d    ", sample);
894                 }
895         }
896         else {
897                 extern char info_time_str[32];  // header_info.c
898                 timestr(time, info_time_str);
899                 spos+= sprintf(spos, " Time:%s ", info_time_str);
900         }
901         
902         if(render_win) {
903                 if(render_win->render_text) MEM_freeN(render_win->render_text);
904                 render_win->render_text= BLI_strdup(str);
905                 glDrawBuffer(GL_FRONT);
906                 renderwin_draw_render_info(render_win);
907                 glFlush();
908                 glDrawBuffer(GL_BACK);
909         }
910         else renderview_draw_render_info(str);
911 }
912
913 /* -------------- callback system to allow ESC from rendering ----------------------- */
914
915 /* POSIX & WIN32: this function is called all the time, and should not use cpu or resources */
916 static int test_break(void)
917 {
918
919         if(G.afbreek==2) { /* code for testing queue */
920
921                 G.afbreek= 0;
922
923                 blender_test_break(); /* tests blender interface */
924
925                 if (G.afbreek==0 && render_win) { /* tests window */
926                         winlay_process_events(0);
927                         // render_win can be closed in winlay_process_events()
928                         if (render_win == 0 || (render_win->flags & RW_FLAGS_ESCAPE)) {
929                                 G.afbreek= 1;
930                         }
931                 }
932         }
933
934         if(G.afbreek==1) return 1;
935         else return 0;
936 }
937
938
939
940 #ifdef _WIN32
941 /* we use the multimedia time here */
942 static UINT uRenderTimerId;
943
944 void CALLBACK interruptESC(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
945 {
946         if(G.afbreek==0) G.afbreek= 2;  /* code for read queue */
947 }
948
949 /* WIN32: init SetTimer callback */
950 static void init_test_break_callback()
951 {
952         timeBeginPeriod(50);
953         uRenderTimerId = timeSetEvent(250, 1, interruptESC, 0, TIME_PERIODIC);
954 }
955
956 /* WIN32: stop SetTimer callback */
957 static void end_test_break_callback()
958 {
959         timeEndPeriod(50);
960         timeKillEvent(uRenderTimerId);
961 }
962
963 #else
964 /* all other OS's support signal(SIGVTALRM) */
965
966 /* POSIX: this function goes in the signal() callback */
967 static void interruptESC(int sig)
968 {
969
970         if(G.afbreek==0) G.afbreek= 2;  /* code for read queue */
971
972         /* call again, timer was reset */
973         signal(SIGVTALRM, interruptESC);
974 }
975
976 /* POSIX: initialize timer and signal */
977 static void init_test_break_callback()
978 {
979
980         struct itimerval tmevalue;
981
982         tmevalue.it_interval.tv_sec = 0;
983         tmevalue.it_interval.tv_usec = 250000;
984         /* wanneer de eerste ? */
985         tmevalue.it_value.tv_sec = 0;
986         tmevalue.it_value.tv_usec = 10000;
987
988         signal(SIGVTALRM, interruptESC);
989         setitimer(ITIMER_VIRTUAL, &tmevalue, 0);
990
991 }
992
993 /* POSIX: stop timer and callback */
994 static void end_test_break_callback()
995 {
996         struct itimerval tmevalue;
997
998         tmevalue.it_value.tv_sec = 0;
999         tmevalue.it_value.tv_usec = 0;
1000         setitimer(ITIMER_VIRTUAL, &tmevalue, 0);
1001         signal(SIGVTALRM, SIG_IGN);
1002
1003 }
1004
1005
1006 #endif
1007
1008
1009
1010 /* -------------- callbacks for render loop: init & run! ----------------------- */
1011
1012
1013 /* - initialize displays
1014    - both opengl render as blender render
1015    - set callbacks
1016    - cleanup
1017 */
1018
1019 static void do_render(View3D *ogl_render_view3d, int anim, int force_dispwin)
1020 {
1021         
1022         /* we set this flag to prevent renderwindow queue to execute another render */
1023         R.flag= R_RENDERING;
1024
1025         if (G.displaymode == R_DISPLAYWIN || force_dispwin) {
1026                 RE_set_initrenderdisplay_callback(NULL);
1027                 RE_set_clearrenderdisplay_callback(renderwin_clear_display_cb);
1028                 RE_set_renderdisplay_callback(renderwin_progress_display_cb);
1029
1030                 renderwin_init_display_cb();
1031         } 
1032         else {
1033                 BIF_close_render_display();
1034                 RE_set_initrenderdisplay_callback(renderview_init_display_cb);
1035                 RE_set_clearrenderdisplay_callback(NULL);
1036                 RE_set_renderdisplay_callback(renderview_progress_display_cb);
1037         }
1038
1039         init_test_break_callback();
1040         RE_set_test_break_callback(test_break);
1041         
1042         RE_set_timecursor_callback(set_timecursor);
1043         RE_set_printrenderinfo_callback(printrenderinfo_cb);
1044         
1045         if (render_win) {
1046                 window_set_cursor(render_win->win, CURSOR_WAIT);
1047                 // when opening new window... not cross platform identical behaviour, so
1048                 // for now call it each time
1049                 // if(ogl_render_view3d) init_gl_stuff();
1050         }
1051         waitcursor(1);
1052
1053         G.afbreek= 0;
1054         if(G.obedit && !(G.scene->r.scemode & R_OGL)) {
1055                 exit_editmode(0);       /* 0 = no free data */
1056         }
1057
1058         if(anim) {
1059                 RE_animrender(ogl_render_view3d);
1060         }
1061         else {
1062                 RE_initrender(ogl_render_view3d);
1063         }
1064
1065         if(anim) update_for_newframe_muted();  // only when anim, causes redraw event which frustrates dispview
1066         
1067         if (render_win) window_set_cursor(render_win->win, CURSOR_STD);
1068
1069         free_filesel_spec(G.scene->r.pic);
1070
1071         G.afbreek= 0;
1072         end_test_break_callback();
1073         
1074         /* in dispiew it will destroy the image otherwise
1075            window_make_active() raises window at osx and sends redraws */
1076         if(G.displaymode==R_DISPLAYWIN) {
1077                 mainwindow_make_active();
1078         
1079                 /* after an envmap creation...  */
1080                 if(R.flag & R_REDRAW_PRV) {
1081                         BIF_all_preview_changed();
1082                 }
1083                 allqueue(REDRAWBUTSSCENE, 0);   // visualize fbuf for example
1084         }
1085         
1086         R.flag= 0;
1087         waitcursor(0);  // waitcursor checks rendering R.flag...
1088 }
1089
1090 /* finds area with a 'dispview' set */
1091 static ScrArea *find_dispimage_v3d(void)
1092 {
1093         ScrArea *sa;
1094         
1095         for (sa= G.curscreen->areabase.first; sa; sa= sa->next) {
1096                 if (sa->spacetype==SPACE_VIEW3D) {
1097                         View3D *vd= sa->spacedata.first;
1098                         if (vd->flag & V3D_DISPIMAGE)
1099                                 return sa;
1100                 }
1101         }
1102         
1103         return NULL;
1104 }
1105
1106 /* used for swapping with spare buffer, when images are different size */
1107 static void scalefastrect(unsigned int *recto, unsigned int *rectn, int oldx, int oldy, int newx, int newy)
1108 {
1109         unsigned int *rect, *newrect;
1110         int x, y;
1111         int ofsx, ofsy, stepx, stepy;
1112
1113         stepx = (int)((65536.0 * (oldx - 1.0) / (newx - 1.0)) + 0.5);
1114         stepy = (int)((65536.0 * (oldy - 1.0) / (newy - 1.0)) + 0.5);
1115         ofsy = 32768;
1116         newrect= rectn;
1117         
1118         for (y = newy; y > 0 ; y--){
1119                 rect = recto;
1120                 rect += (ofsy >> 16) * oldx;
1121                 ofsy += stepy;
1122                 ofsx = 32768;
1123                 for (x = newx ; x>0 ; x--){
1124                         *newrect++ = rect[ofsx >> 16];
1125                         ofsx += stepx;
1126                 }
1127         }
1128 }
1129
1130 /* -------------- API: externally called --------------- */
1131
1132 /* not used anywhere ??? */
1133 #if 0
1134 void BIF_renderwin_make_active(void)
1135 {
1136         if(render_win) {
1137                 window_make_active(render_win->win);
1138                 mywinset(2);
1139         }
1140 }
1141 #endif
1142
1143 /* set up display, render an image or scene */
1144 void BIF_do_render(int anim)
1145 {
1146         /* if start render in 3d win, use layer from window (e.g also local view) */
1147         if(curarea && curarea->spacetype==SPACE_VIEW3D) {
1148                 int lay= G.scene->lay;
1149                 if(G.vd->lay & 0xFF000000)      // localview
1150                         G.scene->lay |= G.vd->lay;
1151                 else G.scene->lay= G.vd->lay;
1152                 
1153                 do_render(NULL, anim, 0);
1154                 
1155                 G.scene->lay= lay;
1156         }
1157         else do_render(NULL, anim, 0);
1158 }
1159
1160 /* set up display, render the current area view in an image */
1161 void BIF_do_ogl_render(View3D *ogl_render_view3d, int anim)
1162 {
1163         G.scene->r.scemode |= R_OGL;
1164         do_render(ogl_render_view3d, anim, 1);
1165         G.scene->r.scemode &= ~R_OGL;
1166 }
1167
1168 void BIF_redraw_render_rect(void)
1169 {
1170         
1171         /* redraw */
1172         if (G.displaymode == R_DISPLAYWIN) {
1173                 // don't open render_win if rendering has been
1174             // canceled or the render_win has been actively closed
1175                 if (render_win) {
1176                         renderwin_queue_redraw(render_win);
1177                 }
1178         } else {
1179                 renderview_init_display_cb();
1180                 renderview_progress_display_cb(0, R.recty-1, R.rectx, R.recty, R.rectot);
1181         }
1182 }       
1183
1184 void BIF_swap_render_rects(void)
1185 {
1186         unsigned int *temp;
1187
1188         if(R.rectspare==0) {
1189                 R.rectspare= (unsigned int *)MEM_callocN(sizeof(int)*R.rectx*R.recty, "rectot");
1190                 R.sparex= R.rectx;
1191                 R.sparey= R.recty;
1192         }
1193         else if(R.sparex!=R.rectx || R.sparey!=R.recty) {
1194                 temp= (unsigned int *)MEM_callocN(sizeof(int)*R.rectx*R.recty, "rectot");
1195                                         
1196                 scalefastrect(R.rectspare, temp, R.sparex, R.sparey, R.rectx, R.recty);
1197                 MEM_freeN(R.rectspare);
1198                 R.rectspare= temp;
1199                                         
1200                 R.sparex= R.rectx;
1201                 R.sparey= R.recty;
1202         }
1203         
1204         temp= R.rectot;
1205         R.rectot= R.rectspare;
1206         R.rectspare= temp;
1207         
1208         if (G.displaymode == R_DISPLAYWIN) {
1209                 if (render_win) {
1210                         char *tmp= render_win->render_text_spare;
1211                         render_win->render_text_spare= render_win->render_text;
1212                         render_win->render_text= tmp;
1213                         
1214                         window_set_title(render_win->win, renderwin_get_title(1));
1215                         
1216                 }
1217         }
1218
1219         /* redraw */
1220         BIF_redraw_render_rect();
1221 }                               
1222
1223 /* called from usiblender.c too, to free and close renderwin */
1224 void BIF_close_render_display(void)
1225 {
1226         if (render_win) {
1227
1228                 if (render_win->info_text) MEM_freeN(render_win->info_text);
1229                 if (render_win->render_text) MEM_freeN(render_win->render_text);
1230                 if (render_win->render_text_spare) MEM_freeN(render_win->render_text_spare);
1231
1232                 window_destroy(render_win->win); /* ghost close window */
1233                 MEM_freeN(render_win);
1234
1235                 render_win= NULL;
1236         }
1237 }
1238
1239
1240 /* typical with F11 key, show image or hide/close */
1241 void BIF_toggle_render_display(void) 
1242 {
1243         ScrArea *sa= find_dispimage_v3d();
1244         
1245         if(R.rectot==NULL);             // do nothing
1246         else if (render_win && G.displaymode==R_DISPLAYWIN) {
1247                 if(render_win->active) {
1248                         mainwindow_raise();
1249                         mainwindow_make_active();
1250                         render_win->active= 0;
1251                 }
1252                 else {
1253                         window_raise(render_win->win);
1254                         window_make_active(render_win->win);
1255                         render_win->active= 1;
1256                 }
1257         } 
1258         else if (sa && G.displaymode==R_DISPLAYVIEW) {
1259                 View3D *vd= sa->spacedata.first;
1260                 vd->flag &= ~V3D_DISPIMAGE;
1261                 scrarea_queue_winredraw(sa);
1262         } 
1263         else {
1264                 if (G.displaymode == R_DISPLAYWIN) {
1265                         renderwin_init_display_cb();
1266                 } else {
1267                         if (render_win) {
1268                                 BIF_close_render_display();
1269                         }
1270                         renderview_init_display_cb();
1271                         renderview_progress_display_cb(0, R.recty-1, R.rectx, R.recty, R.rectot);
1272                 }
1273         }
1274 }
1275
1276 void BIF_renderwin_set_custom_cursor(unsigned char mask[16][2], unsigned char bitmap[16][2])
1277 {
1278         if (render_win) {
1279                 window_set_custom_cursor(render_win->win, mask, bitmap, 7, 7);
1280         }
1281 }