Final merge of HEAD (bf-blender) into the orange branch.
[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 == 0) {
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 *unused)
725 {
726         rcti win_rct;
727         float *rectf, fullrect[2][2];
728         
729         /* renderwindow cruft */
730         win_rct.xmin= win_rct.ymin= 0;
731         window_get_size(rw->win, &win_rct.xmax, &win_rct.ymax);
732         win_rct.ymax-= RW_HEADERY;
733         renderwin_get_fullrect(rw, fullrect);
734         
735         /* find current float rect for display, first case is after composit... still weak */
736         if(rr->rectf)
737                 rectf= rr->rectf;
738         else {
739                 RenderLayer *rl= BLI_findlink(&rr->layers, rr->actlay);
740                 rectf= rl->rectf;
741         }       
742         /* when rendering more pixels than needed, we crop away cruft */
743         if(rr->crop)
744                 rectf+= 4*(rr->crop*rr->rectx + rr->crop);
745         
746         /* tilerect defines drawing offset from (0,0) */
747         /* however, tilerect (xmin, ymin) is first pixel */
748         fullrect[0][0] += (rr->tilerect.xmin+rr->crop)*rw->zoom;
749         fullrect[0][1] += (rr->tilerect.ymin+rr->crop)*rw->zoom;
750
751         glEnable(GL_SCISSOR_TEST);
752         glaDefine2DArea(&win_rct);
753
754         glDrawBuffer(GL_FRONT);
755         glPixelZoom(rw->zoom, rw->zoom);
756         glaDrawPixelsSafe(fullrect[0][0], fullrect[0][1], rr->rectx-2*rr->crop, rr->recty-2*rr->crop, rr->rectx, 
757                                           GL_RGBA, GL_FLOAT, rectf);
758         glPixelZoom(1.0, 1.0);
759         glFlush();
760         glDrawBuffer(GL_BACK);
761 }
762
763
764 /* in render window; display a couple of scanlines of rendered image */
765 static void renderwin_progress_display_cb(RenderResult *rr, rcti *rect)
766 {
767         if (render_win) {
768                 renderwin_progress(render_win, rr, rect);
769         }
770 }
771
772 /* -------------- callbacks for render loop: interactivity ----------------------- */
773
774
775 /* callback for print info in top header of renderwin */
776 static void printrenderinfo_cb(RenderStats *rs)
777 {
778         extern char info_time_str[32];  // header_info.c
779         extern int mem_in_use;
780         static float megs_used_memory;
781         char str[300], *spos= str;
782                 
783         if(render_win) {
784                 megs_used_memory= mem_in_use/(1024.0*1024.0);
785                 
786                 if(rs->tothalo)
787                         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);
788                 else 
789                         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);
790
791                 BLI_timestr(rs->lastframetime, info_time_str);
792                 spos+= sprintf(spos, " Time:%s ", info_time_str);
793                 
794                 if(render_win) {
795                         if(render_win->render_text) MEM_freeN(render_win->render_text);
796                         render_win->render_text= BLI_strdup(str);
797                         glDrawBuffer(GL_FRONT);
798                         renderwin_draw_render_info(render_win);
799                         glFlush();
800                         glDrawBuffer(GL_BACK);
801                 }
802         }
803 }
804
805 /* -------------- callback system to allow ESC from rendering ----------------------- */
806
807 /* POSIX & WIN32: this function is called all the time, and should not use cpu or resources */
808 static int test_break(void)
809 {
810
811         if(G.afbreek==2) { /* code for testing queue */
812
813                 G.afbreek= 0;
814
815                 blender_test_break(); /* tests blender interface */
816
817                 if (G.afbreek==0 && render_win) { /* tests window */
818                         winlay_process_events(0);
819                         // render_win can be closed in winlay_process_events()
820                         if (render_win == 0 || (render_win->flags & RW_FLAGS_ESCAPE)) {
821                                 G.afbreek= 1;
822                         }
823                 }
824         }
825
826         if(G.afbreek==1) return 1;
827         else return 0;
828 }
829
830
831
832 #ifdef _WIN32
833 /* we use the multimedia time here */
834 static UINT uRenderTimerId;
835
836 void CALLBACK interruptESC(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
837 {
838         if(G.afbreek==0) G.afbreek= 2;  /* code for read queue */
839 }
840
841 /* WIN32: init SetTimer callback */
842 static void init_test_break_callback()
843 {
844         timeBeginPeriod(50);
845         uRenderTimerId = timeSetEvent(250, 1, interruptESC, 0, TIME_PERIODIC);
846 }
847
848 /* WIN32: stop SetTimer callback */
849 static void end_test_break_callback()
850 {
851         timeEndPeriod(50);
852         timeKillEvent(uRenderTimerId);
853 }
854
855 #else
856 /* all other OS's support signal(SIGVTALRM) */
857
858 /* POSIX: this function goes in the signal() callback */
859 static void interruptESC(int sig)
860 {
861
862         if(G.afbreek==0) G.afbreek= 2;  /* code for read queue */
863
864         /* call again, timer was reset */
865         signal(SIGVTALRM, interruptESC);
866 }
867
868 /* POSIX: initialize timer and signal */
869 static void init_test_break_callback()
870 {
871
872         struct itimerval tmevalue;
873
874         tmevalue.it_interval.tv_sec = 0;
875         tmevalue.it_interval.tv_usec = 250000;
876         /* wanneer de eerste ? */
877         tmevalue.it_value.tv_sec = 0;
878         tmevalue.it_value.tv_usec = 10000;
879
880         signal(SIGVTALRM, interruptESC);
881         setitimer(ITIMER_VIRTUAL, &tmevalue, 0);
882
883 }
884
885 /* POSIX: stop timer and callback */
886 static void end_test_break_callback()
887 {
888         struct itimerval tmevalue;
889
890         tmevalue.it_value.tv_sec = 0;
891         tmevalue.it_value.tv_usec = 0;
892         setitimer(ITIMER_VIRTUAL, &tmevalue, 0);
893         signal(SIGVTALRM, SIG_IGN);
894
895 }
896
897
898 #endif
899
900
901
902 /* -------------- callbacks for render loop: init & run! ----------------------- */
903
904
905 /* - initialize displays
906    - set callbacks
907    - cleanup
908 */
909
910 static void do_render(int anim)
911 {
912         Render *re= RE_NewRender("Render");
913         
914         /* we set this flag to prevent renderwindow queue to execute another render */
915         G.rendering= 1;
916         G.afbreek= 0;
917
918         /* set callbacks */
919         RE_display_init_cb(re, renderwin_init_display_cb);
920         RE_display_draw_cb(re, renderwin_progress_display_cb);
921         RE_display_clear_cb(re, renderwin_clear_display_cb);
922         init_test_break_callback();
923         RE_test_break_cb(re, test_break);
924         RE_timecursor_cb(re, set_timecursor);
925         RE_stats_draw_cb(re, printrenderinfo_cb);
926         
927         if(render_win) window_set_cursor(render_win->win, CURSOR_WAIT);
928         waitcursor(1);
929
930         if(G.obedit)
931                 exit_editmode(0);       /* 0 = no free data */
932
933         if(anim)
934                 RE_BlenderAnim(re, G.scene, G.scene->r.sfra, G.scene->r.efra);
935         else
936                 RE_BlenderFrame(re, G.scene, G.scene->r.cfra);
937
938         if(render_win) window_set_cursor(render_win->win, CURSOR_STD);
939
940         free_filesel_spec(G.scene->r.pic);
941
942         G.afbreek= 0;
943         end_test_break_callback();
944         
945         mainwindow_make_active();
946         
947         /* after an envmap creation...  */
948 //              if(R.flag & R_REDRAW_PRV) {
949 //                      BIF_preview_changed(ID_TE);
950 //              }
951         allqueue(REDRAWBUTSSCENE, 0);   // visualize fbuf for example
952         
953         // before scene update!
954         G.rendering= 0;
955         
956         scene_update_for_newframe(G.scene, G.scene->lay);       // no redraw needed, this restores to view as we left it
957         
958         waitcursor(0);  // waitcursor checks rendering R.flag...
959 }
960
961
962 /* used for swapping with spare buffer, when images are different size */
963 static void scalefastrect(unsigned int *recto, unsigned int *rectn, int oldx, int oldy, int newx, int newy)
964 {
965         unsigned int *rect, *newrect;
966         int x, y;
967         int ofsx, ofsy, stepx, stepy;
968
969         stepx = (int)((65536.0 * (oldx - 1.0) / (newx - 1.0)) + 0.5);
970         stepy = (int)((65536.0 * (oldy - 1.0) / (newy - 1.0)) + 0.5);
971         ofsy = 32768;
972         newrect= rectn;
973         
974         for (y = newy; y > 0 ; y--){
975                 rect = recto;
976                 rect += (ofsy >> 16) * oldx;
977                 ofsy += stepy;
978                 ofsx = 32768;
979                 for (x = newx ; x>0 ; x--){
980                         *newrect++ = rect[ofsx >> 16];
981                         ofsx += stepx;
982                 }
983         }
984 }
985
986 /* -------------- API: externally called --------------- */
987
988 /* not used anywhere ??? */
989 #if 0
990 void BIF_renderwin_make_active(void)
991 {
992         if(render_win) {
993                 window_make_active(render_win->win);
994                 mywinset(2);
995         }
996 }
997 #endif
998
999 /* set up display, render an image or scene */
1000 void BIF_do_render(int anim)
1001 {
1002         int slink_flag = 0;
1003
1004         if (G.f & G_DOSCRIPTLINKS) {
1005                 BPY_do_all_scripts(SCRIPT_RENDER);
1006                 if (!anim) { /* avoid FRAMECHANGED slink in render callback */
1007                         G.f &= ~G_DOSCRIPTLINKS;
1008                         slink_flag = 1;
1009                 }
1010         }
1011
1012         /* if start render in 3d win, use layer from window (e.g also local view) */
1013         if(curarea && curarea->spacetype==SPACE_VIEW3D) {
1014                 int lay= G.scene->lay;
1015                 /*
1016                  * if view is defined (might not be if called form script), check
1017                  * and set layers
1018                  */
1019                 if(G.vd) {
1020                         if(G.vd->lay & 0xFF000000)      // localview
1021                                 G.scene->lay |= G.vd->lay;
1022                         else
1023                                 G.scene->lay= G.vd->lay;
1024                 }
1025                 
1026                 do_render(anim);
1027                 
1028                 G.scene->lay= lay;
1029         }
1030         else do_render(anim);
1031
1032         if(G.scene->use_nodes)
1033                 allqueue(REDRAWNODE, 1);
1034         if (slink_flag) G.f |= G_DOSCRIPTLINKS;
1035         if (G.f & G_DOSCRIPTLINKS) BPY_do_all_scripts(SCRIPT_POSTRENDER);
1036 }
1037
1038 /* set up display, render the current area view in an image */
1039 /* the RE_Render is only used to make sure we got the picture in the result */
1040 void BIF_do_ogl_render(View3D *v3d, int anim)
1041 {
1042         Render *re= RE_NewRender("Render");
1043         RenderResult *rr;
1044         int winx, winy;
1045         
1046         G.afbreek= 0;
1047         init_test_break_callback();
1048         
1049         winx= (G.scene->r.size*G.scene->r.xsch)/100;
1050         winy= (G.scene->r.size*G.scene->r.ysch)/100;
1051         
1052         RE_InitState(re, &G.scene->r, winx, winy, NULL);
1053         
1054         /* for now, result is defaulting to floats still... */
1055         rr= RE_GetResult(re);
1056         if(rr->rect32==NULL)
1057                 rr->rect32= MEM_mallocN(sizeof(int)*winx*winy, "32 bits rects");
1058         
1059         /* open window */
1060         renderwin_init_display_cb(rr);
1061         init_gl_stuff();
1062         
1063         waitcursor(1);
1064
1065         if(anim) {
1066                 bMovieHandle *mh= BKE_get_movie_handle(G.scene->r.imtype);
1067                 int cfrao= CFRA;
1068                 
1069                 mh->start_movie(&G.scene->r, winx, winy);
1070                 
1071                 for(CFRA= SFRA; CFRA<=EFRA; CFRA++) {
1072                         drawview3d_render(v3d, winx, winy);
1073                         glReadPixels(0, 0, winx, winy, GL_RGBA, GL_UNSIGNED_BYTE, rr->rect32);
1074                         window_swap_buffers(render_win->win);
1075                         
1076                         mh->append_movie(CFRA, rr->rect32, winx, winy);
1077                         if(test_break()) break;
1078                 }
1079                 mh->end_movie();
1080                 CFRA= cfrao;
1081         }
1082         else {
1083                 drawview3d_render(v3d, winx, winy);
1084                 glReadPixels(0, 0, winx, winy, GL_RGBA, GL_UNSIGNED_BYTE, rr->rect32);
1085                 window_swap_buffers(render_win->win);
1086         }
1087         
1088         mainwindow_make_active();
1089         
1090         if(anim)
1091                 scene_update_for_newframe(G.scene, G.scene->lay);       // no redraw needed, this restores to view as we left it
1092         
1093         end_test_break_callback();
1094         waitcursor(0);
1095 }
1096
1097 void BIF_redraw_render_rect(void)
1098 {
1099         /* redraw */
1100         if (render_win) {
1101                 renderwin_queue_redraw(render_win);
1102         }
1103 }       
1104
1105 void BIF_swap_render_rects(void)
1106 {
1107 #if 0
1108         unsigned int *temp;
1109
1110         if(R.rectspare==0) {
1111                 R.rectspare= (unsigned int *)MEM_callocN(sizeof(int)*R.rectx*R.recty, "rectot");
1112                 R.sparex= R.rectx;
1113                 R.sparey= R.recty;
1114         }
1115         else if(R.sparex!=R.rectx || R.sparey!=R.recty) {
1116                 temp= (unsigned int *)MEM_callocN(sizeof(int)*R.rectx*R.recty, "rectot");
1117                                         
1118                 scalefastrect(R.rectspare, temp, R.sparex, R.sparey, R.rectx, R.recty);
1119                 MEM_freeN(R.rectspare);
1120                 R.rectspare= temp;
1121                                         
1122                 R.sparex= R.rectx;
1123                 R.sparey= R.recty;
1124         }
1125         
1126         temp= R.rectot;
1127         R.rectot= R.rectspare;
1128         R.rectspare= temp;
1129         
1130         if (render_win) {
1131                 char *tmp= render_win->render_text_spare;
1132                 render_win->render_text_spare= render_win->render_text;
1133                 render_win->render_text= tmp;
1134                 
1135                 window_set_title(render_win->win, renderwin_get_title(1));
1136                 
1137         }
1138
1139         /* redraw */
1140         BIF_redraw_render_rect();
1141 #endif
1142 }                               
1143
1144 /* called from usiblender.c too, to free and close renderwin */
1145 void BIF_close_render_display(void)
1146 {
1147         if (render_win) {
1148                 if (render_win->info_text) MEM_freeN(render_win->info_text);
1149                 if (render_win->render_text) MEM_freeN(render_win->render_text);
1150                 if (render_win->render_text_spare) MEM_freeN(render_win->render_text_spare);
1151
1152                 window_destroy(render_win->win); /* ghost close window */
1153                 MEM_freeN(render_win);
1154
1155                 render_win= NULL;
1156         }
1157 }
1158
1159
1160 /* typical with F11 key, show image or hide/close */
1161 void BIF_toggle_render_display(void) 
1162 {
1163         
1164         if (render_win) {
1165                 if(render_win->active) {
1166                         mainwindow_raise();
1167                         mainwindow_make_active();
1168                         render_win->active= 0;
1169                 }
1170                 else {
1171                         window_raise(render_win->win);
1172                         window_make_active(render_win->win);
1173                         render_win->active= 1;
1174                 }
1175         } 
1176         else {
1177                 RenderResult *rr= RE_GetResult(RE_GetRender("Render"));
1178                 if(rr) renderwin_init_display_cb(rr);
1179         }
1180 }
1181
1182 void BIF_renderwin_set_custom_cursor(unsigned char mask[16][2], unsigned char bitmap[16][2])
1183 {
1184         if (render_win) {
1185                 window_set_custom_cursor(render_win->win, mask, bitmap, 7, 7);
1186         }
1187 }