diff options
author | Dalai Felinto <dfelinto@gmail.com> | 2015-04-06 16:40:12 +0300 |
---|---|---|
committer | Dalai Felinto <dfelinto@gmail.com> | 2015-04-06 16:40:12 +0300 |
commit | d5f1b9c2223333e03f2e4994171ad9df8c1c4f21 (patch) | |
tree | be6b81d2c439249af96b8b28b5013dcbb735b937 /source/blender/render/intern | |
parent | 74df307ca43df14b759fd9eb6a049a6c5d90dcda (diff) |
Multi-View and Stereo 3D
Official Documentation:
http://www.blender.org/manual/render/workflows/multiview.html
Implemented Features
====================
Builtin Stereo Camera
* Convergence Mode
* Interocular Distance
* Convergence Distance
* Pivot Mode
Viewport
* Cameras
* Plane
* Volume
Compositor
* View Switch Node
* Image Node Multi-View OpenEXR support
Sequencer
* Image/Movie Strips 'Use Multiview'
UV/Image Editor
* Option to see Multi-View images in Stereo-3D or its individual images
* Save/Open Multi-View (OpenEXR, Stereo3D, individual views) images
I/O
* Save/Open Multi-View (OpenEXR, Stereo3D, individual views) images
Scene Render Views
* Ability to have an arbitrary number of views in the scene
Missing Bits
============
First rule of Multi-View bug report: If something is not working as it should *when Views is off* this is a severe bug, do mention this in the report.
Second rule is, if something works *when Views is off* but doesn't (or crashes) when *Views is on*, this is a important bug. Do mention this in the report.
Everything else is likely small todos, and may wait until we are sure none of the above is happening.
Apart from that there are those known issues:
* Compositor Image Node poorly working for Multi-View OpenEXR
(this was working prefectly before the 'Use Multi-View' functionality)
* Selecting camera from Multi-View when looking from camera is problematic
* Animation Playback (ctrl+F11) doesn't support stereo formats
* Wrong filepath when trying to play back animated scene
* Viewport Rendering doesn't support Multi-View
* Overscan Rendering
* Fullscreen display modes need to warn the user
* Object copy should be aware of views suffix
Acknowledgments
===============
* Francesco Siddi for the help with the original feature specs and design
* Brecht Van Lommel for the original review of the code and design early on
* Blender Foundation for the Development Fund to support the project wrap up
Final patch reviewers:
* Antony Riakiotakis (psy-fi)
* Campbell Barton (ideasman42)
* Julian Eisel (Severin)
* Sergey Sharybin (nazgul)
* Thomas Dinged (dingto)
Code contributors of the original branch in github:
* Alexey Akishin
* Gabriel Caraballo
Diffstat (limited to 'source/blender/render/intern')
-rw-r--r-- | source/blender/render/intern/include/render_result.h | 21 | ||||
-rw-r--r-- | source/blender/render/intern/include/render_types.h | 3 | ||||
-rw-r--r-- | source/blender/render/intern/source/convertblender.c | 6 | ||||
-rw-r--r-- | source/blender/render/intern/source/envmap.c | 6 | ||||
-rw-r--r-- | source/blender/render/intern/source/external_engine.c | 27 | ||||
-rw-r--r-- | source/blender/render/intern/source/initrender.c | 20 | ||||
-rw-r--r-- | source/blender/render/intern/source/pipeline.c | 1099 | ||||
-rw-r--r-- | source/blender/render/intern/source/render_result.c | 815 | ||||
-rw-r--r-- | source/blender/render/intern/source/rendercore.c | 111 | ||||
-rw-r--r-- | source/blender/render/intern/source/zbuf.c | 14 |
10 files changed, 1521 insertions, 601 deletions
diff --git a/source/blender/render/intern/include/render_result.h b/source/blender/render/intern/include/render_result.h index 90ff69dbfbe..e7a2cbf99cd 100644 --- a/source/blender/render/intern/include/render_result.h +++ b/source/blender/render/intern/include/render_result.h @@ -38,6 +38,7 @@ #define RR_USE_EXR 1 #define RR_ALL_LAYERS NULL +#define RR_ALL_VIEWS NULL struct ImBuf; struct ListBase; @@ -53,12 +54,14 @@ struct ColorManagedViewSettings; /* New */ struct RenderResult *render_result_new(struct Render *re, - struct rcti *partrct, int crop, int savebuffers, const char *layername); + struct rcti *partrct, int crop, int savebuffers, const char *layername, const char *viewname); struct RenderResult *render_result_new_full_sample(struct Render *re, - struct ListBase *lb, struct rcti *partrct, int crop, int savebuffers); + struct ListBase *lb, struct rcti *partrct, int crop, int savebuffers, const char *viewname); struct RenderResult *render_result_new_from_exr(void *exrhandle, const char *colorspace, bool predivide, int rectx, int recty); +void render_result_views_new(struct RenderResult *rr, struct RenderData *rd); + /* Merge */ void render_result_merge(struct RenderResult *rr, struct RenderResult *rrpart); @@ -78,7 +81,7 @@ void render_result_single_layer_end(struct Render *re); void render_result_exr_file_begin(struct Render *re); void render_result_exr_file_end(struct Render *re); -void render_result_exr_file_merge(struct RenderResult *rr, struct RenderResult *rrpart); +void render_result_exr_file_merge(struct RenderResult *rr, struct RenderResult *rrpart, const char *viewname); void render_result_exr_file_path(struct Scene *scene, const char *layname, int sample, char *filepath); int render_result_exr_file_read_sample(struct Render *re, int sample); @@ -91,15 +94,19 @@ bool render_result_exr_file_cache_read(struct Render *re); /* Combined Pixel Rect */ -struct ImBuf *render_result_rect_to_ibuf(struct RenderResult *rr, struct RenderData *rd); +struct ImBuf *render_result_rect_to_ibuf(struct RenderResult *rr, struct RenderData *rd, const int view_id); void render_result_rect_from_ibuf(struct RenderResult *rr, struct RenderData *rd, - struct ImBuf *ibuf); + struct ImBuf *ibuf, const int view_id); -void render_result_rect_fill_zero(struct RenderResult *rr); +void render_result_rect_fill_zero(struct RenderResult *rr, const int view_id); void render_result_rect_get_pixels(struct RenderResult *rr, unsigned int *rect, int rectx, int recty, const struct ColorManagedViewSettings *view_settings, - const struct ColorManagedDisplaySettings *display_settings); + const struct ColorManagedDisplaySettings *display_settings, + const int view_id); + +void render_result_views_shallowcopy(struct RenderResult *dst, struct RenderResult *src); +void render_result_views_shallowdelete(struct RenderResult *rr); #endif /* __RENDER_RESULT_H__ */ diff --git a/source/blender/render/intern/include/render_types.h b/source/blender/render/intern/include/render_types.h index 0d23c81afb0..8fe12015e87 100644 --- a/source/blender/render/intern/include/render_types.h +++ b/source/blender/render/intern/include/render_types.h @@ -275,6 +275,9 @@ struct Render struct ImagePool *pool; struct EvaluationContext *eval_ctx; + + void **movie_ctx_arr; + char viewname[MAX_NAME]; }; /* ------------------------------------------------------------------------- */ diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c index c0ba2f43c36..d36ab46d145 100644 --- a/source/blender/render/intern/source/convertblender.c +++ b/source/blender/render/intern/source/convertblender.c @@ -5171,8 +5171,7 @@ void RE_Database_FromScene(Render *re, Main *bmain, Scene *scene, unsigned int l * above call to BKE_scene_update_for_newframe, fixes bug. [#22702]. * following calls don't depend on 'RE_SetCamera' */ RE_SetCamera(re, camera); - - normalize_m4_m4(mat, camera->obmat); + RE_GetCameraModelMatrix(re, camera, mat); invert_m4(mat); RE_SetView(re, mat); @@ -5342,7 +5341,8 @@ static void database_fromscene_vectors(Render *re, Scene *scene, unsigned int la /* if no camera, viewmat should have been set! */ if (camera) { - normalize_m4_m4(mat, camera->obmat); + RE_GetCameraModelMatrix(re, camera, mat); + normalize_m4(mat); invert_m4(mat); RE_SetView(re, mat); } diff --git a/source/blender/render/intern/source/envmap.c b/source/blender/render/intern/source/envmap.c index 0698f849073..ed3033d8393 100644 --- a/source/blender/render/intern/source/envmap.c +++ b/source/blender/render/intern/source/envmap.c @@ -495,9 +495,11 @@ static void render_envmap(Render *re, EnvMap *env) RenderLayer *rl = envre->result->layers.first; int y; float *alpha; - + float *rect; + + rect = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, re->viewname); ibuf = IMB_allocImBuf(envre->rectx, envre->recty, 24, IB_rect | IB_rectfloat); - memcpy(ibuf->rect_float, rl->rectf, ibuf->channels * ibuf->x * ibuf->y * sizeof(float)); + memcpy(ibuf->rect_float, rect, ibuf->channels * ibuf->x * ibuf->y * sizeof(float)); /* envmap renders without alpha */ alpha = ibuf->rect_float + 3; diff --git a/source/blender/render/intern/source/external_engine.c b/source/blender/render/intern/source/external_engine.c index e56466bdf48..343c4a9387a 100644 --- a/source/blender/render/intern/source/external_engine.c +++ b/source/blender/render/intern/source/external_engine.c @@ -42,6 +42,7 @@ #include "BLI_string.h" #include "BLI_utildefines.h" +#include "BKE_camera.h" #include "BKE_global.h" #include "BKE_report.h" #include "BKE_scene.h" @@ -181,7 +182,7 @@ static RenderPart *get_part_from_result(Render *re, RenderResult *result) return NULL; } -RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h, const char *layername) +RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h, const char *layername, const char *viewname) { Render *re = engine->re; RenderResult *result; @@ -204,7 +205,7 @@ RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, disprect.ymin = y; disprect.ymax = y + h; - result = render_result_new(re, &disprect, 0, RR_USE_MEM, layername); + result = render_result_new(re, &disprect, 0, RR_USE_MEM, layername, viewname); /* todo: make this thread safe */ @@ -269,7 +270,7 @@ void RE_engine_end_result(RenderEngine *engine, RenderResult *result, int cancel if (!cancel || merge_results) { if (re->result->do_exr_tile) { if (!cancel) { - render_result_exr_file_merge(re->result, result); + render_result_exr_file_merge(re->result, result, re->viewname); } } else if (!(re->test_break(re->tbh) && (re->r.scemode & R_BUTS_PREVIEW))) @@ -368,6 +369,24 @@ void RE_engine_set_error_message(RenderEngine *engine, const char *msg) } } +void RE_engine_active_view_set(RenderEngine *engine, const char *viewname) +{ + Render *re = engine->re; + RE_SetActiveRenderView(re, viewname); +} + +float RE_engine_get_camera_shift_x(RenderEngine *engine, Object *camera) +{ + Render *re = engine->re; + return BKE_camera_multiview_shift_x(re ? &re->r : NULL, camera, re->viewname); +} + +void RE_engine_get_camera_model_matrix(RenderEngine *engine, Object *camera, float *r_modelmat) +{ + Render *re = engine->re; + BKE_camera_multiview_model_matrix(re ? &re->r : NULL, camera, re->viewname, (float (*)[4])r_modelmat); +} + rcti* RE_engine_get_current_tiles(Render *re, int *r_total_tiles, bool *r_needs_free) { static rcti tiles_static[BLENDER_MAX_THREADS]; @@ -612,7 +631,7 @@ int RE_engine_render(Render *re, int do_all) if ((type->flag & RE_USE_SAVE_BUFFERS) && (re->r.scemode & R_EXR_TILE_FILE)) savebuffers = RR_USE_EXR; - re->result = render_result_new(re, &re->disprect, 0, savebuffers, RR_ALL_LAYERS); + re->result = render_result_new(re, &re->disprect, 0, savebuffers, RR_ALL_LAYERS, RR_ALL_VIEWS); } BLI_rw_mutex_unlock(&re->resultmutex); diff --git a/source/blender/render/intern/source/initrender.c b/source/blender/render/intern/source/initrender.c index a0fcc7cdcd2..970a3937657 100644 --- a/source/blender/render/intern/source/initrender.c +++ b/source/blender/render/intern/source/initrender.c @@ -426,10 +426,10 @@ void make_sample_tables(Render *re) /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - struct Object *RE_GetCamera(Render *re) { - return re->camera_override ? re->camera_override : re->scene->camera; + Object *camera = re->camera_override ? re->camera_override : re->scene->camera; + return BKE_camera_multiview_render(re->scene, camera, re->viewname); } static void re_camera_params_get(Render *re, CameraParams *params, Object *cam_ob) @@ -470,6 +470,16 @@ void RE_SetEnvmapCamera(Render *re, Object *cam_ob, float viewscale, float clips re_camera_params_get(re, ¶ms, cam_ob); } +void RE_SetOverrideCamera(Render *re, Object *camera) +{ + re->camera_override = camera; +} + +static void re_camera_params_stereo3d(Render *re, CameraParams *params, Object *cam_ob) +{ + BKE_camera_multiview_params(&re->r, params, cam_ob, re->viewname); +} + /* call this after InitState() */ /* per render, there's one persistent viewplane. Parts will set their own viewplanes */ void RE_SetCamera(Render *re, Object *cam_ob) @@ -479,6 +489,7 @@ void RE_SetCamera(Render *re, Object *cam_ob) /* setup parameters */ BKE_camera_params_init(¶ms); BKE_camera_params_from_object(¶ms, cam_ob); + re_camera_params_stereo3d(re, ¶ms, cam_ob); params.use_fields = (re->r.mode & R_FIELDS); params.field_second = (re->flag & R_SEC_FIELD); @@ -505,6 +516,11 @@ void RE_GetCameraWindow(struct Render *re, struct Object *camera, int frame, flo copy_m4_m4(mat, re->winmat); } +void RE_GetCameraModelMatrix(Render *re, struct Object *camera, float r_mat[4][4]) +{ + BKE_camera_multiview_model_matrix(&re->r, camera, re->viewname, r_mat); +} + /* ~~~~~~~~~~~~~~~~ part (tile) calculus ~~~~~~~~~~~~~~~~~~~~~~ */ diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index 4bde3dddaff..25adb4db4f7 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -70,6 +70,7 @@ #include "BKE_scene.h" #include "BKE_sequencer.h" #include "BKE_writeavi.h" /* <------ should be replaced once with generic movie module */ +#include "BKE_object.h" #include "PIL_time.h" #include "IMB_colormanagement.h" @@ -131,7 +132,7 @@ Render R; /* ********* alloc and free ******** */ -static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovieHandle *mh, const char *name_override); +static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovieHandle *mh, const size_t totvideos, const char *name_override); static volatile int g_break = 0; static int thread_break(void *UNUSED(arg)) @@ -190,14 +191,23 @@ void RE_FreeRenderResult(RenderResult *res) render_result_free(res); } -float *RE_RenderLayerGetPass(RenderLayer *rl, int passtype) +float *RE_RenderLayerGetPass(volatile RenderLayer *rl, int passtype, const char *viewname) { RenderPass *rpass; - - for (rpass = rl->passes.first; rpass; rpass = rpass->next) - if (rpass->passtype == passtype) - return rpass->rect; - return NULL; + float *rect = NULL; + + for (rpass = rl->passes.last; rpass; rpass = rpass->prev) { + if (rpass->passtype == passtype) { + rect = rpass->rect; + + if (viewname == NULL) + break; + else if (strcmp(rpass->view, viewname) == 0) + break; + } + } + + return rect; } RenderLayer *RE_GetRenderLayer(RenderResult *rr, const char *name) @@ -307,8 +317,69 @@ Scene *RE_GetScene(Render *re) return NULL; } +/** + * Same as #RE_AcquireResultImage but creating the necessary views to store the result + * fill provided result struct with a copy of thew views of what is done so far the + * #RenderResult.views #ListBase needs to be freed after with #RE_ReleaseResultImageViews +*/ +void RE_AcquireResultImageViews(Render *re, RenderResult *rr) +{ + memset(rr, 0, sizeof(RenderResult)); + + if (re) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_READ); + + if (re->result) { + RenderLayer *rl; + RenderView *rv, *rview; + + rr->rectx = re->result->rectx; + rr->recty = re->result->recty; + + /* creates a temporary duplication of views */ + render_result_views_shallowcopy(rr, re->result); + + rv = rr->views.first; + + rr->have_combined = (rv->rectf != NULL); + + /* active layer */ + rl = render_get_active_layer(re, re->result); + + if (rl) { + if (rv->rectf == NULL) { + for (rview = (RenderView *)rr->views.first; rview; rview = rview->next) { + rview->rectf = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, rview->name); + } + } + + if (rv->rectz == NULL) { + for (rview = (RenderView *)rr->views.first; rview; rview = rview->next) { + rview->rectz = RE_RenderLayerGetPass(rl, SCE_PASS_Z, rview->name); + } + } + } + + rr->layers = re->result->layers; + rr->xof = re->disprect.xmin; + rr->yof = re->disprect.ymin; + } + } +} + +/* clear temporary renderresult struct */ +void RE_ReleaseResultImageViews(Render *re, RenderResult *rr) +{ + if (re) { + if (rr) { + render_result_views_shallowdelete(rr); + } + BLI_rw_mutex_unlock(&re->resultmutex); + } +} + /* fill provided result struct with what's currently active or done */ -void RE_AcquireResultImage(Render *re, RenderResult *rr) +void RE_AcquireResultImage(Render *re, RenderResult *rr, const int view_id) { memset(rr, 0, sizeof(RenderResult)); @@ -317,27 +388,35 @@ void RE_AcquireResultImage(Render *re, RenderResult *rr) if (re->result) { RenderLayer *rl; + RenderView *rv; rr->rectx = re->result->rectx; rr->recty = re->result->recty; - rr->rectf = re->result->rectf; - rr->rectz = re->result->rectz; - rr->rect32 = re->result->rect32; - + /* actview view */ + rv = BLI_findlink(&re->result->views, view_id); + if (rv == NULL) + rv = (RenderView *)re->result->views.first; + + rr->rectf = rv ? rv->rectf : NULL; + rr->rectz = rv ? rv->rectz : NULL; + rr->rect32 = rv ? rv->rect32 : NULL; + /* active layer */ rl = render_get_active_layer(re, re->result); - if (rl) { - if (rr->rectf == NULL) - rr->rectf = rl->rectf; - if (rr->rectz == NULL) - rr->rectz = RE_RenderLayerGetPass(rl, SCE_PASS_Z); + if (rl && rv) { + if (rv->rectf == NULL) + rr->rectf = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, rv->name); + + if (rv->rectz == NULL) + rr->rectz = RE_RenderLayerGetPass(rl, SCE_PASS_Z, rv->name); } - rr->have_combined = (re->result->rectf != NULL); + rr->have_combined = rv ? (rv->rectf != NULL) : false; rr->layers = re->result->layers; - + rr->views = re->result->views; + rr->xof = re->disprect.xmin; rr->yof = re->disprect.ymin; } @@ -354,17 +433,18 @@ void RE_ReleaseResultImage(Render *re) void RE_ResultGet32(Render *re, unsigned int *rect) { RenderResult rres; - - RE_AcquireResultImage(re, &rres); - render_result_rect_get_pixels(&rres, rect, re->rectx, re->recty, &re->scene->view_settings, &re->scene->display_settings); + const size_t view_id = BKE_scene_multiview_view_id_get(&re->r, re->viewname); + + RE_AcquireResultImage(re, &rres, view_id); + render_result_rect_get_pixels(&rres, rect, re->rectx, re->recty, &re->scene->view_settings, &re->scene->display_settings, 0); RE_ReleaseResultImage(re); } /* caller is responsible for allocating rect in correct size! */ /* Only for acquired results, for lock */ -void RE_AcquiredResultGet32(Render *re, RenderResult *result, unsigned int *rect) +void RE_AcquiredResultGet32(Render *re, RenderResult *result, unsigned int *rect, const int view_id) { - render_result_rect_get_pixels(result, rect, re->rectx, re->recty, &re->scene->view_settings, &re->scene->display_settings); + render_result_rect_get_pixels(result, rect, re->rectx, re->recty, &re->scene->view_settings, &re->scene->display_settings, view_id); } RenderStats *RE_GetStats(Render *re) @@ -683,6 +763,8 @@ void RE_InitState(Render *re, Render *source, RenderData *rd, RE_init_threadcount(re); } +/* This function is only called by view3d rendering, which doesn't support + * multiview at the moment. so handle only one view here */ static void render_result_rescale(Render *re) { RenderResult *result = re->result; @@ -694,7 +776,7 @@ static void render_result_rescale(Render *re) if (src_rectf == NULL) { RenderLayer *rl = render_get_active_layer(re, re->result); if (rl != NULL) { - src_rectf = rl->rectf; + src_rectf = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, ""); } } @@ -704,7 +786,8 @@ static void render_result_rescale(Render *re) &re->disprect, 0, RR_USE_MEM, - RR_ALL_LAYERS); + RR_ALL_LAYERS, + ""); if (re->result != NULL) { dst_rectf = re->result->rectf; @@ -712,7 +795,7 @@ static void render_result_rescale(Render *re) RenderLayer *rl; rl = render_get_active_layer(re, re->result); if (rl != NULL) { - dst_rectf = rl->rectf; + dst_rectf = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, ""); } } @@ -910,9 +993,9 @@ static void *do_part_thread(void *pa_v) if (R.test_break(R.tbh) == 0) { if (!R.sss_points && (R.r.scemode & R_FULL_SAMPLE)) - pa->result = render_result_new_full_sample(&R, &pa->fullresult, &pa->disprect, pa->crop, RR_USE_MEM); + pa->result = render_result_new_full_sample(&R, &pa->fullresult, &pa->disprect, pa->crop, RR_USE_MEM, R.viewname); else - pa->result = render_result_new(&R, &pa->disprect, pa->crop, RR_USE_MEM, RR_ALL_LAYERS); + pa->result = render_result_new(&R, &pa->disprect, pa->crop, RR_USE_MEM, RR_ALL_LAYERS, R.viewname); /* Copy EXR tile settings, so pipeline knows whether this is a result * for Save Buffers enabled rendering. @@ -935,7 +1018,7 @@ static void *do_part_thread(void *pa_v) /* merge too on break! */ if (R.result->do_exr_tile) { - render_result_exr_file_merge(R.result, pa->result); + render_result_exr_file_merge(R.result, pa->result, R.viewname); } else if (render_display_update_enabled(&R)) { /* on break, don't merge in result for preview renders, looks nicer */ @@ -1115,16 +1198,23 @@ static void *do_render_thread(void *thread_v) return NULL; } -static void threaded_tile_processor(Render *re) +static void main_render_result_end(Render *re) +{ + if (re->result->do_exr_tile) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + render_result_exr_file_end(re); + BLI_rw_mutex_unlock(&re->resultmutex); + } + + if (re->r.scemode & R_EXR_CACHE_FILE) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + render_result_exr_file_cache_write(re); + BLI_rw_mutex_unlock(&re->resultmutex); + } +} + +static void main_render_result_new(Render *re) { - RenderThread thread[BLENDER_MAX_THREADS]; - ThreadQueue *workqueue, *donequeue; - ListBase threads; - RenderPart *pa; - rctf viewplane = re->viewplane; - double lastdraw, elapsed, redrawtime = 1.0f; - int totpart = 0, minx = 0, slice = 0, a, wait; - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); /* first step; free the entire render result, make new, and/or prepare exr buffer saving */ @@ -1132,25 +1222,35 @@ static void threaded_tile_processor(Render *re) render_result_free(re->result); if (re->sss_points && render_display_update_enabled(re)) - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS); + re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); else if (re->r.scemode & R_FULL_SAMPLE) - re->result = render_result_new_full_sample(re, &re->fullresult, &re->disprect, 0, RR_USE_EXR); + re->result = render_result_new_full_sample(re, &re->fullresult, &re->disprect, 0, RR_USE_EXR, RR_ALL_VIEWS); else re->result = render_result_new(re, &re->disprect, 0, - (re->r.scemode & R_EXR_TILE_FILE) ? RR_USE_EXR : RR_USE_MEM, RR_ALL_LAYERS); + (re->r.scemode & R_EXR_TILE_FILE) ? RR_USE_EXR : RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); } BLI_rw_mutex_unlock(&re->resultmutex); + + if (re->result->do_exr_tile) + render_result_exr_file_begin(re); +} + +static void threaded_tile_processor(Render *re) +{ + RenderThread thread[BLENDER_MAX_THREADS]; + ThreadQueue *workqueue, *donequeue; + ListBase threads; + RenderPart *pa; + rctf viewplane = re->viewplane; + double lastdraw, elapsed, redrawtime = 1.0f; + int totpart = 0, minx = 0, slice = 0, a, wait; if (re->result == NULL) return; /* warning; no return here without closing exr file */ - RE_parts_init(re, true); - - if (re->result->do_exr_tile) - render_result_exr_file_begin(re); /* assuming no new data gets added to dbase... */ R = *re; @@ -1256,18 +1356,6 @@ static void threaded_tile_processor(Render *re) BLI_thread_queue_free(donequeue); BLI_thread_queue_free(workqueue); - if (re->result->do_exr_tile) { - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - render_result_exr_file_end(re); - BLI_rw_mutex_unlock(&re->resultmutex); - } - - if (re->r.scemode & R_EXR_CACHE_FILE) { - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - render_result_exr_file_cache_write(re); - BLI_rw_mutex_unlock(&re->resultmutex); - } - /* unset threadsafety */ g_break = 0; BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); @@ -1284,6 +1372,7 @@ static void free_all_freestyle_renders(void); /* currently only called by preview renders and envmap */ void RE_TileProcessor(Render *re) { + main_render_result_new(re); threaded_tile_processor(re); re->i.lastframetime = PIL_check_seconds_timer() - re->i.starttime; @@ -1309,6 +1398,7 @@ void RE_TileProcessor(Render *re) static void do_render_3d(Render *re) { + RenderView *rv; int cfra_backup; re->current_scene_update(re->suh, re->scene); @@ -1325,40 +1415,49 @@ static void do_render_3d(Render *re) BKE_scene_frame_set(re->scene, (double)re->scene->r.cfra + (double)re->mblur_offs + (double)re->field_offs); - /* lock drawing in UI during data phase */ - if (re->draw_lock) - re->draw_lock(re->dlh, 1); - - /* make render verts/faces/halos/lamps */ - if (render_scene_needs_vector(re)) { - RE_Database_FromScene_Vectors(re, re->main, re->scene, re->lay); - } - else { - RE_Database_FromScene(re, re->main, re->scene, re->lay, 1); - RE_Database_Preprocess(re); - } + /* init main render result */ + main_render_result_new(re); + + /* we need a new database for each view */ + for (rv = re->result->views.first; rv; rv = rv->next) { + RE_SetActiveRenderView(re, rv->name); + + /* lock drawing in UI during data phase */ + if (re->draw_lock) + re->draw_lock(re->dlh, 1); + + /* make render verts/faces/halos/lamps */ + if (render_scene_needs_vector(re)) + RE_Database_FromScene_Vectors(re, re->main, re->scene, re->lay); + else { + RE_Database_FromScene(re, re->main, re->scene, re->lay, 1); + RE_Database_Preprocess(re); + } - /* clear UI drawing locks */ - if (re->draw_lock) - re->draw_lock(re->dlh, 0); + /* clear UI drawing locks */ + if (re->draw_lock) + re->draw_lock(re->dlh, 0); - threaded_tile_processor(re); + threaded_tile_processor(re); #ifdef WITH_FREESTYLE - /* Freestyle */ - if (re->r.mode & R_EDGE_FRS) - if (!re->test_break(re->tbh)) - add_freestyle(re, 1); + /* Freestyle */ + if (re->r.mode & R_EDGE_FRS) + if (!re->test_break(re->tbh)) + add_freestyle(re, 1); #endif - /* do left-over 3d post effects (flares) */ - if (re->flag & R_HALO) - if (!re->test_break(re->tbh)) - add_halo_flare(re); - - /* free all render verts etc */ - RE_Database_Free(re); - + /* do left-over 3d post effects (flares) */ + if (re->flag & R_HALO) + if (!re->test_break(re->tbh)) + add_halo_flare(re); + + /* free all render verts etc */ + RE_Database_Free(re); + } + + main_render_result_end(re); + re->scene->r.cfra = cfra_backup; re->scene->r.subframe = 0.f; } @@ -1431,19 +1530,13 @@ static void merge_renderresult_blur(RenderResult *rr, RenderResult *brr, float b rl1 = brr->layers.first; for (rl = rr->layers.first; rl && rl1; rl = rl->next, rl1 = rl1->next) { - - /* combined */ - if (rl->rectf && rl1->rectf) { - if (key_alpha) - addblur_rect_key(rr, rl->rectf, rl1->rectf, blurfac); - else - addblur_rect(rr, rl->rectf, rl1->rectf, blurfac, 4); - } - /* passes are allocated in sync */ rpass1 = rl1->passes.first; for (rpass = rl->passes.first; rpass && rpass1; rpass = rpass->next, rpass1 = rpass1->next) { - addblur_rect(rr, rpass->rect, rpass1->rect, blurfac, rpass->channels); + if ((rpass->passtype & SCE_PASS_COMBINED) && key_alpha) + addblur_rect_key(rr, rpass->rect, rpass1->rect, blurfac); + else + addblur_rect(rr, rpass->rect, rpass1->rect, blurfac, rpass->channels); } } } @@ -1456,7 +1549,7 @@ static void do_render_blur_3d(Render *re) int blur = re->r.mblur_samples; /* create accumulation render result */ - rres = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS); + rres = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); /* do the blur steps */ while (blur--) { @@ -1518,10 +1611,6 @@ static void merge_renderresult_fields(RenderResult *rr, RenderResult *rr1, Rende rl2 = rr2->layers.first; for (rl = rr->layers.first; rl && rl1 && rl2; rl = rl->next, rl1 = rl1->next, rl2 = rl2->next) { - /* combined */ - if (rl->rectf && rl1->rectf && rl2->rectf) - interleave_rect(rr, rl->rectf, rl1->rectf, rl2->rectf, 4); - /* passes are allocated in sync */ rpass1 = rl1->passes.first; rpass2 = rl2->passes.first; @@ -1589,7 +1678,7 @@ static void do_render_fields_3d(Render *re) re->disprect.ymax *= 2; BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS); + re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); if (rr2) { if (re->r.mode & R_ODDFIELD) @@ -1661,7 +1750,7 @@ static void do_render_fields_blur_3d(Render *re) /* weak is: it chances disprect from border */ render_result_disprect_to_full_resolution(re); - rres = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS); + rres = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); render_result_merge(rres, re->result); render_result_free(re->result); @@ -2068,11 +2157,14 @@ static void free_all_freestyle_renders(void) } #endif -/* reads all buffers, calls optional composite, merges in first result->rectf */ +/* reads all buffers, calls optional composite, merges in first result->views rectf */ static void do_merge_fullsample(Render *re, bNodeTree *ntree) { + ListBase *rectfs; + RenderView *rv; float *rectf, filt[3][3]; int x, y, sample; + int nr, numviews; /* interaction callbacks */ if (ntree) { @@ -2087,9 +2179,18 @@ static void do_merge_fullsample(Render *re, bNodeTree *ntree) /* filtmask needs it */ R = *re; - /* we accumulate in here */ - rectf = MEM_mapallocN(re->rectx * re->recty * sizeof(float) * 4, "fullsample rgba"); - + /* temporary storage of the acccumulation buffers */ + rectfs = MEM_callocN(sizeof(ListBase), "fullsample accumulation buffers"); + + numviews = BLI_listbase_count(&re->result->views); + for (nr = 0; nr < numviews; nr++) { + rv = MEM_callocN(sizeof(RenderView), "fullsample renderview"); + + /* we accumulate in here */ + rv->rectf = MEM_mapallocN(re->rectx * re->recty * sizeof(float) * 4, "fullsample rgba"); + BLI_addtail(rectfs, rv); + } + for (sample = 0; sample < re->r.osa; sample++) { Scene *sce; Render *re1; @@ -2126,54 +2227,70 @@ static void do_merge_fullsample(Render *re, bNodeTree *ntree) if (ntree) { ntreeCompositTagRender(re->scene); ntreeCompositTagAnimated(ntree); - - ntreeCompositExecTree(re->scene, ntree, &re->r, true, G.background == 0, &re->scene->view_settings, &re->scene->display_settings); + + for (rv = re->result->views.first; rv; rv = rv->next) { + ntreeCompositExecTree(re->scene, ntree, &re->r, true, G.background == 0, &re->scene->view_settings, &re->scene->display_settings, rv->name); + } } - - /* ensure we get either composited result or the active layer */ - RE_AcquireResultImage(re, &rres); - - /* accumulate with filter, and clip */ - mask = (1 << sample); - mask_array(mask, filt); - for (y = 0; y < re->recty; y++) { - float *rf = rectf + 4 * y * re->rectx; - float *col = rres.rectf + 4 * y * re->rectx; - - for (x = 0; x < re->rectx; x++, rf += 4, col += 4) { - /* clamping to 1.0 is needed for correct AA */ - if (col[0] < 0.0f) col[0] = 0.0f; else if (col[0] > 1.0f) col[0] = 1.0f; - if (col[1] < 0.0f) col[1] = 0.0f; else if (col[1] > 1.0f) col[1] = 1.0f; - if (col[2] < 0.0f) col[2] = 0.0f; else if (col[2] > 1.0f) col[2] = 1.0f; + for (nr = 0, rv = re->result->views.first; rv; rv = rv->next, nr++) { + rectf = rv->rectf; + + /* ensure we get either composited result or the active layer */ + RE_AcquireResultImage(re, &rres, nr); + + /* accumulate with filter, and clip */ + mask = (1 << sample); + mask_array(mask, filt); + + for (y = 0; y < re->recty; y++) { + float *rf = rectf + 4 * y * re->rectx; + float *col = rres.rectf + 4 * y * re->rectx; - add_filt_fmask_coord(filt, col, rf, re->rectx, re->recty, x, y); + for (x = 0; x < re->rectx; x++, rf += 4, col += 4) { + /* clamping to 1.0 is needed for correct AA */ + if (col[0] < 0.0f) col[0] = 0.0f; else if (col[0] > 1.0f) col[0] = 1.0f; + if (col[1] < 0.0f) col[1] = 0.0f; else if (col[1] > 1.0f) col[1] = 1.0f; + if (col[2] < 0.0f) col[2] = 0.0f; else if (col[2] > 1.0f) col[2] = 1.0f; + + add_filt_fmask_coord(filt, col, rf, re->rectx, re->recty, x, y); + } } - } - RE_ReleaseResultImage(re); + RE_ReleaseResultImage(re); - /* show stuff */ - if (sample != re->osa - 1) { - /* weak... the display callback wants an active renderlayer pointer... */ - re->result->renlay = render_get_active_layer(re, re->result); - re->display_update(re->duh, re->result, NULL); + /* show stuff */ + if (sample != re->osa - 1) { + /* weak... the display callback wants an active renderlayer pointer... */ + re->result->renlay = render_get_active_layer(re, re->result); + RE_SetActiveRenderView(re, rv->name); + re->display_update(re->duh, re->result, NULL); + } } - - if (re->test_break(re->tbh)) - break; } - /* clamp alpha and RGB to 0..1 and 0..inf, can go outside due to filter */ - for (y = 0; y < re->recty; y++) { - float *rf = rectf + 4 * y * re->rectx; + for (nr = 0; nr < numviews; nr++) { + rectf = ((RenderView *)BLI_findlink(rectfs, nr))->rectf; + + /* clamp alpha and RGB to 0..1 and 0..inf, can go outside due to filter */ + for (y = 0; y < re->recty; y++) { + float *rf = rectf + 4 * y * re->rectx; - for (x = 0; x < re->rectx; x++, rf += 4) { - rf[0] = MAX2(rf[0], 0.0f); - rf[1] = MAX2(rf[1], 0.0f); - rf[2] = MAX2(rf[2], 0.0f); - CLAMP(rf[3], 0.0f, 1.0f); + for (x = 0; x < re->rectx; x++, rf += 4) { + rf[0] = MAX2(rf[0], 0.0f); + rf[1] = MAX2(rf[1], 0.0f); + rf[2] = MAX2(rf[2], 0.0f); + CLAMP(rf[3], 0.0f, 1.0f); + } } + + /* store the final result */ + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + rv = BLI_findlink(&re->result->views, nr); + if (rv->rectf) + MEM_freeN(rv->rectf); + rv->rectf = rectf; + BLI_rw_mutex_unlock(&re->resultmutex); } /* clear interaction callbacks */ @@ -2186,12 +2303,14 @@ static void do_merge_fullsample(Render *re, bNodeTree *ntree) /* disable full sample print */ R.i.curfsa = 0; - - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - if (re->result->rectf) - MEM_freeN(re->result->rectf); - re->result->rectf = rectf; - BLI_rw_mutex_unlock(&re->resultmutex); + + /* garbage collection */ + while (rectfs->first) { + RenderView *rv = rectfs->first; + BLI_remlink(rectfs, rv); + MEM_freeN(rv); + } + MEM_freeN(rectfs); } /* called externally, via compositor */ @@ -2267,7 +2386,11 @@ static void do_render_composite_fields_blur_3d(Render *re) if (composite_needs_render(re->scene, 1)) { /* save memory... free all cached images */ ntreeFreeCache(ntree); - + + /* render the frames + * it could be optimized to render only the needed view + * but what if a scene has a different number of views + * than the main scene? */ do_render_fields_blur_3d(re); } else { @@ -2280,7 +2403,7 @@ static void do_render_composite_fields_blur_3d(Render *re) if ((re->r.mode & R_CROP) == 0) { render_result_disprect_to_full_resolution(re); } - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS); + re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); BLI_rw_mutex_unlock(&re->resultmutex); @@ -2325,7 +2448,10 @@ static void do_render_composite_fields_blur_3d(Render *re) if (re->r.scemode & R_FULL_SAMPLE) do_merge_fullsample(re, ntree); else { - ntreeCompositExecTree(re->scene, ntree, &re->r, true, G.background == 0, &re->scene->view_settings, &re->scene->display_settings); + RenderView *rv; + for (rv = re->result->views.first; rv; rv = rv->next) { + ntreeCompositExecTree(re->scene, ntree, &re->r, true, G.background == 0, &re->scene->view_settings, &re->scene->display_settings, rv->name); + } } ntree->stats_draw = NULL; @@ -2350,13 +2476,17 @@ static void do_render_composite_fields_blur_3d(Render *re) static void renderresult_stampinfo(Render *re) { RenderResult rres; + RenderView *rv; + int nr; /* this is the basic trick to get the displayed float or char rect from render result */ - RE_AcquireResultImage(re, &rres); - BKE_image_stamp_buf( - re->scene, RE_GetCamera(re), - (unsigned char *)rres.rect32, rres.rectf, rres.rectx, rres.recty, 4); - RE_ReleaseResultImage(re); + nr = 0; + for (rv = re->result->views.first;rv;rv = rv->next, nr++) { + RE_SetActiveRenderView(re, rv->name); + RE_AcquireResultImage(re, &rres, nr); + BKE_image_stamp_buf(re->scene, RE_GetCamera(re), (unsigned char *)rv->rect32, rv->rectf, rres.rectx, rres.recty, 4); + RE_ReleaseResultImage(re); + } } int RE_seq_render_active(Scene *scene, RenderData *rd) @@ -2380,10 +2510,12 @@ int RE_seq_render_active(Scene *scene, RenderData *rd) static void do_render_seq(Render *re) { static int recurs_depth = 0; - struct ImBuf *ibuf, *out; + struct ImBuf *out; RenderResult *rr; /* don't assign re->result here as it might change during give_ibuf_seq */ int cfra = re->r.cfra; SeqRenderData context; + size_t view_id, tot_views; + struct ImBuf **ibuf_arr; int re_x, re_y; re->i.cfra = cfra; @@ -2406,45 +2538,67 @@ static void do_render_seq(Render *re) re_y = re->result->recty; } + tot_views = BKE_scene_multiview_num_views_get(&re->r); + ibuf_arr = MEM_mallocN(sizeof(ImBuf *) * tot_views, "Sequencer Views ImBufs"); + BKE_sequencer_new_render_data( re->eval_ctx, re->main, re->scene, re_x, re_y, 100, &context); - out = BKE_sequencer_give_ibuf(&context, cfra, 0); + /* the renderresult gets destroyed during the rendering, so we first collect all ibufs + * and then we populate the final renderesult */ - if (out) { - ibuf = IMB_dupImBuf(out); - IMB_freeImBuf(out); - BKE_sequencer_imbuf_from_sequencer_space(re->scene, ibuf); - } - else { - ibuf = NULL; - } + for (view_id = 0; view_id < tot_views; view_id++) { + context.view_id = view_id; + out = BKE_sequencer_give_ibuf(&context, cfra, 0); - recurs_depth--; + if (out) { + ibuf_arr[view_id] = IMB_dupImBuf(out); + IMB_freeImBuf(out); + BKE_sequencer_imbuf_from_sequencer_space(re->scene, ibuf_arr[view_id]); + } + else { + ibuf_arr[view_id] = NULL; + } + } rr = re->result; - + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + render_result_views_new(rr, &re->r); + BLI_rw_mutex_unlock(&re->resultmutex); - if (ibuf) { - /* copy ibuf into combined pixel rect */ - render_result_rect_from_ibuf(rr, &re->r, ibuf); - - if (recurs_depth == 0) { /* with nested scenes, only free on toplevel... */ - Editing *ed = re->scene->ed; - if (ed) - BKE_sequencer_free_imbuf(re->scene, &ed->seqbase, true); + for (view_id = 0; view_id < tot_views; view_id++) { + RenderView *rv = BLI_findlink(&rr->views, view_id); + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + + if (ibuf_arr[view_id]) { + /* copy ibuf into combined pixel rect */ + render_result_rect_from_ibuf(rr, &re->r, ibuf_arr[view_id], view_id); + + if (recurs_depth == 0) { /* with nested scenes, only free on toplevel... */ + Editing *ed = re->scene->ed; + if (ed) + BKE_sequencer_free_imbuf(re->scene, &ed->seqbase, true); + } + IMB_freeImBuf(ibuf_arr[view_id]); } - IMB_freeImBuf(ibuf); - } - else { - /* render result is delivered empty in most cases, nevertheless we handle all cases */ - render_result_rect_fill_zero(rr); + else { + /* render result is delivered empty in most cases, nevertheless we handle all cases */ + render_result_rect_fill_zero(rr, view_id); + } + + BLI_rw_mutex_unlock(&re->resultmutex); + + /* would mark display buffers as invalid */ + RE_SetActiveRenderView(re, rv->name); + re->display_update(re->duh, re->result, NULL); } - BLI_rw_mutex_unlock(&re->resultmutex); + MEM_freeN(ibuf_arr); + + recurs_depth--; /* just in case this flag went missing at some point */ re->r.scemode |= R_DOSEQ; @@ -2454,9 +2608,6 @@ static void do_render_seq(Render *re) re->progress(re->prh, (float)(cfra - re->r.sfra) / (re->r.efra - re->r.sfra)); else re->progress(re->prh, 1.0f); - - /* would mark display buffers as invalid */ - re->display_update(re->duh, re->result, NULL); } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -2543,11 +2694,50 @@ static bool check_valid_compositing_camera(Scene *scene, Object *camera_override } } +static bool check_valid_camera_multiview(Scene *scene, Object *camera, ReportList *reports) +{ + SceneRenderView *srv; + bool active_view = false; + + if ((scene->r.scemode & R_MULTIVIEW) == 0) + return true; + + for (srv = scene->r.views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(&scene->r, srv)) { + active_view = true; + + if (scene->r.views_format == SCE_VIEWS_FORMAT_MULTIVIEW) { + Object *view_camera; + view_camera = BKE_camera_multiview_render(scene, camera, srv->name); + + if (view_camera == camera) { + /* if the suffix is not in the camera, means we are using the fallback camera */ + if (!BLI_str_endswith(view_camera->id.name + 2, srv->suffix)) { + BKE_reportf(reports, RPT_ERROR, "Camera \"%s\" is not a multi-view camera", + camera->id.name + 2); + return false; + } + } + } + } + } + + if (!active_view) { + BKE_reportf(reports, RPT_ERROR, "No active view found in scene \"%s\"", scene->id.name + 2); + return false; + } + + return true; +} + static int check_valid_camera(Scene *scene, Object *camera_override, ReportList *reports) { if (camera_override == NULL && scene->camera == NULL) scene->camera = BKE_scene_camera_find(scene); + if (!check_valid_camera_multiview(scene, scene->camera, reports)) + return false; + if (RE_seq_render_active(scene, &scene->r)) { if (scene->ed) { Sequence *seq = scene->ed->seqbase.first; @@ -2565,6 +2755,8 @@ static int check_valid_camera(Scene *scene, Object *camera_override, ReportList } } } + else if (!check_valid_camera_multiview(seq->scene, seq->scene_camera, reports)) + return false; } seq = seq->next; @@ -2729,6 +2921,17 @@ static void update_physics_cache(Render *re, Scene *scene, int UNUSED(anim_init) BKE_ptcache_bake(&baker); } + +void RE_SetActiveRenderView(Render *re, const char *viewname) +{ + BLI_strncpy(re->viewname, viewname, sizeof(re->viewname)); +} + +const char *RE_GetActiveRenderView(Render *re) +{ + return re->viewname; +} + /* evaluating scene options for general Blender render */ static int render_initialize_from_main(Render *re, RenderData *rd, Main *bmain, Scene *scene, SceneRenderLayer *srl, Object *camera_override, unsigned int lay_override, int anim, int anim_init) @@ -2764,7 +2967,8 @@ static int render_initialize_from_main(Render *re, RenderData *rd, Main *bmain, re->lay = lay_override ? lay_override : scene->lay; re->layer_override = lay_override; re->i.localview = (re->lay & 0xFF000000) != 0; - + re->viewname[0] = '\0'; + /* not too nice, but it survives anim-border render */ if (anim) { render_update_anim_renderdata(re, &scene->r); @@ -2838,10 +3042,10 @@ void RE_BlenderFrame(Render *re, Main *bmain, Scene *scene, SceneRenderLayer *sr char name[FILE_MAX]; BKE_image_path_from_imformat( name, scene->r.pic, bmain->name, scene->r.cfra, - &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, false); + &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, false, NULL); /* reports only used for Movie */ - do_write_image_or_movie(re, bmain, scene, NULL, name); + do_write_image_or_movie(re, bmain, scene, NULL, 0, name); } } @@ -2879,96 +3083,269 @@ void RE_RenderFreestyleExternal(Render *re) } #endif -static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovieHandle *mh, const char *name_override) +bool RE_WriteRenderViewsImage(ReportList *reports, RenderResult *rr, Scene *scene, struct Object *camera, const bool stamp, char *name) { - char name[FILE_MAX]; - RenderResult rres; - Object *camera = RE_GetCamera(re); - double render_time; - int ok = 1; - - RE_AcquireResultImage(re, &rres); + bool is_mono; + bool ok = true; + RenderData *rd = &scene->r; - /* write movie or image */ - if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { - bool do_free = false; - ImBuf *ibuf = render_result_rect_to_ibuf(&rres, &scene->r); + if (!rr) + return false; - /* note; the way it gets 32 bits rects is weak... */ - if (ibuf->rect == NULL) { - ibuf->rect = MEM_mapallocN(sizeof(int) * rres.rectx * rres.recty, "temp 32 bits rect"); - ibuf->mall |= IB_rect; - RE_AcquiredResultGet32(re, &rres, ibuf->rect); - do_free = true; - } + is_mono = BLI_listbase_count_ex(&rr->views, 2) < 2; + if (ELEM(rd->im_format.imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER) && + rd->im_format.views_format == R_IMF_VIEWS_MULTIVIEW) + { + RE_WriteRenderResult(reports, rr, name, &rd->im_format, true, NULL); + printf("Saved: %s\n", name); + } - IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings, - &scene->display_settings, &scene->r.im_format); + /* mono, legacy code */ + else if (is_mono || (rd->im_format.views_format == R_IMF_VIEWS_INDIVIDUAL)) + { + RenderView *rv; + size_t view_id; + char filepath[FILE_MAX]; - ok = mh->append_movie(&re->r, scene->r.sfra, scene->r.cfra, (int *) ibuf->rect, - ibuf->x, ibuf->y, re->reports); - if (do_free) { - MEM_freeN(ibuf->rect); - ibuf->rect = NULL; - ibuf->mall &= ~IB_rect; - } + BLI_strncpy(filepath, name, sizeof(filepath)); - /* imbuf knows which rects are not part of ibuf */ - IMB_freeImBuf(ibuf); + for (view_id = 0, rv = rr->views.first; rv; rv = rv->next, view_id++) { + BKE_scene_multiview_view_filepath_get(&scene->r, filepath, rv->name, name); - printf("Append frame %d", scene->r.cfra); - } - else { - if (name_override) - BLI_strncpy(name, name_override, sizeof(name)); - else - BKE_image_path_from_imformat( - name, scene->r.pic, bmain->name, scene->r.cfra, - &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true); - - if (re->r.im_format.imtype == R_IMF_IMTYPE_MULTILAYER) { - if (re->result) { - RE_WriteRenderResult(re->reports, re->result, name, scene->r.im_format.exr_codec); - printf("Saved: %s", name); + if (rd->im_format.imtype == R_IMF_IMTYPE_MULTILAYER) { + + RE_WriteRenderResult(reports, rr, name, &rd->im_format, false, rv->name); + printf("Saved: %s\n", name); } + else { + ImBuf *ibuf = render_result_rect_to_ibuf(rr, rd, view_id); + + IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings, + &scene->display_settings, &rd->im_format); + + if (stamp) { + /* writes the name of the individual cameras */ + Object *view_camera = BKE_camera_multiview_render(scene, camera, rv->name); + ok = BKE_imbuf_write_stamp(scene, view_camera, ibuf, name, &rd->im_format); + } + else { + ok = BKE_imbuf_write(ibuf, name, &rd->im_format); + } + + if (ok == false) { + printf("Render error: cannot save %s\n", name); + } + else printf("Saved: %s\n", name); + + /* optional preview images for exr */ + if (ok && rd->im_format.imtype == R_IMF_IMTYPE_OPENEXR && (rd->im_format.flag & R_IMF_FLAG_PREVIEW_JPG)) { + ImageFormatData imf = rd->im_format; + imf.imtype = R_IMF_IMTYPE_JPEG90; + + if (BLI_testextensie(name, ".exr")) + name[strlen(name) - 4] = 0; + BKE_image_path_ensure_ext_from_imformat(name, &imf); + ibuf->planes = 24; + + IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings, + &scene->display_settings, &rd->im_format); + + if (stamp) { + /* writes the name of the individual cameras */ + Object *view_camera = BKE_camera_multiview_render(scene, camera, rv->name); + ok = BKE_imbuf_write_stamp(scene, view_camera, ibuf, name, &rd->im_format); + } + else { + ok = BKE_imbuf_write(ibuf, name, &rd->im_format); + } + printf("Saved: %s\n", name); + } + + /* imbuf knows which rects are not part of ibuf */ + IMB_freeImBuf(ibuf); + } + } + } + else { /* R_IMF_VIEWS_STEREO_3D */ + BLI_assert(scene->r.im_format.views_format == R_IMF_VIEWS_STEREO_3D); + + if (rd->im_format.imtype == R_IMF_IMTYPE_MULTILAYER) { + printf("Stereo 3D not support for MultiLayer image: %s\n", name); } else { - ImBuf *ibuf = render_result_rect_to_ibuf(&rres, &scene->r); + ImBuf *ibuf_arr[3] = {NULL}; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + int i; + + for (i = 0; i < 2; i++) { + int view_id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name)); + ibuf_arr[i] = render_result_rect_to_ibuf(rr, rd, view_id); + IMB_colormanagement_imbuf_for_write(ibuf_arr[i], true, false, &scene->view_settings, + &scene->display_settings, &scene->r.im_format); + IMB_prepare_write_ImBuf(IMB_isfloat(ibuf_arr[i]), ibuf_arr[i]); + } - IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings, - &scene->display_settings, &scene->r.im_format); + ibuf_arr[2] = IMB_stereo3d_ImBuf(&scene->r.im_format, ibuf_arr[0], ibuf_arr[1]); - ok = BKE_imbuf_write_stamp(scene, camera, ibuf, name, &scene->r.im_format); - - if (ok == 0) { + if (stamp) + ok = BKE_imbuf_write_stamp(scene, camera, ibuf_arr[2], name, &rd->im_format); + else + ok = BKE_imbuf_write(ibuf_arr[2], name, &rd->im_format); + + if (ok == false) printf("Render error: cannot save %s\n", name); - } - else printf("Saved: %s", name); - + else + printf("Saved: %s\n", name); + /* optional preview images for exr */ - if (ok && scene->r.im_format.imtype == R_IMF_IMTYPE_OPENEXR && (scene->r.im_format.flag & R_IMF_FLAG_PREVIEW_JPG)) { - ImageFormatData imf = scene->r.im_format; + if (ok && rd->im_format.imtype == R_IMF_IMTYPE_OPENEXR && + (rd->im_format.flag & R_IMF_FLAG_PREVIEW_JPG)) + { + ImageFormatData imf = rd->im_format; imf.imtype = R_IMF_IMTYPE_JPEG90; if (BLI_testextensie(name, ".exr")) name[strlen(name) - 4] = 0; + BKE_image_path_ensure_ext_from_imformat(name, &imf); - ibuf->planes = 24; + ibuf_arr[2]->planes = 24; - IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings, + IMB_colormanagement_imbuf_for_write(ibuf_arr[2], true, false, &scene->view_settings, &scene->display_settings, &imf); - BKE_imbuf_write_stamp(scene, camera, ibuf, name, &imf); - printf("\nSaved: %s", name); + if (stamp) + ok = BKE_imbuf_write_stamp(scene, camera, ibuf_arr[2], name, &rd->im_format); + else + ok = BKE_imbuf_write(ibuf_arr[2], name, &imf); + + printf("Saved: %s\n", name); } - + + /* imbuf knows which rects are not part of ibuf */ + for (i = 0; i < 3; i++) { + IMB_freeImBuf(ibuf_arr[i]); + } + } + } + + return ok; +} + +bool RE_WriteRenderViewsMovie(ReportList *reports, RenderResult *rr, Scene *scene, RenderData *rd, bMovieHandle *mh, + const size_t width, const size_t height, void **movie_ctx_arr, const size_t totvideos) +{ + bool is_mono; + bool ok = true; + + if (!rr) + return false; + + is_mono = BLI_listbase_count_ex(&rr->views, 2) < 2; + + if (is_mono || (scene->r.im_format.views_format == R_IMF_VIEWS_INDIVIDUAL)) { + size_t view_id; + for (view_id = 0; view_id < totvideos; view_id++) { + bool do_free = false; + const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, view_id); + ImBuf *ibuf = render_result_rect_to_ibuf(rr, &scene->r, view_id); + + /* note; the way it gets 32 bits rects is weak... */ + if (ibuf->rect == NULL) { + ibuf->rect = MEM_mapallocN(sizeof(int) * rr->rectx * rr->recty, "temp 32 bits rect"); + ibuf->mall |= IB_rect; + render_result_rect_get_pixels(rr, ibuf->rect, width, height, &scene->view_settings, &scene->display_settings, view_id); + do_free = true; + } + + IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings, + &scene->display_settings, &scene->r.im_format); + + ok &= mh->append_movie(movie_ctx_arr[view_id], rd, scene->r.sfra, scene->r.cfra, + (int *) ibuf->rect, ibuf->x, ibuf->y, suffix, reports); + + if (do_free) { + MEM_freeN(ibuf->rect); + ibuf->rect = NULL; + ibuf->mall &= ~IB_rect; + } + /* imbuf knows which rects are not part of ibuf */ IMB_freeImBuf(ibuf); } + printf("Append frame %d\n", scene->r.cfra); + } + else { /* R_IMF_VIEWS_STEREO_3D */ + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + ImBuf *ibuf_arr[3] = {NULL}; + bool do_free[2] = {false, false}; + size_t i; + + BLI_assert((totvideos == 1) && (scene->r.im_format.views_format == R_IMF_VIEWS_STEREO_3D)); + + for (i = 0; i < 2; i++) { + int view_id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name)); + ibuf_arr[i] = render_result_rect_to_ibuf(rr, &scene->r, view_id); + + /* note; the way it gets 32 bits rects is weak... */ + if (ibuf_arr[i]->rect == NULL) { + ibuf_arr[i]->rect = MEM_mapallocN(sizeof(int) * width * height, "temp 32 bits rect"); + ibuf_arr[i]->mall |= IB_rect; + render_result_rect_get_pixels(rr, ibuf_arr[i]->rect, width, height, &scene->view_settings, &scene->display_settings, view_id); + do_free[i] = true; + } + + IMB_colormanagement_imbuf_for_write(ibuf_arr[i], true, false, &scene->view_settings, + &scene->display_settings, &scene->r.im_format); + } + + ibuf_arr[2] = IMB_stereo3d_ImBuf(&scene->r.im_format, ibuf_arr[0], ibuf_arr[1]); + + ok = mh->append_movie(movie_ctx_arr[0], rd, scene->r.sfra, scene->r.cfra, (int *) ibuf_arr[2]->rect, + ibuf_arr[2]->x, ibuf_arr[2]->y, "", reports); + + for (i = 0; i < 2; i++) { + if (do_free[i]) { + MEM_freeN(ibuf_arr[i]->rect); + ibuf_arr[i]->rect = NULL; + ibuf_arr[i]->mall &= ~IB_rect; + } + + /* imbuf knows which rects are not part of ibuf */ + IMB_freeImBuf(ibuf_arr[i]); + } + } + + return ok; +} + +static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovieHandle *mh, const size_t totvideos, const char *name_override) +{ + char name[FILE_MAX]; + RenderResult rres; + double render_time; + bool ok = true; + Object *camera = RE_GetCamera(re); + + RE_AcquireResultImageViews(re, &rres); + + /* write movie or image */ + if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { + RE_WriteRenderViewsMovie(re->reports, &rres, scene, &re->r, mh, re->rectx, re->recty, re->movie_ctx_arr, totvideos); + } + else { + if (name_override) + BLI_strncpy(name, name_override, sizeof(name)); + else + BKE_image_path_from_imformat( + name, scene->r.pic, bmain->name, scene->r.cfra, + &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true, NULL); + + /* write images as individual images or stereo */ + ok = RE_WriteRenderViewsImage(re->reports, &rres, scene, camera, true, name); } - RE_ReleaseResultImage(re); + RE_ReleaseResultImageViews(re, &rres); render_time = re->i.lastframetime; re->i.lastframetime = PIL_check_seconds_timer() - re->i.starttime; @@ -2987,20 +3364,51 @@ static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovie return ok; } +static void get_videos_dimensions(Render *re, RenderData *rd, size_t *r_width, size_t *r_height) +{ + size_t width, height; + if (re->r.mode & R_BORDER) { + if ((re->r.mode & R_CROP) == 0) { + width = re->winx; + height = re->winy; + } + else { + width = re->rectx; + height = re->recty; + } + } + else { + width = re->rectx; + height = re->recty; + } + + BKE_scene_multiview_videos_dimensions_get(rd, width, height, r_width, r_height); +} + /* saves images to disk */ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_override, unsigned int lay_override, int sfra, int efra, int tfra) { RenderData rd = scene->r; - bMovieHandle *mh = BKE_movie_handle_get(scene->r.im_format.imtype); + bMovieHandle *mh = NULL; int cfrao = scene->r.cfra; int nfra, totrendered = 0, totskipped = 0; - + const size_t totvideos = BKE_scene_multiview_num_videos_get(&rd); + const bool is_movie = BKE_imtype_is_movie(scene->r.im_format.imtype); + const bool is_multiview_name = ((scene->r.scemode & R_MULTIVIEW) != 0 && + (scene->r.im_format.views_format == R_IMF_VIEWS_INDIVIDUAL)); + BLI_callback_exec(re->main, (ID *)scene, BLI_CB_EVT_RENDER_INIT); /* do not fully call for each frame, it initializes & pops output window */ if (!render_initialize_from_main(re, &rd, bmain, scene, NULL, camera_override, lay_override, 0, 1)) return; + + /* we don't support Frame Server and streaming of individual views */ + if ((rd.im_format.imtype == R_IMF_IMTYPE_FRAMESERVER) && (totvideos > 1)) { + BKE_report(re->reports, RPT_ERROR, "Frame Server only support stereo output for multiview rendering"); + return; + } /* ugly global still... is to prevent renderwin events and signal subsurfs etc to make full resol */ /* is also set by caller renderwin.c */ @@ -3008,31 +3416,33 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri re->flag |= R_ANIMATION; - if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { - int width, height; - if (re->r.mode & R_BORDER) { - if ((re->r.mode & R_CROP) == 0) { - width = re->winx; - height = re->winy; - } - else { - width = re->rectx; - height = re->recty; - } - } - else { - width = re->rectx; - height = re->recty; - } + if (is_movie) { + size_t i, width, height; + + get_videos_dimensions(re, &rd, &width, &height); + + mh = BKE_movie_handle_get(scene->r.im_format.imtype); + re->movie_ctx_arr = MEM_mallocN(sizeof(void *) * totvideos, "Movies' Context"); + + for (i = 0; i < totvideos; i++) { + const char *suffix = BKE_scene_multiview_view_id_suffix_get(&re->r, i); - /* last argument here depends on users really, but no users using preview have been found so far */ - if (!mh->start_movie(scene, &re->r, width, height, re->reports, false)) - G.is_break = true; + re->movie_ctx_arr[i] = mh->context_create(); + + if (!mh->start_movie(re->movie_ctx_arr[i], scene, &re->r, width, height, re->reports, false, suffix)) + G.is_break = true; + } } - if (mh->get_next_frame) { + if (mh && mh->get_next_frame) { + /* MULTIVIEW_TODO: + * in case a new video format is added that implements get_next_frame multiview has to be addressed + * or the error throwing for R_IMF_IMTYPE_FRAMESERVER has to be extended for those cases as well + */ + BLI_assert(totvideos < 2); + while (!(G.is_break == 1)) { - int nf = mh->get_next_frame(&re->r, re->reports); + int nf = mh->get_next_frame(re->movie_ctx_arr[0], &re->r, re->reports); if (nf >= 0 && nf >= scene->r.sfra && nf <= scene->r.efra) { scene->r.cfra = re->r.cfra = nf; @@ -3042,7 +3452,7 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri totrendered++; if (re->test_break(re->tbh) == 0) { - if (!do_write_image_or_movie(re, bmain, scene, mh, NULL)) + if (!do_write_image_or_movie(re, bmain, scene, mh, totvideos, NULL)) G.is_break = true; } @@ -3085,20 +3495,67 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri nfra += tfra; /* Touch/NoOverwrite options are only valid for image's */ - if (BKE_imtype_is_movie(scene->r.im_format.imtype) == 0) { + if (is_movie == false) { if (scene->r.mode & (R_NO_OVERWRITE | R_TOUCH)) BKE_image_path_from_imformat( name, scene->r.pic, bmain->name, scene->r.cfra, - &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true); + &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true, NULL); + + if (scene->r.mode & R_NO_OVERWRITE) { + if (!is_multiview_name) { + if (BLI_exists(name)) { + printf("skipping existing frame \"%s\"\n", name); + totskipped++; + continue; + } + } + else { + SceneRenderView *srv; + bool is_skip = false; + char filepath[FILE_MAX]; - if (scene->r.mode & R_NO_OVERWRITE && BLI_exists(name)) { - printf("skipping existing frame \"%s\"\n", name); - totskipped++; - continue; + for (srv = scene->r.views.first; srv; srv = srv->next) { + if (!BKE_scene_multiview_is_render_view_active(&scene->r, srv)) + continue; + + BKE_scene_multiview_filepath_get(srv, name, filepath); + + if (BLI_exists(filepath)) { + is_skip = true; + printf("skipping existing frame \"%s\" for view \"%s\"\n", filepath, srv->name); + } + } + + if (is_skip) { + totskipped++; + continue; + } + } } - if (scene->r.mode & R_TOUCH && !BLI_exists(name)) { - BLI_make_existing_file(name); /* makes the dir if its not there */ - BLI_file_touch(name); + + if (scene->r.mode & R_TOUCH) { + if (!is_multiview_name) { + if (!BLI_exists(name)) { + BLI_make_existing_file(name); /* makes the dir if its not there */ + BLI_file_touch(name); + } + } + else { + SceneRenderView *srv; + char filepath[FILE_MAX]; + + for (srv = scene->r.views.first; srv; srv = srv->next) { + if (!BKE_scene_multiview_is_render_view_active(&scene->r, srv)) + continue; + + BKE_scene_multiview_filepath_get(srv, name, filepath); + + if (!BLI_exists(filepath)) { + BLI_make_existing_file(filepath); /* makes the dir if its not there */ + BLI_file_touch(filepath); + } + } + } } } @@ -3113,7 +3570,7 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri if (re->test_break(re->tbh) == 0) { if (!G.is_break) - if (!do_write_image_or_movie(re, bmain, scene, mh, NULL)) + if (!do_write_image_or_movie(re, bmain, scene, mh, totvideos, NULL)) G.is_break = true; } else @@ -3121,10 +3578,30 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri if (G.is_break == true) { /* remove touched file */ - if (BKE_imtype_is_movie(scene->r.im_format.imtype) == 0) { - if ((scene->r.mode & R_TOUCH) && (BLI_file_size(name) == 0)) { - /* BLI_exists(name) is implicit */ - BLI_delete(name, false, false); + if (is_movie == false) { + if ((scene->r.mode & R_TOUCH)) { + if (!is_multiview_name) { + if ((BLI_file_size(name) == 0)) { + /* BLI_exists(name) is implicit */ + BLI_delete(name, false, false); + } + } + else { + SceneRenderView *srv; + char filepath[FILE_MAX]; + + for (srv = scene->r.views.first; srv; srv = srv->next) { + if (!BKE_scene_multiview_is_render_view_active(&scene->r, srv)) + continue; + + BKE_scene_multiview_filepath_get(srv, name, filepath); + + if ((BLI_file_size(filepath) == 0)) { + /* BLI_exists(filepath) is implicit */ + BLI_delete(filepath, false, false); + } + } + } } } @@ -3139,8 +3616,17 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri } /* end movie */ - if (BKE_imtype_is_movie(scene->r.im_format.imtype)) - mh->end_movie(); + if (is_movie) { + size_t i; + for (i = 0; i < totvideos; i++) { + mh->end_movie(re->movie_ctx_arr[i]); + mh->context_free(re->movie_ctx_arr[i]); + } + + if (re->movie_ctx_arr) { + MEM_freeN(re->movie_ctx_arr); + } + } if (totskipped && totrendered == 0) BKE_report(re->reports, RPT_INFO, "No frames rendered, skipped to not overwrite"); @@ -3238,13 +3724,22 @@ void RE_layer_load_from_file(RenderLayer *layer, ReportList *reports, const char { /* OCIO_TODO: assume layer was saved in defaule color space */ ImBuf *ibuf = IMB_loadiffname(filename, IB_rect, NULL); + RenderPass *rpass = NULL; + + /* multiview: since the API takes no 'view', we use the first combined pass found */ + for (rpass = layer->passes.first; rpass; rpass = rpass->next) + if (rpass->passtype == SCE_PASS_COMBINED) + break; + + if (rpass == NULL) + BKE_reportf(reports, RPT_ERROR, "RE_layer_load_from_file: no Combined pass found in the render layer '%s'", filename); 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); + memcpy(rpass->rect, ibuf->rect_float, sizeof(float) * 4 * layer->rectx * layer->recty); } else { if ((ibuf->x - x >= layer->rectx) && (ibuf->y - y >= layer->recty)) { @@ -3257,7 +3752,7 @@ void RE_layer_load_from_file(RenderLayer *layer, ReportList *reports, const char if (ibuf_clip) { IMB_rectcpy(ibuf_clip, ibuf, 0, 0, x, y, layer->rectx, layer->recty); - memcpy(layer->rectf, ibuf_clip->rect_float, sizeof(float) * 4 * layer->rectx * layer->recty); + memcpy(rpass->rect, ibuf_clip->rect_float, sizeof(float) * 4 * layer->rectx * layer->recty); IMB_freeImBuf(ibuf_clip); } else { @@ -3345,3 +3840,21 @@ bool RE_WriteEnvmapResult(struct ReportList *reports, Scene *scene, EnvMap *env, } } +/* used in the interface to decide whether to show layers */ +bool RE_layers_have_name(struct RenderResult *rr) +{ + switch (BLI_listbase_count_ex(&rr->layers, 2)) { + case 0: + return false; + break; + case 1: + return (((RenderLayer *)rr->layers.first)->name[0] != '\0'); + break; + default: + return true; + break; + } + return false; +} + + diff --git a/source/blender/render/intern/source/render_result.c b/source/blender/render/intern/source/render_result.c index 6486844bacf..90f6eab0cc1 100644 --- a/source/blender/render/intern/source/render_result.c +++ b/source/blender/render/intern/source/render_result.c @@ -48,9 +48,8 @@ #include "BKE_global.h" #include "BKE_main.h" #include "BKE_report.h" -#ifdef WITH_CYCLES_DEBUG -# include "BKE_scene.h" -#endif +#include "BKE_camera.h" +#include "BKE_scene.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -63,14 +62,32 @@ /********************************** Free *************************************/ +static void render_result_views_free(RenderResult *res) +{ + while (res->views.first) { + RenderView *rv = res->views.first; + BLI_remlink(&res->views, rv); + + if (rv->rect32) + MEM_freeN(rv->rect32); + + if (rv->rectz) + MEM_freeN(rv->rectz); + + if (rv->rectf) + MEM_freeN(rv->rectf); + + MEM_freeN(rv); + } +} + void render_result_free(RenderResult *res) { if (res == NULL) return; while (res->layers.first) { RenderLayer *rl = res->layers.first; - - if (rl->rectf) MEM_freeN(rl->rectf); + /* acolrect and scolrect are optionally allocated in shade_tile, only free here since it can be used for drawing */ if (rl->acolrect) MEM_freeN(rl->acolrect); if (rl->scolrect) MEM_freeN(rl->scolrect); @@ -85,7 +102,9 @@ void render_result_free(RenderResult *res) BLI_remlink(&res->layers, rl); MEM_freeN(rl); } - + + render_result_views_free(res); + if (res->rect32) MEM_freeN(res->rect32); if (res->rectz) @@ -115,13 +134,44 @@ void render_result_free_list(ListBase *lb, RenderResult *rr) } } -/********************************* Names *************************************/ +/********************************* multiview *************************************/ -/* NOTE: OpenEXR only supports 32 chars for layer+pass names - * In blender we now use max 10 chars for pass, max 20 for layer */ -static const char *get_pass_name(int passtype, int channel) +/* create a new views Listbase in rr without duplicating the memory pointers */ +void render_result_views_shallowcopy(RenderResult *dst, RenderResult *src) +{ + RenderView *rview; + + if (dst == NULL || src == NULL) + return; + + for (rview = src->views.first; rview; rview = rview->next) { + RenderView *rv; + + rv = MEM_mallocN(sizeof(RenderView), "new render view"); + BLI_addtail(&dst->views, rv); + + BLI_strncpy(rv->name, rview->name, sizeof(rv->name)); + rv->rectf = rview->rectf; + rv->rectz = rview->rectz; + rv->rect32 = rview->rect32; + } +} + +/* free the views created temporarily */ +void render_result_views_shallowdelete(RenderResult *rr) +{ + if (rr == NULL) + return; + + while (rr->views.first) { + RenderView *rv = rr->views.first; + BLI_remlink(&rr->views, rv); + MEM_freeN(rv); + } +} + +static const char *name_from_passtype(int passtype, int channel) { - if (passtype == SCE_PASS_COMBINED) { if (channel == -1) return "Combined"; if (channel == 0) return "Combined.R"; @@ -308,108 +358,131 @@ static const char *get_pass_name(int passtype, int channel) static int passtype_from_name(const char *str) { - - if (STREQ(str, "Combined")) + if (STRPREFIX(str, "Combined")) return SCE_PASS_COMBINED; - if (STREQ(str, "Depth")) + if (STRPREFIX(str, "Depth")) return SCE_PASS_Z; - if (STREQ(str, "Vector")) + if (STRPREFIX(str, "Vector")) return SCE_PASS_VECTOR; - if (STREQ(str, "Normal")) + if (STRPREFIX(str, "Normal")) return SCE_PASS_NORMAL; - if (STREQ(str, "UV")) + if (STRPREFIX(str, "UV")) return SCE_PASS_UV; - if (STREQ(str, "Color")) + if (STRPREFIX(str, "Color")) return SCE_PASS_RGBA; - if (STREQ(str, "Emit")) + if (STRPREFIX(str, "Emit")) return SCE_PASS_EMIT; - if (STREQ(str, "Diffuse")) + if (STRPREFIX(str, "Diffuse")) return SCE_PASS_DIFFUSE; - if (STREQ(str, "Spec")) + if (STRPREFIX(str, "Spec")) return SCE_PASS_SPEC; - if (STREQ(str, "Shadow")) + if (STRPREFIX(str, "Shadow")) return SCE_PASS_SHADOW; - if (STREQ(str, "AO")) + if (STRPREFIX(str, "AO")) return SCE_PASS_AO; - if (STREQ(str, "Env")) + if (STRPREFIX(str, "Env")) return SCE_PASS_ENVIRONMENT; - if (STREQ(str, "Indirect")) + if (STRPREFIX(str, "Indirect")) return SCE_PASS_INDIRECT; - if (STREQ(str, "Reflect")) + if (STRPREFIX(str, "Reflect")) return SCE_PASS_REFLECT; - if (STREQ(str, "Refract")) + if (STRPREFIX(str, "Refract")) return SCE_PASS_REFRACT; - if (STREQ(str, "IndexOB")) + if (STRPREFIX(str, "IndexOB")) return SCE_PASS_INDEXOB; - if (STREQ(str, "IndexMA")) + if (STRPREFIX(str, "IndexMA")) return SCE_PASS_INDEXMA; - if (STREQ(str, "Mist")) + if (STRPREFIX(str, "Mist")) return SCE_PASS_MIST; - if (STREQ(str, "RayHits")) + if (STRPREFIX(str, "RayHits")) return SCE_PASS_RAYHITS; - if (STREQ(str, "DiffDir")) + if (STRPREFIX(str, "DiffDir")) return SCE_PASS_DIFFUSE_DIRECT; - if (STREQ(str, "DiffInd")) + if (STRPREFIX(str, "DiffInd")) return SCE_PASS_DIFFUSE_INDIRECT; - if (STREQ(str, "DiffCol")) + if (STRPREFIX(str, "DiffCol")) return SCE_PASS_DIFFUSE_COLOR; - if (STREQ(str, "GlossDir")) + if (STRPREFIX(str, "GlossDir")) return SCE_PASS_GLOSSY_DIRECT; - if (STREQ(str, "GlossInd")) + if (STRPREFIX(str, "GlossInd")) return SCE_PASS_GLOSSY_INDIRECT; - if (STREQ(str, "GlossCol")) + if (STRPREFIX(str, "GlossCol")) return SCE_PASS_GLOSSY_COLOR; - if (STREQ(str, "TransDir")) + if (STRPREFIX(str, "TransDir")) return SCE_PASS_TRANSM_DIRECT; - if (STREQ(str, "TransInd")) + if (STRPREFIX(str, "TransInd")) return SCE_PASS_TRANSM_INDIRECT; - if (STREQ(str, "TransCol")) + if (STRPREFIX(str, "TransCol")) return SCE_PASS_TRANSM_COLOR; - if (STREQ(str, "SubsurfaceDir")) + if (STRPREFIX(str, "SubsurfaceDir")) return SCE_PASS_SUBSURFACE_DIRECT; - if (STREQ(str, "SubsurfaceInd")) + if (STRPREFIX(str, "SubsurfaceInd")) return SCE_PASS_SUBSURFACE_INDIRECT; - if (STREQ(str, "SubsurfaceCol")) + if (STRPREFIX(str, "SubsurfaceCol")) return SCE_PASS_SUBSURFACE_COLOR; return 0; } + +static void set_pass_name(char *passname, int passtype, int channel, const char *view) +{ + const char *end; + const char *token; + int len; + + const char *passtype_name = name_from_passtype(passtype, channel); + + if (view == NULL || view[0] == '\0') { + BLI_strncpy(passname, passtype_name, EXR_PASS_MAXNAME); + return; + } + + end = passtype_name + strlen(passtype_name); + len = IMB_exr_split_token(passtype_name, end, &token); + + if (len == strlen(passtype_name)) + sprintf(passname, "%s.%s", passtype_name, view); + else + sprintf(passname, "%.*s%s.%s", (int)(end-passtype_name) - len, passtype_name, view, token); +} + /********************************** New **************************************/ -static RenderPass *render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int channels, int passtype) +static RenderPass *render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int channels, int passtype, const char *viewname) { - const char *typestr = get_pass_name(passtype, 0); + const size_t view_id = BLI_findstringindex(&rr->views, viewname, offsetof(RenderView, name)); + const char *typestr = name_from_passtype(passtype, -1); RenderPass *rpass = MEM_callocN(sizeof(RenderPass), typestr); int rectsize = rr->rectx * rr->recty * channels; @@ -418,12 +491,16 @@ static RenderPass *render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int rpass->channels = channels; rpass->rectx = rl->rectx; rpass->recty = rl->recty; - BLI_strncpy(rpass->name, get_pass_name(rpass->passtype, -1), sizeof(rpass->name)); + rpass->view_id = view_id; + + set_pass_name(rpass->name, rpass->passtype, -1, viewname); + BLI_strncpy(rpass->internal_name, typestr, sizeof(rpass->internal_name)); + BLI_strncpy(rpass->view, viewname, sizeof(rpass->view)); if (rl->exrhandle) { int a; for (a = 0; a < channels; a++) - IMB_exr_add_channel(rl->exrhandle, rl->name, get_pass_name(passtype, a), 0, 0, NULL); + IMB_exr_add_channel(rl->exrhandle, rl->name, name_from_passtype(passtype, a), viewname, 0, 0, NULL); } else { float *rect; @@ -460,9 +537,10 @@ static RenderPass *render_layer_add_debug_pass(RenderResult *rr, RenderLayer *rl, int channels, int pass_type, - int debug_type) + int debug_type, + const char *view) { - RenderPass *rpass = render_layer_add_pass(rr, rl, channels, pass_type); + RenderPass *rpass = render_layer_add_pass(rr, rl, channels, pass_type, view); rpass->debug_type = debug_type; BLI_strncpy(rpass->name, debug_pass_type_name_get(debug_type), @@ -475,12 +553,14 @@ static RenderPass *render_layer_add_debug_pass(RenderResult *rr, /* will read info from Render *re to define layers */ /* called in threads */ /* re->winx,winy is coordinate space of entire image, partrct the part within */ -RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuffers, const char *layername) +RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuffers, const char *layername, const char *viewname) { RenderResult *rr; RenderLayer *rl; + RenderView *rv; SceneRenderLayer *srl; - int rectx, recty, nr; + int rectx, recty; + int nr, i; rectx = BLI_rcti_size_x(partrct); recty = BLI_rcti_size_y(partrct); @@ -505,6 +585,8 @@ RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuf rr->do_exr_tile = true; } + render_result_views_new(rr, &re->r); + /* check renderdata for amount of layers */ for (nr = 0, srl = re->r.layers.first; srl; srl = srl->next, nr++) { @@ -538,84 +620,90 @@ RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuf if (rr->do_exr_tile) { rl->display_buffer = MEM_mapallocN(rectx * recty * sizeof(unsigned int), "Combined display space rgba"); - rl->exrhandle = IMB_exr_get_handle(); - - IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.R", 0, 0, NULL); - IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.G", 0, 0, NULL); - IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.B", 0, 0, NULL); - IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.A", 0, 0, NULL); } - else - rl->rectf = MEM_mapallocN(rectx * recty * sizeof(float) * 4, "Combined rgba"); - - if (srl->passflag & SCE_PASS_Z) - render_layer_add_pass(rr, rl, 1, SCE_PASS_Z); - if (srl->passflag & SCE_PASS_VECTOR) - render_layer_add_pass(rr, rl, 4, SCE_PASS_VECTOR); - if (srl->passflag & SCE_PASS_NORMAL) - render_layer_add_pass(rr, rl, 3, SCE_PASS_NORMAL); - if (srl->passflag & SCE_PASS_UV) - render_layer_add_pass(rr, rl, 3, SCE_PASS_UV); - if (srl->passflag & SCE_PASS_RGBA) - render_layer_add_pass(rr, rl, 4, SCE_PASS_RGBA); - if (srl->passflag & SCE_PASS_EMIT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_EMIT); - if (srl->passflag & SCE_PASS_DIFFUSE) - render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE); - if (srl->passflag & SCE_PASS_SPEC) - render_layer_add_pass(rr, rl, 3, SCE_PASS_SPEC); - if (srl->passflag & SCE_PASS_AO) - render_layer_add_pass(rr, rl, 3, SCE_PASS_AO); - if (srl->passflag & SCE_PASS_ENVIRONMENT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_ENVIRONMENT); - if (srl->passflag & SCE_PASS_INDIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_INDIRECT); - if (srl->passflag & SCE_PASS_SHADOW) - render_layer_add_pass(rr, rl, 3, SCE_PASS_SHADOW); - if (srl->passflag & SCE_PASS_REFLECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_REFLECT); - if (srl->passflag & SCE_PASS_REFRACT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_REFRACT); - if (srl->passflag & SCE_PASS_INDEXOB) - render_layer_add_pass(rr, rl, 1, SCE_PASS_INDEXOB); - if (srl->passflag & SCE_PASS_INDEXMA) - render_layer_add_pass(rr, rl, 1, SCE_PASS_INDEXMA); - if (srl->passflag & SCE_PASS_MIST) - render_layer_add_pass(rr, rl, 1, SCE_PASS_MIST); - if (rl->passflag & SCE_PASS_RAYHITS) - render_layer_add_pass(rr, rl, 4, SCE_PASS_RAYHITS); - if (srl->passflag & SCE_PASS_DIFFUSE_DIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE_DIRECT); - if (srl->passflag & SCE_PASS_DIFFUSE_INDIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE_INDIRECT); - if (srl->passflag & SCE_PASS_DIFFUSE_COLOR) - render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE_COLOR); - if (srl->passflag & SCE_PASS_GLOSSY_DIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_GLOSSY_DIRECT); - if (srl->passflag & SCE_PASS_GLOSSY_INDIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_GLOSSY_INDIRECT); - if (srl->passflag & SCE_PASS_GLOSSY_COLOR) - render_layer_add_pass(rr, rl, 3, SCE_PASS_GLOSSY_COLOR); - if (srl->passflag & SCE_PASS_TRANSM_DIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_TRANSM_DIRECT); - if (srl->passflag & SCE_PASS_TRANSM_INDIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_TRANSM_INDIRECT); - if (srl->passflag & SCE_PASS_TRANSM_COLOR) - render_layer_add_pass(rr, rl, 3, SCE_PASS_TRANSM_COLOR); - if (srl->passflag & SCE_PASS_SUBSURFACE_DIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_DIRECT); - if (srl->passflag & SCE_PASS_SUBSURFACE_INDIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_INDIRECT); - if (srl->passflag & SCE_PASS_SUBSURFACE_COLOR) - render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_COLOR); + + for (rv = rr->views.first; rv; rv = rv->next) { + const char *view = rv->name; + + if (viewname && viewname[0]) + if (!STREQ(view, viewname)) + continue; + + if (rr->do_exr_tile) + IMB_exr_add_view(rl->exrhandle, view); + + /* a renderlayer should always have a Combined pass*/ + render_layer_add_pass(rr, rl, 4, SCE_PASS_COMBINED, view); + + if (srl->passflag & SCE_PASS_Z) + render_layer_add_pass(rr, rl, 1, SCE_PASS_Z, view); + if (srl->passflag & SCE_PASS_VECTOR) + render_layer_add_pass(rr, rl, 4, SCE_PASS_VECTOR, view); + if (srl->passflag & SCE_PASS_NORMAL) + render_layer_add_pass(rr, rl, 3, SCE_PASS_NORMAL, view); + if (srl->passflag & SCE_PASS_UV) + render_layer_add_pass(rr, rl, 3, SCE_PASS_UV, view); + if (srl->passflag & SCE_PASS_RGBA) + render_layer_add_pass(rr, rl, 4, SCE_PASS_RGBA, view); + if (srl->passflag & SCE_PASS_EMIT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_EMIT, view); + if (srl->passflag & SCE_PASS_DIFFUSE) + render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE, view); + if (srl->passflag & SCE_PASS_SPEC) + render_layer_add_pass(rr, rl, 3, SCE_PASS_SPEC, view); + if (srl->passflag & SCE_PASS_AO) + render_layer_add_pass(rr, rl, 3, SCE_PASS_AO, view); + if (srl->passflag & SCE_PASS_ENVIRONMENT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_ENVIRONMENT, view); + if (srl->passflag & SCE_PASS_INDIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_INDIRECT, view); + if (srl->passflag & SCE_PASS_SHADOW) + render_layer_add_pass(rr, rl, 3, SCE_PASS_SHADOW, view); + if (srl->passflag & SCE_PASS_REFLECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_REFLECT, view); + if (srl->passflag & SCE_PASS_REFRACT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_REFRACT, view); + if (srl->passflag & SCE_PASS_INDEXOB) + render_layer_add_pass(rr, rl, 1, SCE_PASS_INDEXOB, view); + if (srl->passflag & SCE_PASS_INDEXMA) + render_layer_add_pass(rr, rl, 1, SCE_PASS_INDEXMA, view); + if (srl->passflag & SCE_PASS_MIST) + render_layer_add_pass(rr, rl, 1, SCE_PASS_MIST, view); + if (rl->passflag & SCE_PASS_RAYHITS) + render_layer_add_pass(rr, rl, 4, SCE_PASS_RAYHITS, view); + if (srl->passflag & SCE_PASS_DIFFUSE_DIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE_DIRECT, view); + if (srl->passflag & SCE_PASS_DIFFUSE_INDIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE_INDIRECT, view); + if (srl->passflag & SCE_PASS_DIFFUSE_COLOR) + render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE_COLOR, view); + if (srl->passflag & SCE_PASS_GLOSSY_DIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_GLOSSY_DIRECT, view); + if (srl->passflag & SCE_PASS_GLOSSY_INDIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_GLOSSY_INDIRECT, view); + if (srl->passflag & SCE_PASS_GLOSSY_COLOR) + render_layer_add_pass(rr, rl, 3, SCE_PASS_GLOSSY_COLOR, view); + if (srl->passflag & SCE_PASS_TRANSM_DIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_TRANSM_DIRECT, view); + if (srl->passflag & SCE_PASS_TRANSM_INDIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_TRANSM_INDIRECT, view); + if (srl->passflag & SCE_PASS_TRANSM_COLOR) + render_layer_add_pass(rr, rl, 3, SCE_PASS_TRANSM_COLOR, view); + if (srl->passflag & SCE_PASS_SUBSURFACE_DIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_DIRECT, view); + if (srl->passflag & SCE_PASS_SUBSURFACE_INDIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_INDIRECT, view); + if (srl->passflag & SCE_PASS_SUBSURFACE_COLOR) + render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_COLOR, view); #ifdef WITH_CYCLES_DEBUG - if (BKE_scene_use_new_shading_nodes(re->scene)) { - render_layer_add_debug_pass(rr, rl, 1, SCE_PASS_DEBUG, - RENDER_PASS_DEBUG_BVH_TRAVERSAL_STEPS); - } + if (BKE_scene_use_new_shading_nodes(re->scene)) { + render_layer_add_debug_pass(rr, rl, 1, SCE_PASS_DEBUG, + RENDER_PASS_DEBUG_BVH_TRAVERSAL_STEPS, view); + } #endif + } } /* sss, previewrender and envmap don't do layers, so we make a default one */ if (BLI_listbase_is_empty(&rr->layers) && !(layername && layername[0])) { @@ -628,18 +716,27 @@ RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuf /* duplicate code... */ if (rr->do_exr_tile) { rl->display_buffer = MEM_mapallocN(rectx * recty * sizeof(unsigned int), "Combined display space rgba"); - rl->exrhandle = IMB_exr_get_handle(); - - IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.R", 0, 0, NULL); - IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.G", 0, 0, NULL); - IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.B", 0, 0, NULL); - IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.A", 0, 0, NULL); } - else { - rl->rectf = MEM_mapallocN(rectx * recty * sizeof(float) * 4, "Combined rgba"); + + for (rv = rr->views.first; rv; rv = rv->next) { + const char *view = rv->name; + + if (viewname && viewname[0]) + if (strcmp(view, viewname) != 0) + continue; + + if (rr->do_exr_tile) { + IMB_exr_add_view(rl->exrhandle, view); + + for (i=0; i < 4; i++) + IMB_exr_add_channel(rl->exrhandle, rl->name, name_from_passtype(SCE_PASS_COMBINED, i), view, 0, 0, NULL); + } + else { + render_layer_add_pass(rr, rl, 4, SCE_PASS_COMBINED, view); + } } - + /* note, this has to be in sync with scene.c */ rl->lay = (1 << 20) - 1; rl->layflag = 0x7FFF; /* solid ztra halo strand */ @@ -657,15 +754,15 @@ RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuf } /* allocate osa new results for samples */ -RenderResult *render_result_new_full_sample(Render *re, ListBase *lb, rcti *partrct, int crop, int savebuffers) +RenderResult *render_result_new_full_sample(Render *re, ListBase *lb, rcti *partrct, int crop, int savebuffers, const char *viewname) { int a; if (re->osa == 0) - return render_result_new(re, partrct, crop, savebuffers, RR_ALL_LAYERS); + return render_result_new(re, partrct, crop, savebuffers, RR_ALL_LAYERS, viewname); for (a = 0; a < re->osa; a++) { - RenderResult *rr = render_result_new(re, partrct, crop, savebuffers, RR_ALL_LAYERS); + RenderResult *rr = render_result_new(re, partrct, crop, savebuffers, RR_ALL_LAYERS, viewname); BLI_addtail(lb, rr); rr->sample_nr = a; } @@ -686,28 +783,98 @@ static void *ml_addlayer_cb(void *base, const char *str) return rl; } -static void ml_addpass_cb(void *UNUSED(base), void *lay, const char *str, float *rect, int totchan, const char *chan_id) +static void ml_addpass_cb(void *base, void *lay, const char *str, float *rect, int totchan, const char *chan_id, const char *view) { + RenderResult *rr = base; RenderLayer *rl = lay; RenderPass *rpass = MEM_callocN(sizeof(RenderPass), "loaded pass"); int a; 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; - BLI_strncpy(rpass->name, str, EXR_PASS_MAXNAME); /* channel id chars */ for (a = 0; a < totchan; a++) rpass->chan_id[a] = chan_id[a]; - + rpass->rect = rect; + if (view[0] != '\0') { + BLI_snprintf(rpass->name, sizeof(rpass->name), "%s.%s", str, view); + rpass->view_id = BLI_findstringindex(&rr->views, view, offsetof(RenderView, name)); + } + else { + BLI_strncpy(rpass->name, str, sizeof(rpass->name)); + rpass->view_id = 0; + } + + BLI_strncpy(rpass->view, view, sizeof(rpass->view)); + BLI_strncpy(rpass->internal_name, str, sizeof(rpass->internal_name)); } -/* from imbuf, if a handle was returned we convert this to render result */ +static void *ml_addview_cb(void *base, const char *str) +{ + RenderResult *rr = base; + RenderView *rv; + + rv = MEM_callocN(sizeof(RenderView), "new render view"); + BLI_strncpy(rv->name, str, EXR_VIEW_MAXNAME); + + /* For stereo drawing we need to ensure: + * STEREO_LEFT_NAME == STEREO_LEFT_ID and + * STEREO_RIGHT_NAME == STEREO_RIGHT_ID */ + + if (STREQ(str, STEREO_LEFT_NAME)) { + BLI_addhead(&rr->views, rv); + } + else if (STREQ(str, STEREO_RIGHT_NAME)) { + RenderView *left_rv = BLI_findstring(&rr->views, STEREO_LEFT_NAME, offsetof(RenderView, name)); + + if (left_rv == NULL) { + BLI_addhead(&rr->views, rv); + } + else { + BLI_insertlinkafter(&rr->views, left_rv, rv); + } + } + else { + BLI_addtail(&rr->views, rv); + } + + return rv; +} + +static int order_render_passes(const void *a, const void *b) +{ + // 1 if a is after b + RenderPass *rpa = (RenderPass *) a; + RenderPass *rpb = (RenderPass *) b; + + if (rpa->passtype > rpb->passtype) + return 1; + else if (rpa->passtype < rpb->passtype) + return 0; + + /* they have the same type */ + /* left first */ + if (STREQ(rpa->view, STEREO_LEFT_NAME)) + return 0; + else if (STREQ(rpb->view, STEREO_LEFT_NAME)) + return 1; + + /* right second */ + if (STREQ(rpa->view, STEREO_RIGHT_NAME)) + return 0; + else if (STREQ(rpb->view, STEREO_RIGHT_NAME)) + return 1; + + /* remaining in ascending id order */ + return (rpa->view_id < rpb->view_id); +} + +/* from imbuf, if a handle was returned and it's not a singlelayer multiview we convert this to render result */ RenderResult *render_result_new_from_exr(void *exrhandle, const char *colorspace, bool predivide, int rectx, int recty) { RenderResult *rr = MEM_callocN(sizeof(RenderResult), __func__); @@ -718,13 +885,17 @@ RenderResult *render_result_new_from_exr(void *exrhandle, const char *colorspace rr->rectx = rectx; rr->recty = recty; - IMB_exr_multilayer_convert(exrhandle, rr, ml_addlayer_cb, ml_addpass_cb); + IMB_exr_multilayer_convert(exrhandle, rr, ml_addview_cb, ml_addlayer_cb, ml_addpass_cb); for (rl = rr->layers.first; rl; rl = rl->next) { + int c=0; rl->rectx = rectx; rl->recty = recty; + BLI_listbase_sort(&rl->passes, order_render_passes); + for (rpass = rl->passes.first; rpass; rpass = rpass->next) { + printf("%d: %s\n", c++, rpass->name); rpass->rectx = rectx; rpass->recty = recty; @@ -738,6 +909,33 @@ RenderResult *render_result_new_from_exr(void *exrhandle, const char *colorspace return rr; } +void render_result_views_new(RenderResult *rr, RenderData *rd) +{ + SceneRenderView *srv; + RenderView *rv; + + /* clear previously existing views - for sequencer */ + render_result_views_free(rr); + + /* check renderdata for amount of views */ + if ((rd->scemode & R_MULTIVIEW)) { + for (srv = rd->views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(rd, srv) == false) continue; + + rv = MEM_callocN(sizeof(RenderView), "new render view"); + BLI_addtail(&rr->views, rv); + + BLI_strncpy(rv->name, srv->name, sizeof(rv->name)); + } + } + + /* we always need at least one view */ + if (BLI_listbase_count_ex(&rr->views, 1) == 0) { + rv = MEM_callocN(sizeof(RenderView), "new render view"); + BLI_addtail(&rr->views, rv); + } +} + /*********************************** Merge ***********************************/ static void do_merge_tile(RenderResult *rr, RenderResult *rrpart, float *target, float *tile, int pixsize) @@ -783,16 +981,19 @@ void render_result_merge(RenderResult *rr, RenderResult *rrpart) for (rl = rr->layers.first; rl; rl = rl->next) { rlp = RE_GetRenderLayer(rrpart, rl->name); if (rlp) { - /* combined */ - if (rl->rectf && rlp->rectf) - do_merge_tile(rr, rrpart, rl->rectf, rlp->rectf, 4); - /* passes are allocated in sync */ for (rpass = rl->passes.first, rpassp = rlp->passes.first; rpass && rpassp; - rpass = rpass->next, rpassp = rpassp->next) + rpass = rpass->next) { + /* renderresult have all passes, renderpart only the active view's passes */ + if (strcmp(rpassp->name, rpass->name) != 0) + continue; + do_merge_tile(rr, rrpart, rpass->rect, rpassp->rect, rpass->channels); + + /* manually get next render pass */ + rpassp = rpassp->next; } } } @@ -801,7 +1002,7 @@ void render_result_merge(RenderResult *rr, RenderResult *rrpart) /* for passes read from files, these have names stored */ static char *make_pass_name(RenderPass *rpass, int chan) { - static char name[16]; + static char name[EXR_PASS_MAXNAME]; int len; BLI_strncpy(name, rpass->name, EXR_PASS_MAXNAME); @@ -813,65 +1014,111 @@ static char *make_pass_name(RenderPass *rpass, int chan) return name; } -/* filename already made absolute */ -/* called from within UI, saves both rendered result as a file-read result */ -bool RE_WriteRenderResult(ReportList *reports, RenderResult *rr, const char *filename, int compress) +/* called from within UI and render pipeline, saves both rendered result as a file-read result + * if multiview is true saves all views in a multiview exr + * else if view is not NULL saves single view + * else saves stereo3d + */ +bool RE_WriteRenderResult(ReportList *reports, RenderResult *rr, const char *filename, ImageFormatData *imf, const bool multiview, const char *view) { RenderLayer *rl; RenderPass *rpass; + RenderView *rview; void *exrhandle = IMB_exr_get_handle(); - bool success; - - BLI_make_existing_file(filename); - - /* composite result */ - if (rr->rectf) { - IMB_exr_add_channel(exrhandle, "Composite", "Combined.R", 4, 4 * rr->rectx, rr->rectf); - IMB_exr_add_channel(exrhandle, "Composite", "Combined.G", 4, 4 * rr->rectx, rr->rectf + 1); - IMB_exr_add_channel(exrhandle, "Composite", "Combined.B", 4, 4 * rr->rectx, rr->rectf + 2); - IMB_exr_add_channel(exrhandle, "Composite", "Combined.A", 4, 4 * rr->rectx, rr->rectf + 3); + bool success = false; + int a, nr; + const char *chan_view = NULL; + int compress = (imf ? imf->exr_codec : 0); + size_t width, height; + + const bool is_mono = view && !multiview; + + width = rr->rectx; + height = rr->recty; + + if (imf && imf->imtype == R_IMF_IMTYPE_OPENEXR && multiview) { + /* single layer OpenEXR */ + const char *RGBAZ[] = {"R", "G", "B", "A", "Z"}; + for (nr = 0, rview = rr->views.first; rview; rview = rview->next, nr++) { + IMB_exr_add_view(exrhandle, rview->name); + + if (rview->rectf) { + for (a = 0; a < 4; a++) + IMB_exr_add_channel(exrhandle, "", RGBAZ[a], + rview->name, 4, 4 * width, rview->rectf + a); + if (rview->rectz) + IMB_exr_add_channel(exrhandle, "", RGBAZ[4], + rview->name, 1, width, rview->rectz); + } + } } - - /* add layers/passes and assign channels */ - for (rl = rr->layers.first; rl; rl = rl->next) { - - /* combined */ - if (rl->rectf) { - int a, xstride = 4; - for (a = 0; a < xstride; a++) { - IMB_exr_add_channel(exrhandle, rl->name, get_pass_name(SCE_PASS_COMBINED, a), - xstride, xstride * rr->rectx, rl->rectf + a); + else { + for (nr = 0, rview = rr->views.first; rview; rview = rview->next, nr++) { + if (is_mono) { + if (!STREQ(view, rview->name)) { + continue; + } + chan_view = ""; + } + else { + /* if rendered only one view, we treat as a a non-view render */ + chan_view = rview->name; + } + + IMB_exr_add_view(exrhandle, rview->name); + + if (rview->rectf) { + for (a = 0; a < 4; a++) + IMB_exr_add_channel(exrhandle, "Composite", name_from_passtype(SCE_PASS_COMBINED, a), + chan_view, 4, 4 * width, rview->rectf + a); } } - - /* passes are allocated in sync */ - for (rpass = rl->passes.first; rpass; rpass = rpass->next) { - int a, xstride = rpass->channels; - for (a = 0; a < xstride; a++) { - if (rpass->passtype) { - IMB_exr_add_channel(exrhandle, rl->name, get_pass_name(rpass->passtype, a), - xstride, xstride * rr->rectx, rpass->rect + a); + + /* add layers/passes and assign channels */ + for (rl = rr->layers.first; rl; rl = rl->next) { + + /* passes are allocated in sync */ + for (rpass = rl->passes.first; rpass; rpass = rpass->next) { + const int xstride = rpass->channels; + + if (is_mono) { + if (!STREQ(view, rpass->view)) { + continue; + } + chan_view = ""; } else { - IMB_exr_add_channel(exrhandle, rl->name, make_pass_name(rpass, a), - xstride, xstride * rr->rectx, rpass->rect + a); + /* if rendered only one view, we treat as a a non-view render */ + chan_view = (nr > 1 ? rpass->view :""); + } + + for (a = 0; a < xstride; a++) { + + if (rpass->passtype) { + IMB_exr_add_channel(exrhandle, rl->name, name_from_passtype(rpass->passtype, a), chan_view, + xstride, xstride * width, rpass->rect + a); + } + else { + IMB_exr_add_channel(exrhandle, rl->name, make_pass_name(rpass, a), chan_view, + xstride, xstride * width, rpass->rect + a); + } } } } } - /* when the filename has no permissions, this can fail */ - if (IMB_exr_begin_write(exrhandle, filename, rr->rectx, rr->recty, compress)) { + BLI_make_existing_file(filename); + + if (IMB_exr_begin_write(exrhandle, filename, width, height, compress)) { IMB_exr_write_channels(exrhandle); - success = true; } else { /* TODO, get the error from openexr's exception */ BKE_report(reports, RPT_ERROR, "Error writing render result (see console)"); success = false; } - IMB_exr_close(exrhandle); + IMB_exr_close(exrhandle); return success; } @@ -932,7 +1179,7 @@ void render_result_single_layer_end(Render *re) /************************* EXR Tile File Rendering ***************************/ -static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart) +static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart, const char *viewname) { RenderLayer *rlp, *rl; RenderPass *rpassp; @@ -955,21 +1202,17 @@ static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart) else { offs = 0; } - - /* combined */ - if (rlp->rectf) { - int a, xstride = 4; - for (a = 0; a < xstride; a++) { - IMB_exr_set_channel(rl->exrhandle, rlp->name, get_pass_name(SCE_PASS_COMBINED, a), - xstride, xstride * rrpart->rectx, rlp->rectf + a + xstride * offs); - } - } - + /* passes are allocated in sync */ for (rpassp = rlp->passes.first; rpassp; rpassp = rpassp->next) { - int a, xstride = rpassp->channels; + const int xstride = rpassp->channels; + int a; + char passname[EXR_PASS_MAXNAME]; + for (a = 0; a < xstride; a++) { - IMB_exr_set_channel(rl->exrhandle, rlp->name, get_pass_name(rpassp->passtype, a), + set_pass_name(passname, rpassp->passtype, a, rpassp->view); + + IMB_exr_set_channel(rl->exrhandle, rlp->name, passname, xstride, xstride * rrpart->rectx, rpassp->rect + a + xstride * offs); } } @@ -988,7 +1231,7 @@ static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart) continue; } - IMB_exrtile_write_channels(rl->exrhandle, partx, party, 0); + IMB_exrtile_write_channels(rl->exrhandle, partx, party, 0, viewname); } BLI_unlock_thread(LOCK_IMAGE); @@ -1002,13 +1245,13 @@ static void save_empty_result_tiles(Render *re) for (rr = re->result; rr; rr = rr->next) { for (rl = rr->layers.first; rl; rl = rl->next) { - IMB_exrtile_clear_channels(rl->exrhandle); + IMB_exr_clear_channels(rl->exrhandle); for (pa = re->parts.first; pa; pa = pa->next) { if (pa->status != PART_STATUS_READY) { int party = pa->disprect.ymin - re->disprect.ymin + pa->crop; int partx = pa->disprect.xmin - re->disprect.xmin + pa->crop; - IMB_exrtile_write_channels(rl->exrhandle, partx, party, 0); + IMB_exrtile_write_channels(rl->exrhandle, partx, party, 0, re->viewname); } } } @@ -1055,10 +1298,10 @@ void render_result_exr_file_end(Render *re) } /* save part into exr file */ -void render_result_exr_file_merge(RenderResult *rr, RenderResult *rrpart) +void render_result_exr_file_merge(RenderResult *rr, RenderResult *rrpart, const char *viewname) { for (; rr && rrpart; rr = rr->next, rrpart = rrpart->next) - save_render_result_tile(rr, rrpart); + save_render_result_tile(rr, rrpart, viewname); } /* path to temporary exr file */ @@ -1088,7 +1331,7 @@ int render_result_exr_file_read_sample(Render *re, int sample) bool success = true; RE_FreeRenderResult(re->result); - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS); + re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); for (rl = re->result->layers.first; rl; rl = rl->next) { render_result_exr_file_path(re->scene, rl->name, sample, str); @@ -1129,23 +1372,20 @@ int render_result_exr_file_read_path(RenderResult *rr, RenderLayer *rl_single, c for (rl = rr->layers.first; rl; rl = rl->next) { if (rl_single && rl_single != rl) continue; - - /* combined */ - if (rl->rectf) { - int a, xstride = 4; - for (a = 0; a < xstride; a++) - IMB_exr_set_channel(exrhandle, rl->name, get_pass_name(SCE_PASS_COMBINED, a), - xstride, xstride * rectx, rl->rectf + a); - } /* passes are allocated in sync */ for (rpass = rl->passes.first; rpass; rpass = rpass->next) { - int a, xstride = rpass->channels; - for (a = 0; a < xstride; a++) - IMB_exr_set_channel(exrhandle, rl->name, get_pass_name(rpass->passtype, a), + const int xstride = rpass->channels; + int a; + char passname[EXR_PASS_MAXNAME]; + + for (a = 0; a < xstride; a++) { + set_pass_name(passname, rpass->passtype, a, rpass->view); + IMB_exr_set_channel(exrhandle, rl->name, passname, xstride, xstride * rectx, rpass->rect + a); + } - BLI_strncpy(rpass->name, get_pass_name(rpass->passtype, -1), sizeof(rpass->name)); + set_pass_name(rpass->name, rpass->passtype, -1, rpass->view); } } @@ -1191,7 +1431,8 @@ void render_result_exr_file_cache_write(Render *re) render_result_exr_file_cache_path(re->scene, root, str); printf("Caching exr file, %dx%d, %s\n", rr->rectx, rr->recty, str); - RE_WriteRenderResult(NULL, rr, str, 0); + + RE_WriteRenderResult(NULL, rr, str, NULL, true, NULL); } /* For cache, makes exact copy of render result */ @@ -1201,7 +1442,7 @@ bool render_result_exr_file_cache_read(Render *re) char *root = U.render_cachedir; RE_FreeRenderResult(re->result); - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS); + re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); /* First try cache. */ render_result_exr_file_cache_path(re->scene, root, str); @@ -1216,14 +1457,14 @@ bool render_result_exr_file_cache_read(Render *re) /*************************** Combined Pixel Rect *****************************/ -ImBuf *render_result_rect_to_ibuf(RenderResult *rr, RenderData *rd) +ImBuf *render_result_rect_to_ibuf(RenderResult *rr, RenderData *rd, const int view_id) { ImBuf *ibuf = IMB_allocImBuf(rr->rectx, rr->recty, rd->im_format.planes, 0); /* if not exists, BKE_imbuf_write makes one */ - ibuf->rect = (unsigned int *)rr->rect32; - ibuf->rect_float = rr->rectf; - ibuf->zbuf_float = rr->rectz; + ibuf->rect = (unsigned int *) RE_RenderViewGetRect32(rr, view_id); + ibuf->rect_float = RE_RenderViewGetRectf(rr, view_id); + ibuf->zbuf_float = RE_RenderViewGetRectz(rr, view_id); /* float factor for random dither, imbuf takes care of it */ ibuf->dither = rd->dither_intensity; @@ -1259,53 +1500,73 @@ ImBuf *render_result_rect_to_ibuf(RenderResult *rr, RenderData *rd) return ibuf; } -void render_result_rect_from_ibuf(RenderResult *rr, RenderData *UNUSED(rd), ImBuf *ibuf) +void render_result_rect_from_ibuf(RenderResult *rr, RenderData *UNUSED(rd), ImBuf *ibuf, const int view_id) { + RenderView *rv = BLI_findlink(&rr->views, view_id); + if (ibuf->rect_float) { - if (!rr->rectf) - rr->rectf = MEM_mallocN(4 * sizeof(float) * rr->rectx * rr->recty, "render_seq rectf"); + if (!rv->rectf) + rv->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); + memcpy(rv->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; + if (rv->rect32) { + MEM_freeN(rv->rect32); + rv->rect32 = NULL; } } else if (ibuf->rect) { - if (!rr->rect32) - rr->rect32 = MEM_mallocN(sizeof(int) * rr->rectx * rr->recty, "render_seq rect"); + if (!rv->rect32) + rv->rect32 = MEM_mallocN(sizeof(int) * rr->rectx * rr->recty, "render_seq rect"); - memcpy(rr->rect32, ibuf->rect, 4 * rr->rectx * rr->recty); + memcpy(rv->rect32, ibuf->rect, 4 * rr->rectx * rr->recty); /* Same things as above, old rectf can hang around from previous render. */ + if (rv->rectf) { + MEM_freeN(rv->rectf); + rv->rectf = NULL; + } + } + + /* clean up non-view buffers */ + if (rr) { if (rr->rectf) { MEM_freeN(rr->rectf); rr->rectf = NULL; } + + if (rr->rect32) { + MEM_freeN(rr->rect32); + rr->rect32 = NULL; + } } } -void render_result_rect_fill_zero(RenderResult *rr) +void render_result_rect_fill_zero(RenderResult *rr, const int view_id) { - 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); + RenderView *rv = BLI_findlink(&rr->views, view_id); + + if (rv->rectf) + memset(rv->rectf, 0, 4 * sizeof(float) * rr->rectx * rr->recty); + else if (rv->rect32) + memset(rv->rect32, 0, 4 * rr->rectx * rr->recty); else - rr->rect32 = MEM_callocN(sizeof(int) * rr->rectx * rr->recty, "render_seq rect"); + rv->rect32 = MEM_callocN(sizeof(int) * rr->rectx * rr->recty, "render_seq rect"); } void render_result_rect_get_pixels(RenderResult *rr, unsigned int *rect, int rectx, int recty, - const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings) + const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, + const int view_id) { if (rr->rect32) { - memcpy(rect, rr->rect32, sizeof(int) * rr->rectx * rr->recty); + int *rect32 = RE_RenderViewGetRect32(rr, view_id); + memcpy(rect, (rect32 ? rect32 : rr->rect32), sizeof(int) * rr->rectx * rr->recty); } else if (rr->rectf) { - IMB_display_buffer_transform_apply((unsigned char *) rect, rr->rectf, rr->rectx, rr->recty, 4, + float *rectf = RE_RenderViewGetRectf(rr, view_id); + IMB_display_buffer_transform_apply((unsigned char *) rect, (rectf ? rectf : rr->rectf), rr->rectx, rr->recty, 4, view_settings, display_settings, true); } else @@ -1313,3 +1574,75 @@ void render_result_rect_get_pixels(RenderResult *rr, unsigned int *rect, int rec memset(rect, 0, sizeof(int) * rectx * recty); } + +/*************************** multiview functions *****************************/ + +bool RE_HasFakeLayer(RenderResult *res) +{ + RenderView *rv; + + if (res == NULL) + return false; + + rv = res->views.first; + if (rv == NULL) + return false; + + return (rv->rect32 || rv->rectf); +} + +bool RE_RenderResult_is_stereo(RenderResult *res) +{ + if (! BLI_findstring(&res->views, STEREO_LEFT_NAME, offsetof(RenderView, name))) + return false; + + if (! BLI_findstring(&res->views, STEREO_RIGHT_NAME, offsetof(RenderView, name))) + return false; + + return true; +} + +void RE_RenderViewSetRectf(RenderResult *res, const int view_id, float *rect) +{ + RenderView *rv = BLI_findlink(&res->views, view_id); + if (rv) { + rv->rectf = rect; + } +} + +void RE_RenderViewSetRectz(RenderResult *res, const int view_id, float *rect) +{ + RenderView *rv = BLI_findlink(&res->views, view_id); + if (rv) { + rv->rectz = rect; + } +} + +float *RE_RenderViewGetRectz(RenderResult *res, const int view_id) +{ + RenderView *rv = BLI_findlink(&res->views, view_id); + if (rv) { + return rv->rectz; + } + return res->rectz; +} + +float *RE_RenderViewGetRectf(RenderResult *res, const int view_id) +{ + RenderView *rv = BLI_findlink(&res->views, view_id); + if (rv) { + return rv->rectf; + } + return res->rectf; +} + +int *RE_RenderViewGetRect32(RenderResult *res, const int view_id) +{ + RenderView *rv = BLI_findlink(&res->views, view_id); + if (rv) { + return rv->rect32; + } + return res->rect32; +} + + diff --git a/source/blender/render/intern/source/rendercore.c b/source/blender/render/intern/source/rendercore.c index 5b054005bac..99611a9c5a8 100644 --- a/source/blender/render/intern/source/rendercore.c +++ b/source/blender/render/intern/source/rendercore.c @@ -181,8 +181,10 @@ static void halo_pixelstruct(HaloRen *har, RenderLayer **rlpp, int totsample, in if (fullsample) { for (sample=0; sample<totsample; sample++) - if (ps->mask & (1 << sample)) - addalphaAddfacFloat(rlpp[sample]->rectf + od*4, col, har->add); + if (ps->mask & (1 << sample)) { + float *pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + addalphaAddfacFloat(pass + od*4, col, har->add); + } } else { fac= ((float)amountm)/(float)R.osa; @@ -214,8 +216,10 @@ static void halo_pixelstruct(HaloRen *har, RenderLayer **rlpp, int totsample, in if (fullsample) { for (sample=0; sample<totsample; sample++) - if (!(mask & (1 << sample))) - addalphaAddfacFloat(rlpp[sample]->rectf + od*4, col, har->add); + if (!(mask & (1 << sample))) { + float *pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + addalphaAddfacFloat(pass + od*4, col, har->add); + } } else { col[0]= accol[0]; @@ -223,8 +227,10 @@ static void halo_pixelstruct(HaloRen *har, RenderLayer **rlpp, int totsample, in col[2]= accol[2]; col[3]= accol[3]; - for (sample=0; sample<totsample; sample++) - addalphaAddfacFloat(rlpp[sample]->rectf + od*4, col, har->add); + for (sample=0; sample<totsample; sample++) { + float *pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + addalphaAddfacFloat(pass + od*4, col, har->add); + } } } @@ -305,8 +311,10 @@ static void halo_tile(RenderPart *pa, RenderLayer *rl) zz= calchalo_z(har, *rz); if ((zz> har->zs) || (har->mat && (har->mat->mode & MA_HALO_SOFT))) { if (shadeHaloFloat(har, col, zz, dist, xn, yn, har->flarec)) { - for (sample=0; sample<totsample; sample++) - addalphaAddfacFloat(rlpp[sample]->rectf + od*4, col, har->add); + for (sample=0; sample<totsample; sample++) { + float * rect= RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + addalphaAddfacFloat(rect + od*4, col, har->add); + } } } } @@ -359,7 +367,8 @@ static void lamphalo_tile(RenderPart *pa, RenderLayer *rl) if (fullsample) { for (sample=0; sample<totsample; sample++) { if (ps->mask & (1 << sample)) { - pass= rlpp[sample]->rectf + od*4; + pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + pass += od * 4; pass[0]+= col[0]; pass[1]+= col[1]; pass[2]+= col[2]; @@ -370,7 +379,8 @@ static void lamphalo_tile(RenderPart *pa, RenderLayer *rl) } else { fac= ((float)count)/(float)R.osa; - pass= rl->rectf + od*4; + pass = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, R.viewname); + pass += od * 4; pass[0]+= fac*col[0]; pass[1]+= fac*col[1]; pass[2]+= fac*col[2]; @@ -390,7 +400,9 @@ static void lamphalo_tile(RenderPart *pa, RenderLayer *rl) if (fullsample) { for (sample=0; sample<totsample; sample++) { if (!(mask & (1 << sample))) { - pass= rlpp[sample]->rectf + od*4; + + pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + pass += od * 4; pass[0]+= col[0]; pass[1]+= col[1]; pass[2]+= col[2]; @@ -401,7 +413,8 @@ static void lamphalo_tile(RenderPart *pa, RenderLayer *rl) } else { fac= ((float)R.osa-totsamp)/(float)R.osa; - pass= rl->rectf + od*4; + pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + pass += od * 4; pass[0]+= fac*col[0]; pass[1]+= fac*col[1]; pass[2]+= fac*col[2]; @@ -420,7 +433,8 @@ static void lamphalo_tile(RenderPart *pa, RenderLayer *rl) renderspothalo(&shi, col, 1.0f); for (sample=0; sample<totsample; sample++) { - pass= rlpp[sample]->rectf + od*4; + pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + pass += od * 4; pass[0]+= col[0]; pass[1]+= col[1]; pass[2]+= col[2]; @@ -444,14 +458,14 @@ static void add_filt_passes(RenderLayer *rl, int curmask, int rectx, int offset, { RenderPass *rpass; - /* combined rgb */ - add_filt_fmask(curmask, shr->combined, rl->rectf + 4*offset, rectx); - for (rpass= rl->passes.first; rpass; rpass= rpass->next) { float *fp, *col= NULL; int pixsize= 3; switch (rpass->passtype) { + case SCE_PASS_COMBINED: + add_filt_fmask(curmask, shr->combined, rpass->rect + 4*offset, rectx); + break; case SCE_PASS_Z: fp= rpass->rect + offset; *fp= shr->z; @@ -555,15 +569,16 @@ static void add_passes(RenderLayer *rl, int offset, ShadeInput *shi, ShadeResult { RenderPass *rpass; float *fp; - - fp= rl->rectf + 4*offset; - copy_v4_v4(fp, shr->combined); - + for (rpass= rl->passes.first; rpass; rpass= rpass->next) { float *col= NULL, uvcol[3]; int a, pixsize= 3; switch (rpass->passtype) { + case SCE_PASS_COMBINED: + /* copy combined to use for preview */ + copy_v4_v4(rpass->rect + 4*offset, shr->combined); + break; case SCE_PASS_Z: fp= rpass->rect + offset; *fp= shr->z; @@ -681,7 +696,8 @@ static void sky_tile(RenderPart *pa, RenderLayer *rl) bool done = false; for (sample= 0; sample<totsample; sample++) { - float *pass= rlpp[sample]->rectf + od; + float *pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + pass += od; if (pass[3]<1.0f) { @@ -742,8 +758,8 @@ static void atm_tile(RenderPart *pa, RenderLayer *rl) int sample; for (sample=0; sample<totsample; sample++) { - const float *zrect= RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_Z) + od; - float *rgbrect = rlpp[sample]->rectf + 4*od; + const float *zrect = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_Z, R.viewname) + od; + float *rgbrect = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname) + 4*od; float rgb[3] = {0}; bool done = false; @@ -978,8 +994,8 @@ static void clamp_alpha_rgb_range(RenderPart *pa, RenderLayer *rl) return; for (sample= 0; sample<totsample; sample++) { - float *rectf= rlpp[sample]->rectf; - + float *rectf = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + for (y= pa->rectx*pa->recty; y>0; y--, rectf+=4) { rectf[0] = MAX2(rectf[0], 0.0f); rectf[1] = MAX2(rectf[1], 0.0f); @@ -1060,7 +1076,7 @@ static void reset_sky_speed(RenderPart *pa, RenderLayer *rl) totsample= get_sample_layers(pa, rl, rlpp); for (sample= 0; sample<totsample; sample++) { - fp= RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_VECTOR); + fp= RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_VECTOR, R.viewname); if (fp==NULL) break; for (a= 4*pa->rectx*pa->recty - 1; a>=0; a--) @@ -1171,6 +1187,8 @@ void zbufshadeDA_tile(RenderPart *pa) pa->rectp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectp"); pa->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz"); for (rl= rr->layers.first; rl; rl= rl->next) { + float *rect = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, R.viewname); + if ((rl->layflag & SCE_LAY_ZMASK) && (rl->layflag & SCE_LAY_NEG_ZMASK)) pa->rectmask= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectmask"); @@ -1211,7 +1229,7 @@ void zbufshadeDA_tile(RenderPart *pa) if (R.flag & R_ZTRA || R.totstrand) { if (rl->layflag & (SCE_LAY_ZTRA|SCE_LAY_STRAND)) { if (pa->fullresult.first) { - zbuffer_transp_shade(pa, rl, rl->rectf, &psmlist); + zbuffer_transp_shade(pa, rl, rect, &psmlist); } else { unsigned short *ztramask, *solidmask= NULL; /* 16 bits, MAX_OSA */ @@ -1220,9 +1238,9 @@ void zbufshadeDA_tile(RenderPart *pa) rl->acolrect= MEM_callocN(4*sizeof(float)*pa->rectx*pa->recty, "alpha layer"); /* swap for live updates, and it is used in zbuf.c!!! */ - SWAP(float *, rl->acolrect, rl->rectf); - ztramask= zbuffer_transp_shade(pa, rl, rl->rectf, &psmlist); - SWAP(float *, rl->acolrect, rl->rectf); + SWAP(float *, rl->acolrect, rect); + ztramask = zbuffer_transp_shade(pa, rl, rect, &psmlist); + SWAP(float *, rl->acolrect, rect); /* zbuffer transp only returns ztramask if there's solid rendered */ if (ztramask) @@ -1231,7 +1249,8 @@ void zbufshadeDA_tile(RenderPart *pa) if (ztramask && solidmask) { unsigned short *sps= solidmask, *spz= ztramask; unsigned short fullmask= (1<<R.osa)-1; - float *fcol= rl->rectf; float *acol= rl->acolrect; + float *fcol= rect; + float *acol= rl->acolrect; int x; for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4, sps++, spz++) { @@ -1242,7 +1261,8 @@ void zbufshadeDA_tile(RenderPart *pa) } } else { - float *fcol= rl->rectf; float *acol= rl->acolrect; + float *fcol= rect; + float *acol= rl->acolrect; int x; for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4) { addAlphaOverFloat(fcol, acol); @@ -1265,7 +1285,7 @@ void zbufshadeDA_tile(RenderPart *pa) /* extra layers */ if (rl->layflag & SCE_LAY_EDGE) if (R.r.mode & R_EDGE) - edge_enhance_add(pa, rl->rectf, edgerect); + edge_enhance_add(pa, rect, edgerect); if (rl->passflag & SCE_PASS_VECTOR) reset_sky_speed(pa, rl); @@ -1319,6 +1339,7 @@ void zbufshade_tile(RenderPart *pa) pa->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz"); for (rl= rr->layers.first; rl; rl= rl->next) { + float *rect= RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, R.viewname); if ((rl->layflag & SCE_LAY_ZMASK) && (rl->layflag & SCE_LAY_NEG_ZMASK)) pa->rectmask= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectmask"); @@ -1342,7 +1363,7 @@ void zbufshade_tile(RenderPart *pa) rr->renlay= rl; if (rl->layflag & SCE_LAY_SOLID) { - const float *fcol= rl->rectf; + const float *fcol = rect; const int *ro= pa->recto, *rp= pa->rectp, *rz= pa->rectz; int x, y, offs=0, seed; @@ -1405,11 +1426,11 @@ void zbufshade_tile(RenderPart *pa) rl->acolrect= MEM_callocN(4*sizeof(float)*pa->rectx*pa->recty, "alpha layer"); /* swap for live updates */ - SWAP(float *, rl->acolrect, rl->rectf); - zbuffer_transp_shade(pa, rl, rl->rectf, NULL); - SWAP(float *, rl->acolrect, rl->rectf); + SWAP(float *, rl->acolrect, rect); + zbuffer_transp_shade(pa, rl, rect, NULL); + SWAP(float *, rl->acolrect, rect); - fcol= rl->rectf; acol= rl->acolrect; + fcol= rect; acol= rl->acolrect; for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4) { addAlphaOverFloat(fcol, acol); } @@ -1427,7 +1448,7 @@ void zbufshade_tile(RenderPart *pa) if (!R.test_break(R.tbh)) { if (rl->layflag & SCE_LAY_EDGE) if (R.r.mode & R_EDGE) - edge_enhance_add(pa, rl->rectf, edgerect); + edge_enhance_add(pa, rect, edgerect); } if (rl->passflag & SCE_PASS_VECTOR) @@ -1654,7 +1675,7 @@ void zbufshade_sss_tile(RenderPart *pa) return; } - fcol= rl->rectf; + fcol= RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, R.viewname); co= MEM_mallocN(sizeof(float)*3*handle.totps, "SSSCo"); color= MEM_mallocN(sizeof(float)*3*handle.totps, "SSSColor"); @@ -1938,6 +1959,7 @@ void add_halo_flare(Render *re) RenderLayer *rl; HaloRen *har; int a, mode; + float *rect; /* for now, we get the first renderlayer in list with halos set */ for (rl= rr->layers.first; rl; rl= rl->next) { @@ -1945,8 +1967,11 @@ void add_halo_flare(Render *re) if ((rl->layflag & SCE_LAY_HALO) == 0) continue; - if (rl->rectf==NULL) - continue; + + rect = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, re->viewname); + + if (rl==NULL || rect) + return; mode= R.r.mode; R.r.mode &= ~R_PANORAMA; @@ -1958,7 +1983,7 @@ void add_halo_flare(Render *re) if (har->flarec && (har->lay & rl->lay)) { do_draw = true; - renderflare(rr, rl->rectf, har); + renderflare(rr, rect, har); } } diff --git a/source/blender/render/intern/source/zbuf.c b/source/blender/render/intern/source/zbuf.c index d5d8595bf3f..b6628796e0d 100644 --- a/source/blender/render/intern/source/zbuf.c +++ b/source/blender/render/intern/source/zbuf.c @@ -3959,7 +3959,7 @@ static void reset_sky_speedvectors(RenderPart *pa, RenderLayer *rl, float *rectf float *fp, *col; int a; - fp= RE_RenderLayerGetPass(rl, SCE_PASS_VECTOR); + fp = RE_RenderLayerGetPass(rl, SCE_PASS_VECTOR, R.viewname); if (fp==NULL) return; col= rectf+3; @@ -4052,9 +4052,10 @@ unsigned short *zbuffer_transp_shade(RenderPart *pa, RenderLayer *rl, float *pas /* zero alpha pixels get speed vector max again */ if (addpassflag & SCE_PASS_VECTOR) - if (rl->layflag & SCE_LAY_SOLID) - reset_sky_speedvectors(pa, rl, rl->acolrect?rl->acolrect:rl->rectf); /* if acolrect is set we use it */ - + if (rl->layflag & SCE_LAY_SOLID) { + float *rect = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, R.viewname); + reset_sky_speedvectors(pa, rl, rl->acolrect ? rl->acolrect : rect); /* if acolrect is set we use it */ + } /* filtered render, for now we assume only 1 filter size */ if (pa->crop) { crop= 1; @@ -4239,8 +4240,9 @@ unsigned short *zbuffer_transp_shade(RenderPart *pa, RenderLayer *rl, float *pas alpha= samp_shr[a].combined[3]; if (alpha!=0.0f) { RenderLayer *rl= ssamp.rlpp[a]; - - addAlphaOverFloat(rl->rectf + 4*od, samp_shr[a].combined); + + float *rect = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, R.viewname); + addAlphaOverFloat(rect + 4 * od, samp_shr[a].combined); add_transp_passes(rl, od, &samp_shr[a], alpha); if (addpassflag & SCE_PASS_VECTOR) |