Killed silly modal PoseMode mode! :)
[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.0);
223         ndc_r[1]=  ((float)(win_co[1]*2)/(h-1) - 1.0);
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*= (G.scene->r.border.xmax-G.scene->r.border.xmin);
252                         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         
292 }
293
294 static void renderwin_draw(RenderWin *rw, int just_clear)
295 {
296         float disprect[2][2];
297         int set_back_mainwindow;
298         rcti rect;
299         
300         /* since renderwin uses callbacks (controlled by ghost) it can
301            mess up active window output with redraw events after a render. 
302            this is patchy, still WIP */
303         set_back_mainwindow = (winlay_get_active_window() != rw->win);
304         window_make_active(rw->win);
305         
306         rect.xmin= rect.ymin= 0;
307         window_get_size(rw->win, &rect.xmax, &rect.ymax);
308         rect.ymax-= RW_HEADERY;
309         
310         renderwin_get_disprect(rw, disprect);
311         
312         /* do this first, so window ends with correct scissor */
313         renderwin_draw_render_info(rw);
314         
315         glEnable(GL_SCISSOR_TEST);
316         glaDefine2DArea(&rect);
317         
318         glClearColor(.1875, .1875, .1875, 1.0); 
319         glClear(GL_COLOR_BUFFER_BIT);
320
321         if (just_clear || !R.rectot) {
322                 glColor3ub(0, 0, 0);
323                 glRectfv(disprect[0], disprect[1]);
324         } else {
325                 glPixelZoom(rw->zoom, rw->zoom);
326                 if(rw->flags & RW_FLAGS_ALPHA) {
327                         char *rect= (char *)R.rectot;
328                         
329                         glColorMask(1, 0, 0, 0);
330                         glaDrawPixelsSafe(disprect[0][0], disprect[0][1], R.rectx, R.recty, rect+3);
331                         glColorMask(0, 1, 0, 0);
332                         glaDrawPixelsSafe(disprect[0][0], disprect[0][1], R.rectx, R.recty, rect+2);
333                         glColorMask(0, 0, 1, 0);
334                         glaDrawPixelsSafe(disprect[0][0], disprect[0][1], R.rectx, R.recty, rect+1);
335                         glColorMask(1, 1, 1, 1);
336                         
337                 }
338                 else {
339                         glaDrawPixelsSafe(disprect[0][0], disprect[0][1], R.rectx, R.recty, R.rectot);
340                 }
341                 glPixelZoom(1.0, 1.0);
342         }
343         
344         /* info text is overlayed on bottom */
345         if (rw->info_text) {
346                 float w;
347                 glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
348                 glEnable(GL_BLEND);
349                 w=186.0*strlen(rw->info_text)/30;
350                 glColor4f(.5,.5,.5,.25);
351                 glRectf(0.0,0.0,w,30.0);
352                 glDisable(GL_BLEND);
353                 glColor3ub(255, 255, 255);
354                 glRasterPos2i(10, 10);
355                 BMF_DrawString(G.font, rw->info_text);
356         }
357         
358         window_swap_buffers(rw->win);
359         
360         if (set_back_mainwindow) mainwindow_make_active();      
361 }
362
363 /* ------ interactivity calls for RenderWin ------------- */
364
365 static void renderwin_mouse_moved(RenderWin *rw)
366 {
367         if (rw->flags & RW_FLAGS_PIXEL_EXAMINING) {
368                 int imgco[2];
369                 char buf[64];
370                 int *pxlz;      // zbuffer is signed 
371                 char *pxl;
372
373                 if (R.rectot && renderwin_win_to_image_co(rw, rw->lmouse, imgco)) {
374                         pxl= (char*) &R.rectot[R.rectx*imgco[1] + imgco[0]];
375                         
376                         if (R.rectz) {
377                                 pxlz= &R.rectz[R.rectx*imgco[1] + imgco[0]];                    
378                                 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) );
379                         }
380                         else {
381                                 sprintf(buf, "R: %d, G: %d, B: %d, A: %d", pxl[0], pxl[1], pxl[2], pxl[3]);                     
382                         }
383
384                         renderwin_set_infotext(rw, buf);
385                         renderwin_queue_redraw(rw);
386                 } else {
387                         renderwin_set_infotext(rw, NULL);
388                         renderwin_queue_redraw(rw);
389                 }
390         } 
391         else if (rw->flags & RW_FLAGS_PANNING) {
392                 int delta_x= rw->lmouse[0] - rw->pan_mouse_start[0];
393                 int delta_y= rw->lmouse[1] - rw->pan_mouse_start[1];
394         
395                 rw->zoomofs[0]= rw->pan_ofs_start[0] - delta_x/rw->zoom;
396                 rw->zoomofs[1]= rw->pan_ofs_start[1] - delta_y/rw->zoom;
397                 rw->zoomofs[0]= CLAMPIS(rw->zoomofs[0], -R.rectx/2, R.rectx/2);
398                 rw->zoomofs[1]= CLAMPIS(rw->zoomofs[1], -R.recty/2, R.recty/2);
399
400                 renderwin_queue_redraw(rw);
401         } 
402         else if (rw->flags & RW_FLAGS_OLDZOOM) {
403                 float ndc[2];
404                 int w, h;
405
406                 window_get_size(rw->win, &w, &h);
407                 h-= RW_HEADERY;
408                 renderwin_win_to_ndc(rw, rw->lmouse, ndc);
409
410                 rw->zoomofs[0]= -0.5*ndc[0]*(w-R.rectx*rw->zoom)/rw->zoom;
411                 rw->zoomofs[1]= -0.5*ndc[1]*(h-R.recty*rw->zoom)/rw->zoom;
412
413                 renderwin_queue_redraw(rw);
414         }
415 }
416
417 static void renderwin_mousebut_changed(RenderWin *rw)
418 {
419         if (rw->mbut[0]) {
420                 rw->flags|= RW_FLAGS_PIXEL_EXAMINING;
421         } 
422         else if (rw->mbut[1]) {
423                 rw->flags|= RW_FLAGS_PANNING;
424                 rw->pan_mouse_start[0]= rw->lmouse[0];
425                 rw->pan_mouse_start[1]= rw->lmouse[1];
426                 rw->pan_ofs_start[0]= rw->zoomofs[0];
427                 rw->pan_ofs_start[1]= rw->zoomofs[1];
428         } 
429         else {
430                 if (rw->flags & RW_FLAGS_PANNING) {
431                         rw->flags &= ~RW_FLAGS_PANNING;
432                         renderwin_queue_redraw(rw);
433                 }
434                 if (rw->flags & RW_FLAGS_PIXEL_EXAMINING) {
435                         rw->flags&= ~RW_FLAGS_PIXEL_EXAMINING;
436                         renderwin_set_infotext(rw, NULL);
437                         renderwin_queue_redraw(rw);
438                 }
439         }
440 }
441
442
443 /* handler for renderwin, passed on to Ghost */
444 static void renderwin_handler(Window *win, void *user_data, short evt, short val, char ascii)
445 {
446         RenderWin *rw= user_data;
447
448         // added this for safety, while render it's just creating bezerk results
449         if(R.flag & R_RENDERING) {
450                 if(evt==ESCKEY && val) 
451                         rw->flags|= RW_FLAGS_ESCAPE;
452                 return;
453         }
454         
455         if (evt==RESHAPE) {
456                 renderwin_reshape(rw);
457         } 
458         else if (evt==REDRAW) {
459                 renderwin_draw(rw, 0);
460         } 
461         else if (evt==WINCLOSE) {
462                 BIF_close_render_display();
463         } 
464         else if (evt==INPUTCHANGE) {
465                 rw->active= val;
466
467                 if (!val && (rw->flags&RW_FLAGS_OLDZOOM)) {
468                         rw->flags&= ~RW_FLAGS_OLDZOOM;
469                         renderwin_reset_view(rw);
470                 }
471         } 
472         else if (ELEM(evt, MOUSEX, MOUSEY)) {
473                 rw->lmouse[evt==MOUSEY]= val;
474                 renderwin_mouse_moved(rw);
475         } 
476         else if (ELEM3(evt, LEFTMOUSE, MIDDLEMOUSE, RIGHTMOUSE)) {
477                 int which= (evt==LEFTMOUSE)?0:(evt==MIDDLEMOUSE)?1:2;
478                 rw->mbut[which]= val;
479                 renderwin_mousebut_changed(rw);
480         } 
481         else if (val) {
482                 if (evt==ESCKEY) {
483                         if (rw->flags&RW_FLAGS_OLDZOOM) {
484                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
485                                 renderwin_reset_view(rw);
486                         } 
487                         else {
488                                 rw->flags|= RW_FLAGS_ESCAPE;
489                                 mainwindow_raise();
490                                 mainwindow_make_active();
491                                 rw->active= 0;
492                         }
493                 } 
494                 else if( evt==AKEY) {
495                         rw->flags ^= RW_FLAGS_ALPHA;
496                         renderwin_queue_redraw(render_win);
497                 }
498                 else if (evt==JKEY) {
499                         if(R.flag==0) BIF_swap_render_rects();
500                 } 
501                 else if (evt==ZKEY) {
502                         if (rw->flags&RW_FLAGS_OLDZOOM) {
503                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
504                                 renderwin_reset_view(rw);
505                         } else {
506                                 rw->zoom= 2.0;
507                                 rw->flags|= RW_FLAGS_OLDZOOM;
508                                 renderwin_mouse_moved(rw);
509                         }
510                 } 
511                 else if (evt==PADPLUSKEY) {
512                         if (rw->zoom<15.9) {
513                                 if(rw->zoom>0.5 && rw->zoom<1.0) rw->zoom= 1.0;
514                                 else rw->zoom*= 2.0;
515                                 renderwin_queue_redraw(rw);
516                         }
517                 } 
518                 else if (evt==PADMINUS) {
519                         if (rw->zoom>0.26) {
520                                 if(rw->zoom>1.0 && rw->zoom<2.0) rw->zoom= 1.0;
521                                 else rw->zoom*= 0.5;
522                                 renderwin_queue_redraw(rw);
523                         }
524                 } 
525                 else if (evt==PADENTER || evt==HOMEKEY) {
526                         if (rw->flags&RW_FLAGS_OLDZOOM) {
527                                 rw->flags&= ~RW_FLAGS_OLDZOOM;
528                         }
529                         renderwin_reset_view(rw);
530                 } 
531                 else if (evt==F3KEY) {
532                         if(R.flag==0) {
533                                 mainwindow_raise();
534                                 mainwindow_make_active();
535                                 rw->active= 0;
536                                 areawinset(find_biggest_area()->win);
537                                 BIF_save_rendered_image();
538                         }
539                 } 
540                 else if (evt==F11KEY) {
541                         BIF_toggle_render_display();
542                 } 
543                 else if (evt==F12KEY) {
544                         /* if it's rendering, this flag is set */
545                         if(R.flag==0) BIF_do_render(0);
546                 }
547         }
548 }
549
550 static char *renderwin_get_title(int doswap)
551 {
552         static int swap= 0;
553         char *title="";
554         
555         swap+= doswap;
556         
557         if(swap & 1) {
558                 if (G.scene->r.renderer==R_YAFRAY) title = "YafRay:Render (spare)";
559                 else title = "Blender:Render (spare)";
560         }
561         else {
562                 if (G.scene->r.renderer==R_YAFRAY) title = "YafRay:Render";
563                 else title = "Blender:Render";
564         }
565
566         return title;
567 }
568
569 /* opens window and allocs struct */
570 static void open_renderwin(int winpos[2], int winsize[2])
571 {
572         extern void mywindow_build_and_set_renderwin( int orx, int ory, int sizex, int sizey); // mywindow.c
573         Window *win;
574         char *title;
575         
576         title= renderwin_get_title(0);  /* 0 = no swap */
577         win= window_open(title, winpos[0], winpos[1], winsize[0], winsize[1]+RW_HEADERY, 0);
578
579         render_win= renderwin_alloc(win);
580
581         /* Ghost calls handler */
582         window_set_handler(win, renderwin_handler, render_win);
583
584         winlay_process_events(0);
585         window_make_active(render_win->win);
586         winlay_process_events(0);
587         
588         /* mywindow has to know about it too */
589         mywindow_build_and_set_renderwin(winpos[0], winpos[1], winsize[0], winsize[1]+RW_HEADERY);
590         /* and we should be able to draw 3d in it */
591         init_gl_stuff();
592         
593         renderwin_draw(render_win, 1);
594         renderwin_draw(render_win, 1);
595 }
596
597 /* -------------- callbacks for render loop: Window (RenderWin) ----------------------- */
598
599 /* calculations for window size and position */
600 void calc_renderwin_rectangle(int posmask, int renderpos_r[2], int rendersize_r[2]) 
601 {
602         int scr_w, scr_h, x, y, div= 0;
603         float ndc_x= 0.0, ndc_y= 0.0;
604
605                 /* XXX, we temporarily hack the screen size and position so
606                  * the window is always 60 pixels away from a side, really need
607                  * a GHOST_GetMaxWindowBounds or so - zr
608                  */
609         winlay_get_screensize(&scr_w, &scr_h);
610         
611         rendersize_r[0]= (G.scene->r.size*G.scene->r.xsch)/100;
612         rendersize_r[1]= (G.scene->r.size*G.scene->r.ysch)/100;
613         
614         /* crop option makes image smaller */
615         if ((G.scene->r.mode & R_BORDER) && (G.scene->r.mode & R_MOVIECROP)) { 
616                 rendersize_r[0]*= (G.scene->r.border.xmax-G.scene->r.border.xmin);
617                 rendersize_r[1]*= (G.scene->r.border.ymax-G.scene->r.border.ymin);
618         }
619         
620         if(G.scene->r.mode & R_PANORAMA) {
621                 rendersize_r[0]*= G.scene->r.xparts;
622                 rendersize_r[1]*= G.scene->r.yparts;
623         }
624         /* increased size of clipping for OSX, should become an option instead */
625         rendersize_r[0]= CLAMPIS(rendersize_r[0], 100, scr_w-120);
626         rendersize_r[1]= CLAMPIS(rendersize_r[1], 100, scr_h-120);
627
628         for (y=-1; y<=1; y++) {
629                 for (x=-1; x<=1; x++) {
630                         if (posmask & (1<<((y+1)*3 + (x+1)))) {
631                                 ndc_x+= x;
632                                 ndc_y+= y;
633                                 div++;
634                         }
635                 }
636         }
637
638         if (div) {
639                 ndc_x/= div;
640                 ndc_y/= div;
641         }
642                 
643         renderpos_r[0]= 60 + (scr_w-90-rendersize_r[0])*(ndc_x*0.5 + 0.5);
644         renderpos_r[1]= 30 + (scr_h-90-rendersize_r[1])*(ndc_y*0.5 + 0.5);
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         scene_update_for_newframe(G.scene, G.scene->lay);       // no redraw needed, this restores to view as we left it
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;
1097         waitcursor(0);  // waitcursor checks rendering R.flag...
1098 }
1099
1100 /* finds area with a 'dispview' set */
1101 static ScrArea *find_dispimage_v3d(void)
1102 {
1103         ScrArea *sa;
1104         
1105         for (sa= G.curscreen->areabase.first; sa; sa= sa->next) {
1106                 if (sa->spacetype==SPACE_VIEW3D) {
1107                         View3D *vd= sa->spacedata.first;
1108                         if (vd->flag & V3D_DISPIMAGE)
1109                                 return sa;
1110                 }
1111         }
1112         
1113         return NULL;
1114 }
1115
1116 /* used for swapping with spare buffer, when images are different size */
1117 static void scalefastrect(unsigned int *recto, unsigned int *rectn, int oldx, int oldy, int newx, int newy)
1118 {
1119         unsigned int *rect, *newrect;
1120         int x, y;
1121         int ofsx, ofsy, stepx, stepy;
1122
1123         stepx = (int)((65536.0 * (oldx - 1.0) / (newx - 1.0)) + 0.5);
1124         stepy = (int)((65536.0 * (oldy - 1.0) / (newy - 1.0)) + 0.5);
1125         ofsy = 32768;
1126         newrect= rectn;
1127         
1128         for (y = newy; y > 0 ; y--){
1129                 rect = recto;
1130                 rect += (ofsy >> 16) * oldx;
1131                 ofsy += stepy;
1132                 ofsx = 32768;
1133                 for (x = newx ; x>0 ; x--){
1134                         *newrect++ = rect[ofsx >> 16];
1135                         ofsx += stepx;
1136                 }
1137         }
1138 }
1139
1140 /* -------------- API: externally called --------------- */
1141
1142 /* not used anywhere ??? */
1143 #if 0
1144 void BIF_renderwin_make_active(void)
1145 {
1146         if(render_win) {
1147                 window_make_active(render_win->win);
1148                 mywinset(2);
1149         }
1150 }
1151 #endif
1152
1153 /* set up display, render an image or scene */
1154 void BIF_do_render(int anim)
1155 {
1156         int slink_flag = 0;
1157
1158         if (G.f & G_DOSCRIPTLINKS) {
1159                 BPY_do_all_scripts(SCRIPT_RENDER);
1160                 if (!anim) { /* avoid FRAMECHANGED slink in render callback */
1161                         G.f &= ~G_DOSCRIPTLINKS;
1162                         slink_flag = 1;
1163                 }
1164         }
1165
1166         /* if start render in 3d win, use layer from window (e.g also local view) */
1167         if(curarea && curarea->spacetype==SPACE_VIEW3D) {
1168                 int lay= G.scene->lay;
1169                 if(G.vd->lay & 0xFF000000)      // localview
1170                         G.scene->lay |= G.vd->lay;
1171                 else G.scene->lay= G.vd->lay;
1172                 
1173                 do_render(NULL, anim, 0);
1174                 
1175                 G.scene->lay= lay;
1176         }
1177         else do_render(NULL, anim, 0);
1178
1179         if (slink_flag) G.f |= G_DOSCRIPTLINKS;
1180         if (G.f & G_DOSCRIPTLINKS) BPY_do_all_scripts(SCRIPT_POSTRENDER);
1181 }
1182
1183 /* set up display, render the current area view in an image */
1184 void BIF_do_ogl_render(View3D *ogl_render_view3d, int anim)
1185 {
1186         G.scene->r.scemode |= R_OGL;
1187         do_render(ogl_render_view3d, anim, 1);
1188         G.scene->r.scemode &= ~R_OGL;
1189 }
1190
1191 void BIF_redraw_render_rect(void)
1192 {
1193         
1194         /* redraw */
1195         if (G.displaymode == R_DISPLAYWIN) {
1196                 // don't open render_win if rendering has been
1197             // canceled or the render_win has been actively closed
1198                 if (render_win) {
1199                         renderwin_queue_redraw(render_win);
1200                 }
1201         } else {
1202                 renderview_init_display_cb();
1203                 renderview_progress_display_cb(0, R.recty-1, R.rectx, R.recty, R.rectot);
1204         }
1205 }       
1206
1207 void BIF_swap_render_rects(void)
1208 {
1209         unsigned int *temp;
1210
1211         if(R.rectspare==0) {
1212                 R.rectspare= (unsigned int *)MEM_callocN(sizeof(int)*R.rectx*R.recty, "rectot");
1213                 R.sparex= R.rectx;
1214                 R.sparey= R.recty;
1215         }
1216         else if(R.sparex!=R.rectx || R.sparey!=R.recty) {
1217                 temp= (unsigned int *)MEM_callocN(sizeof(int)*R.rectx*R.recty, "rectot");
1218                                         
1219                 scalefastrect(R.rectspare, temp, R.sparex, R.sparey, R.rectx, R.recty);
1220                 MEM_freeN(R.rectspare);
1221                 R.rectspare= temp;
1222                                         
1223                 R.sparex= R.rectx;
1224                 R.sparey= R.recty;
1225         }
1226         
1227         temp= R.rectot;
1228         R.rectot= R.rectspare;
1229         R.rectspare= temp;
1230         
1231         if (G.displaymode == R_DISPLAYWIN) {
1232                 if (render_win) {
1233                         char *tmp= render_win->render_text_spare;
1234                         render_win->render_text_spare= render_win->render_text;
1235                         render_win->render_text= tmp;
1236                         
1237                         window_set_title(render_win->win, renderwin_get_title(1));
1238                         
1239                 }
1240         }
1241
1242         /* redraw */
1243         BIF_redraw_render_rect();
1244 }                               
1245
1246 /* called from usiblender.c too, to free and close renderwin */
1247 void BIF_close_render_display(void)
1248 {
1249         if (render_win) {
1250
1251                 if (render_win->info_text) MEM_freeN(render_win->info_text);
1252                 if (render_win->render_text) MEM_freeN(render_win->render_text);
1253                 if (render_win->render_text_spare) MEM_freeN(render_win->render_text_spare);
1254
1255                 window_destroy(render_win->win); /* ghost close window */
1256                 MEM_freeN(render_win);
1257
1258                 render_win= NULL;
1259         }
1260 }
1261
1262
1263 /* typical with F11 key, show image or hide/close */
1264 void BIF_toggle_render_display(void) 
1265 {
1266         ScrArea *sa= find_dispimage_v3d();
1267         
1268         if(R.rectot==NULL);             // do nothing
1269         else if (render_win && G.displaymode==R_DISPLAYWIN) {
1270                 if(render_win->active) {
1271                         mainwindow_raise();
1272                         mainwindow_make_active();
1273                         render_win->active= 0;
1274                 }
1275                 else {
1276                         window_raise(render_win->win);
1277                         window_make_active(render_win->win);
1278                         render_win->active= 1;
1279                 }
1280         } 
1281         else if (sa && G.displaymode==R_DISPLAYVIEW) {
1282                 View3D *vd= sa->spacedata.first;
1283                 vd->flag &= ~V3D_DISPIMAGE;
1284                 scrarea_queue_winredraw(sa);
1285         } 
1286         else {
1287                 if (G.displaymode == R_DISPLAYWIN) {
1288                         renderwin_init_display_cb();
1289                 } else {
1290                         if (render_win) {
1291                                 BIF_close_render_display();
1292                         }
1293                         renderview_init_display_cb();
1294                         renderview_progress_display_cb(0, R.recty-1, R.rectx, R.recty, R.rectot);
1295                 }
1296         }
1297 }
1298
1299 void BIF_renderwin_set_custom_cursor(unsigned char mask[16][2], unsigned char bitmap[16][2])
1300 {
1301         if (render_win) {
1302                 window_set_custom_cursor(render_win->win, mask, bitmap, 7, 7);
1303         }
1304 }