Blender V2.61 - r43446

render_internal.c

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * The Original Code is Copyright (C) 2008 Blender Foundation.
00019  * All rights reserved.
00020  *
00021  *
00022  * ***** END GPL LICENSE BLOCK *****
00023  */
00024 
00030 #include <math.h>
00031 #include <string.h>
00032 #include <stddef.h>
00033 
00034 #include "MEM_guardedalloc.h"
00035 
00036 #include "BLI_blenlib.h"
00037 #include "BLI_math.h"
00038 #include "BLI_threads.h"
00039 #include "BLI_rand.h"
00040 #include "BLI_utildefines.h"
00041 
00042 #include "DNA_scene_types.h"
00043 
00044 #include "BKE_blender.h"
00045 #include "BKE_context.h"
00046 #include "BKE_global.h"
00047 #include "BKE_image.h"
00048 #include "BKE_library.h"
00049 #include "BKE_main.h"
00050 #include "BKE_node.h"
00051 #include "BKE_multires.h"
00052 #include "BKE_report.h"
00053 #include "BKE_sequencer.h"
00054 #include "BKE_screen.h"
00055 #include "BKE_scene.h"
00056 
00057 #include "WM_api.h"
00058 #include "WM_types.h"
00059 
00060 #include "ED_screen.h"
00061 #include "ED_object.h"
00062 
00063 #include "RE_pipeline.h"
00064 #include "IMB_imbuf.h"
00065 #include "IMB_imbuf_types.h"
00066 
00067 #include "RNA_access.h"
00068 #include "RNA_define.h"
00069 
00070 #include "wm_window.h"
00071 
00072 #include "render_intern.h"
00073 
00074 /* Render Callbacks */
00075 
00076 /* called inside thread! */
00077 void image_buffer_rect_update(Scene *scene, RenderResult *rr, ImBuf *ibuf, volatile rcti *renrect)
00078 {
00079     float *rectf= NULL;
00080     int ymin, ymax, xmin, xmax;
00081     int rymin, rxmin, predivide, profile_from;
00082     unsigned char *rectc;
00083 
00084     /* if renrect argument, we only refresh scanlines */
00085     if(renrect) {
00086         /* if ymax==recty, rendering of layer is ready, we should not draw, other things happen... */
00087         if(rr->renlay==NULL || renrect->ymax>=rr->recty)
00088             return;
00089 
00090         /* xmin here is first subrect x coord, xmax defines subrect width */
00091         xmin = renrect->xmin + rr->crop;
00092         xmax = renrect->xmax - xmin + rr->crop;
00093         if(xmax<2)
00094             return;
00095 
00096         ymin= renrect->ymin + rr->crop;
00097         ymax= renrect->ymax - ymin + rr->crop;
00098         if(ymax<2)
00099             return;
00100         renrect->ymin= renrect->ymax;
00101 
00102     }
00103     else {
00104         xmin = ymin = rr->crop;
00105         xmax = rr->rectx - 2*rr->crop;
00106         ymax = rr->recty - 2*rr->crop;
00107     }
00108 
00109     /* xmin ymin is in tile coords. transform to ibuf */
00110     rxmin= rr->tilerect.xmin + xmin;
00111     if(rxmin >= ibuf->x) return;
00112     rymin= rr->tilerect.ymin + ymin;
00113     if(rymin >= ibuf->y) return;
00114 
00115     if(rxmin + xmax > ibuf->x)
00116         xmax= ibuf->x - rxmin;
00117     if(rymin + ymax > ibuf->y)
00118         ymax= ibuf->y - rymin;
00119 
00120     if(xmax < 1 || ymax < 1) return;
00121 
00122     /* find current float rect for display, first case is after composit... still weak */
00123     if(rr->rectf)
00124         rectf= rr->rectf;
00125     else {
00126         if(rr->rect32)
00127             return;
00128         else {
00129             if(rr->renlay==NULL || rr->renlay->rectf==NULL) return;
00130             rectf= rr->renlay->rectf;
00131         }
00132     }
00133     if(rectf==NULL) return;
00134 
00135     if(ibuf->rect==NULL)
00136         imb_addrectImBuf(ibuf);
00137     
00138     rectf+= 4*(rr->rectx*ymin + xmin);
00139     rectc= (unsigned char*)(ibuf->rect + ibuf->x*rymin + rxmin);
00140 
00141     if(scene && (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT)) {
00142         profile_from= IB_PROFILE_LINEAR_RGB;
00143         predivide= (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT_PREDIVIDE);
00144     }
00145     else {
00146         profile_from= IB_PROFILE_SRGB;
00147         predivide= 0;
00148     }
00149 
00150     IMB_buffer_byte_from_float(rectc, rectf,
00151         4, ibuf->dither, IB_PROFILE_SRGB, profile_from, predivide,
00152         xmax, ymax, ibuf->x, rr->rectx);
00153 }
00154 
00155 /* ****************************** render invoking ***************** */
00156 
00157 /* set callbacks, exported to sequence render too.
00158  Only call in foreground (UI) renders. */
00159 
00160 static void screen_render_scene_layer_set(wmOperator *op, Main *mainp, Scene **scene, SceneRenderLayer **srl)
00161 {
00162     /* single layer re-render */
00163     if(RNA_struct_property_is_set(op->ptr, "scene")) {
00164         Scene *scn;
00165         char scene_name[MAX_ID_NAME-2];
00166 
00167         RNA_string_get(op->ptr, "scene", scene_name);
00168         scn = (Scene *)BLI_findstring(&mainp->scene, scene_name, offsetof(ID, name) + 2);
00169         
00170         if (scn) {
00171             /* camera switch wont have updated */
00172             scn->r.cfra= (*scene)->r.cfra;
00173             scene_camera_switch_update(scn);
00174 
00175             *scene = scn;
00176         }
00177     }
00178 
00179     if(RNA_struct_property_is_set(op->ptr, "layer")) {
00180         SceneRenderLayer *rl;
00181         char rl_name[RE_MAXNAME];
00182 
00183         RNA_string_get(op->ptr, "layer", rl_name);
00184         rl = (SceneRenderLayer *)BLI_findstring(&(*scene)->r.layers, rl_name, offsetof(SceneRenderLayer, name));
00185         
00186         if (rl)
00187             *srl = rl;
00188     }
00189 }
00190 
00191 /* executes blocking render */
00192 static int screen_render_exec(bContext *C, wmOperator *op)
00193 {
00194     Scene *scene= CTX_data_scene(C);
00195     SceneRenderLayer *srl= NULL;
00196     Render *re;
00197     Image *ima;
00198     View3D *v3d= CTX_wm_view3d(C);
00199     Main *mainp= CTX_data_main(C);
00200     unsigned int lay;
00201     const short is_animation= RNA_boolean_get(op->ptr, "animation");
00202     const short is_write_still= RNA_boolean_get(op->ptr, "write_still");
00203     struct Object *camera_override= v3d ? V3D_CAMERA_LOCAL(v3d) : NULL;
00204 
00205     /* custom scene and single layer re-render */
00206     screen_render_scene_layer_set(op, mainp, &scene, &srl);
00207 
00208     if(!is_animation && is_write_still && BKE_imtype_is_movie(scene->r.im_format.imtype)) {
00209         BKE_report(op->reports, RPT_ERROR, "Can't write a single file with an animation format selected");
00210         return OPERATOR_CANCELLED;
00211     }
00212 
00213     re= RE_NewRender(scene->id.name);
00214     lay= (v3d)? v3d->lay: scene->lay;
00215 
00216     G.afbreek= 0;
00217     RE_test_break_cb(re, NULL, (int (*)(void *)) blender_test_break);
00218 
00219     ima= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
00220     BKE_image_signal(ima, NULL, IMA_SIGNAL_FREE);
00221     BKE_image_backup_render(scene, ima);
00222 
00223     /* cleanup sequencer caches before starting user triggered render.
00224        otherwise, invalidated cache entries can make their way into
00225        the output rendering. We can't put that into RE_BlenderFrame,
00226        since sequence rendering can call that recursively... (peter) */
00227     seq_stripelem_cache_cleanup();
00228 
00229     RE_SetReports(re, op->reports);
00230 
00231     if(is_animation)
00232         RE_BlenderAnim(re, mainp, scene, camera_override, lay, scene->r.sfra, scene->r.efra, scene->r.frame_step);
00233     else
00234         RE_BlenderFrame(re, mainp, scene, srl, camera_override, lay, scene->r.cfra, is_write_still);
00235 
00236     RE_SetReports(re, NULL);
00237 
00238     // no redraw needed, we leave state as we entered it
00239     ED_update_for_newframe(mainp, scene, CTX_wm_screen(C), 1);
00240 
00241     WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
00242 
00243     return OPERATOR_FINISHED;
00244 }
00245 
00246 typedef struct RenderJob {
00247     Main *main;
00248     Scene *scene;
00249     Render *re;
00250     wmWindow *win;
00251     SceneRenderLayer *srl;
00252     struct Object *camera_override;
00253     int lay;
00254     short anim, write_still;
00255     Image *image;
00256     ImageUser iuser;
00257     short *stop;
00258     short *do_update;
00259     float *progress;
00260     ReportList *reports;
00261 } RenderJob;
00262 
00263 static void render_freejob(void *rjv)
00264 {
00265     RenderJob *rj= rjv;
00266 
00267     MEM_freeN(rj);
00268 }
00269 
00270 /* str is IMA_MAX_RENDER_TEXT in size */
00271 static void make_renderinfo_string(RenderStats *rs, Scene *scene, char *str)
00272 {
00273     char info_time_str[32]; // used to be extern to header_info.c
00274     uintptr_t mem_in_use, mmap_in_use, peak_memory;
00275     float megs_used_memory, mmap_used_memory, megs_peak_memory;
00276     char *spos= str;
00277 
00278     mem_in_use= MEM_get_memory_in_use();
00279     mmap_in_use= MEM_get_mapped_memory_in_use();
00280     peak_memory = MEM_get_peak_memory();
00281 
00282     megs_used_memory= (mem_in_use-mmap_in_use)/(1024.0*1024.0);
00283     mmap_used_memory= (mmap_in_use)/(1024.0*1024.0);
00284     megs_peak_memory = (peak_memory)/(1024.0*1024.0);
00285 
00286     if(scene->lay & 0xFF000000)
00287         spos+= sprintf(spos, "Localview | ");
00288     else if(scene->r.scemode & R_SINGLE_LAYER)
00289         spos+= sprintf(spos, "Single Layer | ");
00290 
00291     if(rs->statstr) {
00292         spos+= sprintf(spos, "%s ", rs->statstr);
00293     }
00294     else {
00295         spos+= sprintf(spos, "Fra:%d  ", (scene->r.cfra));
00296         if(rs->totvert) spos+= sprintf(spos, "Ve:%d ", rs->totvert);
00297         if(rs->totface) spos+= sprintf(spos, "Fa:%d ", rs->totface);
00298         if(rs->tothalo) spos+= sprintf(spos, "Ha:%d ", rs->tothalo);
00299         if(rs->totstrand) spos+= sprintf(spos, "St:%d ", rs->totstrand);
00300         if(rs->totlamp) spos+= sprintf(spos, "La:%d ", rs->totlamp);
00301         spos+= sprintf(spos, "Mem:%.2fM (%.2fM, peak %.2fM) ", megs_used_memory, mmap_used_memory, megs_peak_memory);
00302 
00303         if(rs->curfield)
00304             spos+= sprintf(spos, "Field %d ", rs->curfield);
00305         if(rs->curblur)
00306             spos+= sprintf(spos, "Blur %d ", rs->curblur);
00307     }
00308 
00309     BLI_timestr(rs->lastframetime, info_time_str);
00310     spos+= sprintf(spos, "Time:%s ", info_time_str);
00311 
00312     if(rs->curfsa)
00313         spos+= sprintf(spos, "| Full Sample %d ", rs->curfsa);
00314     
00315     if(rs->infostr && rs->infostr[0])
00316         spos+= sprintf(spos, "| %s ", rs->infostr);
00317 
00318     /* very weak... but 512 characters is quite safe */
00319     if(spos >= str+IMA_MAX_RENDER_TEXT)
00320         if (G.f & G_DEBUG)
00321             printf("WARNING! renderwin text beyond limit \n");
00322 
00323 }
00324 
00325 static void image_renderinfo_cb(void *rjv, RenderStats *rs)
00326 {
00327     RenderJob *rj= rjv;
00328     RenderResult *rr;
00329 
00330     rr= RE_AcquireResultRead(rj->re);
00331 
00332     if(rr) {
00333         /* malloc OK here, stats_draw is not in tile threads */
00334         if(rr->text==NULL)
00335             rr->text= MEM_callocN(IMA_MAX_RENDER_TEXT, "rendertext");
00336 
00337         make_renderinfo_string(rs, rj->scene, rr->text);
00338     }
00339 
00340     RE_ReleaseResult(rj->re);
00341 
00342     /* make jobs timer to send notifier */
00343     *(rj->do_update)= 1;
00344 
00345 }
00346 
00347 static void render_progress_update(void *rjv, float progress)
00348 {
00349     RenderJob *rj= rjv;
00350     
00351     if(rj->progress && *rj->progress != progress) {
00352         *rj->progress = progress;
00353 
00354         /* make jobs timer to send notifier */
00355         *(rj->do_update)= 1;
00356     }
00357 }
00358 
00359 static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrect)
00360 {
00361     RenderJob *rj= rjv;
00362     Image *ima= rj->image;
00363     ImBuf *ibuf;
00364     void *lock;
00365 
00366     /* only update if we are displaying the slot being rendered */
00367     if(ima->render_slot != ima->last_render_slot)
00368         return;
00369 
00370     ibuf= BKE_image_acquire_ibuf(ima, &rj->iuser, &lock);
00371     if(ibuf) {
00372         image_buffer_rect_update(rj->scene, rr, ibuf, renrect);
00373 
00374         /* make jobs timer to send notifier */
00375         *(rj->do_update)= 1;
00376     }
00377     BKE_image_release_ibuf(ima, lock);
00378 }
00379 
00380 static void render_startjob(void *rjv, short *stop, short *do_update, float *progress)
00381 {
00382     RenderJob *rj= rjv;
00383 
00384     rj->stop= stop;
00385     rj->do_update= do_update;
00386     rj->progress= progress;
00387 
00388     RE_SetReports(rj->re, rj->reports);
00389 
00390     if(rj->anim)
00391         RE_BlenderAnim(rj->re, rj->main, rj->scene, rj->camera_override, rj->lay, rj->scene->r.sfra, rj->scene->r.efra, rj->scene->r.frame_step);
00392     else
00393         RE_BlenderFrame(rj->re, rj->main, rj->scene, rj->srl, rj->camera_override, rj->lay, rj->scene->r.cfra, rj->write_still);
00394 
00395     RE_SetReports(rj->re, NULL);
00396 }
00397 
00398 static void render_endjob(void *rjv)
00399 {
00400     RenderJob *rj= rjv; 
00401 
00402     /* this render may be used again by the sequencer without the active 'Render' where the callbacks
00403      * would be re-assigned. assign dummy callbacks to avoid referencing freed renderjobs bug [#24508] */
00404     RE_InitRenderCB(rj->re);
00405 
00406     if(rj->main != G.main)
00407         free_main(rj->main);
00408 
00409     /* else the frame will not update for the original value */
00410     if(!(rj->scene->r.scemode & R_NO_FRAME_UPDATE))
00411         ED_update_for_newframe(G.main, rj->scene, rj->win->screen, 1);
00412     
00413     /* XXX above function sets all tags in nodes */
00414     ntreeCompositClearTags(rj->scene->nodetree);
00415     
00416     /* potentially set by caller */
00417     rj->scene->r.scemode &= ~R_NO_FRAME_UPDATE;
00418     
00419     if(rj->srl) {
00420         nodeUpdateID(rj->scene->nodetree, &rj->scene->id);
00421         WM_main_add_notifier(NC_NODE|NA_EDITED, rj->scene);
00422     }
00423     
00424     /* XXX render stability hack */
00425     G.rendering = 0;
00426     WM_main_add_notifier(NC_WINDOW, NULL);
00427 }
00428 
00429 /* called by render, check job 'stop' value or the global */
00430 static int render_breakjob(void *rjv)
00431 {
00432     RenderJob *rj= rjv;
00433 
00434     if(G.afbreek)
00435         return 1;
00436     if(rj->stop && *(rj->stop))
00437         return 1;
00438     return 0;
00439 }
00440 
00441 /* runs in thread, no cursor setting here works. careful with notifiers too (malloc conflicts) */
00442 /* maybe need a way to get job send notifer? */
00443 static void render_drawlock(void *UNUSED(rjv), int lock)
00444 {
00445     BKE_spacedata_draw_locks(lock);
00446     
00447 }
00448 
00449 /* catch esc */
00450 static int screen_render_modal(bContext *C, wmOperator *UNUSED(op), wmEvent *event)
00451 {
00452     /* no running blender, remove handler and pass through */
00453     if(0==WM_jobs_test(CTX_wm_manager(C), CTX_data_scene(C))) {
00454         return OPERATOR_FINISHED|OPERATOR_PASS_THROUGH;
00455     }
00456 
00457     /* running render */
00458     switch (event->type) {
00459         case ESCKEY:
00460             return OPERATOR_RUNNING_MODAL;
00461             break;
00462     }
00463     return OPERATOR_PASS_THROUGH;
00464 }
00465 
00466 /* using context, starts job */
00467 static int screen_render_invoke(bContext *C, wmOperator *op, wmEvent *event)
00468 {
00469     /* new render clears all callbacks */
00470     Main *mainp;
00471     Scene *scene= CTX_data_scene(C);
00472     SceneRenderLayer *srl=NULL;
00473     bScreen *screen= CTX_wm_screen(C);
00474     View3D *v3d= CTX_wm_view3d(C);
00475     Render *re;
00476     wmJob *steve;
00477     RenderJob *rj;
00478     Image *ima;
00479     int jobflag;
00480     const short is_animation= RNA_boolean_get(op->ptr, "animation");
00481     const short is_write_still= RNA_boolean_get(op->ptr, "write_still");
00482     struct Object *camera_override= v3d ? V3D_CAMERA_LOCAL(v3d) : NULL;
00483     const char *name;
00484     
00485     /* only one render job at a time */
00486     if(WM_jobs_test(CTX_wm_manager(C), scene))
00487         return OPERATOR_CANCELLED;
00488 
00489     if(!RE_is_rendering_allowed(scene, camera_override, op->reports)) {
00490         return OPERATOR_CANCELLED;
00491     }
00492 
00493     if(!is_animation && is_write_still && BKE_imtype_is_movie(scene->r.im_format.imtype)) {
00494         BKE_report(op->reports, RPT_ERROR, "Can't write a single file with an animation format selected");
00495         return OPERATOR_CANCELLED;
00496     }   
00497     
00498     /* stop all running jobs, currently previews frustrate Render */
00499     WM_jobs_stop_all(CTX_wm_manager(C));
00500 
00501     /* get main */
00502     if(G.rt == 101) {
00503         /* thread-safety experiment, copy main from the undo buffer */
00504         mainp= BKE_undo_get_main(&scene);
00505     }
00506     else
00507         mainp= CTX_data_main(C);
00508 
00509     /* cancel animation playback */
00510     if (screen->animtimer)
00511         ED_screen_animation_play(C, 0, 0);
00512     
00513     /* handle UI stuff */
00514     WM_cursor_wait(1);
00515 
00516     /* flush multires changes (for sculpt) */
00517     multires_force_render_update(CTX_data_active_object(C));
00518 
00519     /* cleanup sequencer caches before starting user triggered render.
00520        otherwise, invalidated cache entries can make their way into
00521        the output rendering. We can't put that into RE_BlenderFrame,
00522        since sequence rendering can call that recursively... (peter) */
00523     seq_stripelem_cache_cleanup();
00524 
00525     /* get editmode results */
00526     ED_object_exit_editmode(C, 0);  /* 0 = does not exit editmode */
00527 
00528     // store spare
00529     // get view3d layer, local layer, make this nice api call to render
00530     // store spare
00531 
00532     /* ensure at least 1 area shows result */
00533     render_view_open(C, event->x, event->y);
00534 
00535     jobflag= WM_JOB_EXCL_RENDER|WM_JOB_PRIORITY|WM_JOB_PROGRESS;
00536     
00537     /* custom scene and single layer re-render */
00538     screen_render_scene_layer_set(op, mainp, &scene, &srl);
00539 
00540     if(RNA_struct_property_is_set(op->ptr, "layer"))
00541         jobflag |= WM_JOB_SUSPEND;
00542 
00543     /* job custom data */
00544     rj= MEM_callocN(sizeof(RenderJob), "render job");
00545     rj->main= mainp;
00546     rj->scene= scene;
00547     rj->win= CTX_wm_window(C);
00548     rj->srl = srl;
00549     rj->camera_override = camera_override;
00550     rj->lay = (v3d)? v3d->lay: scene->lay;
00551     rj->anim= is_animation;
00552     rj->write_still= is_write_still && !is_animation;
00553     rj->iuser.scene= scene;
00554     rj->iuser.ok= 1;
00555     rj->reports= op->reports;
00556 
00557     /* setup job */
00558     if(RE_seq_render_active(scene, &scene->r)) name= "Sequence Render";
00559     else name= "Render";
00560 
00561     steve= WM_jobs_get(CTX_wm_manager(C), CTX_wm_window(C), scene, name, jobflag);
00562     WM_jobs_customdata(steve, rj, render_freejob);
00563     WM_jobs_timer(steve, 0.2, NC_SCENE|ND_RENDER_RESULT, 0);
00564     WM_jobs_callbacks(steve, render_startjob, NULL, NULL, render_endjob);
00565 
00566     /* get a render result image, and make sure it is empty */
00567     ima= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result");
00568     BKE_image_signal(ima, NULL, IMA_SIGNAL_FREE);
00569     BKE_image_backup_render(rj->scene, ima);
00570     rj->image= ima;
00571 
00572     /* setup new render */
00573     re= RE_NewRender(scene->id.name);
00574     RE_test_break_cb(re, rj, render_breakjob);
00575     RE_draw_lock_cb(re, rj, render_drawlock);
00576     RE_display_draw_cb(re, rj, image_rect_update);
00577     RE_stats_draw_cb(re, rj, image_renderinfo_cb);
00578     RE_progress_cb(re, rj, render_progress_update);
00579 
00580     rj->re= re;
00581     G.afbreek= 0;
00582 
00583     WM_jobs_start(CTX_wm_manager(C), steve);
00584 
00585     WM_cursor_wait(0);
00586     WM_event_add_notifier(C, NC_SCENE|ND_RENDER_RESULT, scene);
00587 
00588     /* we set G.rendering here already instead of only in the job, this ensure
00589        main loop or other scene updates are disabled in time, since they may
00590        have started before the job thread */
00591     G.rendering = 1;
00592 
00593     /* add modal handler for ESC */
00594     WM_event_add_modal_handler(C, op);
00595 
00596     return OPERATOR_RUNNING_MODAL;
00597 }
00598 
00599 /* contextual render, using current scene, view3d? */
00600 void RENDER_OT_render(wmOperatorType *ot)
00601 {
00602     /* identifiers */
00603     ot->name= "Render";
00604     ot->description= "Render active scene";
00605     ot->idname= "RENDER_OT_render";
00606 
00607     /* api callbacks */
00608     ot->invoke= screen_render_invoke;
00609     ot->modal= screen_render_modal;
00610     ot->exec= screen_render_exec;
00611 
00612     /*ot->poll= ED_operator_screenactive;*/ /* this isnt needed, causes failer in background mode */
00613 
00614     RNA_def_boolean(ot->srna, "animation", 0, "Animation", "Render files from the animation range of this scene");
00615     RNA_def_boolean(ot->srna, "write_still", 0, "Write Image", "Save rendered the image to the output path (used only when animation is disabled)");
00616     RNA_def_string(ot->srna, "layer", "", RE_MAXNAME, "Render Layer", "Single render layer to re-render (used only when animation is disabled)");
00617     RNA_def_string(ot->srna, "scene", "", MAX_ID_NAME-2, "Scene", "Scene to render, current scene if not specified");
00618 }
00619