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