Orange fix: Render OGL skipped last frame...
[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         RenderResult *rr= RE_GetResult(RE_GetRender("Render"));
291
292         if(rr) {
293                 RenderLayer *rl= rr->layers.first;
294                 float fullrect[2][2];
295                 int set_back_mainwindow;
296                 rcti rect;
297
298                 /* since renderwin uses callbacks (controlled by ghost) it can
299                         mess up active window output with redraw events after a render. 
300                         this is patchy, still WIP */
301                 set_back_mainwindow = (winlay_get_active_window() != rw->win);
302                 window_make_active(rw->win);
303                 
304                 rect.xmin= rect.ymin= 0;
305                 window_get_size(rw->win, &rect.xmax, &rect.ymax);
306                 rect.ymax-= RW_HEADERY;
307                 
308                 renderwin_get_fullrect(rw, fullrect);
309                 
310                 /* do this first, so window ends with correct scissor */
311                 renderwin_draw_render_info(rw);
312                 
313                 glEnable(GL_SCISSOR_TEST);
314                 glaDefine2DArea(&rect);
315                 
316                 glClearColor(.1875, .1875, .1875, 1.0); 
317                 glClear(GL_COLOR_BUFFER_BIT);
318
319                 if (just_clear) {
320                         glColor3ub(0, 0, 0);
321                         glRectfv(fullrect[0], fullrect[1]);
322                 } else {
323                         glPixelZoom(rw->zoom, rw->zoom);
324                         if(rw->flags & RW_FLAGS_ALPHA) {
325                                 /* swap bytes, so alpha is most significant one, then just draw it as luminance int */
326 //                              glPixelStorei(GL_UNPACK_SWAP_BYTES, 1);
327 //                              glaDrawPixelsSafe(fullrect[0][0], fullrect[0][1], rr->rectx, rr->recty, GL_LUMINANCE, GL_UNSIGNED_INT, R.rectot);
328 //                              glPixelStorei(GL_UNPACK_SWAP_BYTES, 0);
329                         }
330                         else {
331                                 if(rr->rect32)
332                                         glaDrawPixelsSafe(fullrect[0][0], fullrect[0][1], rr->rectx, rr->recty, rr->rectx, GL_RGBA, GL_UNSIGNED_BYTE, rr->rect32);
333                                 else if(rl->rectf)
334                                         glaDrawPixelsSafe(fullrect[0][0], fullrect[0][1], rr->rectx, rr->recty, rr->rectx, GL_RGBA, GL_FLOAT, rl->rectf);
335                         }
336                         glPixelZoom(1.0, 1.0);
337                 }
338                 
339                 /* info text is overlayed on bottom */
340                 if (rw->info_text) {
341                         float w;
342                         glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
343                         glEnable(GL_BLEND);
344                         w=186.0*strlen(rw->info_text)/30;
345                         glColor4f(.5,.5,.5,.25);
346                         glRectf(0.0,0.0,w,30.0);
347                         glDisable(GL_BLEND);
348                         glColor3ub(255, 255, 255);
349                         glRasterPos2i(10, 10);
350                         BMF_DrawString(G.font, rw->info_text);
351                 }
352                 
353                 window_swap_buffers(rw->win);
354                 
355                 if (set_back_mainwindow) mainwindow_make_active();      
356         }
357 }
358
359 /* ------ interactivity calls for RenderWin ------------- */
360
361 static void renderwin_mouse_moved(RenderWin *rw)
362 {
363         RenderResult *rr= RE_GetResult(RE_GetRender("Render"));
364         RenderLayer *rl= (rr?rr->layers.first:NULL);
365
366         if (rw->flags & RW_FLAGS_PIXEL_EXAMINING) {
367                 int imgco[2], ofs=0;
368                 char buf[128];
369 //              char *pxl;
370
371                 if (rl && renderwin_win_to_image_co(rw, rw->lmouse, imgco)) {
372 //                      pxl= (char*) &R.rectot[R.rectx*imgco[1] + imgco[0]];
373                         
374 //                      ofs= sprintf(buf, "R: %d G: %d B: %d A: %d", pxl[0], pxl[1], pxl[2], pxl[3]);   
375                         
376                         if (rl->rectf) {
377                                 float *pxlf= rl->rectf + 4*(rr->rectx*imgco[1] + imgco[0]);
378                                 ofs+= sprintf(buf+ofs, " | R: %.3f G: %.3f B: %.3f A: %.3f ", pxlf[0], pxlf[1], pxlf[2], pxlf[3]);
379                         }
380                         if (rl->rectz) {
381                                 float *pxlz= &rl->rectz[rr->rectx*imgco[1] + imgco[0]];                 
382                                 sprintf(buf+ofs, "| Z: %.3f", *pxlz );
383                         }
384
385                         renderwin_set_infotext(rw, buf);
386                         renderwin_queue_redraw(rw);
387                 } else {
388                         renderwin_set_infotext(rw, NULL);
389                         renderwin_queue_redraw(rw);
390                 }
391         } 
392         else if (rw->flags & RW_FLAGS_PANNING) {
393                 int delta_x= rw->lmouse[0] - rw->pan_mouse_start[0];
394                 int delta_y= rw->lmouse[1] - rw->pan_mouse_start[1];
395         
396                 rw->zoomofs[0]= rw->pan_ofs_start[0] - delta_x/rw->zoom;
397                 rw->zoomofs[1]= rw->pan_ofs_start[1] - delta_y/rw->zoom;
398                 rw->zoomofs[0]= CLAMPIS(rw->zoomofs[0], -rr->rectx/2, rr->rectx/2);
399                 rw->zoomofs[1]= CLAMPIS(rw->zoomofs[1], -rr->recty/2, rr->recty/2);
400
401                 renderwin_queue_redraw(rw);
402         } 
403         else if (rw->flags & RW_FLAGS_OLDZOOM) {
404                 float ndc[2];
405                 int w, h;
406
407                 window_get_size(rw->win, &w, &h);
408                 h-= RW_HEADERY;
409                 renderwin_win_to_ndc(rw, rw->lmouse, ndc);
410
411                 rw->zoomofs[0]= -0.5*ndc[0]*(w-rr->rectx*rw->zoom)/rw->zoom;
412                 rw->zoomofs[1]= -0.5*ndc[1]*(h-rr->recty*rw->zoom)/rw->zoom;
413
414                 renderwin_queue_redraw(rw);
415         }
416 }
417
418 static void renderwin_mousebut_changed(RenderWin *rw)
419 {
420         if (rw->mbut[0]) {
421                 rw->flags|= RW_FLAGS_PIXEL_EXAMINING;
422         } 
423         else if (rw->mbut[1]) {
424                 rw->flags|= RW_FLAGS_PANNING;
425                 rw->pan_mouse_start[0]= rw->lmouse[0];
426                 rw->pan_mouse_start[1]= rw->lmouse[1];
427                 rw->pan_ofs_start[0]= rw->zoomofs[0];
428                 rw->pan_ofs_start[1]= rw->zoomofs[1];
429         } 
430         else {
431                 if (rw->flags & RW_FLAGS_PANNING) {
432                         rw->flags &= ~RW_FLAGS_PANNING;
433                         renderwin_queue_redraw(rw);
434                 }
435                 if (rw->flags & RW_FLAGS_PIXEL_EXAMINING) {
436                         rw->flags&= ~RW_FLAGS_PIXEL_EXAMINING;
437                         renderwin_set_infotext(rw, NULL);
438                         renderwin_queue_redraw(rw);
439                 }
440         }
441 }
442
443
444 /* handler for renderwin, passed on to Ghost */
445 static void renderwin_handler(Window *win, void *user_data, short evt, short val, char ascii)
446 {
447         RenderWin *rw= user_data;
448
449         // added this for safety, while render it's just creating bezerk results
450         if(G.rendering) {
451                 if(evt==ESCKEY && val) 
452                         rw->flags|= RW_FLAGS_ESCAPE;
453                 return;
454         }
455         
456         if (evt==RESHAPE) {
457                 renderwin_reshape(rw);
458         } 
459         else if (evt==REDRAW) {
460                 renderwin_draw(rw, 0);
461         } 
462         else if (evt==WINCLOSE) {
463                 BIF_close_render_display();
464         } 
465         else if (evt==INPUTCHANGE) {
466                 rw->active= val;
467
468                 if (!val && (rw->flags&RW_FLAGS_OLDZOOM)) {
469                         rw->flags&= ~RW_FLAGS_OLDZOOM;
470                         renderwin_reset_view(rw);
471                 }
472         } 
473         else if (ELEM(evt, MOUSEX, MOUSEY)) {
474                 rw->lmouse[evt==MOUSEY]= val;
475                 renderwin_mouse_moved(rw);
476         } 
477         else if (ELEM3(evt, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) {
478                 int which= (evt==LEFTMOUSE)?0:(evt==MIDDLEMOUSE)?1:2;
479                 rw->mbut[which]= val;
480                 renderwin_mousebut_changed(rw);
481         } 
482         else if (val) {
483                 if (evt==ESCKEY) {
484                         if (rw->flags&RW_FLAGS_OLDZOOM) {
485                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
486                                 renderwin_reset_view(rw);
487                         } 
488                         else {
489                                 rw->flags|= RW_FLAGS_ESCAPE;
490                                 mainwindow_raise();
491                                 mainwindow_make_active();
492                                 rw->active= 0;
493                         }
494                 } 
495                 else if( evt==AKEY) {
496                         rw->flags ^= RW_FLAGS_ALPHA;
497                         renderwin_queue_redraw(render_win);
498                 }
499                 else if (evt==JKEY) {
500 //                      if(R.flag==0) BIF_swap_render_rects();
501                 } 
502                 else if (evt==ZKEY) {
503                         if (rw->flags&RW_FLAGS_OLDZOOM) {
504                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
505                                 renderwin_reset_view(rw);
506                         } else {
507                                 rw->zoom= 2.0;
508                                 rw->flags|= RW_FLAGS_OLDZOOM;
509                                 renderwin_mouse_moved(rw);
510                         }
511                 } 
512                 else if (evt==PADPLUSKEY) {
513                         if (rw->zoom<15.9) {
514                                 if(rw->zoom>0.5 && rw->zoom<1.0) rw->zoom= 1.0;
515                                 else rw->zoom*= 2.0;
516                                 renderwin_queue_redraw(rw);
517                         }
518                 } 
519                 else if (evt==PADMINUS) {
520                         if (rw->zoom>0.26) {
521                                 if(rw->zoom>1.0 && rw->zoom<2.0) rw->zoom= 1.0;
522                                 else rw->zoom*= 0.5;
523                                 renderwin_queue_redraw(rw);
524                         }
525                 } 
526                 else if (evt==PADENTER || evt==HOMEKEY) {
527                         if (rw->flags&RW_FLAGS_OLDZOOM) {
528                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
529                         }
530                         renderwin_reset_view(rw);
531                 } 
532                 else if (evt==F3KEY) {
533 //                      if(R.flag==0) {
534 //                              mainwindow_raise();
535 //                              mainwindow_make_active();
536 //                              rw->active= 0;
537 //                              areawinset(find_biggest_area()->win);
538 //                              BIF_save_rendered_image_fs();
539 //                      }
540                 } 
541                 else if (evt==F11KEY) {
542                         BIF_toggle_render_display();
543                 } 
544                 else if (evt==F12KEY) {
545                         /* if it's rendering, this flag is set */
546 //                      if(R.flag==0) BIF_do_render(0);
547                 }
548         }
549 }
550
551 static char *renderwin_get_title(int doswap)
552 {
553         static int swap= 0;
554         char *title="";
555         
556         swap+= doswap;
557         
558         if(swap & 1) {
559                 if (G.scene->r.renderer==R_YAFRAY) title = "YafRay:Render (spare)";
560                 else title = "Blender:Render (spare)";
561         }
562         else {
563                 if (G.scene->r.renderer==R_YAFRAY) title = "YafRay:Render";
564                 else title = "Blender:Render";
565         }
566
567         return title;
568 }
569
570 /* opens window and allocs struct */
571 static void open_renderwin(int winpos[2], int winsize[2], int imagesize[2])
572 {
573         extern void mywindow_build_and_set_renderwin( int orx, int ory, int sizex, int sizey); // mywindow.c
574         Window *win;
575         char *title;
576         
577         title= renderwin_get_title(0);  /* 0 = no swap */
578         win= window_open(title, winpos[0], winpos[1], winsize[0], winsize[1]+RW_HEADERY, 0);
579
580         render_win= renderwin_alloc(win);
581         render_win->rectx= imagesize[0];
582         render_win->recty= imagesize[1];
583         
584         /* Ghost calls handler */
585         window_set_handler(win, renderwin_handler, render_win);
586
587         winlay_process_events(0);
588         window_make_active(render_win->win);
589         winlay_process_events(0);
590         
591         /* mywindow has to know about it too */
592         mywindow_build_and_set_renderwin(winpos[0], winpos[1], winsize[0], winsize[1]+RW_HEADERY);
593         /* and we should be able to draw 3d in it */
594         init_gl_stuff();
595         
596         renderwin_draw(render_win, 1);
597         renderwin_draw(render_win, 1);
598 }
599
600 /* -------------- callbacks for render loop: Window (RenderWin) ----------------------- */
601
602 /* calculations for window size and position */
603 void calc_renderwin_rectangle(int rectx, int recty, int posmask, int renderpos_r[2], int rendersize_r[2]) 
604 {
605         int scr_w, scr_h, x, y, div= 0;
606         float ndc_x= 0.0, ndc_y= 0.0;
607
608         winlay_get_screensize(&scr_w, &scr_h);
609
610         rendersize_r[0]= rectx;
611         rendersize_r[1]= recty;
612
613         rendersize_r[0]= CLAMPIS(rendersize_r[0], 0, scr_w);     
614         rendersize_r[1]= CLAMPIS(rendersize_r[1], 0, scr_h-RW_HEADERY);  
615         
616         for (y=-1; y<=1; y++) {
617                 for (x=-1; x<=1; x++) {
618                         if (posmask & (1<<((y+1)*3 + (x+1)))) {
619                                 ndc_x+= x;
620                                 ndc_y+= y;
621                                 div++;
622                         }
623                 }
624         }
625
626         if (div) {
627                 ndc_x/= div;
628                 ndc_y/= div;
629         }
630         
631         renderpos_r[0]= (scr_w-rendersize_r[0])*(ndc_x*0.5 + 0.5);
632 #ifdef __APPLE__
633         /* 44 pixels is topbar and window header... awaiting better fixes in ghost :) */
634         rendersize_r[1]= CLAMPIS(rendersize_r[1], 0, scr_h-44-RW_HEADERY);       
635         renderpos_r[1]= -44-RW_HEADERY+(scr_h-rendersize_r[1])*(ndc_y*0.5 + 0.5);
636 #else
637         renderpos_r[1]= -RW_HEADERY+(scr_h-rendersize_r[1])*(ndc_y*0.5 + 0.5);
638 #endif
639 }
640         
641 /* init renderwin, alloc/open/resize */
642 static void renderwin_init_display_cb(RenderResult *rr) 
643 {
644         if (G.afbreek == 0) {
645                 int rendersize[2], renderpos[2], imagesize[2];
646
647                 calc_renderwin_rectangle(rr->rectx, rr->recty, G.winpos, renderpos, rendersize);
648                 
649                 imagesize[0]= rr->rectx;
650                 imagesize[1]= rr->recty;
651
652                 if (!render_win) {
653                         open_renderwin(renderpos, rendersize, imagesize);
654                         renderwin_reset_view(render_win); // incl. autozoom for large images
655                 } else {
656                         int win_x, win_y;
657                         int win_w, win_h;
658
659                         window_get_position(render_win->win, &win_x, &win_y);
660                         window_get_size(render_win->win, &win_w, &win_h);
661                         win_h-= RW_HEADERY;
662
663                                 /* XXX, this is nasty and I guess bound to cause problems,
664                                  * but to ensure the window is at the user specified position
665                                  * and size we reopen the window all the time... we need
666                                  * a ghost _set_position to fix this -zr
667                                  */
668                                  
669                                  /* XXX, well... it is nasty yes, and reopens windows each time on
670                                     subsequent renders. Better rule is to make it reopen only only
671                                     size change, and use the preferred position only on open_renderwin
672                                         cases (ton)
673                                  */
674                         if(rendersize[0]!= win_w || rendersize[1]!= win_h) {
675                                 BIF_close_render_display();
676                                 open_renderwin(renderpos, rendersize, imagesize);
677                         }
678                         else {
679                                 window_raise(render_win->win);
680                                 window_make_active(render_win->win);
681                                 
682                                 mywinset(2);    // to assign scissor/viewport again in mywindow.c. is hackish yes, but otherwise it draws in header of button for ogl header
683                                 {
684                                         rcti win_rct;
685                                         win_rct.xmin= win_rct.ymin= 0;
686                                         window_get_size(render_win->win, &win_rct.xmax, &win_rct.ymax);
687                                         win_rct.ymax-= RW_HEADERY;
688                                         glaDefine2DArea(&win_rct);
689                                 }
690                         }
691
692                         renderwin_reset_view(render_win);
693                         render_win->flags&= ~RW_FLAGS_ESCAPE;
694                         render_win->active= 1;
695                 }
696                 /* make sure we are in normal draw again */
697                 render_win->flags &= ~RW_FLAGS_ALPHA;
698                 
699                 glFinish();
700         }
701 }
702
703 /* callback for redraw render win */
704 static void renderwin_clear_display_cb(RenderResult *rr) 
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 /* can get as well the full picture, as the parts while rendering */
722 static void renderwin_progress(RenderWin *rw, RenderResult *rr, rcti *unused)
723 {
724         RenderLayer *rl;
725         rcti win_rct;
726         float *rectf, fullrect[2][2];
727         
728         /* renderwindow cruft */
729         win_rct.xmin= win_rct.ymin= 0;
730         window_get_size(rw->win, &win_rct.xmax, &win_rct.ymax);
731         win_rct.ymax-= RW_HEADERY;
732         renderwin_get_fullrect(rw, fullrect);
733         
734         /* find current float rect for display */
735         rl= rr->layers.first;
736         rectf= rl->rectf;
737         
738         /* when rendering more pixels than needed, we crop away cruft */
739         if(rr->crop)
740                 rectf+= 4*(rr->crop*rr->rectx + rr->crop);
741         
742         /* tilerect defines drawing offset from (0,0) */
743         /* however, tilerect (xmin, ymin) is first pixel */
744         fullrect[0][0] += (rr->tilerect.xmin+rr->crop)*rw->zoom;
745         fullrect[0][1] += (rr->tilerect.ymin+rr->crop)*rw->zoom;
746
747         glEnable(GL_SCISSOR_TEST);
748         glaDefine2DArea(&win_rct);
749
750         glDrawBuffer(GL_FRONT);
751         glPixelZoom(rw->zoom, rw->zoom);
752         glaDrawPixelsSafe(fullrect[0][0], fullrect[0][1], rr->rectx-2*rr->crop, rr->recty-2*rr->crop, rr->rectx, 
753                                           GL_RGBA, GL_FLOAT, rectf);
754         glPixelZoom(1.0, 1.0);
755         glFlush();
756         glDrawBuffer(GL_BACK);
757 }
758
759
760 /* in render window; display a couple of scanlines of rendered image */
761 static void renderwin_progress_display_cb(RenderResult *rr, rcti *rect)
762 {
763         if (render_win) {
764                 renderwin_progress(render_win, rr, rect);
765         }
766 }
767
768 /* -------------- callbacks for render loop: interactivity ----------------------- */
769
770
771 /* callback for print info in top header of renderwin */
772 static void printrenderinfo_cb(RenderStats *rs)
773 {
774         extern char info_time_str[32];  // header_info.c
775         extern int mem_in_use;
776         static float megs_used_memory;
777         char str[300], *spos= str;
778                 
779         if(render_win) {
780                 megs_used_memory= mem_in_use/(1024.0*1024.0);
781                 
782                 if(rs->tothalo)
783                         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);
784                 else 
785                         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);
786
787                 BLI_timestr(rs->lastframetime, info_time_str);
788                 spos+= sprintf(spos, " Time:%s ", info_time_str);
789                 
790                 if(render_win) {
791                         if(render_win->render_text) MEM_freeN(render_win->render_text);
792                         render_win->render_text= BLI_strdup(str);
793                         glDrawBuffer(GL_FRONT);
794                         renderwin_draw_render_info(render_win);
795                         glFlush();
796                         glDrawBuffer(GL_BACK);
797                 }
798         }
799 }
800
801 /* -------------- callback system to allow ESC from rendering ----------------------- */
802
803 /* POSIX & WIN32: this function is called all the time, and should not use cpu or resources */
804 static int test_break(void)
805 {
806
807         if(G.afbreek==2) { /* code for testing queue */
808
809                 G.afbreek= 0;
810
811                 blender_test_break(); /* tests blender interface */
812
813                 if (G.afbreek==0 && render_win) { /* tests window */
814                         winlay_process_events(0);
815                         // render_win can be closed in winlay_process_events()
816                         if (render_win == 0 || (render_win->flags & RW_FLAGS_ESCAPE)) {
817                                 G.afbreek= 1;
818                         }
819                 }
820         }
821
822         if(G.afbreek==1) return 1;
823         else return 0;
824 }
825
826
827
828 #ifdef _WIN32
829 /* we use the multimedia time here */
830 static UINT uRenderTimerId;
831
832 void CALLBACK interruptESC(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
833 {
834         if(G.afbreek==0) G.afbreek= 2;  /* code for read queue */
835 }
836
837 /* WIN32: init SetTimer callback */
838 static void init_test_break_callback()
839 {
840         timeBeginPeriod(50);
841         uRenderTimerId = timeSetEvent(250, 1, interruptESC, 0, TIME_PERIODIC);
842 }
843
844 /* WIN32: stop SetTimer callback */
845 static void end_test_break_callback()
846 {
847         timeEndPeriod(50);
848         timeKillEvent(uRenderTimerId);
849 }
850
851 #else
852 /* all other OS's support signal(SIGVTALRM) */
853
854 /* POSIX: this function goes in the signal() callback */
855 static void interruptESC(int sig)
856 {
857
858         if(G.afbreek==0) G.afbreek= 2;  /* code for read queue */
859
860         /* call again, timer was reset */
861         signal(SIGVTALRM, interruptESC);
862 }
863
864 /* POSIX: initialize timer and signal */
865 static void init_test_break_callback()
866 {
867
868         struct itimerval tmevalue;
869
870         tmevalue.it_interval.tv_sec = 0;
871         tmevalue.it_interval.tv_usec = 250000;
872         /* wanneer de eerste ? */
873         tmevalue.it_value.tv_sec = 0;
874         tmevalue.it_value.tv_usec = 10000;
875
876         signal(SIGVTALRM, interruptESC);
877         setitimer(ITIMER_VIRTUAL, &tmevalue, 0);
878
879 }
880
881 /* POSIX: stop timer and callback */
882 static void end_test_break_callback()
883 {
884         struct itimerval tmevalue;
885
886         tmevalue.it_value.tv_sec = 0;
887         tmevalue.it_value.tv_usec = 0;
888         setitimer(ITIMER_VIRTUAL, &tmevalue, 0);
889         signal(SIGVTALRM, SIG_IGN);
890
891 }
892
893
894 #endif
895
896
897
898 /* -------------- callbacks for render loop: init & run! ----------------------- */
899
900
901 /* - initialize displays
902    - set callbacks
903    - cleanup
904 */
905
906 static void do_render(int anim)
907 {
908         Render *re= RE_NewRender("Render");
909         
910         /* we set this flag to prevent renderwindow queue to execute another render */
911         G.rendering= 1;
912         G.afbreek= 0;
913
914         /* set callbacks */
915         RE_display_init_cb(re, renderwin_init_display_cb);
916         RE_display_draw_cb(re, renderwin_progress_display_cb);
917         RE_display_clear_cb(re, renderwin_clear_display_cb);
918         init_test_break_callback();
919         RE_test_break_cb(re, test_break);
920         RE_timecursor_cb(re, set_timecursor);
921         RE_stats_draw_cb(re, printrenderinfo_cb);
922         
923         if(render_win) window_set_cursor(render_win->win, CURSOR_WAIT);
924         waitcursor(1);
925
926         if(G.obedit)
927                 exit_editmode(0);       /* 0 = no free data */
928
929         if(anim)
930                 RE_BlenderAnim(re, G.scene, G.scene->r.sfra, G.scene->r.efra);
931         else
932                 RE_BlenderFrame(re, G.scene, G.scene->r.cfra);
933
934         if(render_win) window_set_cursor(render_win->win, CURSOR_STD);
935
936         free_filesel_spec(G.scene->r.pic);
937
938         G.afbreek= 0;
939         end_test_break_callback();
940         
941         mainwindow_make_active();
942         
943         /* after an envmap creation...  */
944 //              if(R.flag & R_REDRAW_PRV) {
945 //                      BIF_preview_changed(ID_TE);
946 //              }
947         allqueue(REDRAWBUTSSCENE, 0);   // visualize fbuf for example
948         
949         // before scene update!
950         G.rendering= 0;
951         
952         scene_update_for_newframe(G.scene, G.scene->lay);       // no redraw needed, this restores to view as we left it
953         
954         waitcursor(0);  // waitcursor checks rendering R.flag...
955 }
956
957
958 /* used for swapping with spare buffer, when images are different size */
959 static void scalefastrect(unsigned int *recto, unsigned int *rectn, int oldx, int oldy, int newx, int newy)
960 {
961         unsigned int *rect, *newrect;
962         int x, y;
963         int ofsx, ofsy, stepx, stepy;
964
965         stepx = (int)((65536.0 * (oldx - 1.0) / (newx - 1.0)) + 0.5);
966         stepy = (int)((65536.0 * (oldy - 1.0) / (newy - 1.0)) + 0.5);
967         ofsy = 32768;
968         newrect= rectn;
969         
970         for (y = newy; y > 0 ; y--){
971                 rect = recto;
972                 rect += (ofsy >> 16) * oldx;
973                 ofsy += stepy;
974                 ofsx = 32768;
975                 for (x = newx ; x>0 ; x--){
976                         *newrect++ = rect[ofsx >> 16];
977                         ofsx += stepx;
978                 }
979         }
980 }
981
982 /* -------------- API: externally called --------------- */
983
984 /* not used anywhere ??? */
985 #if 0
986 void BIF_renderwin_make_active(void)
987 {
988         if(render_win) {
989                 window_make_active(render_win->win);
990                 mywinset(2);
991         }
992 }
993 #endif
994
995 /* set up display, render an image or scene */
996 void BIF_do_render(int anim)
997 {
998         int slink_flag = 0;
999
1000         if (G.f & G_DOSCRIPTLINKS) {
1001                 BPY_do_all_scripts(SCRIPT_RENDER);
1002                 if (!anim) { /* avoid FRAMECHANGED slink in render callback */
1003                         G.f &= ~G_DOSCRIPTLINKS;
1004                         slink_flag = 1;
1005                 }
1006         }
1007
1008         /* if start render in 3d win, use layer from window (e.g also local view) */
1009         if(curarea && curarea->spacetype==SPACE_VIEW3D) {
1010                 int lay= G.scene->lay;
1011                 if(G.vd->lay & 0xFF000000)      // localview
1012                         G.scene->lay |= G.vd->lay;
1013                 else G.scene->lay= G.vd->lay;
1014                 
1015                 do_render(anim);
1016                 
1017                 G.scene->lay= lay;
1018         }
1019         else do_render(anim);
1020
1021         if (slink_flag) G.f |= G_DOSCRIPTLINKS;
1022         if (G.f & G_DOSCRIPTLINKS) BPY_do_all_scripts(SCRIPT_POSTRENDER);
1023 }
1024
1025 /* set up display, render the current area view in an image */
1026 /* the RE_Render is only used to make sure we got the picture in the result */
1027 void BIF_do_ogl_render(View3D *v3d, int anim)
1028 {
1029         Render *re= RE_NewRender("Render");
1030         RenderResult *rr;
1031         int winx, winy;
1032         
1033         G.afbreek= 0;
1034         init_test_break_callback();
1035         
1036         winx= (G.scene->r.size*G.scene->r.xsch)/100;
1037         winy= (G.scene->r.size*G.scene->r.ysch)/100;
1038         
1039         RE_InitState(re, &G.scene->r, winx, winy, NULL);
1040         
1041         /* for now, result is defaulting to floats still... */
1042         rr= RE_GetResult(re);
1043         if(rr->rect32==NULL)
1044                 rr->rect32= MEM_mallocN(sizeof(int)*winx*winy, "32 bits rects");
1045         
1046         /* open window */
1047         renderwin_init_display_cb(rr);
1048         init_gl_stuff();
1049         
1050         waitcursor(1);
1051
1052         if(anim) {
1053                 bMovieHandle *mh= BKE_get_movie_handle(G.scene->r.imtype);
1054                 
1055                 mh->start_movie(&G.scene->r, winx, winy);
1056                 
1057                 for(CFRA= SFRA; CFRA<=EFRA; CFRA++) {
1058                         drawview3d_render(v3d, winx, winy);
1059                         glReadPixels(0, 0, winx, winy, GL_RGBA, GL_UNSIGNED_BYTE, rr->rect32);
1060                         window_swap_buffers(render_win->win);
1061                         
1062                         mh->append_movie(CFRA, rr->rect32, winx, winy);
1063                         if(test_break()) break;
1064                 }
1065                 mh->end_movie();
1066         }
1067         else {
1068                 drawview3d_render(v3d, winx, winy);
1069                 glReadPixels(0, 0, winx, winy, GL_RGBA, GL_UNSIGNED_BYTE, rr->rect32);
1070                 window_swap_buffers(render_win->win);
1071         }
1072         
1073         mainwindow_make_active();
1074         
1075         if(anim)
1076                 scene_update_for_newframe(G.scene, G.scene->lay);       // no redraw needed, this restores to view as we left it
1077         
1078         end_test_break_callback();
1079         waitcursor(0);
1080 }
1081
1082 void BIF_redraw_render_rect(void)
1083 {
1084         /* redraw */
1085         if (render_win) {
1086                 renderwin_queue_redraw(render_win);
1087         }
1088 }       
1089
1090 void BIF_swap_render_rects(void)
1091 {
1092 #if 0
1093         unsigned int *temp;
1094
1095         if(R.rectspare==0) {
1096                 R.rectspare= (unsigned int *)MEM_callocN(sizeof(int)*R.rectx*R.recty, "rectot");
1097                 R.sparex= R.rectx;
1098                 R.sparey= R.recty;
1099         }
1100         else if(R.sparex!=R.rectx || R.sparey!=R.recty) {
1101                 temp= (unsigned int *)MEM_callocN(sizeof(int)*R.rectx*R.recty, "rectot");
1102                                         
1103                 scalefastrect(R.rectspare, temp, R.sparex, R.sparey, R.rectx, R.recty);
1104                 MEM_freeN(R.rectspare);
1105                 R.rectspare= temp;
1106                                         
1107                 R.sparex= R.rectx;
1108                 R.sparey= R.recty;
1109         }
1110         
1111         temp= R.rectot;
1112         R.rectot= R.rectspare;
1113         R.rectspare= temp;
1114         
1115         if (render_win) {
1116                 char *tmp= render_win->render_text_spare;
1117                 render_win->render_text_spare= render_win->render_text;
1118                 render_win->render_text= tmp;
1119                 
1120                 window_set_title(render_win->win, renderwin_get_title(1));
1121                 
1122         }
1123
1124         /* redraw */
1125         BIF_redraw_render_rect();
1126 #endif
1127 }                               
1128
1129 /* called from usiblender.c too, to free and close renderwin */
1130 void BIF_close_render_display(void)
1131 {
1132         if (render_win) {
1133                 if (render_win->info_text) MEM_freeN(render_win->info_text);
1134                 if (render_win->render_text) MEM_freeN(render_win->render_text);
1135                 if (render_win->render_text_spare) MEM_freeN(render_win->render_text_spare);
1136
1137                 window_destroy(render_win->win); /* ghost close window */
1138                 MEM_freeN(render_win);
1139
1140                 render_win= NULL;
1141         }
1142 }
1143
1144
1145 /* typical with F11 key, show image or hide/close */
1146 void BIF_toggle_render_display(void) 
1147 {
1148         
1149         if (render_win) {
1150                 if(render_win->active) {
1151                         mainwindow_raise();
1152                         mainwindow_make_active();
1153                         render_win->active= 0;
1154                 }
1155                 else {
1156                         window_raise(render_win->win);
1157                         window_make_active(render_win->win);
1158                         render_win->active= 1;
1159                 }
1160         } 
1161         else {
1162                 RenderResult *rr= RE_GetResult(RE_GetRender("Render"));
1163                 if(rr) renderwin_init_display_cb(rr);
1164         }
1165 }
1166
1167 void BIF_renderwin_set_custom_cursor(unsigned char mask[16][2], unsigned char bitmap[16][2])
1168 {
1169         if (render_win) {
1170                 window_set_custom_cursor(render_win->win, mask, bitmap, 7, 7);
1171         }
1172 }