==renderer==
[blender.git] / source / blender / src / renderwin.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * The Original Code is: all of this file.
27  *
28  * Contributor(s): none yet.
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31  */
32  
33 #ifdef WIN32
34 /* for the multimedia timer */
35 #include <windows.h>
36 #include <mmsystem.h>
37 #endif
38
39 #include <string.h>
40 #include <stdarg.h>
41 #include <math.h>
42
43 #ifdef HAVE_CONFIG_H
44 #include <config.h>
45 #endif
46
47 #ifdef WIN32
48 #include "BLI_winstuff.h"
49 #else
50  /* for signal callback, not (fully) supported at windows */
51 #include <sys/time.h>
52 #include <signal.h>
53
54 #endif
55
56 #include <limits.h>
57
58 #include "BLI_blenlib.h"
59
60 #include "MEM_guardedalloc.h"
61
62 #include "BMF_Api.h"
63
64 #include "DNA_view3d_types.h"
65 #include "DNA_screen_types.h"
66 #include "DNA_vec_types.h"
67
68 #include "BKE_global.h"
69 #include "BKE_scene.h"
70 #include "BKE_utildefines.h"
71
72 #include "BIF_gl.h"
73 #include "BIF_glutil.h"
74 #include "BIF_graphics.h"
75 #include "BIF_screen.h"
76 #include "BIF_space.h"
77 #include "BIF_mywindow.h"
78 #include "BIF_previewrender.h"
79 #include "BIF_renderwin.h"
80 #include "BIF_resources.h"
81 #include "BIF_toets.h"
82
83 #include "BDR_editobject.h"
84 #include "BPY_extern.h" /* for BPY_do_all_scripts */
85
86 #include "BSE_view.h"
87 #include "BSE_drawview.h"
88 #include "BSE_filesel.h"
89 #include "BSE_headerbuttons.h"
90
91 #include "blendef.h"
92 #include "mydevice.h"
93 #include "winlay.h"
94 #include "render.h"
95
96 /* ------------ renderwin struct, to prevent too much global vars --------- */
97 /* ------------ only used for display in a 2nd window  --------- */
98
99
100 /* flags escape presses during event handling
101 * so we can test for user break later.
102 */
103 #define RW_FLAGS_ESCAPE         (1<<0)
104 /* old zoom style (2x, locked to mouse, exits
105 * when mouse leaves window), to be removed
106 * at some point.
107 */
108 #define RW_FLAGS_OLDZOOM                (1<<1)
109 /* on when image is being panned with middlemouse
110 */
111 #define RW_FLAGS_PANNING                (1<<2)
112 /* on when the mouse is dragging over the image
113 * to examine pixel values.
114 */
115 #define RW_FLAGS_PIXEL_EXAMINING        (1<<3)
116
117 /* forces draw of alpha */
118 #define RW_FLAGS_ALPHA          (1<<4)
119
120 /* space for info text */
121 #define RW_HEADERY              18
122
123 typedef struct {
124         Window *win;
125
126         float zoom, zoomofs[2];
127         int active;
128         
129         int mbut[3];
130         int lmouse[2];
131         
132         unsigned int flags;
133         
134         float pan_mouse_start[2], pan_ofs_start[2];
135
136         char *info_text;
137         char *render_text, *render_text_spare;
138         
139 } RenderWin;
140
141 static RenderWin *render_win= NULL;
142
143 /* --------------- help functions for RenderWin struct ---------------------------- */
144
145
146 /* only called in function open_renderwin */
147 static RenderWin *renderwin_alloc(Window *win)
148 {
149         RenderWin *rw= MEM_mallocN(sizeof(*rw), "RenderWin");
150         rw->win= win;
151         rw->zoom= 1.0;
152         rw->active= 0;
153         rw->flags= 0;
154         rw->zoomofs[0]= rw->zoomofs[1]= 0;
155         rw->info_text= NULL;
156         rw->render_text= rw->render_text_spare= NULL;
157
158         rw->lmouse[0]= rw->lmouse[1]= 0;
159         rw->mbut[0]= rw->mbut[1]= rw->mbut[2]= 0;
160
161         return rw;
162 }
163
164
165 static void renderwin_queue_redraw(RenderWin *rw)
166 {
167         window_queue_redraw(rw->win); // to ghost
168 }
169
170 static void renderwin_reshape(RenderWin *rw)
171 {
172         ;
173 }
174
175 static void renderwin_get_disprect(RenderWin *rw, float disprect_r[2][2])
176 {
177         float display_w, display_h;
178         float cent_x, cent_y;
179         int w, h;
180
181         window_get_size(rw->win, &w, &h);
182         h-= RW_HEADERY;
183
184         display_w= R.rectx*rw->zoom;
185         display_h= R.recty*rw->zoom;
186         cent_x= (rw->zoomofs[0] + R.rectx/2)*rw->zoom;
187         cent_y= (rw->zoomofs[1] + R.recty/2)*rw->zoom;
188         
189         disprect_r[0][0]= w/2 - cent_x;
190         disprect_r[0][1]= h/2 - cent_y;
191         disprect_r[1][0]= disprect_r[0][0] + display_w;
192         disprect_r[1][1]= disprect_r[0][1] + display_h;
193 }
194
195         /** 
196          * Project window coordinate to image pixel coordinate.
197          * Returns true if resulting coordinate is within image.
198          */
199 static int renderwin_win_to_image_co(RenderWin *rw, int winco[2], int imgco_r[2])
200 {
201         float disprect[2][2];
202         
203         renderwin_get_disprect(rw, disprect);
204         
205         imgco_r[0]= (int) ((winco[0]-disprect[0][0])/rw->zoom);
206         imgco_r[1]= (int) ((winco[1]-disprect[0][1])/rw->zoom);
207         
208         return (imgco_r[0]>=0 && imgco_r[1]>=0 && imgco_r[0]<R.rectx && imgco_r[1]<R.recty);
209 }
210
211         /**
212          * Project window coordinates to normalized device coordinates
213          * Returns true if resulting coordinate is within window.
214          */
215 static int renderwin_win_to_ndc(RenderWin *rw, int win_co[2], float ndc_r[2])
216 {
217         int w, h;
218
219         window_get_size(rw->win, &w, &h);
220         h-= RW_HEADERY;
221
222         ndc_r[0]=  ((float)(win_co[0]*2)/(w-1) - 1.0f);
223         ndc_r[1]=  ((float)(win_co[1]*2)/(h-1) - 1.0f);
224
225         return (fabs(ndc_r[0])<=1.0 && fabs(ndc_r[1])<=1.0);
226 }
227
228 static void renderwin_set_infotext(RenderWin *rw, char *info_text)
229 {
230         if (rw->info_text) MEM_freeN(rw->info_text);
231         rw->info_text= info_text?BLI_strdup(info_text):NULL;
232 }
233
234 static void renderwin_reset_view(RenderWin *rw)
235 {
236         int w, h, rectx, recty;
237
238         if (rw->info_text) renderwin_set_infotext(rw, NULL);
239
240         /* now calculate a zoom for when image is larger than window */
241         window_get_size(rw->win, &w, &h);
242         h-= RW_HEADERY;
243         
244         /* at this point the r.rectx/y values are not correct yet */
245         rectx= (G.scene->r.size*G.scene->r.xsch)/100;
246         recty= (G.scene->r.size*G.scene->r.ysch)/100;
247         
248         /* crop option makes image smaller */
249         if ((G.scene->r.mode & R_BORDER) && (G.scene->r.mode & R_MOVIECROP)) { 
250                 if(!(G.scene->r.scemode & R_OGL)) {
251                         rectx= (int) (rectx*(G.scene->r.border.xmax-G.scene->r.border.xmin));
252                         recty= (int) (recty*(G.scene->r.border.ymax-G.scene->r.border.ymin));
253                 }
254         }
255         
256         if(rectx>w || recty>h) {
257                 if(rectx-w > recty-h) rw->zoom= ((float)w)/((float)rectx);
258                 else rw->zoom= ((float)h)/((float)recty);
259         }
260         else rw->zoom= 1.0;
261
262         rw->zoomofs[0]= rw->zoomofs[1]= 0;
263         renderwin_queue_redraw(rw);
264 }
265
266 static void renderwin_draw_render_info(RenderWin *rw)
267 {
268         /* render text is added to top */
269         if(RW_HEADERY) {
270                 float colf[3];
271                 rcti rect;
272                 
273                 window_get_size(rw->win, &rect.xmax, &rect.ymax);
274                 rect.xmin= 0;
275                 rect.ymin= rect.ymax-RW_HEADERY;
276                 glEnable(GL_SCISSOR_TEST);
277                 glaDefine2DArea(&rect);
278                 
279                 /* clear header rect */
280                 BIF_SetTheme(NULL);     // sets view3d theme by default
281                 BIF_GetThemeColor3fv(TH_HEADER, colf);
282                 glClearColor(colf[0], colf[1], colf[2], 1.0); 
283                 glClear(GL_COLOR_BUFFER_BIT);
284                 
285                 if(rw->render_text) {
286                         BIF_ThemeColor(TH_TEXT);
287                         glRasterPos2i(12, 5);
288                         BMF_DrawString(G.fonts, rw->render_text);
289                 }
290                 
291                 BIF_SetTheme(curarea);  // restore theme
292         }       
293         
294 }
295
296 static void renderwin_draw(RenderWin *rw, int just_clear)
297 {
298         float disprect[2][2];
299         int set_back_mainwindow;
300         rcti rect;
301         
302         /* since renderwin uses callbacks (controlled by ghost) it can
303            mess up active window output with redraw events after a render. 
304            this is patchy, still WIP */
305         set_back_mainwindow = (winlay_get_active_window() != rw->win);
306         window_make_active(rw->win);
307         
308         rect.xmin= rect.ymin= 0;
309         window_get_size(rw->win, &rect.xmax, &rect.ymax);
310         rect.ymax-= RW_HEADERY;
311         
312         renderwin_get_disprect(rw, disprect);
313         
314         /* do this first, so window ends with correct scissor */
315         renderwin_draw_render_info(rw);
316         
317         glEnable(GL_SCISSOR_TEST);
318         glaDefine2DArea(&rect);
319         
320         glClearColor(.1875, .1875, .1875, 1.0); 
321         glClear(GL_COLOR_BUFFER_BIT);
322
323         if (just_clear || !R.rectot) {
324                 glColor3ub(0, 0, 0);
325                 glRectfv(disprect[0], disprect[1]);
326         } else {
327                 glPixelZoom(rw->zoom, rw->zoom);
328                 if(rw->flags & RW_FLAGS_ALPHA) {
329                         char *rect= (char *)R.rectot;
330                         
331                         glColorMask(1, 0, 0, 0);
332                         glaDrawPixelsSafe(disprect[0][0], disprect[0][1], R.rectx, R.recty, rect+3);
333                         glColorMask(0, 1, 0, 0);
334                         glaDrawPixelsSafe(disprect[0][0], disprect[0][1], R.rectx, R.recty, rect+2);
335                         glColorMask(0, 0, 1, 0);
336                         glaDrawPixelsSafe(disprect[0][0], disprect[0][1], R.rectx, R.recty, rect+1);
337                         glColorMask(1, 1, 1, 1);
338                         
339                 }
340                 else {
341                         glaDrawPixelsSafe(disprect[0][0], disprect[0][1], R.rectx, R.recty, R.rectot);
342                 }
343                 glPixelZoom(1.0, 1.0);
344         }
345         
346         /* info text is overlayed on bottom */
347         if (rw->info_text) {
348                 float w;
349                 glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
350                 glEnable(GL_BLEND);
351                 w=186.0*strlen(rw->info_text)/30;
352                 glColor4f(.5,.5,.5,.25);
353                 glRectf(0.0,0.0,w,30.0);
354                 glDisable(GL_BLEND);
355                 glColor3ub(255, 255, 255);
356                 glRasterPos2i(10, 10);
357                 BMF_DrawString(G.font, rw->info_text);
358         }
359         
360         window_swap_buffers(rw->win);
361         
362         if (set_back_mainwindow) mainwindow_make_active();      
363 }
364
365 /* ------ interactivity calls for RenderWin ------------- */
366
367 static void renderwin_mouse_moved(RenderWin *rw)
368 {
369         if (rw->flags & RW_FLAGS_PIXEL_EXAMINING) {
370                 int imgco[2];
371                 char buf[64];
372                 int *pxlz;      // zbuffer is signed 
373                 char *pxl;
374
375                 if (R.rectot && renderwin_win_to_image_co(rw, rw->lmouse, imgco)) {
376                         pxl= (char*) &R.rectot[R.rectx*imgco[1] + imgco[0]];
377                         
378                         if (R.rectz) {
379                                 pxlz= &R.rectz[R.rectx*imgco[1] + imgco[0]];                    
380                                 sprintf(buf, "R: %d, G: %d, B: %d, A: %d, Z: %f", pxl[0], pxl[1], pxl[2], pxl[3], 0.5+0.5*( ((float)*pxlz)/(float)INT_MAX) );
381                         }
382                         else {
383                                 sprintf(buf, "R: %d, G: %d, B: %d, A: %d", pxl[0], pxl[1], pxl[2], pxl[3]);                     
384                         }
385
386                         renderwin_set_infotext(rw, buf);
387                         renderwin_queue_redraw(rw);
388                 } else {
389                         renderwin_set_infotext(rw, NULL);
390                         renderwin_queue_redraw(rw);
391                 }
392         } 
393         else if (rw->flags & RW_FLAGS_PANNING) {
394                 int delta_x= rw->lmouse[0] - rw->pan_mouse_start[0];
395                 int delta_y= rw->lmouse[1] - rw->pan_mouse_start[1];
396         
397                 rw->zoomofs[0]= rw->pan_ofs_start[0] - delta_x/rw->zoom;
398                 rw->zoomofs[1]= rw->pan_ofs_start[1] - delta_y/rw->zoom;
399                 rw->zoomofs[0]= CLAMPIS(rw->zoomofs[0], -R.rectx/2, R.rectx/2);
400                 rw->zoomofs[1]= CLAMPIS(rw->zoomofs[1], -R.recty/2, R.recty/2);
401
402                 renderwin_queue_redraw(rw);
403         } 
404         else if (rw->flags & RW_FLAGS_OLDZOOM) {
405                 float ndc[2];
406                 int w, h;
407
408                 window_get_size(rw->win, &w, &h);
409                 h-= RW_HEADERY;
410                 renderwin_win_to_ndc(rw, rw->lmouse, ndc);
411
412                 rw->zoomofs[0]= -0.5*ndc[0]*(w-R.rectx*rw->zoom)/rw->zoom;
413                 rw->zoomofs[1]= -0.5*ndc[1]*(h-R.recty*rw->zoom)/rw->zoom;
414
415                 renderwin_queue_redraw(rw);
416         }
417 }
418
419 static void renderwin_mousebut_changed(RenderWin *rw)
420 {
421         if (rw->mbut[0]) {
422                 rw->flags|= RW_FLAGS_PIXEL_EXAMINING;
423         } 
424         else if (rw->mbut[1]) {
425                 rw->flags|= RW_FLAGS_PANNING;
426                 rw->pan_mouse_start[0]= rw->lmouse[0];
427                 rw->pan_mouse_start[1]= rw->lmouse[1];
428                 rw->pan_ofs_start[0]= rw->zoomofs[0];
429                 rw->pan_ofs_start[1]= rw->zoomofs[1];
430         } 
431         else {
432                 if (rw->flags & RW_FLAGS_PANNING) {
433                         rw->flags &= ~RW_FLAGS_PANNING;
434                         renderwin_queue_redraw(rw);
435                 }
436                 if (rw->flags & RW_FLAGS_PIXEL_EXAMINING) {
437                         rw->flags&= ~RW_FLAGS_PIXEL_EXAMINING;
438                         renderwin_set_infotext(rw, NULL);
439                         renderwin_queue_redraw(rw);
440                 }
441         }
442 }
443
444
445 /* handler for renderwin, passed on to Ghost */
446 static void renderwin_handler(Window *win, void *user_data, short evt, short val, char ascii)
447 {
448         RenderWin *rw= user_data;
449
450         // added this for safety, while render it's just creating bezerk results
451         if(R.flag & R_RENDERING) {
452                 if(evt==ESCKEY && val) 
453                         rw->flags|= RW_FLAGS_ESCAPE;
454                 return;
455         }
456         
457         if (evt==RESHAPE) {
458                 renderwin_reshape(rw);
459         } 
460         else if (evt==REDRAW) {
461                 renderwin_draw(rw, 0);
462         } 
463         else if (evt==WINCLOSE) {
464                 BIF_close_render_display();
465         } 
466         else if (evt==INPUTCHANGE) {
467                 rw->active= val;
468
469                 if (!val && (rw->flags&RW_FLAGS_OLDZOOM)) {
470                         rw->flags&= ~RW_FLAGS_OLDZOOM;
471                         renderwin_reset_view(rw);
472                 }
473         } 
474         else if (ELEM(evt, MOUSEX, MOUSEY)) {
475                 rw->lmouse[evt==MOUSEY]= val;
476                 renderwin_mouse_moved(rw);
477         } 
478         else if (ELEM3(evt, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) {
479                 int which= (evt==LEFTMOUSE)?0:(evt==MIDDLEMOUSE)?1:2;
480                 rw->mbut[which]= val;
481                 renderwin_mousebut_changed(rw);
482         } 
483         else if (val) {
484                 if (evt==ESCKEY) {
485                         if (rw->flags&RW_FLAGS_OLDZOOM) {
486                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
487                                 renderwin_reset_view(rw);
488                         } 
489                         else {
490                                 rw->flags|= RW_FLAGS_ESCAPE;
491                                 mainwindow_raise();
492                                 mainwindow_make_active();
493                                 rw->active= 0;
494                         }
495                 } 
496                 else if( evt==AKEY) {
497                         rw->flags ^= RW_FLAGS_ALPHA;
498                         renderwin_queue_redraw(render_win);
499                 }
500                 else if (evt==JKEY) {
501                         if(R.flag==0) BIF_swap_render_rects();
502                 } 
503                 else if (evt==ZKEY) {
504                         if (rw->flags&RW_FLAGS_OLDZOOM) {
505                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
506                                 renderwin_reset_view(rw);
507                         } else {
508                                 rw->zoom= 2.0;
509                                 rw->flags|= RW_FLAGS_OLDZOOM;
510                                 renderwin_mouse_moved(rw);
511                         }
512                 } 
513                 else if (evt==PADPLUSKEY) {
514                         if (rw->zoom<15.9) {
515                                 if(rw->zoom>0.5 && rw->zoom<1.0) rw->zoom= 1.0;
516                                 else rw->zoom*= 2.0;
517                                 renderwin_queue_redraw(rw);
518                         }
519                 } 
520                 else if (evt==PADMINUS) {
521                         if (rw->zoom>0.26) {
522                                 if(rw->zoom>1.0 && rw->zoom<2.0) rw->zoom= 1.0;
523                                 else rw->zoom*= 0.5;
524                                 renderwin_queue_redraw(rw);
525                         }
526                 } 
527                 else if (evt==PADENTER || evt==HOMEKEY) {
528                         if (rw->flags&RW_FLAGS_OLDZOOM) {
529                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
530                         }
531                         renderwin_reset_view(rw);
532                 } 
533                 else if (evt==F3KEY) {
534                         if(R.flag==0) {
535                                 mainwindow_raise();
536                                 mainwindow_make_active();
537                                 rw->active= 0;
538                                 areawinset(find_biggest_area()->win);
539                                 BIF_save_rendered_image();
540                         }
541                 } 
542                 else if (evt==F11KEY) {
543                         BIF_toggle_render_display();
544                 } 
545                 else if (evt==F12KEY) {
546                         /* if it's rendering, this flag is set */
547                         if(R.flag==0) BIF_do_render(0);
548                 }
549         }
550 }
551
552 static char *renderwin_get_title(int doswap)
553 {
554         static int swap= 0;
555         char *title="";
556         
557         swap+= doswap;
558         
559         if(swap & 1) {
560                 if (G.scene->r.renderer==R_YAFRAY) title = "YafRay:Render (spare)";
561                 else title = "Blender:Render (spare)";
562         }
563         else {
564                 if (G.scene->r.renderer==R_YAFRAY) title = "YafRay:Render";
565                 else title = "Blender:Render";
566         }
567
568         return title;
569 }
570
571 /* opens window and allocs struct */
572 static void open_renderwin(int winpos[2], int winsize[2])
573 {
574         extern void mywindow_build_and_set_renderwin( int orx, int ory, int sizex, int sizey); // mywindow.c
575         Window *win;
576         char *title;
577         
578         title= renderwin_get_title(0);  /* 0 = no swap */
579         win= window_open(title, winpos[0], winpos[1], winsize[0], winsize[1]+RW_HEADERY, 0);
580
581         render_win= renderwin_alloc(win);
582
583         /* Ghost calls handler */
584         window_set_handler(win, renderwin_handler, render_win);
585
586         winlay_process_events(0);
587         window_make_active(render_win->win);
588         winlay_process_events(0);
589         
590         /* mywindow has to know about it too */
591         mywindow_build_and_set_renderwin(winpos[0], winpos[1], winsize[0], winsize[1]+RW_HEADERY);
592         /* and we should be able to draw 3d in it */
593         init_gl_stuff();
594         
595         renderwin_draw(render_win, 1);
596         renderwin_draw(render_win, 1);
597 }
598
599 /* -------------- callbacks for render loop: Window (RenderWin) ----------------------- */
600
601 /* calculations for window size and position */
602 void calc_renderwin_rectangle(int posmask, int renderpos_r[2], int rendersize_r[2]) 
603 {
604         int scr_w, scr_h, x, y, div= 0;
605         float ndc_x= 0.0, ndc_y= 0.0;
606
607         winlay_get_screensize(&scr_w, &scr_h);
608
609         rendersize_r[0]= (G.scene->r.size*G.scene->r.xsch)/100;
610         rendersize_r[1]= (G.scene->r.size*G.scene->r.ysch)/100;
611         
612         /* crop option makes image smaller */
613         if ((G.scene->r.mode & R_BORDER) && (G.scene->r.mode & R_MOVIECROP)) { 
614                 rendersize_r[0]*= (G.scene->r.border.xmax-G.scene->r.border.xmin);
615                 rendersize_r[1]*= (G.scene->r.border.ymax-G.scene->r.border.ymin);
616         }
617         
618         if(G.scene->r.mode & R_PANORAMA) {
619                 rendersize_r[0]*= G.scene->r.xparts;
620                 rendersize_r[1]*= G.scene->r.yparts;
621         }
622
623         rendersize_r[0]= CLAMPIS(rendersize_r[0], 0, scr_w);     
624         rendersize_r[1]= CLAMPIS(rendersize_r[1], 0, scr_h-RW_HEADERY);  
625         
626         for (y=-1; y<=1; y++) {
627                 for (x=-1; x<=1; x++) {
628                         if (posmask & (1<<((y+1)*3 + (x+1)))) {
629                                 ndc_x+= x;
630                                 ndc_y+= y;
631                                 div++;
632                         }
633                 }
634         }
635
636         if (div) {
637                 ndc_x/= div;
638                 ndc_y/= div;
639         }
640         
641         renderpos_r[0]= (scr_w-rendersize_r[0])*(ndc_x*0.5 + 0.5);
642 #ifdef __APPLE__
643         /* 44 pixels is topbar and window header... awaiting better fixes in ghost :) */
644         rendersize_r[1]= CLAMPIS(rendersize_r[1], 0, scr_h-44-RW_HEADERY);       
645         renderpos_r[1]= -44-RW_HEADERY+(scr_h-rendersize_r[1])*(ndc_y*0.5 + 0.5);
646 #else
647         renderpos_r[1]= -RW_HEADERY+(scr_h-rendersize_r[1])*(ndc_y*0.5 + 0.5);
648 #endif
649 }
650         
651 /* init renderwin, alloc/open/resize */
652 static void renderwin_init_display_cb(void) 
653 {
654         if (G.afbreek == 0) {
655                 int rendersize[2], renderpos[2];
656
657                 calc_renderwin_rectangle(G.winpos, renderpos, rendersize);
658
659                 if (!render_win) {
660                         open_renderwin(renderpos, rendersize);
661                         renderwin_reset_view(render_win); // incl. autozoom for large images
662                 } else {
663                         int win_x, win_y;
664                         int win_w, win_h;
665
666                         window_get_position(render_win->win, &win_x, &win_y);
667                         window_get_size(render_win->win, &win_w, &win_h);
668                         win_h-= RW_HEADERY;
669
670                                 /* XXX, this is nasty and I guess bound to cause problems,
671                                  * but to ensure the window is at the user specified position
672                                  * and size we reopen the window all the time... we need
673                                  * a ghost _set_position to fix this -zr
674                                  */
675                                  
676                                  /* XXX, well... it is nasty yes, and reopens windows each time on
677                                     subsequent renders. Better rule is to make it reopen only only
678                                     size change, and use the preferred position only on open_renderwin
679                                         cases (ton)
680                                  */
681                         if(rendersize[0]!= win_w || rendersize[1]!= win_h) {
682                                 BIF_close_render_display();
683                                 open_renderwin(renderpos, rendersize);
684                         }
685                         else {
686                                 window_raise(render_win->win);
687                                 window_make_active(render_win->win);
688                                 
689                                 mywinset(2);    // to assign scissor/viewport again in mywindow.c. is hackish yes, but otherwise it draws in header of button for ogl header
690                                 {
691                                         rcti win_rct;
692                                         win_rct.xmin= win_rct.ymin= 0;
693                                         window_get_size(render_win->win, &win_rct.xmax, &win_rct.ymax);
694                                         win_rct.ymax-= RW_HEADERY;
695                                         glaDefine2DArea(&win_rct);
696                                 }
697                         }
698
699                         renderwin_reset_view(render_win);
700                         render_win->flags&= ~RW_FLAGS_ESCAPE;
701                         render_win->active= 1;
702                 }
703                 /* make sure we are in normal draw again */
704                 render_win->flags &= ~RW_FLAGS_ALPHA;
705         }
706 }
707
708 /* callback for redraw render win */
709 static void renderwin_clear_display_cb(short ignore) 
710 {
711         if (render_win) {
712                 window_make_active(render_win->win);    
713                 renderwin_draw(render_win, 1);
714         }
715 }
716
717 /* XXX, this is not good, we do this without any regard to state
718 * ... better is to make this an optimization of a more clear
719 * implementation. the bug shows up when you do something like
720 * open the window, then draw part of the progress, then get
721 * a redraw event. whatever can go wrong will. -zr
722 *
723 * Note: blocked queue handling while rendering to prevent that (ton)
724 */
725
726 /* in render window; display a couple of scanlines of rendered image (see callback below) */
727 static void renderwin_progress(RenderWin *rw, int start_y, int nlines, int rect_w, int rect_h, unsigned char *rect)
728 {
729         float disprect[2][2];
730         rcti win_rct;
731
732         win_rct.xmin= win_rct.ymin= 0;
733         window_get_size(rw->win, &win_rct.xmax, &win_rct.ymax);
734         win_rct.ymax-= RW_HEADERY;
735         
736         renderwin_get_disprect(rw, disprect);
737         
738         /* for efficiency & speed; not drawing in Blender UI while rendering */
739         //window_make_active(rw->win);
740
741         glEnable(GL_SCISSOR_TEST);
742         glaDefine2DArea(&win_rct);
743
744         glDrawBuffer(GL_FRONT);
745         glPixelZoom(rw->zoom, rw->zoom);
746         glaDrawPixelsSafe(disprect[0][0], disprect[0][1] + start_y*rw->zoom, rect_w, nlines, &rect[start_y*rect_w*4]);
747         glPixelZoom(1.0, 1.0);
748         glFlush();
749         glDrawBuffer(GL_BACK);
750 }
751
752
753 /* in render window; display a couple of scanlines of rendered image */
754 static void renderwin_progress_display_cb(int y1, int y2, int w, int h, unsigned int *rect)
755 {
756         if (render_win) {
757                 renderwin_progress(render_win, y1, y2-y1+1, w, h, (unsigned char*) rect);
758         }
759 }
760
761
762 /* -------------- callbacks for render loop: in View3D ----------------------- */
763
764
765 static View3D *render_view3d = NULL;
766
767 /* init Render view callback */
768 static void renderview_init_display_cb(void)
769 {
770         ScrArea *sa;
771
772                 /* Choose the first view with a persp camera,
773                  * if one doesn't exist we will get the first
774                  * View3D window.
775                  */ 
776         render_view3d= NULL;
777         for (sa= G.curscreen->areabase.first; sa; sa= sa->next) {
778                 if (sa->win && sa->spacetype==SPACE_VIEW3D) {
779                         View3D *vd= sa->spacedata.first;
780                         
781                         if (vd->persp==2 && vd->camera==G.scene->camera) {
782                                 render_view3d= vd;
783                                 break;
784                         } else if (!render_view3d) {
785                                 render_view3d= vd;
786                         }
787                 }
788         }
789 }
790
791
792 /* in 3d view; display a couple of scanlines of rendered image */
793 static void renderview_progress_display_cb(int y1, int y2, int w, int h, unsigned int *rect)
794 {
795         if (render_view3d) {
796                 View3D *v3d= render_view3d;
797                 int nlines= y2-y1+1;
798                 float sx, sy, facx, facy;
799                 rcti win_rct, vb;
800
801                 calc_viewborder(v3d, &vb);
802                 
803                 /* if border render  */
804                 if(G.scene->r.mode & R_BORDER) { 
805                         
806                         /* but, if image is full (at end of border render, without crop) we don't */
807                         if(R.rectx != (G.scene->r.size*G.scene->r.xsch)/100 ||
808                            R.recty != (G.scene->r.size*G.scene->r.ysch)/100 ) {
809                         
810                                 facx= (float) (vb.xmax-vb.xmin);
811                                 facy= (float) (vb.ymax-vb.ymin);
812                                 
813                                 vb.xmax= vb.xmin + facx*G.scene->r.border.xmax;
814                                 vb.ymax= vb.ymin + facy*G.scene->r.border.ymax;
815                                 vb.xmin+= facx*G.scene->r.border.xmin;
816                                 vb.ymin+= facy*G.scene->r.border.ymin;
817                         }
818                 }
819                         
820                 facx= (float) (vb.xmax-vb.xmin)/R.rectx;
821                 facy= (float) (vb.ymax-vb.ymin)/R.recty;
822
823                 bwin_get_rect(v3d->area->win, &win_rct);
824
825                 glaDefine2DArea(&win_rct);
826         
827                 glDrawBuffer(GL_FRONT);
828                 
829                 sx= vb.xmin;
830                 sy= vb.ymin + facy*y1;
831
832                 glPixelZoom(facx, facy);
833                 glaDrawPixelsSafe(sx, sy, w, nlines, rect+w*y1);
834                 glPixelZoom(1.0, 1.0);
835
836                 glFlush();
837                 glDrawBuffer(GL_BACK);
838                 v3d->flag |= V3D_DISPIMAGE;
839                 v3d->area->win_swap= WIN_FRONT_OK;
840                 
841         }
842 }
843
844 /* in 3d view; display stats of rendered image */
845 static void renderview_draw_render_info(char *str)
846 {
847         if (render_view3d) {
848                 View3D *v3d= render_view3d;
849                 rcti vb, win_rct;
850                 
851                 calc_viewborder(v3d, &vb);
852                 
853                 bwin_get_rect(v3d->area->win, &win_rct);
854                 glaDefine2DArea(&win_rct);
855                 
856                 glDrawBuffer(GL_FRONT);
857                 
858                 /* clear header rect */
859                 BIF_ThemeColor(TH_HEADER);
860                 glRecti(vb.xmin, vb.ymax, vb.xmax, vb.ymax+RW_HEADERY);
861                 
862                 if(str) {
863                         BIF_ThemeColor(TH_TEXT);
864                         glRasterPos2i(vb.xmin+12, vb.ymax+5);
865                         BMF_DrawString(G.fonts, str);
866                 }
867                 
868                 glFlush();
869                 glDrawBuffer(GL_BACK);
870
871                 v3d->area->win_swap= WIN_FRONT_OK;
872                 
873         }
874 }
875
876
877 /* -------------- callbacks for render loop: interactivity ----------------------- */
878
879
880 /* callback for print info in top header of renderwin */
881 /* time is only not zero on last call, we then don't update the other stats */ 
882 static void printrenderinfo_cb(double time, int sample)
883 {
884         extern int mem_in_use;
885         static int totvert=0, totvlak=0, tothalo=0, totlamp=0;
886         static float megs_used_memory=0.0;
887         char str[300], *spos= str;
888                 
889         if(time==0.0) {
890                 megs_used_memory= mem_in_use/(1024.0*1024.0);
891                 totvert= R.totvert;
892                 totvlak= R.totvlak;
893                 totlamp= R.totlamp;
894                 tothalo= R.tothalo;
895         }
896         
897         if(tothalo)
898                 spos+= sprintf(spos, "Fra:%d  Ve:%d Fa:%d Ha:%d La:%d Mem:%.2fM", (G.scene->r.cfra), totvert, totvlak, tothalo, totlamp, megs_used_memory);
899         else 
900                 spos+= sprintf(spos, "Fra:%d  Ve:%d Fa:%d La:%d Mem:%.2fM", (G.scene->r.cfra), totvert, totvlak, totlamp, megs_used_memory);
901
902         if(time==0.0) {
903                 if (R.r.mode & R_FIELDS) {
904                         spos+= sprintf(spos, "Field %c ", (R.flag&R_SEC_FIELD)?'B':'A');
905                 }
906                 if (sample!=-1) {
907                         spos+= sprintf(spos, "Sample: %d    ", sample);
908                 }
909         }
910         else {
911                 extern char info_time_str[32];  // header_info.c
912                 timestr(time, info_time_str);
913                 spos+= sprintf(spos, " Time:%s ", info_time_str);
914         }
915         
916         if(render_win) {
917                 if(render_win->render_text) MEM_freeN(render_win->render_text);
918                 render_win->render_text= BLI_strdup(str);
919                 glDrawBuffer(GL_FRONT);
920                 renderwin_draw_render_info(render_win);
921                 glFlush();
922                 glDrawBuffer(GL_BACK);
923         }
924         else renderview_draw_render_info(str);
925 }
926
927 /* -------------- callback system to allow ESC from rendering ----------------------- */
928
929 /* POSIX & WIN32: this function is called all the time, and should not use cpu or resources */
930 static int test_break(void)
931 {
932
933         if(G.afbreek==2) { /* code for testing queue */
934
935                 G.afbreek= 0;
936
937                 blender_test_break(); /* tests blender interface */
938
939                 if (G.afbreek==0 && render_win) { /* tests window */
940                         winlay_process_events(0);
941                         // render_win can be closed in winlay_process_events()
942                         if (render_win == 0 || (render_win->flags & RW_FLAGS_ESCAPE)) {
943                                 G.afbreek= 1;
944                         }
945                 }
946         }
947
948         if(G.afbreek==1) return 1;
949         else return 0;
950 }
951
952
953
954 #ifdef _WIN32
955 /* we use the multimedia time here */
956 static UINT uRenderTimerId;
957
958 void CALLBACK interruptESC(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
959 {
960         if(G.afbreek==0) G.afbreek= 2;  /* code for read queue */
961 }
962
963 /* WIN32: init SetTimer callback */
964 static void init_test_break_callback()
965 {
966         timeBeginPeriod(50);
967         uRenderTimerId = timeSetEvent(250, 1, interruptESC, 0, TIME_PERIODIC);
968 }
969
970 /* WIN32: stop SetTimer callback */
971 static void end_test_break_callback()
972 {
973         timeEndPeriod(50);
974         timeKillEvent(uRenderTimerId);
975 }
976
977 #else
978 /* all other OS's support signal(SIGVTALRM) */
979
980 /* POSIX: this function goes in the signal() callback */
981 static void interruptESC(int sig)
982 {
983
984         if(G.afbreek==0) G.afbreek= 2;  /* code for read queue */
985
986         /* call again, timer was reset */
987         signal(SIGVTALRM, interruptESC);
988 }
989
990 /* POSIX: initialize timer and signal */
991 static void init_test_break_callback()
992 {
993
994         struct itimerval tmevalue;
995
996         tmevalue.it_interval.tv_sec = 0;
997         tmevalue.it_interval.tv_usec = 250000;
998         /* wanneer de eerste ? */
999         tmevalue.it_value.tv_sec = 0;
1000         tmevalue.it_value.tv_usec = 10000;
1001
1002         signal(SIGVTALRM, interruptESC);
1003         setitimer(ITIMER_VIRTUAL, &tmevalue, 0);
1004
1005 }
1006
1007 /* POSIX: stop timer and callback */
1008 static void end_test_break_callback()
1009 {
1010         struct itimerval tmevalue;
1011
1012         tmevalue.it_value.tv_sec = 0;
1013         tmevalue.it_value.tv_usec = 0;
1014         setitimer(ITIMER_VIRTUAL, &tmevalue, 0);
1015         signal(SIGVTALRM, SIG_IGN);
1016
1017 }
1018
1019
1020 #endif
1021
1022
1023
1024 /* -------------- callbacks for render loop: init & run! ----------------------- */
1025
1026
1027 /* - initialize displays
1028    - both opengl render as blender render
1029    - set callbacks
1030    - cleanup
1031 */
1032
1033 static void do_render(View3D *ogl_render_view3d, int anim, int force_dispwin)
1034 {
1035         
1036         /* we set this flag to prevent renderwindow queue to execute another render */
1037         R.flag= R_RENDERING;
1038         G.afbreek= 0;
1039
1040         if (G.displaymode == R_DISPLAYWIN || force_dispwin) {
1041                 RE_set_initrenderdisplay_callback(NULL);
1042                 RE_set_clearrenderdisplay_callback(renderwin_clear_display_cb);
1043                 RE_set_renderdisplay_callback(renderwin_progress_display_cb);
1044
1045                 renderwin_init_display_cb();
1046         } 
1047         else {
1048                 BIF_close_render_display();
1049                 RE_set_initrenderdisplay_callback(renderview_init_display_cb);
1050                 RE_set_clearrenderdisplay_callback(NULL);
1051                 RE_set_renderdisplay_callback(renderview_progress_display_cb);
1052         }
1053
1054         init_test_break_callback();
1055         RE_set_test_break_callback(test_break);
1056         
1057         RE_set_timecursor_callback(set_timecursor);
1058         RE_set_printrenderinfo_callback(printrenderinfo_cb);
1059         
1060         if (render_win) {
1061                 window_set_cursor(render_win->win, CURSOR_WAIT);
1062                 // when opening new window... not cross platform identical behaviour, so
1063                 // for now call it each time
1064                 // if(ogl_render_view3d) init_gl_stuff();
1065         }
1066         waitcursor(1);
1067
1068         if(G.obedit && !(G.scene->r.scemode & R_OGL)) {
1069                 exit_editmode(0);       /* 0 = no free data */
1070         }
1071
1072         if(anim) {
1073                 RE_animrender(ogl_render_view3d);
1074         }
1075         else {
1076                 RE_initrender(ogl_render_view3d);
1077         }
1078
1079         if (render_win) window_set_cursor(render_win->win, CURSOR_STD);
1080
1081         free_filesel_spec(G.scene->r.pic);
1082
1083         G.afbreek= 0;
1084         end_test_break_callback();
1085         
1086         /* in dispiew it will destroy the image otherwise
1087            window_make_active() raises window at osx and sends redraws */
1088         if(G.displaymode==R_DISPLAYWIN) {
1089                 mainwindow_make_active();
1090         
1091                 /* after an envmap creation...  */
1092                 if(R.flag & R_REDRAW_PRV) {
1093                         BIF_all_preview_changed();
1094                 }
1095                 allqueue(REDRAWBUTSSCENE, 0);   // visualize fbuf for example
1096         }
1097         
1098         R.flag= 0;      // before scene update!
1099         
1100         scene_update_for_newframe(G.scene, G.scene->lay);       // no redraw needed, this restores to view as we left it
1101         
1102         waitcursor(0);  // waitcursor checks rendering R.flag...
1103 }
1104
1105 /* finds area with a 'dispview' set */
1106 static ScrArea *find_dispimage_v3d(void)
1107 {
1108         ScrArea *sa;
1109         
1110         for (sa= G.curscreen->areabase.first; sa; sa= sa->next) {
1111                 if (sa->spacetype==SPACE_VIEW3D) {
1112                         View3D *vd= sa->spacedata.first;
1113                         if (vd->flag & V3D_DISPIMAGE)
1114                                 return sa;
1115                 }
1116         }
1117         
1118         return NULL;
1119 }
1120
1121 /* used for swapping with spare buffer, when images are different size */
1122 static void scalefastrect(unsigned int *recto, unsigned int *rectn, int oldx, int oldy, int newx, int newy)
1123 {
1124         unsigned int *rect, *newrect;
1125         int x, y;
1126         int ofsx, ofsy, stepx, stepy;
1127
1128         stepx = (int)((65536.0 * (oldx - 1.0) / (newx - 1.0)) + 0.5);
1129         stepy = (int)((65536.0 * (oldy - 1.0) / (newy - 1.0)) + 0.5);
1130         ofsy = 32768;
1131         newrect= rectn;
1132         
1133         for (y = newy; y > 0 ; y--){
1134                 rect = recto;
1135                 rect += (ofsy >> 16) * oldx;
1136                 ofsy += stepy;
1137                 ofsx = 32768;
1138                 for (x = newx ; x>0 ; x--){
1139                         *newrect++ = rect[ofsx >> 16];
1140                         ofsx += stepx;
1141                 }
1142         }
1143 }
1144
1145 /* -------------- API: externally called --------------- */
1146
1147 /* not used anywhere ??? */
1148 #if 0
1149 void BIF_renderwin_make_active(void)
1150 {
1151         if(render_win) {
1152                 window_make_active(render_win->win);
1153                 mywinset(2);
1154         }
1155 }
1156 #endif
1157
1158 /* set up display, render an image or scene */
1159 void BIF_do_render(int anim)
1160 {
1161         int slink_flag = 0;
1162
1163         if (G.f & G_DOSCRIPTLINKS) {
1164                 BPY_do_all_scripts(SCRIPT_RENDER);
1165                 if (!anim) { /* avoid FRAMECHANGED slink in render callback */
1166                         G.f &= ~G_DOSCRIPTLINKS;
1167                         slink_flag = 1;
1168                 }
1169         }
1170
1171         /* if start render in 3d win, use layer from window (e.g also local view) */
1172         if(curarea && curarea->spacetype==SPACE_VIEW3D) {
1173                 int lay= G.scene->lay;
1174                 /*
1175                  * if view is defined (might not be if called form script), check
1176                  * and set layers
1177                  */
1178                 if(G.vd) {
1179                         if(G.vd->lay & 0xFF000000)      // localview
1180                                 G.scene->lay |= G.vd->lay;
1181                         else
1182                                 G.scene->lay= G.vd->lay;
1183                 }
1184                 
1185                 do_render(NULL, anim, 0);
1186                 
1187                 G.scene->lay= lay;
1188         }
1189         else do_render(NULL, anim, 0);
1190
1191         if (slink_flag) G.f |= G_DOSCRIPTLINKS;
1192         if (G.f & G_DOSCRIPTLINKS) BPY_do_all_scripts(SCRIPT_POSTRENDER);
1193 }
1194
1195 /* set up display, render the current area view in an image */
1196 void BIF_do_ogl_render(View3D *ogl_render_view3d, int anim)
1197 {
1198         G.scene->r.scemode |= R_OGL;
1199         do_render(ogl_render_view3d, anim, 1);
1200         G.scene->r.scemode &= ~R_OGL;
1201 }
1202
1203 void BIF_redraw_render_rect(void)
1204 {
1205         
1206         /* redraw */
1207         if (G.displaymode == R_DISPLAYWIN) {
1208                 // don't open render_win if rendering has been
1209             // canceled or the render_win has been actively closed
1210                 if (render_win) {
1211                         renderwin_queue_redraw(render_win);
1212                 }
1213         } else {
1214                 renderview_init_display_cb();
1215                 renderview_progress_display_cb(0, R.recty-1, R.rectx, R.recty, R.rectot);
1216         }
1217 }       
1218
1219 void BIF_swap_render_rects(void)
1220 {
1221         unsigned int *temp;
1222
1223         if(R.rectspare==0) {
1224                 R.rectspare= (unsigned int *)MEM_callocN(sizeof(int)*R.rectx*R.recty, "rectot");
1225                 R.sparex= R.rectx;
1226                 R.sparey= R.recty;
1227         }
1228         else if(R.sparex!=R.rectx || R.sparey!=R.recty) {
1229                 temp= (unsigned int *)MEM_callocN(sizeof(int)*R.rectx*R.recty, "rectot");
1230                                         
1231                 scalefastrect(R.rectspare, temp, R.sparex, R.sparey, R.rectx, R.recty);
1232                 MEM_freeN(R.rectspare);
1233                 R.rectspare= temp;
1234                                         
1235                 R.sparex= R.rectx;
1236                 R.sparey= R.recty;
1237         }
1238         
1239         temp= R.rectot;
1240         R.rectot= R.rectspare;
1241         R.rectspare= temp;
1242         
1243         if (G.displaymode == R_DISPLAYWIN) {
1244                 if (render_win) {
1245                         char *tmp= render_win->render_text_spare;
1246                         render_win->render_text_spare= render_win->render_text;
1247                         render_win->render_text= tmp;
1248                         
1249                         window_set_title(render_win->win, renderwin_get_title(1));
1250                         
1251                 }
1252         }
1253
1254         /* redraw */
1255         BIF_redraw_render_rect();
1256 }                               
1257
1258 /* called from usiblender.c too, to free and close renderwin */
1259 void BIF_close_render_display(void)
1260 {
1261         if (render_win) {
1262
1263                 if (render_win->info_text) MEM_freeN(render_win->info_text);
1264                 if (render_win->render_text) MEM_freeN(render_win->render_text);
1265                 if (render_win->render_text_spare) MEM_freeN(render_win->render_text_spare);
1266
1267                 window_destroy(render_win->win); /* ghost close window */
1268                 MEM_freeN(render_win);
1269
1270                 render_win= NULL;
1271         }
1272 }
1273
1274
1275 /* typical with F11 key, show image or hide/close */
1276 void BIF_toggle_render_display(void) 
1277 {
1278         ScrArea *sa= find_dispimage_v3d();
1279         
1280         if(R.rectot==NULL);             // do nothing
1281         else if (render_win && G.displaymode==R_DISPLAYWIN) {
1282                 if(render_win->active) {
1283                         mainwindow_raise();
1284                         mainwindow_make_active();
1285                         render_win->active= 0;
1286                 }
1287                 else {
1288                         window_raise(render_win->win);
1289                         window_make_active(render_win->win);
1290                         render_win->active= 1;
1291                 }
1292         } 
1293         else if (sa && G.displaymode==R_DISPLAYVIEW) {
1294                 View3D *vd= sa->spacedata.first;
1295                 vd->flag &= ~V3D_DISPIMAGE;
1296                 scrarea_queue_winredraw(sa);
1297         } 
1298         else {
1299                 if (G.displaymode == R_DISPLAYWIN) {
1300                         renderwin_init_display_cb();
1301                 } else {
1302                         if (render_win) {
1303                                 BIF_close_render_display();
1304                         }
1305                         renderview_init_display_cb();
1306                         renderview_progress_display_cb(0, R.recty-1, R.rectx, R.recty, R.rectot);
1307                 }
1308         }
1309 }
1310
1311 void BIF_renderwin_set_custom_cursor(unsigned char mask[16][2], unsigned char bitmap[16][2])
1312 {
1313         if (render_win) {
1314                 window_set_custom_cursor(render_win->win, mask, bitmap, 7, 7);
1315         }
1316 }