fix [#36842] OpenGL Animation Playblast renders 1 too many frames
[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         WM_cursor_modal_restore(oglrender->win);
473
474         WM_event_add_notifier(C, NC_SCENE | ND_RENDER_RESULT, oglrender->scene);
475
476         U.obcenter_dia = oglrender->obcenter_dia_back;
477
478         GPU_offscreen_free(oglrender->ofs);
479
480         oglrender->scene->customdata_mask_modal = 0;
481
482         CTX_wm_area_set(C, oglrender->prevsa);
483         CTX_wm_region_set(C, oglrender->prevar);
484
485         MEM_freeN(oglrender);
486 }
487
488 static int screen_opengl_render_cancel(bContext *C, wmOperator *op)
489 {
490         screen_opengl_render_end(C, op->customdata);
491
492         return OPERATOR_CANCELLED;
493 }
494
495 /* share between invoke and exec */
496 static int screen_opengl_render_anim_initialize(bContext *C, wmOperator *op)
497 {
498         /* initialize animation */
499         OGLRender *oglrender;
500         Scene *scene;
501
502         oglrender = op->customdata;
503         scene = oglrender->scene;
504
505         oglrender->reports = op->reports;
506         oglrender->mh = BKE_movie_handle_get(scene->r.im_format.imtype);
507         if (BKE_imtype_is_movie(scene->r.im_format.imtype)) {
508                 if (!oglrender->mh->start_movie(scene, &scene->r, oglrender->sizex, oglrender->sizey, oglrender->reports)) {
509                         screen_opengl_render_end(C, oglrender);
510                         return 0;
511                 }
512         }
513
514         oglrender->cfrao = scene->r.cfra;
515         oglrender->nfra = PSFRA;
516         scene->r.cfra = PSFRA;
517
518         return 1;
519 }
520 static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op)
521 {
522         Main *bmain = CTX_data_main(C);
523         OGLRender *oglrender = op->customdata;
524         Scene *scene = oglrender->scene;
525         ImBuf *ibuf, *ibuf_save = NULL;
526         void *lock;
527         char name[FILE_MAX];
528         int ok = 0;
529         const short view_context = (oglrender->v3d != NULL);
530         Object *camera = NULL;
531         int is_movie;
532
533         /* go to next frame */
534         if (CFRA < oglrender->nfra)
535                 CFRA++;
536         while (CFRA < oglrender->nfra) {
537                 unsigned int lay = screen_opengl_layers(oglrender);
538
539                 if (lay & 0xFF000000)
540                         lay &= 0xFF000000;
541
542                 BKE_scene_update_for_newframe(bmain, scene, lay);
543                 CFRA++;
544         }
545
546         is_movie = BKE_imtype_is_movie(scene->r.im_format.imtype);
547
548         if (!is_movie) {
549                 BKE_makepicstring(name, scene->r.pic, oglrender->bmain->name, scene->r.cfra, &scene->r.im_format, scene->r.scemode & R_EXTENSION, TRUE);
550
551                 if ((scene->r.mode & R_NO_OVERWRITE) && BLI_exists(name)) {
552                         BKE_reportf(op->reports, RPT_INFO, "Skipping existing frame \"%s\"", name);
553                         ok = true;
554                         goto finally;
555                 }
556         }
557
558         WM_cursor_time(oglrender->win, scene->r.cfra);
559
560         BKE_scene_update_for_newframe(bmain, scene, screen_opengl_layers(oglrender));
561
562         if (view_context) {
563                 if (oglrender->rv3d->persp == RV3D_CAMOB && oglrender->v3d->camera && oglrender->v3d->scenelock) {
564                         /* since BKE_scene_update_for_newframe() is used rather
565                          * then ED_update_for_newframe() the camera needs to be set */
566                         if (BKE_scene_camera_switch_update(scene)) {
567                                 oglrender->v3d->camera = scene->camera;
568                         }
569
570                         camera = oglrender->v3d->camera;
571                 }
572         }
573         else {
574                 BKE_scene_camera_switch_update(scene);
575
576                 camera = scene->camera;
577         }
578
579         /* render into offscreen buffer */
580         screen_opengl_render_apply(oglrender);
581
582         /* save to disk */
583         ibuf = BKE_image_acquire_ibuf(oglrender->ima, &oglrender->iuser, &lock);
584
585         if (ibuf) {
586                 int needs_free = FALSE;
587
588                 ibuf_save = ibuf;
589
590                 if (is_movie || !BKE_imtype_requires_linear_float(scene->r.im_format.imtype)) {
591                         ibuf_save = IMB_colormanagement_imbuf_for_write(ibuf, true, true, &scene->view_settings,
592                                                                         &scene->display_settings, &scene->r.im_format);
593
594                         needs_free = TRUE;
595                 }
596
597                 /* color -> grayscale */
598                 /* editing directly would alter the render view */
599                 if (scene->r.im_format.planes == R_IMF_PLANES_BW) {
600                         ImBuf *ibuf_bw = IMB_dupImBuf(ibuf_save);
601                         IMB_color_to_bw(ibuf_bw);
602
603                         if (needs_free)
604                                 IMB_freeImBuf(ibuf_save);
605
606                         ibuf_save = ibuf_bw;
607                 }
608                 else {
609                         /* this is lightweight & doesnt re-alloc the buffers, only do this
610                          * to save the correct bit depth since the image is always RGBA */
611                         ImBuf *ibuf_cpy = IMB_allocImBuf(ibuf_save->x, ibuf_save->y, scene->r.im_format.planes, 0);
612
613                         ibuf_cpy->rect = ibuf_save->rect;
614                         ibuf_cpy->rect_float = ibuf_save->rect_float;
615                         ibuf_cpy->zbuf_float = ibuf_save->zbuf_float;
616
617                         if (needs_free) {
618                                 ibuf_cpy->mall = ibuf_save->mall;
619                                 ibuf_save->mall = 0;
620                                 IMB_freeImBuf(ibuf_save);
621                         }
622
623                         ibuf_save = ibuf_cpy;
624                 }
625
626                 if (is_movie) {
627                         ok = oglrender->mh->append_movie(&scene->r, PSFRA, CFRA, (int *)ibuf_save->rect,
628                                                          oglrender->sizex, oglrender->sizey, oglrender->reports);
629                         if (ok) {
630                                 printf("Append frame %d", scene->r.cfra);
631                                 BKE_reportf(op->reports, RPT_INFO, "Appended frame: %d", scene->r.cfra);
632                         }
633                 }
634                 else {
635                         ok = BKE_imbuf_write_stamp(scene, camera, ibuf_save, name, &scene->r.im_format);
636
637                         if (ok == 0) {
638                                 printf("Write error: cannot save %s\n", name);
639                                 BKE_reportf(op->reports, RPT_ERROR, "Write error: cannot save %s", name);
640                         }
641                         else {
642                                 printf("Saved: %s", name);
643                                 BKE_reportf(op->reports, RPT_INFO, "Saved file: %s", name);
644                         }
645                 }
646
647                 if (needs_free)
648                         IMB_freeImBuf(ibuf_save);
649         }
650
651         BKE_image_release_ibuf(oglrender->ima, ibuf, lock);
652
653         /* movie stats prints have no line break */
654         printf("\n");
655
656
657 finally:  /* Step the frame and bail early if needed */
658
659         /* go to next frame */
660         oglrender->nfra += scene->r.frame_step;
661
662         /* stop at the end or on error */
663         if (CFRA >= PEFRA || !ok) {
664                 screen_opengl_render_end(C, op->customdata);
665                 return 0;
666         }
667
668         return 1;
669 }
670
671
672 static int screen_opengl_render_modal(bContext *C, wmOperator *op, const wmEvent *event)
673 {
674         OGLRender *oglrender = op->customdata;
675         int anim = RNA_boolean_get(op->ptr, "animation");
676         bool ret;
677
678         switch (event->type) {
679                 case ESCKEY:
680                         /* cancel */
681                         screen_opengl_render_end(C, op->customdata);
682                         return OPERATOR_FINISHED;
683                 case TIMER:
684                         /* render frame? */
685                         if (oglrender->timer == event->customdata)
686                                 break;
687                         /* fall-through */
688                 default:
689                         /* nothing to do */
690                         return OPERATOR_RUNNING_MODAL;
691         }
692
693         /* run first because screen_opengl_render_anim_step can free oglrender */
694         WM_event_add_notifier(C, NC_SCENE | ND_RENDER_RESULT, oglrender->scene);
695         
696         if (anim == 0) {
697                 screen_opengl_render_apply(op->customdata);
698                 screen_opengl_render_end(C, op->customdata);
699                 return OPERATOR_FINISHED;
700         }
701         else {
702                 ret = screen_opengl_render_anim_step(C, op);
703         }
704
705         /* stop at the end or on error */
706         if (ret == false) {
707                 return OPERATOR_FINISHED;
708         }
709
710         return OPERATOR_RUNNING_MODAL;
711 }
712
713 static int screen_opengl_render_invoke(bContext *C, wmOperator *op, const wmEvent *event)
714 {
715         OGLRender *oglrender;
716         int anim = RNA_boolean_get(op->ptr, "animation");
717
718         if (!screen_opengl_render_init(C, op))
719                 return OPERATOR_CANCELLED;
720
721         if (anim) {
722                 if (!screen_opengl_render_anim_initialize(C, op))
723                         return OPERATOR_CANCELLED;
724         }
725         
726         oglrender = op->customdata;
727         render_view_open(C, event->x, event->y);
728         
729         /* view may be changed above (R_OUTPUT_WINDOW) */
730         oglrender->win = CTX_wm_window(C);
731
732         WM_event_add_modal_handler(C, op);
733         oglrender->timer = WM_event_add_timer(oglrender->wm, oglrender->win, TIMER, 0.01f);
734         
735         return OPERATOR_RUNNING_MODAL;
736 }
737
738 /* executes blocking render */
739 static int screen_opengl_render_exec(bContext *C, wmOperator *op)
740 {
741         const short is_animation = RNA_boolean_get(op->ptr, "animation");
742
743         if (!screen_opengl_render_init(C, op))
744                 return OPERATOR_CANCELLED;
745
746         if (!is_animation) { /* same as invoke */
747                 /* render image */
748                 screen_opengl_render_apply(op->customdata);
749                 screen_opengl_render_end(C, op->customdata);
750
751                 return OPERATOR_FINISHED;
752         }
753         else {
754                 bool ret = true;
755
756                 if (!screen_opengl_render_anim_initialize(C, op))
757                         return OPERATOR_CANCELLED;
758
759                 while (ret) {
760                         ret = screen_opengl_render_anim_step(C, op);
761                 }
762         }
763
764         /* no redraw needed, we leave state as we entered it */
765 //      ED_update_for_newframe(C, 1);
766         WM_event_add_notifier(C, NC_SCENE | ND_RENDER_RESULT, CTX_data_scene(C));
767
768         return OPERATOR_FINISHED;
769 }
770
771 void RENDER_OT_opengl(wmOperatorType *ot)
772 {
773         PropertyRNA *prop;
774
775         /* identifiers */
776         ot->name = "OpenGL Render";
777         ot->description = "OpenGL render active viewport";
778         ot->idname = "RENDER_OT_opengl";
779
780         /* api callbacks */
781         ot->invoke = screen_opengl_render_invoke;
782         ot->exec = screen_opengl_render_exec; /* blocking */
783         ot->modal = screen_opengl_render_modal;
784         ot->cancel = screen_opengl_render_cancel;
785
786         ot->poll = ED_operator_screenactive;
787
788         prop = RNA_def_boolean(ot->srna, "animation", 0, "Animation", "Render files from the animation range of this scene");
789         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
790         prop = RNA_def_boolean(ot->srna, "sequencer", 0, "Sequencer", "Render using the sequencer's OpenGL display");
791         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
792         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)");
793         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
794         prop = RNA_def_boolean(ot->srna, "view_context", 1, "View Context", "Use the current 3D view for rendering, else use scene settings");
795         RNA_def_property_flag(prop, PROP_SKIP_SAVE);
796
797 }
798
799 /* function for getting an opengl buffer from a View3D, used by sequencer */
800 // extern void *sequencer_view3d_cb;