Blender V2.61 - r43446
|
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