test if merging with bf-blender works for Orange branch.
[blender.git] / source / blender / src / renderwin.c
1 /**
2  * $Id$
3  *
4  * ***** BEGIN GPL/BL DUAL LICENSE BLOCK *****
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version. The Blender
10  * Foundation also sells licenses for use in proprietary software under
11  * the Blender License.  See http://www.blender.org/BL/ for information
12  * about this.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software Foundation,
21  * Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22  *
23  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
24  * All rights reserved.
25  *
26  * The Original Code is: all of this file.
27  *
28  * Contributor(s): none yet.
29  *
30  * ***** END GPL/BL DUAL LICENSE BLOCK *****
31  */
32  
33 #ifdef WIN32
34 /* for the multimedia timer */
35 #include <windows.h>
36 #include <mmsystem.h>
37 #endif
38
39 #include <string.h>
40 #include <stdarg.h>
41 #include <math.h>
42
43 #ifdef HAVE_CONFIG_H
44 #include <config.h>
45 #endif
46
47 #ifdef WIN32
48 #include "BLI_winstuff.h"
49 #else
50  /* for signal callback, not (fully) supported at windows */
51 #include <sys/time.h>
52 #include <signal.h>
53
54 #endif
55
56 #include <limits.h>
57
58 #include "BLI_blenlib.h"
59
60 #include "MEM_guardedalloc.h"
61
62 #include "BMF_Api.h"
63
64 #include "DNA_view3d_types.h"
65 #include "DNA_screen_types.h"
66 #include "DNA_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                 /* XXX, we temporarily hack the screen size and position so
608                  * the window is always 60 pixels away from a side, really need
609                  * a GHOST_GetMaxWindowBounds or so - zr
610                  */
611         winlay_get_screensize(&scr_w, &scr_h);
612         
613         rendersize_r[0]= (G.scene->r.size*G.scene->r.xsch)/100;
614         rendersize_r[1]= (G.scene->r.size*G.scene->r.ysch)/100;
615         
616         /* crop option makes image smaller */
617         if ((G.scene->r.mode & R_BORDER) && (G.scene->r.mode & R_MOVIECROP)) { 
618                 rendersize_r[0]*= (G.scene->r.border.xmax-G.scene->r.border.xmin);
619                 rendersize_r[1]*= (G.scene->r.border.ymax-G.scene->r.border.ymin);
620         }
621         
622         if(G.scene->r.mode & R_PANORAMA) {
623                 rendersize_r[0]*= G.scene->r.xparts;
624                 rendersize_r[1]*= G.scene->r.yparts;
625         }
626         /* increased size of clipping for OSX, should become an option instead */
627         rendersize_r[0]= CLAMPIS(rendersize_r[0], 100, scr_w-120);
628         rendersize_r[1]= CLAMPIS(rendersize_r[1], 100, scr_h-120);
629
630         for (y=-1; y<=1; y++) {
631                 for (x=-1; x<=1; x++) {
632                         if (posmask & (1<<((y+1)*3 + (x+1)))) {
633                                 ndc_x+= x;
634                                 ndc_y+= y;
635                                 div++;
636                         }
637                 }
638         }
639
640         if (div) {
641                 ndc_x/= div;
642                 ndc_y/= div;
643         }
644                 
645         renderpos_r[0]= 60 + (scr_w-90-rendersize_r[0])*(ndc_x*0.5 + 0.5);
646         renderpos_r[1]= 30 + (scr_h-90-rendersize_r[1])*(ndc_y*0.5 + 0.5);
647 }
648         
649 /* init renderwin, alloc/open/resize */
650 static void renderwin_init_display_cb(void) 
651 {
652         if (G.afbreek == 0) {
653                 int rendersize[2], renderpos[2];
654
655                 calc_renderwin_rectangle(G.winpos, renderpos, rendersize);
656
657                 if (!render_win) {
658                         open_renderwin(renderpos, rendersize);
659                         renderwin_reset_view(render_win); // incl. autozoom for large images
660                 } else {
661                         int win_x, win_y;
662                         int win_w, win_h;
663
664                         window_get_position(render_win->win, &win_x, &win_y);
665                         window_get_size(render_win->win, &win_w, &win_h);
666                         win_h-= RW_HEADERY;
667
668                                 /* XXX, this is nasty and I guess bound to cause problems,
669                                  * but to ensure the window is at the user specified position
670                                  * and size we reopen the window all the time... we need
671                                  * a ghost _set_position to fix this -zr
672                                  */
673                                  
674                                  /* XXX, well... it is nasty yes, and reopens windows each time on
675                                     subsequent renders. Better rule is to make it reopen only only
676                                     size change, and use the preferred position only on open_renderwin
677                                         cases (ton)
678                                  */
679                         if(rendersize[0]!= win_w || rendersize[1]!= win_h) {
680                                 BIF_close_render_display();
681                                 open_renderwin(renderpos, rendersize);
682                         }
683                         else {
684                                 window_raise(render_win->win);
685                                 window_make_active(render_win->win);
686                                 
687                                 mywinset(2);    // to assign scissor/viewport again in mywindow.c. is hackish yes, but otherwise it draws in header of button for ogl header
688                                 {
689                                         rcti win_rct;
690                                         win_rct.xmin= win_rct.ymin= 0;
691                                         window_get_size(render_win->win, &win_rct.xmax, &win_rct.ymax);
692                                         win_rct.ymax-= RW_HEADERY;
693                                         glaDefine2DArea(&win_rct);
694                                 }
695                         }
696
697                         renderwin_reset_view(render_win);
698                         render_win->flags&= ~RW_FLAGS_ESCAPE;
699                         render_win->active= 1;
700                 }
701                 /* make sure we are in normal draw again */
702                 render_win->flags &= ~RW_FLAGS_ALPHA;
703         }
704 }
705
706 /* callback for redraw render win */
707 static void renderwin_clear_display_cb(short ignore) 
708 {
709         if (render_win) {
710                 window_make_active(render_win->win);    
711                 renderwin_draw(render_win, 1);
712         }
713 }
714
715 /* XXX, this is not good, we do this without any regard to state
716 * ... better is to make this an optimization of a more clear
717 * implementation. the bug shows up when you do something like
718 * open the window, then draw part of the progress, then get
719 * a redraw event. whatever can go wrong will. -zr
720 *
721 * Note: blocked queue handling while rendering to prevent that (ton)
722 */
723
724 /* in render window; display a couple of scanlines of rendered image (see callback below) */
725 static void renderwin_progress(RenderWin *rw, int start_y, int nlines, int rect_w, int rect_h, unsigned char *rect)
726 {
727         float disprect[2][2];
728         rcti win_rct;
729
730         win_rct.xmin= win_rct.ymin= 0;
731         window_get_size(rw->win, &win_rct.xmax, &win_rct.ymax);
732         win_rct.ymax-= RW_HEADERY;
733         
734         renderwin_get_disprect(rw, disprect);
735         
736         /* for efficiency & speed; not drawing in Blender UI while rendering */
737         //window_make_active(rw->win);
738
739         glEnable(GL_SCISSOR_TEST);
740         glaDefine2DArea(&win_rct);
741
742         glDrawBuffer(GL_FRONT);
743         glPixelZoom(rw->zoom, rw->zoom);
744         glaDrawPixelsSafe(disprect[0][0], disprect[0][1] + start_y*rw->zoom, rect_w, nlines, &rect[start_y*rect_w*4]);
745         glPixelZoom(1.0, 1.0);
746         glFlush();
747         glDrawBuffer(GL_BACK);
748 }
749
750
751 /* in render window; display a couple of scanlines of rendered image */
752 static void renderwin_progress_display_cb(int y1, int y2, int w, int h, unsigned int *rect)
753 {
754         if (render_win) {
755                 renderwin_progress(render_win, y1, y2-y1+1, w, h, (unsigned char*) rect);
756         }
757 }
758
759
760 /* -------------- callbacks for render loop: in View3D ----------------------- */
761
762
763 static View3D *render_view3d = NULL;
764
765 /* init Render view callback */
766 static void renderview_init_display_cb(void)
767 {
768         ScrArea *sa;
769
770                 /* Choose the first view with a persp camera,
771                  * if one doesn't exist we will get the first
772                  * View3D window.
773                  */ 
774         render_view3d= NULL;
775         for (sa= G.curscreen->areabase.first; sa; sa= sa->next) {
776                 if (sa->win && sa->spacetype==SPACE_VIEW3D) {
777                         View3D *vd= sa->spacedata.first;
778                         
779                         if (vd->persp==2 && vd->camera==G.scene->camera) {
780                                 render_view3d= vd;
781                                 break;
782                         } else if (!render_view3d) {
783                                 render_view3d= vd;
784                         }
785                 }
786         }
787 }
788
789
790 /* in 3d view; display a couple of scanlines of rendered image */
791 static void renderview_progress_display_cb(int y1, int y2, int w, int h, unsigned int *rect)
792 {
793         if (render_view3d) {
794                 View3D *v3d= render_view3d;
795                 int nlines= y2-y1+1;
796                 float sx, sy, facx, facy;
797                 rcti win_rct, vb;
798
799                 calc_viewborder(v3d, &vb);
800                 
801                 /* if border render  */
802                 if(G.scene->r.mode & R_BORDER) { 
803                         
804                         /* but, if image is full (at end of border render, without crop) we don't */
805                         if(R.rectx != (G.scene->r.size*G.scene->r.xsch)/100 ||
806                            R.recty != (G.scene->r.size*G.scene->r.ysch)/100 ) {
807                         
808                                 facx= (float) (vb.xmax-vb.xmin);
809                                 facy= (float) (vb.ymax-vb.ymin);
810                                 
811                                 vb.xmax= vb.xmin + facx*G.scene->r.border.xmax;
812                                 vb.ymax= vb.ymin + facy*G.scene->r.border.ymax;
813                                 vb.xmin+= facx*G.scene->r.border.xmin;
814                                 vb.ymin+= facy*G.scene->r.border.ymin;
815                         }
816                 }
817                         
818                 facx= (float) (vb.xmax-vb.xmin)/R.rectx;
819                 facy= (float) (vb.ymax-vb.ymin)/R.recty;
820
821                 bwin_get_rect(v3d->area->win, &win_rct);
822
823                 glaDefine2DArea(&win_rct);
824         
825                 glDrawBuffer(GL_FRONT);
826                 
827                 sx= vb.xmin;
828                 sy= vb.ymin + facy*y1;
829
830                 glPixelZoom(facx, facy);
831                 glaDrawPixelsSafe(sx, sy, w, nlines, rect+w*y1);
832                 glPixelZoom(1.0, 1.0);
833
834                 glFlush();
835                 glDrawBuffer(GL_BACK);
836                 v3d->flag |= V3D_DISPIMAGE;
837                 v3d->area->win_swap= WIN_FRONT_OK;
838                 
839         }
840 }
841
842 /* in 3d view; display stats of rendered image */
843 static void renderview_draw_render_info(char *str)
844 {
845         if (render_view3d) {
846                 View3D *v3d= render_view3d;
847                 rcti vb, win_rct;
848                 
849                 calc_viewborder(v3d, &vb);
850                 
851                 bwin_get_rect(v3d->area->win, &win_rct);
852                 glaDefine2DArea(&win_rct);
853                 
854                 glDrawBuffer(GL_FRONT);
855                 
856                 /* clear header rect */
857                 BIF_ThemeColor(TH_HEADER);
858                 glRecti(vb.xmin, vb.ymax, vb.xmax, vb.ymax+RW_HEADERY);
859                 
860                 if(str) {
861                         BIF_ThemeColor(TH_TEXT);
862                         glRasterPos2i(vb.xmin+12, vb.ymax+5);
863                         BMF_DrawString(G.fonts, str);
864                 }
865                 
866                 glFlush();
867                 glDrawBuffer(GL_BACK);
868
869                 v3d->area->win_swap= WIN_FRONT_OK;
870                 
871         }
872 }
873
874
875 /* -------------- callbacks for render loop: interactivity ----------------------- */
876
877
878 /* callback for print info in top header of renderwin */
879 /* time is only not zero on last call, we then don't update the other stats */ 
880 static void printrenderinfo_cb(double time, int sample)
881 {
882         extern int mem_in_use;
883         static int totvert=0, totvlak=0, tothalo=0, totlamp=0;
884         static float megs_used_memory=0.0;
885         char str[300], *spos= str;
886                 
887         if(time==0.0) {
888                 megs_used_memory= mem_in_use/(1024.0*1024.0);
889                 totvert= R.totvert;
890                 totvlak= R.totvlak;
891                 totlamp= R.totlamp;
892                 tothalo= R.tothalo;
893         }
894         
895         if(tothalo)
896                 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);
897         else 
898                 spos+= sprintf(spos, "Fra:%d  Ve:%d Fa:%d La:%d Mem:%.2fM", (G.scene->r.cfra), totvert, totvlak, totlamp, megs_used_memory);
899
900         if(time==0.0) {
901                 if (R.r.mode & R_FIELDS) {
902                         spos+= sprintf(spos, "Field %c ", (R.flag&R_SEC_FIELD)?'B':'A');
903                 }
904                 if (sample!=-1) {
905                         spos+= sprintf(spos, "Sample: %d    ", sample);
906                 }
907         }
908         else {
909                 extern char info_time_str[32];  // header_info.c
910                 timestr(time, info_time_str);
911                 spos+= sprintf(spos, " Time:%s ", info_time_str);
912         }
913         
914         if(render_win) {
915                 if(render_win->render_text) MEM_freeN(render_win->render_text);
916                 render_win->render_text= BLI_strdup(str);
917                 glDrawBuffer(GL_FRONT);
918                 renderwin_draw_render_info(render_win);
919                 glFlush();
920                 glDrawBuffer(GL_BACK);
921         }
922         else renderview_draw_render_info(str);
923 }
924
925 /* -------------- callback system to allow ESC from rendering ----------------------- */
926
927 /* POSIX & WIN32: this function is called all the time, and should not use cpu or resources */
928 static int test_break(void)
929 {
930
931         if(G.afbreek==2) { /* code for testing queue */
932
933                 G.afbreek= 0;
934
935                 blender_test_break(); /* tests blender interface */
936
937                 if (G.afbreek==0 && render_win) { /* tests window */
938                         winlay_process_events(0);
939                         // render_win can be closed in winlay_process_events()
940                         if (render_win == 0 || (render_win->flags & RW_FLAGS_ESCAPE)) {
941                                 G.afbreek= 1;
942                         }
943                 }
944         }
945
946         if(G.afbreek==1) return 1;
947         else return 0;
948 }
949
950
951
952 #ifdef _WIN32
953 /* we use the multimedia time here */
954 static UINT uRenderTimerId;
955
956 void CALLBACK interruptESC(UINT uID, UINT uMsg, DWORD dwUser, DWORD dw1, DWORD dw2)
957 {
958         if(G.afbreek==0) G.afbreek= 2;  /* code for read queue */
959 }
960
961 /* WIN32: init SetTimer callback */
962 static void init_test_break_callback()
963 {
964         timeBeginPeriod(50);
965         uRenderTimerId = timeSetEvent(250, 1, interruptESC, 0, TIME_PERIODIC);
966 }
967
968 /* WIN32: stop SetTimer callback */
969 static void end_test_break_callback()
970 {
971         timeEndPeriod(50);
972         timeKillEvent(uRenderTimerId);
973 }
974
975 #else
976 /* all other OS's support signal(SIGVTALRM) */
977
978 /* POSIX: this function goes in the signal() callback */
979 static void interruptESC(int sig)
980 {
981
982         if(G.afbreek==0) G.afbreek= 2;  /* code for read queue */
983
984         /* call again, timer was reset */
985         signal(SIGVTALRM, interruptESC);
986 }
987
988 /* POSIX: initialize timer and signal */
989 static void init_test_break_callback()
990 {
991
992         struct itimerval tmevalue;
993
994         tmevalue.it_interval.tv_sec = 0;
995         tmevalue.it_interval.tv_usec = 250000;
996         /* wanneer de eerste ? */
997         tmevalue.it_value.tv_sec = 0;
998         tmevalue.it_value.tv_usec = 10000;
999
1000         signal(SIGVTALRM, interruptESC);
1001         setitimer(ITIMER_VIRTUAL, &tmevalue, 0);
1002
1003 }
1004
1005 /* POSIX: stop timer and callback */
1006 static void end_test_break_callback()
1007 {
1008         struct itimerval tmevalue;
1009
1010         tmevalue.it_value.tv_sec = 0;
1011         tmevalue.it_value.tv_usec = 0;
1012         setitimer(ITIMER_VIRTUAL, &tmevalue, 0);
1013         signal(SIGVTALRM, SIG_IGN);
1014
1015 }
1016
1017
1018 #endif
1019
1020
1021
1022 /* -------------- callbacks for render loop: init & run! ----------------------- */
1023
1024
1025 /* - initialize displays
1026    - both opengl render as blender render
1027    - set callbacks
1028    - cleanup
1029 */
1030
1031 static void do_render(View3D *ogl_render_view3d, int anim, int force_dispwin)
1032 {
1033         
1034         /* we set this flag to prevent renderwindow queue to execute another render */
1035         R.flag= R_RENDERING;
1036         G.afbreek= 0;
1037
1038         if (G.displaymode == R_DISPLAYWIN || force_dispwin) {
1039                 RE_set_initrenderdisplay_callback(NULL);
1040                 RE_set_clearrenderdisplay_callback(renderwin_clear_display_cb);
1041                 RE_set_renderdisplay_callback(renderwin_progress_display_cb);
1042
1043                 renderwin_init_display_cb();
1044         } 
1045         else {
1046                 BIF_close_render_display();
1047                 RE_set_initrenderdisplay_callback(renderview_init_display_cb);
1048                 RE_set_clearrenderdisplay_callback(NULL);
1049                 RE_set_renderdisplay_callback(renderview_progress_display_cb);
1050         }
1051
1052         init_test_break_callback();
1053         RE_set_test_break_callback(test_break);
1054         
1055         RE_set_timecursor_callback(set_timecursor);
1056         RE_set_printrenderinfo_callback(printrenderinfo_cb);
1057         
1058         if (render_win) {
1059                 window_set_cursor(render_win->win, CURSOR_WAIT);
1060                 // when opening new window... not cross platform identical behaviour, so
1061                 // for now call it each time
1062                 // if(ogl_render_view3d) init_gl_stuff();
1063         }
1064         waitcursor(1);
1065
1066         if(G.obedit && !(G.scene->r.scemode & R_OGL)) {
1067                 exit_editmode(0);       /* 0 = no free data */
1068         }
1069
1070         if(anim) {
1071                 RE_animrender(ogl_render_view3d);
1072         }
1073         else {
1074                 RE_initrender(ogl_render_view3d);
1075         }
1076
1077         if (render_win) window_set_cursor(render_win->win, CURSOR_STD);
1078
1079         free_filesel_spec(G.scene->r.pic);
1080
1081         G.afbreek= 0;
1082         end_test_break_callback();
1083         
1084         /* in dispiew it will destroy the image otherwise
1085            window_make_active() raises window at osx and sends redraws */
1086         if(G.displaymode==R_DISPLAYWIN) {
1087                 mainwindow_make_active();
1088         
1089                 /* after an envmap creation...  */
1090                 if(R.flag & R_REDRAW_PRV) {
1091                         BIF_all_preview_changed();
1092                 }
1093                 allqueue(REDRAWBUTSSCENE, 0);   // visualize fbuf for example
1094         }
1095         
1096         R.flag= 0;      // before scene update!
1097         
1098         scene_update_for_newframe(G.scene, G.scene->lay);       // no redraw needed, this restores to view as we left it
1099         
1100         waitcursor(0);  // waitcursor checks rendering R.flag...
1101 }
1102
1103 /* finds area with a 'dispview' set */
1104 static ScrArea *find_dispimage_v3d(void)
1105 {
1106         ScrArea *sa;
1107         
1108         for (sa= G.curscreen->areabase.first; sa; sa= sa->next) {
1109                 if (sa->spacetype==SPACE_VIEW3D) {
1110                         View3D *vd= sa->spacedata.first;
1111                         if (vd->flag & V3D_DISPIMAGE)
1112                                 return sa;
1113                 }
1114         }
1115         
1116         return NULL;
1117 }
1118
1119 /* used for swapping with spare buffer, when images are different size */
1120 static void scalefastrect(unsigned int *recto, unsigned int *rectn, int oldx, int oldy, int newx, int newy)
1121 {
1122         unsigned int *rect, *newrect;
1123         int x, y;
1124         int ofsx, ofsy, stepx, stepy;
1125
1126         stepx = (int)((65536.0 * (oldx - 1.0) / (newx - 1.0)) + 0.5);
1127         stepy = (int)((65536.0 * (oldy - 1.0) / (newy - 1.0)) + 0.5);
1128         ofsy = 32768;
1129         newrect= rectn;
1130         
1131         for (y = newy; y > 0 ; y--){
1132                 rect = recto;
1133                 rect += (ofsy >> 16) * oldx;
1134                 ofsy += stepy;
1135                 ofsx = 32768;
1136                 for (x = newx ; x>0 ; x--){
1137                         *newrect++ = rect[ofsx >> 16];
1138                         ofsx += stepx;
1139                 }
1140         }
1141 }
1142
1143 /* -------------- API: externally called --------------- */
1144
1145 /* not used anywhere ??? */
1146 #if 0
1147 void BIF_renderwin_make_active(void)
1148 {
1149         if(render_win) {
1150                 window_make_active(render_win->win);
1151                 mywinset(2);
1152         }
1153 }
1154 #endif
1155
1156 /* set up display, render an image or scene */
1157 void BIF_do_render(int anim)
1158 {
1159         int slink_flag = 0;
1160
1161         if (G.f & G_DOSCRIPTLINKS) {
1162                 BPY_do_all_scripts(SCRIPT_RENDER);
1163                 if (!anim) { /* avoid FRAMECHANGED slink in render callback */
1164                         G.f &= ~G_DOSCRIPTLINKS;
1165                         slink_flag = 1;
1166                 }
1167         }
1168
1169         /* if start render in 3d win, use layer from window (e.g also local view) */
1170         if(curarea && curarea->spacetype==SPACE_VIEW3D) {
1171                 int lay= G.scene->lay;
1172                 if(G.vd->lay & 0xFF000000)      // localview
1173                         G.scene->lay |= G.vd->lay;
1174                 else G.scene->lay= G.vd->lay;
1175                 
1176                 do_render(NULL, anim, 0);
1177                 
1178                 G.scene->lay= lay;
1179         }
1180         else do_render(NULL, anim, 0);
1181
1182         if (slink_flag) G.f |= G_DOSCRIPTLINKS;
1183         if (G.f & G_DOSCRIPTLINKS) BPY_do_all_scripts(SCRIPT_POSTRENDER);
1184 }
1185
1186 /* set up display, render the current area view in an image */
1187 void BIF_do_ogl_render(View3D *ogl_render_view3d, int anim)
1188 {
1189         G.scene->r.scemode |= R_OGL;
1190         do_render(ogl_render_view3d, anim, 1);
1191         G.scene->r.scemode &= ~R_OGL;
1192 }
1193
1194 void BIF_redraw_render_rect(void)
1195 {
1196         
1197         /* redraw */
1198         if (G.displaymode == R_DISPLAYWIN) {
1199                 // don't open render_win if rendering has been
1200             // canceled or the render_win has been actively closed
1201                 if (render_win) {
1202                         renderwin_queue_redraw(render_win);
1203                 }
1204         } else {
1205                 renderview_init_display_cb();
1206                 renderview_progress_display_cb(0, R.recty-1, R.rectx, R.recty, R.rectot);
1207         }
1208 }       
1209
1210 void BIF_swap_render_rects(void)
1211 {
1212         unsigned int *temp;
1213
1214         if(R.rectspare==0) {
1215                 R.rectspare= (unsigned int *)MEM_callocN(sizeof(int)*R.rectx*R.recty, "rectot");
1216                 R.sparex= R.rectx;
1217                 R.sparey= R.recty;
1218         }
1219         else if(R.sparex!=R.rectx || R.sparey!=R.recty) {
1220                 temp= (unsigned int *)MEM_callocN(sizeof(int)*R.rectx*R.recty, "rectot");
1221                                         
1222                 scalefastrect(R.rectspare, temp, R.sparex, R.sparey, R.rectx, R.recty);
1223                 MEM_freeN(R.rectspare);
1224                 R.rectspare= temp;
1225                                         
1226                 R.sparex= R.rectx;
1227                 R.sparey= R.recty;
1228         }
1229         
1230         temp= R.rectot;
1231         R.rectot= R.rectspare;
1232         R.rectspare= temp;
1233         
1234         if (G.displaymode == R_DISPLAYWIN) {
1235                 if (render_win) {
1236                         char *tmp= render_win->render_text_spare;
1237                         render_win->render_text_spare= render_win->render_text;
1238                         render_win->render_text= tmp;
1239                         
1240                         window_set_title(render_win->win, renderwin_get_title(1));
1241                         
1242                 }
1243         }
1244
1245         /* redraw */
1246         BIF_redraw_render_rect();
1247 }                               
1248
1249 /* called from usiblender.c too, to free and close renderwin */
1250 void BIF_close_render_display(void)
1251 {
1252         if (render_win) {
1253
1254                 if (render_win->info_text) MEM_freeN(render_win->info_text);
1255                 if (render_win->render_text) MEM_freeN(render_win->render_text);
1256                 if (render_win->render_text_spare) MEM_freeN(render_win->render_text_spare);
1257
1258                 window_destroy(render_win->win); /* ghost close window */
1259                 MEM_freeN(render_win);
1260
1261                 render_win= NULL;
1262         }
1263 }
1264
1265
1266 /* typical with F11 key, show image or hide/close */
1267 void BIF_toggle_render_display(void) 
1268 {
1269         ScrArea *sa= find_dispimage_v3d();
1270         
1271         if(R.rectot==NULL);             // do nothing
1272         else if (render_win && G.displaymode==R_DISPLAYWIN) {
1273                 if(render_win->active) {
1274                         mainwindow_raise();
1275                         mainwindow_make_active();
1276                         render_win->active= 0;
1277                 }
1278                 else {
1279                         window_raise(render_win->win);
1280                         window_make_active(render_win->win);
1281                         render_win->active= 1;
1282                 }
1283         } 
1284         else if (sa && G.displaymode==R_DISPLAYVIEW) {
1285                 View3D *vd= sa->spacedata.first;
1286                 vd->flag &= ~V3D_DISPIMAGE;
1287                 scrarea_queue_winredraw(sa);
1288         } 
1289         else {
1290                 if (G.displaymode == R_DISPLAYWIN) {
1291                         renderwin_init_display_cb();
1292                 } else {
1293                         if (render_win) {
1294                                 BIF_close_render_display();
1295                         }
1296                         renderview_init_display_cb();
1297                         renderview_progress_display_cb(0, R.recty-1, R.rectx, R.recty, R.rectot);
1298                 }
1299         }
1300 }
1301
1302 void BIF_renderwin_set_custom_cursor(unsigned char mask[16][2], unsigned char bitmap[16][2])
1303 {
1304         if (render_win) {
1305                 window_set_custom_cursor(render_win->win, mask, bitmap, 7, 7);
1306         }
1307 }