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