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