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