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