Merging r58280 through r58329 from trunk into soc-2013-depsgaph_mt
[blender.git] / source / blender / editors / render / render_opengl.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2008 Blender Foundation.
19  * All rights reserved.
20  *
21  *
22  * ***** END GPL LICENSE BLOCK *****
23  */
24
25 /** \file blender/editors/render/render_opengl.c
26  *  \ingroup edrend
27  */
28
29
30 #include <math.h>
31 #include <string.h>
32 #include <stddef.h>
33
34 #include <GL/glew.h>
35
36 #include "MEM_guardedalloc.h"
37
38 #include "BLI_math.h"
39 #include "BLI_blenlib.h"
40 #include "BLI_dlrbTree.h"
41 #include "BLI_utildefines.h"
42 #include "BLI_jitter.h"
43
44 #include "DNA_scene_types.h"
45 #include "DNA_object_types.h"
46 #include "DNA_world_types.h"
47
48 #include "BKE_context.h"
49 #include "BKE_global.h"
50 #include "BKE_image.h"
51 #include "BKE_main.h"
52 #include "BKE_report.h"
53 #include "BKE_scene.h"
54 #include "BKE_sequencer.h"
55 #include "BKE_writeavi.h"
56
57 #include "WM_api.h"
58 #include "WM_types.h"
59
60 #include "ED_screen.h"
61 #include "ED_view3d.h"
62 #include "ED_image.h"
63
64 #include "RE_pipeline.h"
65 #include "IMB_imbuf_types.h"
66 #include "IMB_imbuf.h"
67 #include "IMB_colormanagement.h"
68
69 #include "RNA_access.h"
70 #include "RNA_define.h"
71
72 #include "BIF_gl.h"
73 #include "BIF_glutil.h"
74
75 #include "GPU_extensions.h"
76
77 #include "wm_window.h"
78
79 #include "render_intern.h"
80
81 typedef struct OGLRender {
82         Main *bmain;
83         Render *re;
84         Scene *scene;
85
86         View3D *v3d;
87         RegionView3D *rv3d;
88         ARegion *ar;
89
90         ScrArea *prevsa;
91         ARegion *prevar;
92
93         short obcenter_dia_back; /* temp overwrite */
94
95         short is_sequencer;
96         SpaceSeq *sseq;
97
98
99         Image *ima;
100         ImageUser iuser;
101
102         GPUOffScreen *ofs;
103         int sizex, sizey;
104         int write_still;
105
106         ReportList *reports;
107         bMovieHandle *mh;
108         int cfrao, nfra;
109
110         wmTimer *timer; /* use to check if running modal or not (invoke'd or exec'd)*/
111 } OGLRender;
112
113 /* added because v3d is not always valid */
114 static unsigned int screen_opengl_layers(OGLRender *oglrender)
115 {
116         if (oglrender->v3d) {
117                 return oglrender->scene->lay | oglrender->v3d->lay;
118         }
119         else {
120                 return oglrender->scene->lay;
121         }
122 }
123
124 static void screen_opengl_render_apply(OGLRender *oglrender)
125 {
126         Scene *scene = oglrender->scene;
127         ARegion *ar = oglrender->ar;
128         View3D *v3d = oglrender->v3d;
129         RegionView3D *rv3d = oglrender->rv3d;
130         RenderResult *rr;
131         Object *camera = NULL;
132         ImBuf *ibuf;
133         void *lock;
134         float winmat[4][4];
135         int sizex = oglrender->sizex;
136         int sizey = oglrender->sizey;
137         const short view_context = (v3d != NULL);
138         bool draw_bgpic = true;
139         bool draw_sky = (scene->r.alphamode == R_ADDSKY);
140         unsigned char *rect = NULL;
141
142         rr = RE_AcquireResultRead(oglrender->re);
143
144         if (oglrender->is_sequencer) {
145                 SeqRenderData context;
146                 int chanshown = oglrender->sseq ? oglrender->sseq->chanshown : 0;
147
148                 context = BKE_sequencer_new_render_data(oglrender->bmain, scene, oglrender->sizex, oglrender->sizey, 100.0f);
149
150                 ibuf = BKE_sequencer_give_ibuf(context, CFRA, chanshown);
151
152                 if (ibuf) {
153                         ImBuf *linear_ibuf;
154
155                         BLI_assert((oglrender->sizex == ibuf->x) && (oglrender->sizey == ibuf->y));
156
157                         linear_ibuf = IMB_dupImBuf(ibuf);
158                         IMB_freeImBuf(ibuf);
159
160                         if (linear_ibuf->rect_float == NULL) {
161                                 /* internally sequencer working in display space and stores both bytes and float buffers in that space.
162                                  * It is possible that byte->float onversion didn't happen in sequencer (e.g. when adding image sequence/movie
163                                  * into sequencer) there'll be only byte buffer. Create float buffer from existing byte buffer, making it linear
164                                  */
165
166                                 IMB_float_from_rect(linear_ibuf);
167                         }
168                         else {
169                                 /* ensure float buffer is in linear space, not in display space */
170                                 BKE_sequencer_imbuf_from_sequencer_space(scene, linear_ibuf);
171                         }
172
173                         memcpy(rr->rectf, linear_ibuf->rect_float, sizeof(float) * 4 * oglrender->sizex * oglrender->sizey);
174
175                         IMB_freeImBuf(linear_ibuf);
176                 }
177         }
178         else if (view_context) {
179                 ED_view3d_draw_offscreen_init(scene, v3d);
180
181                 GPU_offscreen_bind(oglrender->ofs); /* bind */
182
183                 /* render 3d view */
184                 if (rv3d->persp == RV3D_CAMOB && v3d->camera) {
185                         /*int is_ortho = scene->r.mode & R_ORTHO;*/
186                         camera = v3d->camera;
187                         RE_GetCameraWindow(oglrender->re, camera, scene->r.cfra, winmat);
188                         
189                 }
190                 else {
191                         rctf viewplane;
192                         float clipsta, clipend;
193
194                         int is_ortho = ED_view3d_viewplane_get(v3d, rv3d, sizex, sizey, &viewplane, &clipsta, &clipend, NULL);
195                         if (is_ortho) orthographic_m4(winmat, viewplane.xmin, viewplane.xmax, viewplane.ymin, viewplane.ymax, -clipend, clipend);
196                         else perspective_m4(winmat, viewplane.xmin, viewplane.xmax, viewplane.ymin, viewplane.ymax, clipsta, clipend);
197                 }
198
199                 rect = MEM_mallocN(sizex * sizey * sizeof(unsigned char) * 4, "offscreen rect");
200
201                 if ((scene->r.mode & R_OSA) == 0) {
202                         ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, winmat, draw_bgpic, draw_sky);
203                         GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, rect);
204                 }
205                 else {
206                         /* simple accumulation, less hassle then FSAA FBO's */
207                         static float jit_ofs[32][2];
208                         float winmat_jitter[4][4];
209                         int *accum_buffer = MEM_mallocN(sizex * sizey * sizeof(int) * 4, "accum1");
210                         int i, j;
211
212                         BLI_jitter_init(jit_ofs[0], scene->r.osa);
213
214                         /* first sample buffer, also initializes 'rv3d->persmat' */
215                         ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, winmat, draw_bgpic, draw_sky);
216                         GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, rect);
217
218                         for (i = 0; i < sizex * sizey * 4; i++)
219                                 accum_buffer[i] = rect[i];
220
221                         /* skip the first sample */
222                         for (j = 1; j < scene->r.osa; j++) {
223                                 copy_m4_m4(winmat_jitter, winmat);
224                                 window_translate_m4(winmat_jitter, rv3d->persmat,
225                                                     (jit_ofs[j][0] * 2.0f) / sizex,
226                                                     (jit_ofs[j][1] * 2.0f) / sizey);
227
228                                 ED_view3d_draw_offscreen(scene, v3d, ar, sizex, sizey, NULL, winmat_jitter, draw_bgpic, draw_sky);
229                                 GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, rect);
230
231                                 for (i = 0; i < sizex * sizey * 4; i++)
232                                         accum_buffer[i] += rect[i];
233                         }
234
235                         for (i = 0; i < sizex * sizey * 4; i++)
236                                 rect[i] = accum_buffer[i] / scene->r.osa;
237
238                         MEM_freeN(accum_buffer);
239                 }
240
241                 GPU_offscreen_unbind(oglrender->ofs); /* unbind */
242         }
243         else {
244                 /* shouldnt suddenly give errors mid-render but possible */
245                 char err_out[256] = "unknown";
246                 ImBuf *ibuf_view = ED_view3d_draw_offscreen_imbuf_simple(scene, scene->camera, oglrender->sizex, oglrender->sizey,
247                                                                          IB_rect, OB_SOLID, FALSE, TRUE,
248                                                                          (draw_sky) ? R_ADDSKY: R_ALPHAPREMUL, err_out);
249                 camera = scene->camera;
250
251                 if (ibuf_view) {
252                         /* steal rect reference from ibuf */
253                         rect = (unsigned char *)ibuf_view->rect;
254                         ibuf_view->mall &= ~IB_rect;
255
256                         IMB_freeImBuf(ibuf_view);
257                 }
258                 else {
259                         fprintf(stderr, "%s: failed to get buffer, %s\n", __func__, err_out);
260                 }
261         }
262
263         /* note on color management:
264          *
265          * OpenGL renders into sRGB colors, but render buffers are expected to be
266          * linear So we convert to linear here, so the conversion back to bytes can make it
267          * sRGB (or other display space) again, and so that e.g. openexr saving also saves the
268          * correct linear float buffer.
269          */
270
271         if (rect) {
272                 int profile_to;
273                 
274                 if (BKE_scene_check_color_management_enabled(scene))
275                         profile_to = IB_PROFILE_LINEAR_RGB;
276                 else
277                         profile_to = IB_PROFILE_SRGB;
278
279                 /* sequencer has got trickier conversion happened above
280                  * also assume opengl's space matches byte buffer color space */
281                 IMB_buffer_float_from_byte(rr->rectf, rect,
282                                            profile_to, IB_PROFILE_SRGB, true,
283                                            oglrender->sizex, oglrender->sizey, oglrender->sizex, oglrender->sizex);
284         }
285
286         /* rr->rectf is now filled with image data */
287
288         if ((scene->r.stamp & R_STAMP_ALL) && (scene->r.stamp & R_STAMP_DRAW))
289                 BKE_stamp_buf(scene, camera, rect, rr->rectf, rr->rectx, rr->recty, 4);
290
291         RE_ReleaseResult(oglrender->re);
292
293         /* update byte from float buffer */
294         ibuf = BKE_image_acquire_ibuf(oglrender->ima, &oglrender->iuser, &lock);
295
296         if (ibuf) {
297                 /* update display buffer */
298                 if (ibuf->rect == NULL)
299                         imb_addrectImBuf(ibuf);
300
301                 IMB_partial_display_buffer_update(ibuf, rr->rectf, rect, rr->rectx, 0, 0,
302                                                   &scene->view_settings, &scene->display_settings,
303                                                   0, 0, rr->rectx, rr->recty, true);
304
305                 /* write file for animation */
306                 if (oglrender->write_still) {
307                         char name[FILE_MAX];
308                         int ok;
309
310                         if (scene->r.im_format.planes == R_IMF_CHAN_DEPTH_8) {
311                                 IMB_color_to_bw(ibuf);
312                         }
313
314                         BKE_makepicstring(name, scene->r.pic, oglrender->bmain->name, scene->r.cfra, &scene->r.im_format, scene->r.scemode & R_EXTENSION, FALSE);
315                         ok = BKE_imbuf_write_as(ibuf, name, &scene->r.im_format, TRUE); /* no need to stamp here */
316                         if (ok) printf("OpenGL Render written to '%s'\n", name);
317                         else printf("OpenGL Render failed to write '%s'\n", name);
318                 }
319         }
320         
321         BKE_image_release_ibuf(oglrender->ima, ibuf, lock);
322
323         if (rect)
324                 MEM_freeN(rect);
325 }
326
327 static int screen_opengl_render_init(bContext *C, wmOperator *op)
328 {
329         /* new render clears all callbacks */
330         Scene *scene = CTX_data_scene(C);
331         ScrArea *prevsa = CTX_wm_area(C);
332         ARegion *prevar = CTX_wm_region(C);
333         RenderResult *rr;
334         GPUOffScreen *ofs;
335         OGLRender *oglrender;
336         int sizex, sizey;
337         short is_view_context = RNA_boolean_get(op->ptr, "view_context");
338         const short is_animation = RNA_boolean_get(op->ptr, "animation");
339         const short is_sequencer = RNA_boolean_get(op->ptr, "sequencer");
340         const short is_write_still = RNA_boolean_get(op->ptr, "write_still");
341         char err_out[256] = "unknown";
342
343         if (G.background) {
344                 BKE_report(op->reports, RPT_ERROR, "Cannot use OpenGL render in background mode (no opengl context)");
345                 return 0;
346         }
347
348         /* ensure we have a 3d view */
349
350         if (!ED_view3d_context_activate(C)) {
351                 RNA_boolean_set(op->ptr, "view_context", FALSE);
352                 is_view_context = 0;
353         }
354
355         /* only one render job at a time */
356         if (WM_jobs_test(CTX_wm_manager(C), scene, WM_JOB_TYPE_RENDER))
357                 return 0;
358         
359         if (!is_view_context && scene->camera == NULL) {
360                 BKE_report(op->reports, RPT_ERROR, "Scene has no camera");
361                 return 0;
362         }
363
364         if (!is_animation && is_write_still && BKE_imtype_is_movie(scene->r.im_format.imtype)) {
365                 BKE_report(op->reports, RPT_ERROR, "Cannot write a single file with an animation format selected");
366                 return 0;
367         }
368
369         /* stop all running jobs, except screen one. currently previews frustrate Render */
370         WM_jobs_kill_all_except(CTX_wm_manager(C), CTX_wm_screen(C));
371
372         /* create offscreen buffer */
373         sizex = (scene->r.size * scene->r.xsch) / 100;
374         sizey = (scene->r.size * scene->r.ysch) / 100;
375
376         /* corrects render size with actual size, not every card supports non-power-of-two dimensions */
377         ofs = GPU_offscreen_create(sizex, sizey, err_out);
378
379         if (!ofs) {
380                 BKE_reportf(op->reports, RPT_ERROR, "Failed to create OpenGL off-screen buffer, %s", err_out);
381                 return 0;
382         }
383
384         /* handle UI stuff */
385         WM_cursor_wait(1);
386
387         /* allocate opengl render */
388         oglrender = MEM_callocN(sizeof(OGLRender), "OGLRender");
389         op->customdata = oglrender;
390
391         oglrender->ofs = ofs;
392         oglrender->sizex = sizex;
393         oglrender->sizey = sizey;
394         oglrender->bmain = CTX_data_main(C);
395         oglrender->scene = scene;
396         oglrender->cfrao = scene->r.cfra;
397
398         oglrender->write_still = is_write_still && !is_animation;
399
400         oglrender->is_sequencer = is_sequencer;
401         if (is_sequencer) {
402                 oglrender->sseq = CTX_wm_space_seq(C);
403         }
404
405
406         oglrender->obcenter_dia_back = U.obcenter_dia;
407         U.obcenter_dia = 0;
408
409         oglrender->prevsa = prevsa;
410         oglrender->prevar = prevar;
411
412         if (is_view_context) {
413                 ED_view3d_context_user_region(C, &oglrender->v3d, &oglrender->ar); /* so quad view renders camera */
414                 oglrender->rv3d = oglrender->ar->regiondata;
415
416                 /* MUST be cleared on exit */
417                 oglrender->scene->customdata_mask_modal = ED_view3d_datamask(oglrender->scene, oglrender->v3d);
418
419                 /* apply immediately in case we're rendering from a script,
420                  * running notifiers again will overwrite */
421                 oglrender->scene->customdata_mask |= oglrender->scene->customdata_mask_modal;
422
423         }
424
425         /* create render */
426         oglrender->re = RE_NewRender(scene->id.name);
427
428         /* create image and image user */
429         oglrender->ima = BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
430         BKE_image_signal(oglrender->ima, NULL, IMA_SIGNAL_FREE);
431         BKE_image_backup_render(oglrender->scene, oglrender->ima);
432
433         oglrender->iuser.scene = scene;
434         oglrender->iuser.ok = 1;
435
436         /* create render result */
437         RE_InitState(oglrender->re, NULL, &scene->r, NULL, sizex, sizey, NULL);
438
439         rr = RE_AcquireResultWrite(oglrender->re);
440         if (rr->rectf == NULL)
441                 rr->rectf = MEM_callocN(sizeof(float) * 4 * sizex * sizey, "screen_opengl_render_init rect");
442         RE_ReleaseResult(oglrender->re);
443
444         return 1;
445 }
446
447 static void screen_opengl_render_end(bContext *C, OGLRender *oglrender)
448 {
449         Main *bmain = CTX_data_main(C);
450         Scene *scene = oglrender->scene;
451
452         if (oglrender->mh) {
453                 if (BKE_imtype_is_movie(scene->r.im_format.imtype))
454                         oglrender->mh->end_movie();
455         }
456
457         if (oglrender->timer) { /* exec will not have a timer */
458                 scene->r.cfra = oglrender->cfrao;
459                 BKE_scene_update_for_newframe(bmain, scene, screen_opengl_layers(oglrender));
460
461                 WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), oglrender->timer);
462         }
463
464         WM_cursor_wait(0);
465         WM_event_add_notifier(C, NC_SCENE | ND_RENDER_RESULT, oglrender->scene);
466
467         U.obcenter_dia = oglrender->obcenter_dia_back;
468
469         GPU_offscreen_free(oglrender->ofs);
470
471         oglrender->scene->customdata_mask_modal = 0;
472
473         CTX_wm_area_set(C, oglrender->prevsa);
474         CTX_wm_region_set(C, oglrender->prevar);
475
476         MEM_freeN(oglrender);
477 }
478
479 static int screen_opengl_render_cancel(bContext *C, wmOperator *op)
480 {
481         screen_opengl_render_end(C, op->customdata);
482
483         return OPERATOR_CANCELLED;
484 }
485
486 /* share between invoke and exec */
487 static int screen_opengl_render_anim_initialize(bContext *C, wmOperator *op)
488 {
489         /* initialize animation */
490         OGLRender *oglrender;
491         Scene *scene;
492
493         oglrender = op->customdata;
494         scene = oglrender->scene;
495
496         oglrender->reports = op->reports;
497         oglrender->mh = BKE_movie_handle_get(scene->r.im_format.imtype);
498         if (BKE_imtype_is_movie(scene->r.im_format.imtype)) {
499                 if (!oglrender->mh->start_movie(scene, &scene->r, oglrender->sizex, oglrender->sizey, oglrender->reports)) {
500                         screen_opengl_render_end(C, oglrender);
501                         return 0;
502                 }
503         }
504
505         oglrender->cfrao = scene->r.cfra;
506         oglrender->nfra = PSFRA;
507         scene->r.cfra = PSFRA;
508
509         return 1;
510 }
511 static int screen_opengl_render_anim_step(bContext *C, wmOperator *op)
512 {
513         Main *bmain = CTX_data_main(C);
514         OGLRender *oglrender = op->customdata;
515         Scene *scene = oglrender->scene;
516         ImBuf *ibuf, *ibuf_save = NULL;
517         void *lock;
518         char name[FILE_MAX];
519         int ok = 0;
520         const short view_context = (oglrender->v3d != NULL);
521         Object *camera = NULL;
522         int is_movie;
523
524         /* go to next frame */
525         if (CFRA < oglrender->nfra)
526                 CFRA++;
527         while (CFRA < oglrender->nfra) {
528                 unsigned int lay = screen_opengl_layers(oglrender);
529
530                 if (lay & 0xFF000000)
531                         lay &= 0xFF000000;
532
533                 BKE_scene_update_for_newframe(bmain, scene, lay);
534                 CFRA++;
535         }
536
537         is_movie = BKE_imtype_is_movie(scene->r.im_format.imtype);
538
539         if (!is_movie) {
540                 BKE_makepicstring(name, scene->r.pic, oglrender->bmain->name, scene->r.cfra, &scene->r.im_format, scene->r.scemode & R_EXTENSION, TRUE);
541
542                 if ((scene->r.mode & R_NO_OVERWRITE) && BLI_exists(name)) {
543                         printf("skipping existing frame \"%s\"\n", name);
544
545                         /* go to next frame */
546                         oglrender->nfra += scene->r.frame_step;
547
548                         return 1;
549                 }
550         }
551
552         BKE_scene_update_for_newframe(bmain, scene, screen_opengl_layers(oglrender));
553
554         if (view_context) {
555                 if (oglrender->rv3d->persp == RV3D_CAMOB && oglrender->v3d->camera && oglrender->v3d->scenelock) {
556                         /* since BKE_scene_update_for_newframe() is used rather
557                          * then ED_update_for_newframe() the camera needs to be set */
558                         if (BKE_scene_camera_switch_update(scene)) {
559                                 oglrender->v3d->camera = scene->camera;
560                         }
561
562                         camera = oglrender->v3d->camera;
563                 }
564         }
565         else {
566                 BKE_scene_camera_switch_update(scene);
567
568                 camera = scene->camera;
569         }
570
571         /* render into offscreen buffer */
572         screen_opengl_render_apply(oglrender);
573
574         /* save to disk */
575         ibuf = BKE_image_acquire_ibuf(oglrender->ima, &oglrender->iuser, &lock);
576
577         if (ibuf) {
578                 int needs_free = FALSE;
579
580                 ibuf_save = ibuf;
581
582                 if (is_movie || !BKE_imtype_requires_linear_float(scene->r.im_format.imtype)) {
583                         ibuf_save = IMB_colormanagement_imbuf_for_write(ibuf, TRUE, TRUE, &scene->view_settings,
584                                                                         &scene->display_settings, &scene->r.im_format);
585
586                         needs_free = TRUE;
587                 }
588
589                 /* color -> grayscale */
590                 /* editing directly would alter the render view */
591                 if (scene->r.im_format.planes == R_IMF_PLANES_BW) {
592                         ImBuf *ibuf_bw = IMB_dupImBuf(ibuf_save);
593                         IMB_color_to_bw(ibuf_bw);
594
595                         if (needs_free)
596                                 IMB_freeImBuf(ibuf_save);
597
598                         ibuf_save = ibuf_bw;
599                 }
600                 else {
601                         /* this is lightweight & doesnt re-alloc the buffers, only do this
602                          * to save the correct bit depth since the image is always RGBA */
603                         ImBuf *ibuf_cpy = IMB_allocImBuf(ibuf_save->x, ibuf_save->y, scene->r.im_format.planes, 0);
604
605                         ibuf_cpy->rect = ibuf_save->rect;
606                         ibuf_cpy->rect_float = ibuf_save->rect_float;
607                         ibuf_cpy->zbuf_float = ibuf_save->zbuf_float;
608
609                         if (needs_free) {
610                                 ibuf_cpy->mall = ibuf_save->mall;
611                                 ibuf_save->mall = 0;
612                                 IMB_freeImBuf(ibuf_save);
613                         }
614
615                         ibuf_save = ibuf_cpy;
616                 }
617
618                 if (is_movie) {
619                         ok = oglrender->mh->append_movie(&scene->r, PSFRA, CFRA, (int *)ibuf_save->rect,
620                                                          oglrender->sizex, oglrender->sizey, oglrender->reports);
621                         if (ok) {
622                                 printf("Append frame %d", scene->r.cfra);
623                                 BKE_reportf(op->reports, RPT_INFO, "Appended frame: %d", scene->r.cfra);
624                         }
625                 }
626                 else {
627                         ok = BKE_imbuf_write_stamp(scene, camera, ibuf_save, name, &scene->r.im_format);
628
629                         if (ok == 0) {
630                                 printf("Write error: cannot save %s\n", name);
631                                 BKE_reportf(op->reports, RPT_ERROR, "Write error: cannot save %s", name);
632                         }
633                         else {
634                                 printf("Saved: %s", name);
635                                 BKE_reportf(op->reports, RPT_INFO, "Saved file: %s", name);
636                         }
637                 }
638
639                 if (needs_free)
640                         IMB_freeImBuf(ibuf_save);
641         }
642
643         BKE_image_release_ibuf(oglrender->ima, ibuf, lock);
644
645         /* movie stats prints have no line break */
646         printf("\n");
647
648         /* go to next frame */
649         oglrender->nfra += scene->r.frame_step;
650
651         /* stop at the end or on error */
652         if (CFRA >= PEFRA || !ok) {
653                 screen_opengl_render_end(C, op->customdata);
654                 return 0;
655         }
656
657         return 1;
658 }
659
660
661 static int screen_opengl_render_modal(bContext *C, wmOperator *op, const wmEvent *event)
662 {
663         OGLRender *oglrender = op->customdata;
664         int anim = RNA_boolean_get(op->ptr, "animation");
665         int ret;
666
667         switch (event->type) {
668                 case ESCKEY:
669                         /* cancel */
670                         screen_opengl_render_end(C, op->customdata);
671                         return OPERATOR_FINISHED;
672                 case TIMER:
673                         /* render frame? */
674                         if (oglrender->timer == event->customdata)
675                                 break;
676                 default:
677                         /* nothing to do */
678                         return OPERATOR_RUNNING_MODAL;
679         }
680
681         /* run first because screen_opengl_render_anim_step can free oglrender */
682         WM_event_add_notifier(C, NC_SCENE | ND_RENDER_RESULT, oglrender->scene);
683         
684         if (anim == 0) {
685                 screen_opengl_render_apply(op->customdata);
686                 screen_opengl_render_end(C, op->customdata);
687                 return OPERATOR_FINISHED;
688         }
689         else
690                 ret = screen_opengl_render_anim_step(C, op);
691
692         /* stop at the end or on error */
693         if (ret == 0) {
694                 return OPERATOR_FINISHED;
695         }
696
697         return OPERATOR_RUNNING_MODAL;
698 }
699
700 static int screen_opengl_render_invoke(bContext *C, wmOperator *op, const wmEvent *event)
701 {
702         OGLRender *oglrender;
703         int anim = RNA_boolean_get(op->ptr, "animation");
704
705         if (!screen_opengl_render_init(C, op))
706                 return OPERATOR_CANCELLED;
707
708         if (anim) {
709                 if (!screen_opengl_render_anim_initialize(C, op))
710                         return OPERATOR_CANCELLED;
711         }
712         
713         oglrender = op->customdata;
714         render_view_open(C, event->x, event->y);
715         
716         WM_event_add_modal_handler(C, op);
717         oglrender->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f);
718         
719         return OPERATOR_RUNNING_MODAL;
720 }
721
722 /* executes blocking render */
723 static int screen_opengl_render_exec(bContext *C, wmOperator *op)
724 {
725         const short is_animation = RNA_boolean_get(op->ptr, "animation");
726
727         if (!screen_opengl_render_init(C, op))
728                 return OPERATOR_CANCELLED;
729
730         if (!is_animation) { /* same as invoke */
731                 /* render image */
732                 screen_opengl_render_apply(op->customdata);
733                 screen_opengl_render_end(C, op->customdata);
734
735                 return OPERATOR_FINISHED;
736         }
737         else {
738                 int ret = 1;
739
740                 if (!screen_opengl_render_anim_initialize(C, op))
741                         return OPERATOR_CANCELLED;
742
743                 while (ret) {
744                         ret = screen_opengl_render_anim_step(C, op);
745                 }
746         }
747
748         /* no redraw needed, we leave state as we entered it */
749 //      ED_update_for_newframe(C, 1);
750         WM_event_add_notifier(C, NC_SCENE | ND_RENDER_RESULT, CTX_data_scene(C));
751
752         return OPERATOR_FINISHED;
753 }
754
755 void RENDER_OT_opengl(wmOperatorType *ot)
756 {
757         PropertyRNA *prop;
758
759         /* identifiers */
760         ot->name = "OpenGL Render";
761         ot->description = "OpenGL render active viewport";
762         ot->idname = "RENDER_OT_opengl";
763
764         /* api callbacks */
765         ot->invoke = screen_opengl_render_invoke;
766         ot->exec = screen_opengl_render_exec; /* blocking */
767         ot->modal = screen_opengl_render_modal;
768         ot->cancel = screen_opengl_render_cancel;
769
770         ot->poll = ED_operator_screenactive;
771
772         prop = RNA_def_boolean(ot->srna, "animation", 0, "Animation", "Render files from the animation range of this scene");
773         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
774         prop = RNA_def_boolean(ot->srna, "sequencer", 0, "Sequencer", "Render using the sequencer's OpenGL display");
775         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
776         prop = RNA_def_boolean(ot->srna, "write_still", 0, "Write Image", "Save rendered the image to the output path (used only when animation is disabled)");
777         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
778         prop = RNA_def_boolean(ot->srna, "view_context", 1, "View Context", "Use the current 3D view for rendering, else use scene settings");
779         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
780
781 }
782
783 /* function for getting an opengl buffer from a View3D, used by sequencer */
784 // extern void *sequencer_view3d_cb;