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