2.5: Render Api
[blender.git] / source / blender / render / intern / source / pipeline.c
index ec2660566f5ee248aaf2373827a27017a7ca8c1d..a6b089c60291500fcc14d6b2913fe0c78ea8bc8f 100644 (file)
@@ -36,6 +36,7 @@
 #include "DNA_node_types.h"
 #include "DNA_object_types.h"
 #include "DNA_scene_types.h"
+#include "DNA_sequence_types.h"
 #include "DNA_userdef_types.h"
 
 #include "BKE_utildefines.h"
 #include "BKE_main.h"
 #include "BKE_node.h"
 #include "BKE_object.h"
+#include "BKE_report.h"
 #include "BKE_scene.h"
 #include "BKE_writeavi.h"      /* <------ should be replaced once with generic movie module */
+#include "BKE_sequence.h"
 #include "BKE_pointcache.h"
 
 #include "MEM_guardedalloc.h"
@@ -425,6 +428,8 @@ static void render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int channel
        BLI_addtail(&rl->passes, rpass);
        rpass->passtype= passtype;
        rpass->channels= channels;
+       rpass->rectx= rl->rectx;
+       rpass->recty= rl->recty;
        
        if(rr->exrhandle) {
                int a;
@@ -527,6 +532,8 @@ static RenderResult *new_render_result(Render *re, rcti *partrct, int crop, int
                rl->pass_xor= srl->pass_xor;
                rl->light_override= srl->light_override;
                rl->mat_override= srl->mat_override;
+               rl->rectx= rectx;
+               rl->recty= recty;
                
                if(rr->exrhandle) {
                        IMB_exr_add_channel(rr->exrhandle, rl->name, "Combined.R", 0, 0, NULL);
@@ -572,6 +579,9 @@ static RenderResult *new_render_result(Render *re, rcti *partrct, int crop, int
                rl= MEM_callocN(sizeof(RenderLayer), "new render layer");
                BLI_addtail(&rr->layers, rl);
                
+               rl->rectx= rectx;
+               rl->recty= recty;
+
                /* duplicate code... */
                if(rr->exrhandle) {
                        IMB_exr_add_channel(rr->exrhandle, rl->name, "Combined.R", 0, 0, NULL);
@@ -809,7 +819,7 @@ static void ml_addpass_cb(void *base, void *lay, char *str, float *rect, int tot
        
        BLI_addtail(&rl->passes, rpass);
        rpass->channels= totchan;
-       
+
        rpass->passtype= passtype_from_name(str);
        if(rpass->passtype==0) printf("unknown pass %s\n", str);
        rl->passflag |= rpass->passtype;
@@ -826,11 +836,23 @@ static void ml_addpass_cb(void *base, void *lay, char *str, float *rect, int tot
 RenderResult *RE_MultilayerConvert(void *exrhandle, int rectx, int recty)
 {
        RenderResult *rr= MEM_callocN(sizeof(RenderResult), "loaded render result");
+       RenderLayer *rl;
+       RenderPass *rpass;
        
        rr->rectx= rectx;
        rr->recty= recty;
        
        IMB_exr_multilayer_convert(exrhandle, rr, ml_addlayer_cb, ml_addpass_cb);
+
+       for(rl=rr->layers.first; rl; rl=rl->next) {
+               rl->rectx= rectx;
+               rl->recty= recty;
+
+               for(rpass=rl->passes.first; rpass; rpass=rpass->next) {
+                       rpass->rectx= rectx;
+                       rpass->recty= recty;
+               }
+       }
        
        return rr;
 }
@@ -846,33 +868,26 @@ static void renderresult_add_names(RenderResult *rr)
                        strcpy(rpass->name, get_pass_name(rpass->passtype, -1));
 }
 
-
-/* only for temp buffer files, makes exact copy of render result */
-static void read_render_result(Render *re, int sample)
+/* called for reading temp files, and for external engines */
+static int read_render_result_from_file(char *filename, RenderResult *rr)
 {
        RenderLayer *rl;
        RenderPass *rpass;
        void *exrhandle= IMB_exr_get_handle();
        int rectx, recty;
-       char str[FILE_MAX];
-       
-       RE_FreeRenderResult(re->result);
-       re->result= new_render_result(re, &re->disprect, 0, RR_USEMEM);
 
-       render_unique_exr_name(re, str, sample);
-       if(IMB_exr_begin_read(exrhandle, str, &rectx, &recty)==0) {
+       if(IMB_exr_begin_read(exrhandle, filename, &rectx, &recty)==0) {
                IMB_exr_close(exrhandle);
-               printf("cannot read: %s\n", str);
-               return;
+               return 0;
        }
        
-       printf("read exr tmp file: %s\n", str);
-       
-       if(re->result == NULL || rectx!=re->result->rectx || recty!=re->result->recty) {
+       if(rr == NULL || rectx!=rr->rectx || recty!=rr->recty) {
                printf("error in reading render result\n");
+               IMB_exr_close(exrhandle);
+               return 0;
        }
        else {
-               for(rl= re->result->layers.first; rl; rl= rl->next) {
+               for(rl= rr->layers.first; rl; rl= rl->next) {
                        
                        /* combined */
                        if(rl->rectf) {
@@ -892,10 +907,27 @@ static void read_render_result(Render *re, int sample)
                        
                }
                IMB_exr_read_channels(exrhandle);
-               renderresult_add_names(re->result);
+               renderresult_add_names(rr);
        }
        
        IMB_exr_close(exrhandle);
+
+       return 1;
+}
+
+/* only for temp buffer files, makes exact copy of render result */
+static void read_render_result(Render *re, int sample)
+{
+       char str[FILE_MAX];
+
+       RE_FreeRenderResult(re->result);
+       re->result= new_render_result(re, &re->disprect, 0, RR_USEMEM);
+
+       render_unique_exr_name(re, str, sample);
+       printf("read exr tmp file: %s\n", str);
+
+       if(!read_render_result_from_file(str, re->result))
+               printf("cannot read: %s\n", str);
 }
 
 /* *************************************************** */
@@ -1092,14 +1124,14 @@ void RE_InitState(Render *re, Render *source, RenderData *rd, int winx, int winy
                re->ok= 0;
        }
        else {
-#ifndef WITH_OPENEXR
+#ifdef WITH_OPENEXR
+               if(re->r.scemode & R_FULL_SAMPLE)
+                       re->r.scemode |= R_EXR_TILE_FILE;       /* enable automatic */
+#else
                /* can't do this without openexr support */
-               re->r.scemode &= ~R_EXR_TILE_FILE;
+               re->r.scemode &= ~(R_EXR_TILE_FILE|R_FULL_SAMPLE);
 #endif
                
-               if(!(re->r.scemode & R_EXR_TILE_FILE))
-                       re->r.scemode &= ~R_FULL_SAMPLE;        /* clear, so we can use this flag for test both */
-               
                /* fullsample wants uniform osa levels */
                if(source && (re->r.scemode & R_FULL_SAMPLE)) {
                        /* but, if source has no full sample we disable it */
@@ -1499,7 +1531,7 @@ static void threaded_tile_processor(Render *re)
                else if(re->r.scemode & R_FULL_SAMPLE)
                        re->result= new_full_sample_buffers_exr(re);
                else
-                       re->result= new_render_result(re, &re->disprect, 0, re->r.scemode & R_EXR_TILE_FILE);
+                       re->result= new_render_result(re, &re->disprect, 0, re->r.scemode & (R_EXR_TILE_FILE|R_FULL_SAMPLE));
        }
        
        if(re->result==NULL)
@@ -1644,8 +1676,23 @@ void RE_TileProcessor(Render *re, int firsttile, int threaded)
 
 /* ************  This part uses API, for rendering Blender scenes ********** */
 
+static void external_render_3d(Render *re, RenderEngineType *type);
+
 static void do_render_3d(Render *re)
 {
+       RenderEngineType *type;
+
+       /* try external */
+       for(type=R_engines.first; type; type=type->next)
+               if(strcmp(type->idname, re->r.engine) == 0)
+                       break;
+
+       if(type && type->render) {
+               external_render_3d(re, type);
+               return;
+       }
+
+       /* internal */
        
 //     re->cfra= cfra; /* <- unused! */
        
@@ -1661,7 +1708,6 @@ static void do_render_3d(Render *re)
        if(re->flag & R_HALO)
                if(!re->test_break(re->tbh))
                        add_halo_flare(re);
-
        
        /* free all render verts etc */
        RE_Database_Free(re);
@@ -2268,6 +2314,63 @@ static void renderresult_stampinfo(Scene *scene)
        BKE_stamp_buf(scene, (unsigned char *)rres.rect32, rres.rectf, rres.rectx, rres.recty, 4);
 }
 
+static void do_render_seq(Render * re)
+{
+       static int recurs_depth = 0;
+       struct ImBuf *ibuf;
+       RenderResult *rr = re->result;
+       int cfra = re->r.cfra;
+
+       recurs_depth++;
+
+       ibuf= give_ibuf_seq(re->scene, rr->rectx, rr->recty, cfra, 0, 100.0);
+
+       recurs_depth--;
+       
+       if(ibuf) {
+               if(ibuf->rect_float) {
+                       if (!rr->rectf)
+                               rr->rectf= MEM_mallocN(4*sizeof(float)*rr->rectx*rr->recty, "render_seq rectf");
+                       
+                       memcpy(rr->rectf, ibuf->rect_float, 4*sizeof(float)*rr->rectx*rr->recty);
+                       
+                       /* TSK! Since sequence render doesn't free the *rr render result, the old rect32
+                          can hang around when sequence render has rendered a 32 bits one before */
+                       if(rr->rect32) {
+                               MEM_freeN(rr->rect32);
+                               rr->rect32= NULL;
+                       }
+               }
+               else if(ibuf->rect) {
+                       if (!rr->rect32)
+                               rr->rect32= MEM_mallocN(sizeof(int)*rr->rectx*rr->recty, "render_seq rect");
+
+                       memcpy(rr->rect32, ibuf->rect, 4*rr->rectx*rr->recty);
+
+                       /* if (ibuf->zbuf) { */
+                       /*      if (R.rectz) freeN(R.rectz); */
+                       /*      R.rectz = BLI_dupallocN(ibuf->zbuf); */
+                       /* } */
+               }
+               
+               if (recurs_depth == 0) { /* with nested scenes, only free on toplevel... */
+                       Editing * ed = re->scene->ed;
+                       if (ed) {
+                               free_imbuf_seq(&ed->seqbase, TRUE);
+                       }
+               }
+       }
+       else {
+               /* render result is delivered empty in most cases, nevertheless we handle all cases */
+               if (rr->rectf)
+                       memset(rr->rectf, 0, 4*sizeof(float)*rr->rectx*rr->recty);
+               else if (rr->rect32)
+                       memset(rr->rect32, 0, 4*rr->rectx*rr->recty);
+               else
+                       rr->rect32= MEM_callocN(sizeof(int)*rr->rectx*rr->recty, "render_seq rect");
+       }
+}
+
 /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
 
 /* main loop: doing sequence + fields + blur + 3d render + compositing */
@@ -2278,10 +2381,10 @@ static void do_render_all_options(Render *re)
        /* ensure no images are in memory from previous animated sequences */
        BKE_image_all_free_anim_ibufs(re->r.cfra);
        
-       if(re->r.scemode & R_DOSEQ) {
+       if((re->r.scemode & R_DOSEQ) && re->scene->ed && re->scene->ed->seqbase.first) {
                /* note: do_render_seq() frees rect32 when sequencer returns float images */
                if(!re->test_break(re->tbh)) 
-                       ; //XXX do_render_seq(re->result, re->r.cfra);
+                       do_render_seq(re);
                
                re->stats_draw(re->sdh, &re->i);
                re->display_draw(re->ddh, re->result, NULL);
@@ -2299,7 +2402,7 @@ static void do_render_all_options(Render *re)
        re->stats_draw(re->sdh, &re->i);
        
        /* stamp image info here */
-       if((re->r.scemode & R_STAMP_INFO) && (re->r.stamp & R_STAMP_DRAW)) {
+       if((re->r.stamp & R_STAMP_ALL) && (re->r.stamp & R_STAMP_DRAW)) {
                renderresult_stampinfo(re->scene);
                re->display_draw(re->ddh, re->result, NULL);
        }
@@ -2327,13 +2430,13 @@ static int is_rendering_allowed(Render *re)
                        re->error(re->erh, "No border area selected.");
                        return 0;
                }
-               if(re->r.scemode & R_EXR_TILE_FILE) {
+               if(re->r.scemode & (R_EXR_TILE_FILE|R_FULL_SAMPLE)) {
                        re->error(re->erh, "Border render and Buffer-save not supported yet");
                        return 0;
                }
        }
        
-       if(re->r.scemode & R_EXR_TILE_FILE) {
+       if(re->r.scemode & (R_EXR_TILE_FILE|R_FULL_SAMPLE)) {
                char str[FILE_MAX];
                
                render_unique_exr_name(re, str, 0);
@@ -2467,8 +2570,15 @@ static int render_initialize_from_scene(Render *re, Scene *scene, int anim, int
        /* check all scenes involved */
        tag_scenes_for_render(re);
 
-       /* make sure dynamics are up to date */
-       update_physics_cache(re, scene, anim_init);
+       /*
+        * Disabled completely for now,
+        * can be later set as render profile option
+        * and default for background render.
+       */
+       if(0) {
+               /* make sure dynamics are up to date */
+               update_physics_cache(re, scene, anim_init);
+       }
        
        if(scene->r.scemode & R_SINGLE_LAYER)
                push_render_result(re);
@@ -2542,13 +2652,16 @@ static void do_write_image_or_movie(Render *re, Scene *scene, bMovieHandle *mh)
                        ImBuf *ibuf= IMB_allocImBuf(rres.rectx, rres.recty, scene->r.planes, 0, 0);
                        int ok;
                        
-                                       /* if not exists, BKE_write_ibuf makes one */
+                       /* if not exists, BKE_write_ibuf makes one */
                        ibuf->rect= (unsigned int *)rres.rect32;    
                        ibuf->rect_float= rres.rectf;
                        ibuf->zbuf_float= rres.rectz;
                        
                        /* float factor for random dither, imbuf takes care of it */
                        ibuf->dither= scene->r.dither_intensity;
+                       /* gamma correct to sRGB color space */
+                       if (scene->r.color_mgt_flag & R_COLOR_MANAGEMENT)
+                               ibuf->profile = IB_PROFILE_SRGB;
 
                        ok= BKE_write_ibuf(scene, ibuf, name, scene->r.imtype, scene->r.subimtype, scene->r.quality);
                        
@@ -2596,7 +2709,7 @@ void RE_BlenderAnim(Render *re, Scene *scene, int sfra, int efra, int tfra)
        re->result_ok= 0;
        
        if(BKE_imtype_is_movie(scene->r.imtype))
-               mh->start_movie(&re->r, re->rectx, re->recty);
+               mh->start_movie(scene, &re->r, re->rectx, re->recty);
        
        if (mh->get_next_frame) {
                while (!(G.afbreek == 1)) {
@@ -2743,3 +2856,191 @@ void RE_init_threadcount(Render *re)
                re->r.threads = BLI_system_thread_count();
        }
 }
+
+/************************** External Engines ***************************/
+
+RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h)
+{
+       Render *re= engine->re;
+       RenderResult *result;
+       rcti disprect;
+
+       /* ensure the coordinates are within the right limits */
+       CLAMP(x, 0, re->result->rectx);
+       CLAMP(y, 0, re->result->recty);
+       CLAMP(w, 0, re->result->rectx);
+       CLAMP(h, 0, re->result->recty);
+
+       if(x + w > re->result->rectx)
+               w= re->result->rectx - x;
+       if(y + h > re->result->recty)
+               h= re->result->recty - y;
+
+       /* allocate a render result */
+       disprect.xmin= x;
+       disprect.xmax= x+w;
+       disprect.ymin= y;
+       disprect.ymax= y+h;
+
+       if(0) { // XXX (re->r.scemode & R_FULL_SAMPLE)) {
+               result= new_full_sample_buffers(re, &engine->fullresult, &disprect, 0);
+       }
+       else {
+               result= new_render_result(re, &disprect, 0, RR_USEMEM);
+               BLI_addtail(&engine->fullresult, result);
+       }
+
+       return result;
+}
+
+void RE_engine_update_result(RenderEngine *engine, RenderResult *result)
+{
+       Render *re= engine->re;
+
+       if(result && render_display_draw_enabled(re)) {
+               result->renlay= result->layers.first; // weak
+               re->display_draw(re->ddh, result, NULL);
+       }
+}
+
+void RE_engine_end_result(RenderEngine *engine, RenderResult *result)
+{
+       Render *re= engine->re;
+
+       if(!result)
+               return;
+
+       /* merge */
+       if(re->result->exrhandle) {
+               RenderResult *rr, *rrpart;
+               
+               // XXX crashes, exr expects very particular part sizes
+               for(rr= re->result, rrpart= result; rr && rrpart; rr= rr->next, rrpart= rrpart->next)
+                       save_render_result_tile(rr, rrpart);
+       }
+       else if(render_display_draw_enabled(re)) {
+               /* on break, don't merge in result for preview renders, looks nicer */
+               if(re->test_break(re->tbh) && (re->r.scemode & R_PREVIEWBUTS));
+               else merge_render_result(re->result, result);
+       }
+
+       /* draw */
+       if(!re->test_break(re->tbh) && render_display_draw_enabled(re)) {
+               result->renlay= result->layers.first; // weak
+               re->display_draw(re->ddh, result, NULL);
+       }
+
+       /* free */
+       free_render_result(&engine->fullresult, result);
+}
+
+int RE_engine_test_break(RenderEngine *engine)
+{
+       Render *re= engine->re;
+
+       return re->test_break(re->tbh);
+}
+
+void RE_engine_update_stats(RenderEngine *engine, char *stats, char *info)
+{
+       Render *re= engine->re;
+
+       re->i.statstr= stats;
+       re->i.infostr= info;
+       re->stats_draw(re->sdh, &re->i);
+       re->i.infostr= NULL;
+       re->i.statstr= NULL;
+}
+
+/* loads in image into a result, size must match
+ * x/y offsets are only used on a partial copy when dimensions dont match */
+void RE_layer_load_from_file(RenderLayer *layer, ReportList *reports, char *filename)
+{
+       ImBuf *ibuf = IMB_loadiffname(filename, IB_rect);
+
+       if(ibuf  && (ibuf->rect || ibuf->rect_float)) {
+               if (ibuf->x == layer->rectx && ibuf->y == layer->recty) {
+                       if(ibuf->rect_float==NULL)
+                               IMB_float_from_rect(ibuf);
+
+                       memcpy(layer->rectf, ibuf->rect_float, sizeof(float)*4*layer->rectx*layer->recty);
+               } else {
+                       if ((ibuf->x >= layer->rectx) && (ibuf->y >= layer->recty)) {
+                               ImBuf *ibuf_clip;
+
+                               if(ibuf->rect_float==NULL)
+                                       IMB_float_from_rect(ibuf);
+
+                               ibuf_clip = IMB_allocImBuf(layer->rectx, layer->recty, 32, IB_rectfloat, 0);
+                               if(ibuf_clip) {
+                                       IMB_rectcpy(ibuf_clip, ibuf, 0,0, 0,0, layer->rectx, layer->recty);
+
+                                       memcpy(layer->rectf, ibuf_clip->rect_float, sizeof(float)*4*layer->rectx*layer->recty);
+                                       IMB_freeImBuf(ibuf_clip);
+                               }
+                               else {
+                                       BKE_reportf(reports, RPT_ERROR, "RE_result_rect_from_file: failed to allocate clip buffer '%s'\n", filename);
+                               }
+                       }
+                       else {
+                               BKE_reportf(reports, RPT_ERROR, "RE_result_rect_from_file: incorrect dimensions for partial copy '%s'\n", filename);
+                       }
+               }
+
+               IMB_freeImBuf(ibuf);
+       }
+       else {
+               BKE_reportf(reports, RPT_ERROR, "RE_result_rect_from_file: failed to load '%s'\n", filename);
+       }
+}
+
+void RE_result_load_from_file(RenderResult *result, ReportList *reports, char *filename)
+{
+       if(!read_render_result_from_file(filename, result)) {
+               BKE_reportf(reports, RPT_ERROR, "RE_result_rect_from_file: failed to load '%s'\n", filename);
+               return;
+       }
+}
+
+static void external_render_3d(Render *re, RenderEngineType *type)
+{
+       RenderEngine engine;
+
+       if(re->result==NULL || !(re->r.scemode & R_PREVIEWBUTS)) {
+               RE_FreeRenderResult(re->result);
+       
+               if(0) // XXX re->r.scemode & R_FULL_SAMPLE)
+                       re->result= new_full_sample_buffers_exr(re);
+               else
+                       re->result= new_render_result(re, &re->disprect, 0, 0); // XXX re->r.scemode & (R_EXR_TILE_FILE|R_FULL_SAMPLE));
+       }
+       
+       if(re->result==NULL)
+               return;
+
+       /* external */
+       memset(&engine, 0, sizeof(engine));
+       engine.type= type;
+       engine.re= re;
+
+       type->render(&engine, re->scene);
+
+       free_render_result(&engine.fullresult, engine.fullresult.first);
+
+       if(re->result->exrhandle) {
+               RenderResult *rr;
+
+               save_empty_result_tiles(re);
+               
+               for(rr= re->result; rr; rr= rr->next) {
+                       IMB_exr_close(rr->exrhandle);
+                       rr->exrhandle= NULL;
+               }
+               
+               free_render_result(&re->fullresult, re->result);
+               re->result= NULL;
+               
+               read_render_result(re, 0);
+       }
+}
+