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