More work on render stuff!
[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_scene_types.h"
67 #include "DNA_vec_types.h"
68
69 #include "BKE_global.h"
70 #include "BKE_scene.h"
71 #include "BKE_utildefines.h"
72 #include "BKE_writeavi.h"       /* movie handle */
73
74 #include "BIF_gl.h"
75 #include "BIF_glutil.h"
76 #include "BIF_graphics.h"
77 #include "BIF_screen.h"
78 #include "BIF_space.h"
79 #include "BIF_mywindow.h"
80 #include "BIF_renderwin.h"
81 #include "BIF_resources.h"
82 #include "BIF_toets.h"
83 #include "BIF_writeimage.h"
84
85 #include "BDR_editobject.h"
86 #include "BPY_extern.h" /* for BPY_do_all_scripts */
87
88 #include "BSE_view.h"
89 #include "BSE_drawview.h"
90 #include "BSE_filesel.h"
91 #include "BSE_headerbuttons.h"
92
93 #include "RE_pipeline.h"
94
95 #include "blendef.h"
96 #include "mydevice.h"
97 #include "winlay.h"
98
99 /* ------------ renderwin struct, to prevent too much global vars --------- */
100 /* ------------ only used for display in a 2nd window  --------- */
101
102
103 /* flags escape presses during event handling
104 * so we can test for user break later.
105 */
106 #define RW_FLAGS_ESCAPE         (1<<0)
107 /* old zoom style (2x, locked to mouse, exits
108 * when mouse leaves window), to be removed
109 * at some point.
110 */
111 #define RW_FLAGS_OLDZOOM                (1<<1)
112 /* on when image is being panned with middlemouse
113 */
114 #define RW_FLAGS_PANNING                (1<<2)
115 /* on when the mouse is dragging over the image
116 * to examine pixel values.
117 */
118 #define RW_FLAGS_PIXEL_EXAMINING        (1<<3)
119
120 /* forces draw of alpha */
121 #define RW_FLAGS_ALPHA          (1<<4)
122
123 /* space for info text */
124 #define RW_HEADERY              18
125
126 typedef struct {
127         Window *win;
128
129         int rectx, recty;       /* size of image */
130         float zoom, zoomofs[2];
131         int active;
132         
133         int mbut[3];
134         int lmouse[2];
135         
136         unsigned int flags;
137         
138         float pan_mouse_start[2], pan_ofs_start[2];
139
140         char *info_text;
141         char *render_text, *render_text_spare;
142         
143 } RenderWin;
144
145 static RenderWin *render_win= NULL;
146
147 /* --------------- help functions for RenderWin struct ---------------------------- */
148
149
150 /* only called in function open_renderwin */
151 static RenderWin *renderwin_alloc(Window *win)
152 {
153         RenderWin *rw= MEM_mallocN(sizeof(*rw), "RenderWin");
154         rw->win= win;
155         rw->zoom= 1.0;
156         rw->active= 0;
157         rw->flags= 0;
158         rw->zoomofs[0]= rw->zoomofs[1]= 0;
159         rw->info_text= NULL;
160         rw->render_text= rw->render_text_spare= NULL;
161
162         rw->lmouse[0]= rw->lmouse[1]= 0;
163         rw->mbut[0]= rw->mbut[1]= rw->mbut[2]= 0;
164
165         return rw;
166 }
167
168
169 static void renderwin_queue_redraw(RenderWin *rw)
170 {
171         window_queue_redraw(rw->win); // to ghost
172 }
173
174 static void renderwin_reshape(RenderWin *rw)
175 {
176         ;
177 }
178
179 static void renderwin_get_fullrect(RenderWin *rw, float fullrect_r[2][2])
180 {
181         float display_w, display_h;
182         float cent_x, cent_y;
183         int w, h;
184
185         window_get_size(rw->win, &w, &h);
186         h-= RW_HEADERY;
187
188         display_w= rw->rectx*rw->zoom;
189         display_h= rw->recty*rw->zoom;
190         cent_x= (rw->zoomofs[0] + rw->rectx/2)*rw->zoom;
191         cent_y= (rw->zoomofs[1] + rw->recty/2)*rw->zoom;
192         
193         fullrect_r[0][0]= w/2 - cent_x;
194         fullrect_r[0][1]= h/2 - cent_y;
195         fullrect_r[1][0]= fullrect_r[0][0] + display_w;
196         fullrect_r[1][1]= fullrect_r[0][1] + display_h;
197 }
198
199         /** 
200          * Project window coordinate to image pixel coordinate.
201          * Returns true if resulting coordinate is within image.
202          */
203 static int renderwin_win_to_image_co(RenderWin *rw, int winco[2], int imgco_r[2])
204 {
205         float fullrect[2][2];
206         
207         renderwin_get_fullrect(rw, fullrect);
208         
209         imgco_r[0]= (int) ((winco[0]-fullrect[0][0])/rw->zoom);
210         imgco_r[1]= (int) ((winco[1]-fullrect[0][1])/rw->zoom);
211         
212         return (imgco_r[0]>=0 && imgco_r[1]>=0 && imgco_r[0]<rw->rectx && imgco_r[1]<rw->recty);
213 }
214
215         /**
216          * Project window coordinates to normalized device coordinates
217          * Returns true if resulting coordinate is within window.
218          */
219 static int renderwin_win_to_ndc(RenderWin *rw, int win_co[2], float ndc_r[2])
220 {
221         int w, h;
222
223         window_get_size(rw->win, &w, &h);
224         h-= RW_HEADERY;
225
226         ndc_r[0]=  ((float)(win_co[0]*2)/(w-1) - 1.0f);
227         ndc_r[1]=  ((float)(win_co[1]*2)/(h-1) - 1.0f);
228
229         return (fabs(ndc_r[0])<=1.0 && fabs(ndc_r[1])<=1.0);
230 }
231
232 static void renderwin_set_infotext(RenderWin *rw, char *info_text)
233 {
234         if (rw->info_text) MEM_freeN(rw->info_text);
235         rw->info_text= info_text?BLI_strdup(info_text):NULL;
236 }
237
238 static void renderwin_reset_view(RenderWin *rw)
239 {
240         int w, h;
241
242         if (rw->info_text) renderwin_set_infotext(rw, NULL);
243
244         /* now calculate a zoom for when image is larger than window */
245         window_get_size(rw->win, &w, &h);
246         h-= RW_HEADERY;
247
248         if(rw->rectx>w || rw->recty>h) {
249                 if(rw->rectx-w > rw->recty-h) rw->zoom= ((float)w)/((float)rw->rectx);
250                 else rw->zoom= ((float)h)/((float)rw->recty);
251         }
252         else rw->zoom= 1.0;
253
254         rw->zoomofs[0]= rw->zoomofs[1]= 0;
255         renderwin_queue_redraw(rw);
256 }
257
258 static void renderwin_draw_render_info(RenderWin *rw)
259 {
260         /* render text is added to top */
261         if(RW_HEADERY) {
262                 float colf[3];
263                 rcti rect;
264                 
265                 window_get_size(rw->win, &rect.xmax, &rect.ymax);
266                 rect.xmin= 0;
267                 rect.ymin= rect.ymax-RW_HEADERY;
268                 glEnable(GL_SCISSOR_TEST);
269                 glaDefine2DArea(&rect);
270                 
271                 /* clear header rect */
272                 BIF_SetTheme(NULL);     // sets view3d theme by default
273                 BIF_GetThemeColor3fv(TH_HEADER, colf);
274                 glClearColor(colf[0], colf[1], colf[2], 1.0); 
275                 glClear(GL_COLOR_BUFFER_BIT);
276                 
277                 if(rw->render_text) {
278                         BIF_ThemeColor(TH_TEXT);
279                         glRasterPos2i(12, 5);
280                         BMF_DrawString(G.fonts, rw->render_text);
281                 }
282                 
283                 BIF_SetTheme(curarea);  // restore theme
284         }       
285         
286 }
287
288 static void renderwin_draw(RenderWin *rw, int just_clear)
289 {
290         float fullrect[2][2];
291         int set_back_mainwindow;
292         rcti rect;
293
294         /* since renderwin uses callbacks (controlled by ghost) it can
295                 mess up active window output with redraw events after a render. 
296                 this is patchy, still WIP */
297         set_back_mainwindow = (winlay_get_active_window() != rw->win);
298         window_make_active(rw->win);
299         
300         rect.xmin= rect.ymin= 0;
301         window_get_size(rw->win, &rect.xmax, &rect.ymax);
302         rect.ymax-= RW_HEADERY;
303         
304         renderwin_get_fullrect(rw, fullrect);
305         
306         /* do this first, so window ends with correct scissor */
307         renderwin_draw_render_info(rw);
308         
309         glEnable(GL_SCISSOR_TEST);
310         glaDefine2DArea(&rect);
311         
312         glClearColor(.1875, .1875, .1875, 1.0); 
313         glClear(GL_COLOR_BUFFER_BIT);
314
315         if (just_clear) {
316                 glColor3ub(0, 0, 0);
317                 glRectfv(fullrect[0], fullrect[1]);
318         } else {
319                 RenderResult rres;
320                 
321                 RE_GetResultImage(RE_GetRender("Render"), &rres);
322                 if(rres.rectf) {
323                         
324                         glPixelZoom(rw->zoom, rw->zoom);
325                         if(rw->flags & RW_FLAGS_ALPHA) {
326                                 /* swap bytes, so alpha is most significant one, then just draw it as luminance int */
327         //                              glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
328         //                              glaDrawPixelsSafe(fullrect[0][0], fullrect[0][1], rr->rectx, rr->recty, GL_LUMINANCE, GL_UNSIGNED_INT, R.rectot);
329         //                              glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
330                         }
331                         else {
332                                 if(rres.rect32)
333                                         glaDrawPixelsSafe(fullrect[0][0], fullrect[0][1], rres.rectx, rres.recty, rres.rectx, GL_RGBA, GL_UNSIGNED_BYTE, rres.rect32);
334                                 else if(rres.rectf)
335                                         glaDrawPixelsSafe(fullrect[0][0], fullrect[0][1], rres.rectx, rres.recty, rres.rectx, GL_RGBA, GL_FLOAT, rres.rectf);
336                         }
337                         glPixelZoom(1.0, 1.0);
338                 }
339         }
340         
341         /* info text is overlayed on bottom */
342         if (rw->info_text) {
343                 float w;
344                 glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
345                 glEnable(GL_BLEND);
346                 w=186.0*strlen(rw->info_text)/30;
347                 glColor4f(.5,.5,.5,.25);
348                 glRectf(0.0,0.0,w,30.0);
349                 glDisable(GL_BLEND);
350                 glColor3ub(255, 255, 255);
351                 glRasterPos2i(10, 10);
352                 BMF_DrawString(G.font, rw->info_text);
353         }
354         
355         window_swap_buffers(rw->win);
356         
357         if (set_back_mainwindow) mainwindow_make_active();      
358 }
359
360 /* ------ interactivity calls for RenderWin ------------- */
361
362 static void renderwin_mouse_moved(RenderWin *rw)
363 {
364         RenderResult rres;
365         
366         RE_GetResultImage(RE_GetRender("Render"), &rres);
367
368         if (rw->flags & RW_FLAGS_PIXEL_EXAMINING) {
369                 int imgco[2], ofs=0;
370                 char buf[128];
371                 char *pxl;
372
373                 if (renderwin_win_to_image_co(rw, rw->lmouse, imgco)) {
374                         if (rres.rect32) {
375                                 pxl= (char*) &rres.rect32[rres.rectx*imgco[1] + imgco[0]];
376                                 ofs= sprintf(buf, "R: %d G: %d B: %d A: %d", pxl[0], pxl[1], pxl[2], pxl[3]);   
377                         }
378                         if (rres.rectf) {
379                                 float *pxlf= rres.rectf + 4*(rres.rectx*imgco[1] + imgco[0]);
380                                 ofs+= sprintf(buf+ofs, " | R: %.3f G: %.3f B: %.3f A: %.3f ", pxlf[0], pxlf[1], pxlf[2], pxlf[3]);
381                         }
382                         if (rres.rectz) {
383                                 float *pxlz= &rres.rectz[rres.rectx*imgco[1] + imgco[0]];                       
384                                 sprintf(buf+ofs, "| Z: %.3f", *pxlz );
385                         }
386
387                         renderwin_set_infotext(rw, buf);
388                         renderwin_queue_redraw(rw);
389                 } else {
390                         renderwin_set_infotext(rw, NULL);
391                         renderwin_queue_redraw(rw);
392                 }
393         } 
394         else if (rw->flags & RW_FLAGS_PANNING) {
395                 int delta_x= rw->lmouse[0] - rw->pan_mouse_start[0];
396                 int delta_y= rw->lmouse[1] - rw->pan_mouse_start[1];
397         
398                 rw->zoomofs[0]= rw->pan_ofs_start[0] - delta_x/rw->zoom;
399                 rw->zoomofs[1]= rw->pan_ofs_start[1] - delta_y/rw->zoom;
400                 rw->zoomofs[0]= CLAMPIS(rw->zoomofs[0], -rres.rectx/2, rres.rectx/2);
401                 rw->zoomofs[1]= CLAMPIS(rw->zoomofs[1], -rres.recty/2, rres.recty/2);
402
403                 renderwin_queue_redraw(rw);
404         } 
405         else if (rw->flags & RW_FLAGS_OLDZOOM) {
406                 float ndc[2];
407                 int w, h;
408
409                 window_get_size(rw->win, &w, &h);
410                 h-= RW_HEADERY;
411                 renderwin_win_to_ndc(rw, rw->lmouse, ndc);
412
413                 rw->zoomofs[0]= -0.5*ndc[0]*(w-rres.rectx*rw->zoom)/rw->zoom;
414                 rw->zoomofs[1]= -0.5*ndc[1]*(h-rres.recty*rw->zoom)/rw->zoom;
415
416                 renderwin_queue_redraw(rw);
417         }
418 }
419
420 static void renderwin_mousebut_changed(RenderWin *rw)
421 {
422         if (rw->mbut[0]) {
423                 rw->flags|= RW_FLAGS_PIXEL_EXAMINING;
424         } 
425         else if (rw->mbut[1]) {
426                 rw->flags|= RW_FLAGS_PANNING;
427                 rw->pan_mouse_start[0]= rw->lmouse[0];
428                 rw->pan_mouse_start[1]= rw->lmouse[1];
429                 rw->pan_ofs_start[0]= rw->zoomofs[0];
430                 rw->pan_ofs_start[1]= rw->zoomofs[1];
431         } 
432         else {
433                 if (rw->flags & RW_FLAGS_PANNING) {
434                         rw->flags &= ~RW_FLAGS_PANNING;
435                         renderwin_queue_redraw(rw);
436                 }
437                 if (rw->flags & RW_FLAGS_PIXEL_EXAMINING) {
438                         rw->flags&= ~RW_FLAGS_PIXEL_EXAMINING;
439                         renderwin_set_infotext(rw, NULL);
440                         renderwin_queue_redraw(rw);
441                 }
442         }
443 }
444
445
446 /* handler for renderwin, passed on to Ghost */
447 static void renderwin_handler(Window *win, void *user_data, short evt, short val, char ascii)
448 {
449         RenderWin *rw= user_data;
450
451         // added this for safety, while render it's just creating bezerk results
452         if(G.rendering) {
453                 if(evt==ESCKEY && val) 
454                         rw->flags|= RW_FLAGS_ESCAPE;
455                 return;
456         }
457         
458         if (evt==RESHAPE) {
459                 renderwin_reshape(rw);
460         } 
461         else if (evt==REDRAW) {
462                 renderwin_draw(rw, 0);
463         } 
464         else if (evt==WINCLOSE) {
465                 BIF_close_render_display();
466         } 
467         else if (evt==INPUTCHANGE) {
468                 rw->active= val;
469
470                 if (!val && (rw->flags&RW_FLAGS_OLDZOOM)) {
471                         rw->flags&= ~RW_FLAGS_OLDZOOM;
472                         renderwin_reset_view(rw);
473                 }
474         } 
475         else if (ELEM(evt, MOUSEX, MOUSEY)) {
476                 rw->lmouse[evt==MOUSEY]= val;
477                 renderwin_mouse_moved(rw);
478         } 
479         else if (ELEM3(evt, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) {
480                 int which= (evt==LEFTMOUSE)?0:(evt==MIDDLEMOUSE)?1:2;
481                 rw->mbut[which]= val;
482                 renderwin_mousebut_changed(rw);
483         } 
484         else if (val) {
485                 if (evt==ESCKEY) {
486                         if (rw->flags&RW_FLAGS_OLDZOOM) {
487                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
488                                 renderwin_reset_view(rw);
489                         } 
490                         else {
491                                 rw->flags|= RW_FLAGS_ESCAPE;
492                                 mainwindow_raise();
493                                 mainwindow_make_active();
494                                 rw->active= 0;
495                         }
496                 } 
497                 else if( evt==AKEY) {
498                         rw->flags ^= RW_FLAGS_ALPHA;
499                         renderwin_queue_redraw(render_win);
500                 }
501                 else if (evt==JKEY) {
502 //                      if(R.flag==0) BIF_swap_render_rects();
503                 } 
504                 else if (evt==ZKEY) {
505                         if (rw->flags&RW_FLAGS_OLDZOOM) {
506                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
507                                 renderwin_reset_view(rw);
508                         } else {
509                                 rw->zoom= 2.0;
510                                 rw->flags|= RW_FLAGS_OLDZOOM;
511                                 renderwin_mouse_moved(rw);
512                         }
513                 } 
514                 else if (evt==PADPLUSKEY) {
515                         if (rw->zoom<15.9) {
516                                 if(rw->zoom>0.5 && rw->zoom<1.0) rw->zoom= 1.0;
517                                 else rw->zoom*= 2.0;
518                                 renderwin_queue_redraw(rw);
519                         }
520                 } 
521                 else if (evt==PADMINUS) {
522                         if (rw->zoom>0.26) {
523                                 if(rw->zoom>1.0 && rw->zoom<2.0) rw->zoom= 1.0;
524                                 else rw->zoom*= 0.5;
525                                 renderwin_queue_redraw(rw);
526                         }
527                 } 
528                 else if (evt==PADENTER || evt==HOMEKEY) {
529                         if (rw->flags&RW_FLAGS_OLDZOOM) {
530                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
531                         }
532                         renderwin_reset_view(rw);
533                 } 
534                 else if (evt==F3KEY) {
535 //                      if(R.flag==0) {
536 //                              mainwindow_raise();
537 //                              mainwindow_make_active();
538 //                              rw->active= 0;
539 //                              areawinset(find_biggest_area()->win);
540 //                              BIF_save_rendered_image_fs();
541 //                      }
542                 } 
543                 else if (evt==F11KEY) {
544                         BIF_toggle_render_display();
545                 } 
546                 else if (evt==F12KEY) {
547                         /* if it's rendering, this flag is set */
548 //                      if(R.flag==0) BIF_do_render(0);
549                 }
550         }
551 }
552
553 static char *renderwin_get_title(int doswap)
554 {
555         static int swap= 0;
556         char *title="";
557         
558         swap+= doswap;
559         
560         if(swap & 1) {
561                 if (G.scene->r.renderer==R_YAFRAY) title = "YafRay:Render (spare)";
562                 else title = "Blender:Render (spare)";
563         }
564         else {
565                 if (G.scene->r.renderer==R_YAFRAY) title = "YafRay:Render";
566                 else title = "Blender:Render";
567         }
568
569         return title;
570 }
571
572 /* opens window and allocs struct */
573 static void open_renderwin(int winpos[2], int winsize[2], int imagesize[2])
574 {
575         extern void mywindow_build_and_set_renderwin( int orx, int ory, int sizex, int sizey); // mywindow.c
576         Window *win;
577         char *title;
578         
579         title= renderwin_get_title(0);  /* 0 = no swap */
580         win= window_open(title, winpos[0], winpos[1], winsize[0], winsize[1]+RW_HEADERY, 0);
581
582         render_win= renderwin_alloc(win);
583         render_win->rectx= imagesize[0];
584         render_win->recty= imagesize[1];
585         
586         /* Ghost calls handler */
587         window_set_handler(win, renderwin_handler, render_win);
588
589         winlay_process_events(0);
590         window_make_active(render_win->win);
591         winlay_process_events(0);
592         
593         /* mywindow has to know about it too */
594         mywindow_build_and_set_renderwin(winpos[0], winpos[1], winsize[0], winsize[1]+RW_HEADERY);
595         /* and we should be able to draw 3d in it */
596         init_gl_stuff();
597         
598         renderwin_draw(render_win, 1);
599         renderwin_draw(render_win, 1);
600 }
601
602 /* -------------- callbacks for render loop: Window (RenderWin) ----------------------- */
603
604 /* calculations for window size and position */
605 void calc_renderwin_rectangle(int rectx, int recty, int posmask, int renderpos_r[2], int rendersize_r[2]) 
606 {
607         int scr_w, scr_h, x, y, div= 0;
608         float ndc_x= 0.0, ndc_y= 0.0;
609
610         winlay_get_screensize(&scr_w, &scr_h);
611
612         rendersize_r[0]= rectx;
613         rendersize_r[1]= recty;
614
615         rendersize_r[0]= CLAMPIS(rendersize_r[0], 0, scr_w);     
616         rendersize_r[1]= CLAMPIS(rendersize_r[1], 0, scr_h-RW_HEADERY);  
617         
618         for (y=-1; y<=1; y++) {
619                 for (x=-1; x<=1; x++) {
620                         if (posmask & (1<<((y+1)*3 + (x+1)))) {
621                                 ndc_x+= x;
622                                 ndc_y+= y;
623                                 div++;
624                         }
625                 }
626         }
627
628         if (div) {
629                 ndc_x/= div;
630                 ndc_y/= div;
631         }
632         
633         renderpos_r[0]= (scr_w-rendersize_r[0])*(ndc_x*0.5 + 0.5);
634 #ifdef __APPLE__
635         /* 44 pixels is topbar and window header... awaiting better fixes in ghost :) */
636         rendersize_r[1]= CLAMPIS(rendersize_r[1], 0, scr_h-44-RW_HEADERY);       
637         renderpos_r[1]= -44-RW_HEADERY+(scr_h-rendersize_r[1])*(ndc_y*0.5 + 0.5);
638 #else
639         renderpos_r[1]= -RW_HEADERY+(scr_h-rendersize_r[1])*(ndc_y*0.5 + 0.5);
640 #endif
641 }
642         
643 /* init renderwin, alloc/open/resize */
644 static void renderwin_init_display_cb(RenderResult *rr) 
645 {
646         if (G.afbreek != 1) {
647                 int rendersize[2], renderpos[2], imagesize[2];
648
649                 calc_renderwin_rectangle(rr->rectx, rr->recty, G.winpos, renderpos, rendersize);
650                 
651                 imagesize[0]= rr->rectx;
652                 imagesize[1]= rr->recty;
653
654                 if (!render_win) {
655                         open_renderwin(renderpos, rendersize, imagesize);
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, imagesize);
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                 glFinish();
702         }
703 }
704
705 /* callback for redraw render win */
706 static void renderwin_clear_display_cb(RenderResult *rr) 
707 {
708         if (render_win) {
709                 window_make_active(render_win->win);
710                 renderwin_draw(render_win, 1);
711         }
712 }
713
714 /* XXX, this is not good, we do this without any regard to state
715 * ... better is to make this an optimization of a more clear
716 * implementation. the bug shows up when you do something like
717 * open the window, then draw part of the progress, then get
718 * a redraw event. whatever can go wrong will. -zr
719 *
720 * Note: blocked queue handling while rendering to prevent that (ton)
721 */
722
723 /* can get as well the full picture, as the parts while rendering */
724 static void renderwin_progress(RenderWin *rw, RenderResult *rr, rcti *renrect)
725 {
726         rcti win_rct;
727         float *rectf, fullrect[2][2];
728         int ymin, ymax;
729         
730         /* if renrect argument, we only display scanlines */
731         if(renrect) {
732                 ymin= renrect->ymin;
733                 ymax= renrect->ymax-ymin;
734                 if(ymax<2) return;
735                 renrect->ymin= renrect->ymax;
736         }
737         else {
738                 ymin= 0;
739                 ymax= rr->recty-2*rr->crop;
740         }
741         
742         /* renderwindow cruft */
743         win_rct.xmin= win_rct.ymin= 0;
744         window_get_size(rw->win, &win_rct.xmax, &win_rct.ymax);
745         win_rct.ymax-= RW_HEADERY;
746         renderwin_get_fullrect(rw, fullrect);
747         
748         /* find current float rect for display, first case is after composit... still weak */
749         if(rr->rectf)
750                 rectf= rr->rectf;
751         else {
752                 RenderLayer *rl= rr->renlay;
753                 if(rl==NULL) return;
754                 rectf= rl->rectf;
755         }
756         /* if scanline updates... */
757         rectf+= 4*rr->rectx*ymin;
758         
759         /* when rendering more pixels than needed, we crop away cruft */
760         if(rr->crop)
761                 rectf+= 4*(rr->crop*rr->rectx + rr->crop);
762         
763         /* tilerect defines drawing offset from (0,0) */
764         /* however, tilerect (xmin, ymin) is first pixel */
765         fullrect[0][0] += (rr->tilerect.xmin+rr->crop)*rw->zoom;
766         fullrect[0][1] += (rr->tilerect.ymin+rr->crop + ymin)*rw->zoom;
767
768         glEnable(GL_SCISSOR_TEST);
769         glaDefine2DArea(&win_rct);
770
771         glDrawBuffer(GL_FRONT);
772         glPixelZoom(rw->zoom, rw->zoom);
773         glaDrawPixelsSafe(fullrect[0][0], fullrect[0][1], rr->rectx-2*rr->crop, ymax, rr->rectx, 
774                                           GL_RGBA, GL_FLOAT, rectf);
775         glPixelZoom(1.0, 1.0);
776         glFlush();
777         glDrawBuffer(GL_BACK);
778 }
779
780
781 /* in render window; display a couple of scanlines of rendered image */
782 static void renderwin_progress_display_cb(RenderResult *rr, rcti *rect)
783 {
784         if (render_win) {
785                 renderwin_progress(render_win, rr, rect);
786         }
787 }
788
789 /* -------------- callbacks for render loop: interactivity ----------------------- */
790
791
792 /* callback for print info in top header of renderwin */
793 static void printrenderinfo_cb(RenderStats *rs)
794 {
795         extern char info_time_str[32];  // header_info.c
796         extern int mem_in_use;
797         static float megs_used_memory;
798         char str[300], *spos= str;
799                 
800         if(render_win) {
801                 megs_used_memory= mem_in_use/(1024.0*1024.0);
802                 
803                 if(rs->tothalo)
804                         spos+= sprintf(spos, "Fra:%d  Ve:%d Fa:%d Ha:%d La:%d Mem:%.2fM", (G.scene->r.cfra), rs->totvert, rs->totface, rs->tothalo, rs->totlamp, megs_used_memory);
805                 else 
806                         spos+= sprintf(spos, "Fra:%d  Ve:%d Fa:%d La:%d Mem:%.2fM", (G.scene->r.cfra), rs->totvert, rs->totface, rs->totlamp, megs_used_memory);
807
808                 BLI_timestr(rs->lastframetime, info_time_str);
809                 spos+= sprintf(spos, " Time:%s ", info_time_str);
810                 
811                 if(rs->infostr)
812                         spos+= sprintf(spos, " | %s", rs->infostr);
813                 
814                 if(render_win) {
815                         if(render_win->render_text) MEM_freeN(render_win->render_text);
816                         render_win->render_text= BLI_strdup(str);
817                         glDrawBuffer(GL_FRONT);
818                         renderwin_draw_render_info(render_win);
819                         glFlush();
820                         glDrawBuffer(GL_BACK);
821                 }
822         }
823 }
824
825 /* -------------- callback system to allow ESC from rendering ----------------------- */
826
827 /* POSIX & WIN32: this function is called all the time, and should not use cpu or resources */
828 static int test_break(void)
829 {
830
831         if(G.afbreek==2) { /* code for testing queue */
832
833                 G.afbreek= 0;
834
835                 blender_test_break(); /* tests blender interface */
836
837                 if (G.afbreek==0 && render_win) { /* tests window */
838                         winlay_process_events(0);
839                         // render_win can be closed in winlay_process_events()
840                         if (render_win == 0 || (render_win->flags & RW_FLAGS_ESCAPE)) {
841                                 G.afbreek= 1;
842                         }
843                 }
844         }
845
846         if(G.afbreek==1) return 1;
847         else return 0;
848 }
849
850
851
852 #ifdef _WIN32
853 /* we use the multimedia time here */
854 static UINT uRenderTimerId;
855
856 void CALLBACK interruptESC(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
857 {
858         if(G.afbreek==0) G.afbreek= 2;  /* code for read queue */
859 }
860
861 /* WIN32: init SetTimer callback */
862 static void init_test_break_callback()
863 {
864         timeBeginPeriod(50);
865         uRenderTimerId = timeSetEvent(250, 1, interruptESC, 0, TIME_PERIODIC);
866 }
867
868 /* WIN32: stop SetTimer callback */
869 static void end_test_break_callback()
870 {
871         timeEndPeriod(50);
872         timeKillEvent(uRenderTimerId);
873 }
874
875 #else
876 /* all other OS's support signal(SIGVTALRM) */
877
878 /* POSIX: this function goes in the signal() callback */
879 static void interruptESC(int sig)
880 {
881
882         if(G.afbreek==0) G.afbreek= 2;  /* code for read queue */
883
884         /* call again, timer was reset */
885         signal(SIGVTALRM, interruptESC);
886 }
887
888 /* POSIX: initialize timer and signal */
889 static void init_test_break_callback()
890 {
891
892         struct itimerval tmevalue;
893
894         tmevalue.it_interval.tv_sec = 0;
895         tmevalue.it_interval.tv_usec = 250000;
896         /* wanneer de eerste ? */
897         tmevalue.it_value.tv_sec = 0;
898         tmevalue.it_value.tv_usec = 10000;
899
900         signal(SIGVTALRM, interruptESC);
901         setitimer(ITIMER_VIRTUAL, &tmevalue, 0);
902
903 }
904
905 /* POSIX: stop timer and callback */
906 static void end_test_break_callback()
907 {
908         struct itimerval tmevalue;
909
910         tmevalue.it_value.tv_sec = 0;
911         tmevalue.it_value.tv_usec = 0;
912         setitimer(ITIMER_VIRTUAL, &tmevalue, 0);
913         signal(SIGVTALRM, SIG_IGN);
914
915 }
916
917
918 #endif
919
920
921
922 /* -------------- callbacks for render loop: init & run! ----------------------- */
923
924
925 /* - initialize displays
926    - set callbacks
927    - cleanup
928 */
929
930 static void do_render(int anim)
931 {
932         Render *re= RE_NewRender("Render");
933         
934         /* we set this flag to prevent renderwindow queue to execute another render */
935         G.rendering= 1;
936         G.afbreek= 0;
937
938         /* set callbacks */
939         RE_display_init_cb(re, renderwin_init_display_cb);
940         RE_display_draw_cb(re, renderwin_progress_display_cb);
941         RE_display_clear_cb(re, renderwin_clear_display_cb);
942         init_test_break_callback();
943         RE_test_break_cb(re, test_break);
944         RE_timecursor_cb(re, set_timecursor);
945         RE_stats_draw_cb(re, printrenderinfo_cb);
946         
947         if(render_win) window_set_cursor(render_win->win, CURSOR_WAIT);
948         waitcursor(1);
949
950         if(G.obedit)
951                 exit_editmode(0);       /* 0 = no free data */
952
953         if(anim)
954                 RE_BlenderAnim(re, G.scene, G.scene->r.sfra, G.scene->r.efra);
955         else
956                 RE_BlenderFrame(re, G.scene, G.scene->r.cfra);
957
958         if(render_win) window_set_cursor(render_win->win, CURSOR_STD);
959
960         free_filesel_spec(G.scene->r.pic);
961
962         G.afbreek= 0;
963         end_test_break_callback();
964         
965         mainwindow_make_active();
966         
967         /* after an envmap creation...  */
968 //              if(R.flag & R_REDRAW_PRV) {
969 //                      BIF_preview_changed(ID_TE);
970 //              }
971         allqueue(REDRAWBUTSSCENE, 0);   // visualize fbuf for example
972         
973         // before scene update!
974         G.rendering= 0;
975         
976         scene_update_for_newframe(G.scene, G.scene->lay);       // no redraw needed, this restores to view as we left it
977         
978         waitcursor(0);  // waitcursor checks rendering R.flag...
979 }
980
981 #if 0
982 /* used for swapping with spare buffer, when images are different size */
983 static void scalefastrect(unsigned int *recto, unsigned int *rectn, int oldx, int oldy, int newx, int newy)
984 {
985         unsigned int *rect, *newrect;
986         int x, y;
987         int ofsx, ofsy, stepx, stepy;
988
989         stepx = (int)((65536.0 * (oldx - 1.0) / (newx - 1.0)) + 0.5);
990         stepy = (int)((65536.0 * (oldy - 1.0) / (newy - 1.0)) + 0.5);
991         ofsy = 32768;
992         newrect= rectn;
993         
994         for (y = newy; y > 0 ; y--){
995                 rect = recto;
996                 rect += (ofsy >> 16) * oldx;
997                 ofsy += stepy;
998                 ofsx = 32768;
999                 for (x = newx ; x>0 ; x--){
1000                         *newrect++ = rect[ofsx >> 16];
1001                         ofsx += stepx;
1002                 }
1003         }
1004 }
1005 #endif
1006 /* -------------- API: externally called --------------- */
1007
1008 /* not used anywhere ??? */
1009 #if 0
1010 void BIF_renderwin_make_active(void)
1011 {
1012         if(render_win) {
1013                 window_make_active(render_win->win);
1014                 mywinset(2);
1015         }
1016 }
1017 #endif
1018
1019 /* set up display, render an image or scene */
1020 void BIF_do_render(int anim)
1021 {
1022         int slink_flag = 0;
1023
1024         if (G.f & G_DOSCRIPTLINKS) {
1025                 BPY_do_all_scripts(SCRIPT_RENDER);
1026                 if (!anim) { /* avoid FRAMECHANGED slink in render callback */
1027                         G.f &= ~G_DOSCRIPTLINKS;
1028                         slink_flag = 1;
1029                 }
1030         }
1031
1032         do_render(anim);
1033
1034         if(G.scene->use_nodes) {
1035                 allqueue(REDRAWNODE, 1);
1036                 allqueue(REDRAWIMAGE, 1);
1037         }
1038         if (slink_flag) G.f |= G_DOSCRIPTLINKS;
1039         if (G.f & G_DOSCRIPTLINKS) BPY_do_all_scripts(SCRIPT_POSTRENDER);
1040 }
1041
1042 /* set up display, render the current area view in an image */
1043 /* the RE_Render is only used to make sure we got the picture in the result */
1044 void BIF_do_ogl_render(View3D *v3d, int anim)
1045 {
1046         Render *re= RE_NewRender("Render");
1047         RenderResult *rr;
1048         int winx, winy;
1049         
1050         G.afbreek= 0;
1051         init_test_break_callback();
1052         
1053         winx= (G.scene->r.size*G.scene->r.xsch)/100;
1054         winy= (G.scene->r.size*G.scene->r.ysch)/100;
1055         
1056         RE_InitState(re, &G.scene->r, winx, winy, NULL);
1057         
1058         /* for now, result is defaulting to floats still... */
1059         rr= RE_GetResult(re);
1060         if(rr->rect32==NULL)
1061                 rr->rect32= MEM_mallocN(sizeof(int)*winx*winy, "32 bits rects");
1062         
1063         /* open window */
1064         renderwin_init_display_cb(rr);
1065         init_gl_stuff();
1066         
1067         waitcursor(1);
1068
1069         if(anim) {
1070                 bMovieHandle *mh= BKE_get_movie_handle(G.scene->r.imtype);
1071                 int cfrao= CFRA;
1072                 
1073                 mh->start_movie(&G.scene->r, winx, winy);
1074                 
1075                 for(CFRA= SFRA; CFRA<=EFRA; CFRA++) {
1076                         drawview3d_render(v3d, winx, winy);
1077                         glReadPixels(0, 0, winx, winy, GL_RGBA, GL_UNSIGNED_BYTE, rr->rect32);
1078                         window_swap_buffers(render_win->win);
1079                         
1080                         mh->append_movie(CFRA, rr->rect32, winx, winy);
1081                         if(test_break()) break;
1082                 }
1083                 mh->end_movie();
1084                 CFRA= cfrao;
1085         }
1086         else {
1087                 drawview3d_render(v3d, winx, winy);
1088                 glReadPixels(0, 0, winx, winy, GL_RGBA, GL_UNSIGNED_BYTE, rr->rect32);
1089                 window_swap_buffers(render_win->win);
1090         }
1091         
1092         mainwindow_make_active();
1093         
1094         if(anim)
1095                 scene_update_for_newframe(G.scene, G.scene->lay);       // no redraw needed, this restores to view as we left it
1096         
1097         end_test_break_callback();
1098         waitcursor(0);
1099 }
1100
1101 void BIF_redraw_render_rect(void)
1102 {
1103         /* redraw */
1104         if (render_win) {
1105                 renderwin_queue_redraw(render_win);
1106         }
1107 }       
1108
1109 void BIF_swap_render_rects(void)
1110 {
1111 #if 0
1112         unsigned int *temp;
1113
1114         if(R.rectspare==0) {
1115                 R.rectspare= (unsigned int *)MEM_callocN(sizeof(int)*R.rectx*R.recty, "rectot");
1116                 R.sparex= R.rectx;
1117                 R.sparey= R.recty;
1118         }
1119         else if(R.sparex!=R.rectx || R.sparey!=R.recty) {
1120                 temp= (unsigned int *)MEM_callocN(sizeof(int)*R.rectx*R.recty, "rectot");
1121                                         
1122                 scalefastrect(R.rectspare, temp, R.sparex, R.sparey, R.rectx, R.recty);
1123                 MEM_freeN(R.rectspare);
1124                 R.rectspare= temp;
1125                                         
1126                 R.sparex= R.rectx;
1127                 R.sparey= R.recty;
1128         }
1129         
1130         temp= R.rectot;
1131         R.rectot= R.rectspare;
1132         R.rectspare= temp;
1133         
1134         if (render_win) {
1135                 char *tmp= render_win->render_text_spare;
1136                 render_win->render_text_spare= render_win->render_text;
1137                 render_win->render_text= tmp;
1138                 
1139                 window_set_title(render_win->win, renderwin_get_title(1));
1140                 
1141         }
1142
1143         /* redraw */
1144         BIF_redraw_render_rect();
1145 #endif
1146 }                               
1147
1148 /* called from usiblender.c too, to free and close renderwin */
1149 void BIF_close_render_display(void)
1150 {
1151         if (render_win) {
1152                 if (render_win->info_text) MEM_freeN(render_win->info_text);
1153                 if (render_win->render_text) MEM_freeN(render_win->render_text);
1154                 if (render_win->render_text_spare) MEM_freeN(render_win->render_text_spare);
1155
1156                 window_destroy(render_win->win); /* ghost close window */
1157                 MEM_freeN(render_win);
1158
1159                 render_win= NULL;
1160         }
1161 }
1162
1163
1164 /* typical with F11 key, show image or hide/close */
1165 void BIF_toggle_render_display(void) 
1166 {
1167         
1168         if (render_win) {
1169                 if(render_win->active) {
1170                         mainwindow_raise();
1171                         mainwindow_make_active();
1172                         render_win->active= 0;
1173                 }
1174                 else {
1175                         window_raise(render_win->win);
1176                         window_make_active(render_win->win);
1177                         render_win->active= 1;
1178                 }
1179         } 
1180         else {
1181                 RenderResult *rr= RE_GetResult(RE_GetRender("Render"));
1182                 if(rr) renderwin_init_display_cb(rr);
1183         }
1184 }
1185
1186 void BIF_renderwin_set_custom_cursor(unsigned char mask[16][2], unsigned char bitmap[16][2])
1187 {
1188         if (render_win) {
1189                 window_set_custom_cursor(render_win->win, mask, bitmap, 7, 7);
1190         }
1191 }