Result of 2 weeks of quiet coding work in Greece :)
[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                                 
684                                 mywinset(2);    // to assign scissor/viewport again in mywindow.c. is hackish yes, but otherwise it draws in header of button for ogl header
685                                 {
686                                         rcti win_rct;
687                                         win_rct.xmin= win_rct.ymin= 0;
688                                         window_get_size(render_win->win, &win_rct.xmax, &win_rct.ymax);
689                                         win_rct.ymax-= RW_HEADERY;
690                                         glaDefine2DArea(&win_rct);
691                                 }
692                         }
693
694                         renderwin_reset_view(render_win);
695                         render_win->flags&= ~RW_FLAGS_ESCAPE;
696                         render_win->active= 1;
697                 }
698                 /* make sure we are in normal draw again */
699                 render_win->flags &= ~RW_FLAGS_ALPHA;
700         }
701 }
702
703 /* callback for redraw render win */
704 static void renderwin_clear_display_cb(short ignore) 
705 {
706         if (render_win) {
707                 window_make_active(render_win->win);    
708                 renderwin_draw(render_win, 1);
709         }
710 }
711
712 /* XXX, this is not good, we do this without any regard to state
713 * ... better is to make this an optimization of a more clear
714 * implementation. the bug shows up when you do something like
715 * open the window, then draw part of the progress, then get
716 * a redraw event. whatever can go wrong will. -zr
717 *
718 * Note: blocked queue handling while rendering to prevent that (ton)
719 */
720
721 /* in render window; display a couple of scanlines of rendered image (see callback below) */
722 static void renderwin_progress(RenderWin *rw, int start_y, int nlines, int rect_w, int rect_h, unsigned char *rect)
723 {
724         float disprect[2][2];
725         rcti win_rct;
726
727         win_rct.xmin= win_rct.ymin= 0;
728         window_get_size(rw->win, &win_rct.xmax, &win_rct.ymax);
729         win_rct.ymax-= RW_HEADERY;
730         
731         renderwin_get_disprect(rw, disprect);
732         
733         /* for efficiency & speed; not drawing in Blender UI while rendering */
734         //window_make_active(rw->win);
735
736         glEnable(GL_SCISSOR_TEST);
737         glaDefine2DArea(&win_rct);
738
739         glDrawBuffer(GL_FRONT);
740         glPixelZoom(rw->zoom, rw->zoom);
741         glaDrawPixelsSafe(disprect[0][0], disprect[0][1] + start_y*rw->zoom, rect_w, nlines, &rect[start_y*rect_w*4]);
742         glPixelZoom(1.0, 1.0);
743         glFlush();
744         glDrawBuffer(GL_BACK);
745 }
746
747
748 /* in render window; display a couple of scanlines of rendered image */
749 static void renderwin_progress_display_cb(int y1, int y2, int w, int h, unsigned int *rect)
750 {
751         if (render_win) {
752                 renderwin_progress(render_win, y1, y2-y1+1, w, h, (unsigned char*) rect);
753         }
754 }
755
756
757 /* -------------- callbacks for render loop: in View3D ----------------------- */
758
759
760 static View3D *render_view3d = NULL;
761
762 /* init Render view callback */
763 static void renderview_init_display_cb(void)
764 {
765         ScrArea *sa;
766
767                 /* Choose the first view with a persp camera,
768                  * if one doesn't exist we will get the first
769                  * View3D window.
770                  */ 
771         render_view3d= NULL;
772         for (sa= G.curscreen->areabase.first; sa; sa= sa->next) {
773                 if (sa->win && sa->spacetype==SPACE_VIEW3D) {
774                         View3D *vd= sa->spacedata.first;
775                         
776                         if (vd->persp==2 && vd->camera==G.scene->camera) {
777                                 render_view3d= vd;
778                                 break;
779                         } else if (!render_view3d) {
780                                 render_view3d= vd;
781                         }
782                 }
783         }
784 }
785
786
787 /* in 3d view; display a couple of scanlines of rendered image */
788 static void renderview_progress_display_cb(int y1, int y2, int w, int h, unsigned int *rect)
789 {
790         if (render_view3d) {
791                 View3D *v3d= render_view3d;
792                 int nlines= y2-y1+1;
793                 float sx, sy, facx, facy;
794                 rcti win_rct, vb;
795
796                 calc_viewborder(v3d, &vb);
797                 
798                 /* if border render  */
799                 if(G.scene->r.mode & R_BORDER) { 
800                         
801                         /* but, if image is full (at end of border render, without crop) we don't */
802                         if(R.rectx != (G.scene->r.size*G.scene->r.xsch)/100 ||
803                            R.recty != (G.scene->r.size*G.scene->r.ysch)/100 ) {
804                         
805                                 facx= (float) (vb.xmax-vb.xmin);
806                                 facy= (float) (vb.ymax-vb.ymin);
807                                 
808                                 vb.xmax= vb.xmin + facx*G.scene->r.border.xmax;
809                                 vb.ymax= vb.ymin + facy*G.scene->r.border.ymax;
810                                 vb.xmin+= facx*G.scene->r.border.xmin;
811                                 vb.ymin+= facy*G.scene->r.border.ymin;
812                         }
813                 }
814                         
815                 facx= (float) (vb.xmax-vb.xmin)/R.rectx;
816                 facy= (float) (vb.ymax-vb.ymin)/R.recty;
817
818                 bwin_get_rect(v3d->area->win, &win_rct);
819
820                 glaDefine2DArea(&win_rct);
821         
822                 glDrawBuffer(GL_FRONT);
823                 
824                 sx= vb.xmin;
825                 sy= vb.ymin + facy*y1;
826
827                 glPixelZoom(facx, facy);
828                 glaDrawPixelsSafe(sx, sy, w, nlines, rect+w*y1);
829                 glPixelZoom(1.0, 1.0);
830
831                 glFlush();
832                 glDrawBuffer(GL_BACK);
833                 v3d->flag |= V3D_DISPIMAGE;
834                 v3d->area->win_swap= WIN_FRONT_OK;
835                 
836         }
837 }
838
839 /* in 3d view; display stats of rendered image */
840 static void renderview_draw_render_info(char *str)
841 {
842         if (render_view3d) {
843                 View3D *v3d= render_view3d;
844                 rcti vb, win_rct;
845                 
846                 calc_viewborder(v3d, &vb);
847                 
848                 bwin_get_rect(v3d->area->win, &win_rct);
849                 glaDefine2DArea(&win_rct);
850                 
851                 glDrawBuffer(GL_FRONT);
852                 
853                 /* clear header rect */
854                 BIF_ThemeColor(TH_HEADER);
855                 glRecti(vb.xmin, vb.ymax, vb.xmax, vb.ymax+RW_HEADERY);
856                 
857                 if(str) {
858                         BIF_ThemeColor(TH_TEXT);
859                         glRasterPos2i(vb.xmin+12, vb.ymax+5);
860                         BMF_DrawString(G.fonts, str);
861                 }
862                 
863                 glFlush();
864                 glDrawBuffer(GL_BACK);
865
866                 v3d->area->win_swap= WIN_FRONT_OK;
867                 
868         }
869 }
870
871
872 /* -------------- callbacks for render loop: interactivity ----------------------- */
873
874
875 /* callback for print info in top header of renderwin */
876 /* time is only not zero on last call, we then don't update the other stats */ 
877 static void printrenderinfo_cb(double time, int sample)
878 {
879         extern int mem_in_use;
880         static int totvert=0, totvlak=0, tothalo=0, totlamp=0;
881         static float megs_used_memory=0.0;
882         char str[300], *spos= str;
883                 
884         if(time==0.0) {
885                 megs_used_memory= mem_in_use/(1024.0*1024.0);
886                 totvert= R.totvert;
887                 totvlak= R.totvlak;
888                 totlamp= R.totlamp;
889                 tothalo= R.tothalo;
890         }
891         
892         if(tothalo)
893                 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);
894         else 
895                 spos+= sprintf(spos, "Fra:%d  Ve:%d Fa:%d La:%d Mem:%.2fM", (G.scene->r.cfra), totvert, totvlak, totlamp, megs_used_memory);
896
897         if(time==0.0) {
898                 if (R.r.mode & R_FIELDS) {
899                         spos+= sprintf(spos, "Field %c ", (R.flag&R_SEC_FIELD)?'B':'A');
900                 }
901                 if (sample!=-1) {
902                         spos+= sprintf(spos, "Sample: %d    ", sample);
903                 }
904         }
905         else {
906                 extern char info_time_str[32];  // header_info.c
907                 timestr(time, info_time_str);
908                 spos+= sprintf(spos, " Time:%s ", info_time_str);
909         }
910         
911         if(render_win) {
912                 if(render_win->render_text) MEM_freeN(render_win->render_text);
913                 render_win->render_text= BLI_strdup(str);
914                 glDrawBuffer(GL_FRONT);
915                 renderwin_draw_render_info(render_win);
916                 glFlush();
917                 glDrawBuffer(GL_BACK);
918         }
919         else renderview_draw_render_info(str);
920 }
921
922 /* -------------- callback system to allow ESC from rendering ----------------------- */
923
924 /* POSIX & WIN32: this function is called all the time, and should not use cpu or resources */
925 static int test_break(void)
926 {
927
928         if(G.afbreek==2) { /* code for testing queue */
929
930                 G.afbreek= 0;
931
932                 blender_test_break(); /* tests blender interface */
933
934                 if (G.afbreek==0 && render_win) { /* tests window */
935                         winlay_process_events(0);
936                         // render_win can be closed in winlay_process_events()
937                         if (render_win == 0 || (render_win->flags & RW_FLAGS_ESCAPE)) {
938                                 G.afbreek= 1;
939                         }
940                 }
941         }
942
943         if(G.afbreek==1) return 1;
944         else return 0;
945 }
946
947
948
949 #ifdef _WIN32
950 /* we use the multimedia time here */
951 static UINT uRenderTimerId;
952
953 void CALLBACK interruptESC(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
954 {
955         if(G.afbreek==0) G.afbreek= 2;  /* code for read queue */
956 }
957
958 /* WIN32: init SetTimer callback */
959 static void init_test_break_callback()
960 {
961         timeBeginPeriod(50);
962         uRenderTimerId = timeSetEvent(250, 1, interruptESC, 0, TIME_PERIODIC);
963 }
964
965 /* WIN32: stop SetTimer callback */
966 static void end_test_break_callback()
967 {
968         timeEndPeriod(50);
969         timeKillEvent(uRenderTimerId);
970 }
971
972 #else
973 /* all other OS's support signal(SIGVTALRM) */
974
975 /* POSIX: this function goes in the signal() callback */
976 static void interruptESC(int sig)
977 {
978
979         if(G.afbreek==0) G.afbreek= 2;  /* code for read queue */
980
981         /* call again, timer was reset */
982         signal(SIGVTALRM, interruptESC);
983 }
984
985 /* POSIX: initialize timer and signal */
986 static void init_test_break_callback()
987 {
988
989         struct itimerval tmevalue;
990
991         tmevalue.it_interval.tv_sec = 0;
992         tmevalue.it_interval.tv_usec = 250000;
993         /* wanneer de eerste ? */
994         tmevalue.it_value.tv_sec = 0;
995         tmevalue.it_value.tv_usec = 10000;
996
997         signal(SIGVTALRM, interruptESC);
998         setitimer(ITIMER_VIRTUAL, &tmevalue, 0);
999
1000 }
1001
1002 /* POSIX: stop timer and callback */
1003 static void end_test_break_callback()
1004 {
1005         struct itimerval tmevalue;
1006
1007         tmevalue.it_value.tv_sec = 0;
1008         tmevalue.it_value.tv_usec = 0;
1009         setitimer(ITIMER_VIRTUAL, &tmevalue, 0);
1010         signal(SIGVTALRM, SIG_IGN);
1011
1012 }
1013
1014
1015 #endif
1016
1017
1018
1019 /* -------------- callbacks for render loop: init & run! ----------------------- */
1020
1021
1022 /* - initialize displays
1023    - both opengl render as blender render
1024    - set callbacks
1025    - cleanup
1026 */
1027
1028 static void do_render(View3D *ogl_render_view3d, int anim, int force_dispwin)
1029 {
1030         
1031         /* we set this flag to prevent renderwindow queue to execute another render */
1032         R.flag= R_RENDERING;
1033         G.afbreek= 0;
1034
1035         if (G.displaymode == R_DISPLAYWIN || force_dispwin) {
1036                 RE_set_initrenderdisplay_callback(NULL);
1037                 RE_set_clearrenderdisplay_callback(renderwin_clear_display_cb);
1038                 RE_set_renderdisplay_callback(renderwin_progress_display_cb);
1039
1040                 renderwin_init_display_cb();
1041         } 
1042         else {
1043                 BIF_close_render_display();
1044                 RE_set_initrenderdisplay_callback(renderview_init_display_cb);
1045                 RE_set_clearrenderdisplay_callback(NULL);
1046                 RE_set_renderdisplay_callback(renderview_progress_display_cb);
1047         }
1048
1049         init_test_break_callback();
1050         RE_set_test_break_callback(test_break);
1051         
1052         RE_set_timecursor_callback(set_timecursor);
1053         RE_set_printrenderinfo_callback(printrenderinfo_cb);
1054         
1055         if (render_win) {
1056                 window_set_cursor(render_win->win, CURSOR_WAIT);
1057                 // when opening new window... not cross platform identical behaviour, so
1058                 // for now call it each time
1059                 // if(ogl_render_view3d) init_gl_stuff();
1060         }
1061         waitcursor(1);
1062
1063         if(G.obedit && !(G.scene->r.scemode & R_OGL)) {
1064                 exit_editmode(0);       /* 0 = no free data */
1065         }
1066
1067         if(anim) {
1068                 RE_animrender(ogl_render_view3d);
1069         }
1070         else {
1071                 RE_initrender(ogl_render_view3d);
1072         }
1073
1074         if(anim) update_for_newframe_muted();  // only when anim, causes redraw event which frustrates dispview
1075         
1076         if (render_win) window_set_cursor(render_win->win, CURSOR_STD);
1077
1078         free_filesel_spec(G.scene->r.pic);
1079
1080         G.afbreek= 0;
1081         end_test_break_callback();
1082         
1083         /* in dispiew it will destroy the image otherwise
1084            window_make_active() raises window at osx and sends redraws */
1085         if(G.displaymode==R_DISPLAYWIN) {
1086                 mainwindow_make_active();
1087         
1088                 /* after an envmap creation...  */
1089                 if(R.flag & R_REDRAW_PRV) {
1090                         BIF_all_preview_changed();
1091                 }
1092                 allqueue(REDRAWBUTSSCENE, 0);   // visualize fbuf for example
1093         }
1094         
1095         R.flag= 0;
1096         waitcursor(0);  // waitcursor checks rendering R.flag...
1097 }
1098
1099 /* finds area with a 'dispview' set */
1100 static ScrArea *find_dispimage_v3d(void)
1101 {
1102         ScrArea *sa;
1103         
1104         for (sa= G.curscreen->areabase.first; sa; sa= sa->next) {
1105                 if (sa->spacetype==SPACE_VIEW3D) {
1106                         View3D *vd= sa->spacedata.first;
1107                         if (vd->flag & V3D_DISPIMAGE)
1108                                 return sa;
1109                 }
1110         }
1111         
1112         return NULL;
1113 }
1114
1115 /* used for swapping with spare buffer, when images are different size */
1116 static void scalefastrect(unsigned int *recto, unsigned int *rectn, int oldx, int oldy, int newx, int newy)
1117 {
1118         unsigned int *rect, *newrect;
1119         int x, y;
1120         int ofsx, ofsy, stepx, stepy;
1121
1122         stepx = (int)((65536.0 * (oldx - 1.0) / (newx - 1.0)) + 0.5);
1123         stepy = (int)((65536.0 * (oldy - 1.0) / (newy - 1.0)) + 0.5);
1124         ofsy = 32768;
1125         newrect= rectn;
1126         
1127         for (y = newy; y > 0 ; y--){
1128                 rect = recto;
1129                 rect += (ofsy >> 16) * oldx;
1130                 ofsy += stepy;
1131                 ofsx = 32768;
1132                 for (x = newx ; x>0 ; x--){
1133                         *newrect++ = rect[ofsx >> 16];
1134                         ofsx += stepx;
1135                 }
1136         }
1137 }
1138
1139 /* -------------- API: externally called --------------- */
1140
1141 /* not used anywhere ??? */
1142 #if 0
1143 void BIF_renderwin_make_active(void)
1144 {
1145         if(render_win) {
1146                 window_make_active(render_win->win);
1147                 mywinset(2);
1148         }
1149 }
1150 #endif
1151
1152 /* set up display, render an image or scene */
1153 void BIF_do_render(int anim)
1154 {
1155         int slink_flag = 0;
1156
1157         if (G.f & G_DOSCRIPTLINKS) {
1158                 BPY_do_all_scripts(SCRIPT_RENDER);
1159                 if (!anim) { /* avoid FRAMECHANGED slink in render callback */
1160                         G.f &= ~G_DOSCRIPTLINKS;
1161                         slink_flag = 1;
1162                 }
1163         }
1164
1165         /* if start render in 3d win, use layer from window (e.g also local view) */
1166         if(curarea && curarea->spacetype==SPACE_VIEW3D) {
1167                 int lay= G.scene->lay;
1168                 if(G.vd->lay & 0xFF000000)      // localview
1169                         G.scene->lay |= G.vd->lay;
1170                 else G.scene->lay= G.vd->lay;
1171                 
1172                 do_render(NULL, anim, 0);
1173                 
1174                 G.scene->lay= lay;
1175         }
1176         else do_render(NULL, anim, 0);
1177
1178         if (slink_flag) G.f |= G_DOSCRIPTLINKS;
1179         if (G.f & G_DOSCRIPTLINKS) BPY_do_all_scripts(SCRIPT_POSTRENDER);
1180 }
1181
1182 /* set up display, render the current area view in an image */
1183 void BIF_do_ogl_render(View3D *ogl_render_view3d, int anim)
1184 {
1185         G.scene->r.scemode |= R_OGL;
1186         do_render(ogl_render_view3d, anim, 1);
1187         G.scene->r.scemode &= ~R_OGL;
1188 }
1189
1190 void BIF_redraw_render_rect(void)
1191 {
1192         
1193         /* redraw */
1194         if (G.displaymode == R_DISPLAYWIN) {
1195                 // don't open render_win if rendering has been
1196             // canceled or the render_win has been actively closed
1197                 if (render_win) {
1198                         renderwin_queue_redraw(render_win);
1199                 }
1200         } else {
1201                 renderview_init_display_cb();
1202                 renderview_progress_display_cb(0, R.recty-1, R.rectx, R.recty, R.rectot);
1203         }
1204 }       
1205
1206 void BIF_swap_render_rects(void)
1207 {
1208         unsigned int *temp;
1209
1210         if(R.rectspare==0) {
1211                 R.rectspare= (unsigned int *)MEM_callocN(sizeof(int)*R.rectx*R.recty, "rectot");
1212                 R.sparex= R.rectx;
1213                 R.sparey= R.recty;
1214         }
1215         else if(R.sparex!=R.rectx || R.sparey!=R.recty) {
1216                 temp= (unsigned int *)MEM_callocN(sizeof(int)*R.rectx*R.recty, "rectot");
1217                                         
1218                 scalefastrect(R.rectspare, temp, R.sparex, R.sparey, R.rectx, R.recty);
1219                 MEM_freeN(R.rectspare);
1220                 R.rectspare= temp;
1221                                         
1222                 R.sparex= R.rectx;
1223                 R.sparey= R.recty;
1224         }
1225         
1226         temp= R.rectot;
1227         R.rectot= R.rectspare;
1228         R.rectspare= temp;
1229         
1230         if (G.displaymode == R_DISPLAYWIN) {
1231                 if (render_win) {
1232                         char *tmp= render_win->render_text_spare;
1233                         render_win->render_text_spare= render_win->render_text;
1234                         render_win->render_text= tmp;
1235                         
1236                         window_set_title(render_win->win, renderwin_get_title(1));
1237                         
1238                 }
1239         }
1240
1241         /* redraw */
1242         BIF_redraw_render_rect();
1243 }                               
1244
1245 /* called from usiblender.c too, to free and close renderwin */
1246 void BIF_close_render_display(void)
1247 {
1248         if (render_win) {
1249
1250                 if (render_win->info_text) MEM_freeN(render_win->info_text);
1251                 if (render_win->render_text) MEM_freeN(render_win->render_text);
1252                 if (render_win->render_text_spare) MEM_freeN(render_win->render_text_spare);
1253
1254                 window_destroy(render_win->win); /* ghost close window */
1255                 MEM_freeN(render_win);
1256
1257                 render_win= NULL;
1258         }
1259 }
1260
1261
1262 /* typical with F11 key, show image or hide/close */
1263 void BIF_toggle_render_display(void) 
1264 {
1265         ScrArea *sa= find_dispimage_v3d();
1266         
1267         if(R.rectot==NULL);             // do nothing
1268         else if (render_win && G.displaymode==R_DISPLAYWIN) {
1269                 if(render_win->active) {
1270                         mainwindow_raise();
1271                         mainwindow_make_active();
1272                         render_win->active= 0;
1273                 }
1274                 else {
1275                         window_raise(render_win->win);
1276                         window_make_active(render_win->win);
1277                         render_win->active= 1;
1278                 }
1279         } 
1280         else if (sa && G.displaymode==R_DISPLAYVIEW) {
1281                 View3D *vd= sa->spacedata.first;
1282                 vd->flag &= ~V3D_DISPIMAGE;
1283                 scrarea_queue_winredraw(sa);
1284         } 
1285         else {
1286                 if (G.displaymode == R_DISPLAYWIN) {
1287                         renderwin_init_display_cb();
1288                 } else {
1289                         if (render_win) {
1290                                 BIF_close_render_display();
1291                         }
1292                         renderview_init_display_cb();
1293                         renderview_progress_display_cb(0, R.recty-1, R.rectx, R.recty, R.rectot);
1294                 }
1295         }
1296 }
1297
1298 void BIF_renderwin_set_custom_cursor(unsigned char mask[16][2], unsigned char bitmap[16][2])
1299 {
1300         if (render_win) {
1301                 window_set_custom_cursor(render_win->win, mask, bitmap, 7, 7);
1302         }
1303 }