diff options
Diffstat (limited to 'source/blender/editors')
34 files changed, 1722 insertions, 308 deletions
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index b4f7d806079..90b0b3510bc 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -112,6 +112,7 @@ void ED_screen_full_prevspace(struct bContext *C, ScrArea *sa); void ED_screen_full_restore(struct bContext *C, ScrArea *sa); struct ScrArea *ED_screen_state_toggle(struct bContext *C, struct wmWindow *win, struct ScrArea *sa, const short state); void ED_screens_header_tools_menu_create(struct bContext *C, struct uiLayout *layout, void *arg); +bool ED_screen_stereo3d_required(struct bScreen *screen); /* anim */ void ED_update_for_newframe(struct Main *bmain, struct Scene *scene, int mute); diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 84ac3a7c938..ff4b8a5b631 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -323,12 +323,13 @@ void ED_view3d_draw_offscreen( struct Scene *scene, struct View3D *v3d, struct ARegion *ar, int winx, int winy, float viewmat[4][4], float winmat[4][4], bool do_bgpic, bool do_sky, bool is_persp, struct GPUOffScreen *ofs, - struct GPUFX *fx, struct GPUFXSettings *fx_settings); + struct GPUFX *fx, struct GPUFXSettings *fx_settings, + const char *viewname); struct ImBuf *ED_view3d_draw_offscreen_imbuf(struct Scene *scene, struct View3D *v3d, struct ARegion *ar, int sizex, int sizey, unsigned int flag, - bool draw_background, int alpha_mode, char err_out[256]); + bool draw_background, int alpha_mode, const char *viewname, char err_out[256]); struct ImBuf *ED_view3d_draw_offscreen_imbuf_simple(struct Scene *scene, struct Object *camera, int width, int height, unsigned int flag, int drawtype, - bool use_solid_tex, bool use_gpencil, bool draw_background, int alpha_mode, char err_out[256]); + bool use_solid_tex, bool use_gpencil, bool draw_background, int alpha_mode, const char *viewname, char err_out[256]); struct Base *ED_view3d_give_base_under_cursor(struct bContext *C, const int mval[2]); void ED_view3d_quadview_update(struct ScrArea *sa, struct ARegion *ar, bool do_clip); diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 814bbf94c79..4ca0be7c80d 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -889,6 +889,9 @@ void uiTemplateGameStates(uiLayout *layout, struct PointerRNA *ptr, const char * PointerRNA *used_ptr, const char *used_propname, int active_state); void uiTemplateImage(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname, struct PointerRNA *userptr, int compact); void uiTemplateImageSettings(uiLayout *layout, struct PointerRNA *imfptr, int color_management); +void uiTemplateImageStereo3d(uiLayout *layout, struct PointerRNA *stereo3d_format_ptr); +void uiTemplateImageViews(uiLayout *layout, struct PointerRNA *imaptr); +void uiTemplateImageFormatViews(uiLayout *layout, PointerRNA *imfptr, PointerRNA *ptr); void uiTemplateImageLayers(uiLayout *layout, struct bContext *C, struct Image *ima, struct ImageUser *iuser); void uiTemplateImageInfo(uiLayout *layout, struct bContext *C, Image *ima, ImageUser *iuser); void uiTemplateRunningJobs(uiLayout *layout, struct bContext *C); diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c index 41d2a90e548..72abfcb9293 100644 --- a/source/blender/editors/interface/interface_ops.c +++ b/source/blender/editors/interface/interface_ops.c @@ -613,6 +613,7 @@ static int editsource_exec(bContext *C, wmOperator *op) /* redraw and get active button python info */ ED_region_do_draw(C, ar); + ar->do_draw = false; for (BLI_ghashIterator_init(&ghi, ui_editsource_info->hash); BLI_ghashIterator_done(&ghi) == false; diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index fe9ee71aa8e..d492a4b9dda 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -929,7 +929,7 @@ cage_cleanup: BakeData *bake = &scene->r.bake; char name[FILE_MAX]; - BKE_image_path_from_imtype(name, filepath, bmain->name, 0, bake->im_format.imtype, true, false); + BKE_image_path_from_imtype(name, filepath, bmain->name, 0, bake->im_format.imtype, true, false, NULL); if (is_automatic_name) { BLI_path_suffix(name, FILE_MAX, ob_low->id.name + 2, "_"); diff --git a/source/blender/editors/render/render_intern.h b/source/blender/editors/render/render_intern.h index ee0046b49dc..5c6744b0248 100644 --- a/source/blender/editors/render/render_intern.h +++ b/source/blender/editors/render/render_intern.h @@ -55,6 +55,9 @@ void MATERIAL_OT_paste(struct wmOperatorType *ot); void SCENE_OT_render_layer_add(struct wmOperatorType *ot); void SCENE_OT_render_layer_remove(struct wmOperatorType *ot); +void SCENE_OT_render_view_add(struct wmOperatorType *ot); +void SCENE_OT_render_view_remove(struct wmOperatorType *ot); + #ifdef WITH_FREESTYLE void SCENE_OT_freestyle_module_add(struct wmOperatorType *ot); void SCENE_OT_freestyle_module_remove(struct wmOperatorType *ot); diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index 1c9f32697d4..f76f7cad1ae 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -48,6 +48,7 @@ #include "DNA_userdef_types.h" #include "BKE_blender.h" +#include "BKE_camera.h" #include "BKE_context.h" #include "BKE_colortools.h" #include "BKE_depsgraph.h" @@ -116,7 +117,7 @@ typedef struct RenderJob { } RenderJob; /* called inside thread! */ -static void image_buffer_rect_update(RenderJob *rj, RenderResult *rr, ImBuf *ibuf, ImageUser *iuser, volatile rcti *renrect) +static void image_buffer_rect_update(RenderJob *rj, RenderResult *rr, ImBuf *ibuf, ImageUser *iuser, volatile rcti *renrect, const char *viewname) { Scene *scene = rj->scene; const float *rectf = NULL; @@ -188,11 +189,11 @@ static void image_buffer_rect_update(RenderJob *rj, RenderResult *rr, ImBuf *ibu */ /* TODO(sergey): Need to check has_combined here? */ if (iuser->pass == 0) { + size_t view_id = BKE_scene_multiview_view_id_get(&scene->r, viewname); /* find current float rect for display, first case is after composite... still weak */ - if (rr->rectf) - rectf = rr->rectf; - else { - if (rr->rect32) { + rectf = RE_RenderViewGetRectf(rr, view_id); + if (rectf == NULL) { + if (RE_RenderViewGetRect32(rr, view_id)) { /* special case, currently only happens with sequencer rendering, * which updates the whole frame, so we can only mark display buffer * as invalid here (sergey) @@ -201,8 +202,8 @@ static void image_buffer_rect_update(RenderJob *rj, RenderResult *rr, ImBuf *ibu return; } else { - if (rr->renlay == NULL || rr->renlay->rectf == NULL) return; - rectf = rr->renlay->rectf; + if (rr->renlay == NULL) return; + rectf = RE_RenderLayerGetPass(rr->renlay, SCE_PASS_COMBINED, viewname); } } if (rectf == NULL) return; @@ -531,6 +532,7 @@ static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrec Image *ima = rj->image; ImBuf *ibuf; void *lock; + const char *viewname = RE_GetActiveRenderView(rj->re); /* only update if we are displaying the slot being rendered */ if (ima->render_slot != ima->last_render_slot) { @@ -563,7 +565,7 @@ static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrec ibuf->channels == 1 || U.image_draw_method != IMAGE_DRAW_METHOD_GLSL) { - image_buffer_rect_update(rj, rr, ibuf, &rj->iuser, renrect); + image_buffer_rect_update(rj, rr, ibuf, &rj->iuser, renrect, viewname); } /* make jobs timer to send notifier */ @@ -1487,7 +1489,8 @@ void render_view3d_draw(RenderEngine *engine, const bContext *C) if (re == NULL) return; } - RE_AcquireResultImage(re, &rres); + /* Viewport render preview doesn't support multiview, view hardcoded to 0 */ + RE_AcquireResultImage(re, &rres, 0); if (rres.rectf) { RegionView3D *rv3d = CTX_wm_region_view3d(C); diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c index 5c8bee07420..b59fd10870e 100644 --- a/source/blender/editors/render/render_opengl.c +++ b/source/blender/editors/render/render_opengl.c @@ -39,6 +39,7 @@ #include "BLI_blenlib.h" #include "BLI_utildefines.h" #include "BLI_jitter.h" +#include "BLI_threads.h" #include "DNA_scene_types.h" #include "DNA_object_types.h" @@ -103,11 +104,17 @@ typedef struct OGLRender { bMovieHandle *mh; int cfrao, nfra; + size_t totvideos; + + /* quick lookup */ + int view_id; + /* wm vars for timer and progress cursor */ wmWindowManager *wm; wmWindow *win; wmTimer *timer; /* use to check if running modal or not (invoke'd or exec'd)*/ + void **movie_ctx_arr; } OGLRender; /* added because v3d is not always valid */ @@ -121,16 +128,125 @@ static unsigned int screen_opengl_layers(OGLRender *oglrender) } } -static void screen_opengl_render_apply(OGLRender *oglrender) +static bool screen_opengl_is_multiview(OGLRender *oglrender) +{ + View3D *v3d = oglrender->v3d; + RegionView3D *rv3d = oglrender->rv3d; + RenderData *rd = &oglrender->scene->r; + + if ((rd == NULL) || ((!oglrender->is_sequencer) && ((rv3d == NULL) || (v3d == NULL)))) + return false; + + return (rd->scemode & R_MULTIVIEW) && ((oglrender->is_sequencer) || (rv3d->persp == RV3D_CAMOB && v3d->camera)); +} + +static void screen_opengl_views_setup(OGLRender *oglrender) +{ + RenderResult *rr; + RenderView *rv; + SceneRenderView *srv; + bool is_multiview; + View3D *v3d = oglrender->v3d; + + RenderData *rd = &oglrender->scene->r; + + rr = RE_AcquireResultWrite(oglrender->re); + + is_multiview = screen_opengl_is_multiview(oglrender); + + if (!is_multiview) { + /* we only have one view when multiview is off */ + rv = rr->views.first; + + if (rv == NULL) { + rv = MEM_callocN(sizeof(RenderView), "new opengl render view"); + BLI_addtail(&rr->views, rv); + } + + while (rv->next) { + RenderView *rv_del = rv->next; + BLI_remlink(&rr->views, rv_del); + + if (rv_del->rectf) + MEM_freeN(rv_del->rectf); + + if (rv_del->rectz) + MEM_freeN(rv_del->rectz); + + MEM_freeN(rv_del); + } + } + else { + if (!oglrender->is_sequencer) + RE_SetOverrideCamera(oglrender->re, V3D_CAMERA_SCENE(oglrender->scene, v3d)); + + /* remove all the views that are not needed */ + rv = rr->views.last; + while (rv) { + srv = BLI_findstring(&rd->views, rv->name, offsetof(SceneRenderView, name)); + if (BKE_scene_multiview_is_render_view_active(rd, srv)) { + if (rv->rectf == NULL) + rv->rectf = MEM_callocN(sizeof(float) * 4 * oglrender->sizex * oglrender->sizey, "screen_opengl_render_init rect"); + rv = rv->prev; + } + else { + RenderView *rv_del = rv; + rv = rv_del->prev; + + BLI_remlink(&rr->views, rv_del); + + if (rv_del->rectf) + MEM_freeN(rv_del->rectf); + + if (rv_del->rectz) + MEM_freeN(rv_del->rectz); + + MEM_freeN(rv_del); + } + } + + /* create all the views that are needed */ + for (srv = rd->views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(rd, srv) == false) + continue; + + rv = BLI_findstring(&rr->views, srv->name, offsetof(SceneRenderView, name)); + + if (rv == NULL) { + rv = MEM_callocN(sizeof(RenderView), "new opengl render view"); + BLI_strncpy(rv->name, srv->name, sizeof(rv->name)); + BLI_addtail(&rr->views, rv); + } + } + } + + for (rv = rr->views.first; rv; rv = rv->next) { + if (rv->rectf == NULL) { + rv->rectf = MEM_callocN(sizeof(float) * 4 * oglrender->sizex * oglrender->sizey, "screen_opengl_render_init rect"); + } + } + + BLI_lock_thread(LOCK_DRAW_IMAGE); + if (is_multiview && BKE_scene_multiview_is_stereo3d(rd)) { + oglrender->ima->flag |= IMA_IS_STEREO; + } + else { + oglrender->ima->flag &= ~IMA_IS_STEREO; + oglrender->iuser.flag &= ~IMA_SHOW_STEREO; + } + BLI_unlock_thread(LOCK_DRAW_IMAGE); + + RE_ReleaseResult(oglrender->re); +} + +static void screen_opengl_render_doit(OGLRender *oglrender, RenderResult *rr) { Scene *scene = oglrender->scene; ARegion *ar = oglrender->ar; View3D *v3d = oglrender->v3d; RegionView3D *rv3d = oglrender->rv3d; - RenderResult *rr; Object *camera = NULL; ImBuf *ibuf; - void *lock; float winmat[4][4]; int sizex = oglrender->sizex; int sizey = oglrender->sizey; @@ -138,8 +254,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) bool draw_bgpic = true; bool draw_sky = (scene->r.alphamode == R_ADDSKY); unsigned char *rect = NULL; - - rr = RE_AcquireResultRead(oglrender->re); + const char *viewname = RE_GetActiveRenderView(oglrender->re); if (oglrender->is_sequencer) { SeqRenderData context; @@ -152,9 +267,11 @@ static void screen_opengl_render_apply(OGLRender *oglrender) oglrender->sizex, oglrender->sizey, 100.0f, &context); + context.view_id = BKE_scene_multiview_view_id_get(&scene->r, viewname); ibuf = BKE_sequencer_give_ibuf(&context, CFRA, chanshown); if (ibuf) { + float *rectf; ImBuf *linear_ibuf; BLI_assert((oglrender->sizex == ibuf->x) && (oglrender->sizey == ibuf->y)); @@ -175,7 +292,8 @@ static void screen_opengl_render_apply(OGLRender *oglrender) BKE_sequencer_imbuf_from_sequencer_space(scene, linear_ibuf); } - memcpy(rr->rectf, linear_ibuf->rect_float, sizeof(float) * 4 * oglrender->sizex * oglrender->sizey); + rectf = RE_RenderViewGetRectf(rr, oglrender->view_id); + memcpy(rectf, linear_ibuf->rect_float, sizeof(float) * 4 * oglrender->sizex * oglrender->sizey); IMB_freeImBuf(linear_ibuf); } @@ -221,7 +339,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) /* render 3d view */ if (rv3d->persp == RV3D_CAMOB && v3d->camera) { /*int is_ortho = scene->r.mode & R_ORTHO;*/ - camera = v3d->camera; + camera = BKE_camera_multiview_render(oglrender->scene, v3d->camera, viewname); RE_GetCameraWindow(oglrender->re, camera, scene->r.cfra, winmat); if (camera->type == OB_CAMERA) { Camera *cam = camera->data; @@ -248,7 +366,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) ED_view3d_draw_offscreen( scene, v3d, ar, sizex, sizey, NULL, winmat, draw_bgpic, draw_sky, is_persp, - oglrender->ofs, oglrender->fx, &fx_settings); + oglrender->ofs, oglrender->fx, &fx_settings, viewname); GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, rect); } else { @@ -264,7 +382,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) ED_view3d_draw_offscreen( scene, v3d, ar, sizex, sizey, NULL, winmat, draw_bgpic, draw_sky, is_persp, - oglrender->ofs, oglrender->fx, &fx_settings); + oglrender->ofs, oglrender->fx, &fx_settings, viewname); GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, rect); for (i = 0; i < sizex * sizey * 4; i++) @@ -280,7 +398,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) ED_view3d_draw_offscreen( scene, v3d, ar, sizex, sizey, NULL, winmat_jitter, draw_bgpic, draw_sky, is_persp, - oglrender->ofs, oglrender->fx, &fx_settings); + oglrender->ofs, oglrender->fx, &fx_settings, viewname); GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, rect); for (i = 0; i < sizex * sizey * 4; i++) @@ -300,7 +418,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) char err_out[256] = "unknown"; ImBuf *ibuf_view = ED_view3d_draw_offscreen_imbuf_simple(scene, scene->camera, oglrender->sizex, oglrender->sizey, IB_rect, OB_SOLID, false, true, true, - (draw_sky) ? R_ADDSKY : R_ALPHAPREMUL, err_out); + (draw_sky) ? R_ADDSKY : R_ALPHAPREMUL, viewname, err_out); camera = scene->camera; if (ibuf_view) { @@ -325,6 +443,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender) if (rect) { int profile_to; + float *rectf = RE_RenderViewGetRectf(rr, oglrender->view_id); if (BKE_scene_check_color_management_enabled(scene)) profile_to = IB_PROFILE_LINEAR_RGB; @@ -333,47 +452,60 @@ static void screen_opengl_render_apply(OGLRender *oglrender) /* sequencer has got trickier conversion happened above * also assume opengl's space matches byte buffer color space */ - IMB_buffer_float_from_byte(rr->rectf, rect, + IMB_buffer_float_from_byte(rectf, rect, profile_to, IB_PROFILE_SRGB, true, oglrender->sizex, oglrender->sizey, oglrender->sizex, oglrender->sizex); - } - /* rr->rectf is now filled with image data */ + /* rr->rectf is now filled with image data */ - if ((scene->r.stamp & R_STAMP_ALL) && (scene->r.stamp & R_STAMP_DRAW)) { - BKE_image_stamp_buf(scene, camera, rect, rr->rectf, rr->rectx, rr->recty, 4); + if ((scene->r.stamp & R_STAMP_ALL) && (scene->r.stamp & R_STAMP_DRAW)) + BKE_image_stamp_buf(scene, camera, rect, rectf, rr->rectx, rr->recty, 4); + + MEM_freeN(rect); } +} - RE_ReleaseResult(oglrender->re); +static void screen_opengl_render_write(OGLRender *oglrender) +{ + Scene *scene = oglrender->scene; + RenderResult *rr; + bool ok; + char name[FILE_MAX]; + Object *camera = RE_GetCamera(oglrender->re); + + rr = RE_AcquireResultRead(oglrender->re); - /* update byte from float buffer */ - ibuf = BKE_image_acquire_ibuf(oglrender->ima, &oglrender->iuser, &lock); + BKE_image_path_from_imformat( + name, scene->r.pic, oglrender->bmain->name, scene->r.cfra, + &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, false, NULL); - if (ibuf) { - ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; + /* write images as individual images or stereo */ + ok = RE_WriteRenderViewsImage(oglrender->reports, rr, scene, camera, false, name); - /* write file for animation */ - if (oglrender->write_still) { - char name[FILE_MAX]; - int ok; + RE_ReleaseResultImage(oglrender->re); - if (scene->r.im_format.planes == R_IMF_CHAN_DEPTH_8) { - IMB_color_to_bw(ibuf); - } + if (ok) printf("OpenGL Render written to '%s'\n", name); + else printf("OpenGL Render failed to write '%s'\n", name); +} - BKE_image_path_from_imformat( - name, scene->r.pic, oglrender->bmain->name, scene->r.cfra, - &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, false); - ok = BKE_imbuf_write_as(ibuf, name, &scene->r.im_format, true); /* no need to stamp here */ - if (ok) printf("OpenGL Render written to '%s'\n", name); - else printf("OpenGL Render failed to write '%s'\n", name); - } +static void screen_opengl_render_apply(OGLRender *oglrender) +{ + RenderResult *rr; + RenderView *rv; + int view_id; + + rr = RE_AcquireResultRead(oglrender->re); + for (rv = rr->views.first, view_id = 0; rv; rv = rv->next, view_id++) { + RE_SetActiveRenderView(oglrender->re, rv->name); + oglrender->view_id = view_id; + screen_opengl_render_doit(oglrender, rr); } - - BKE_image_release_ibuf(oglrender->ima, ibuf, lock); - if (rect) - MEM_freeN(rect); + RE_ReleaseResult(oglrender->re); + + if (oglrender->write_still) { + screen_opengl_render_write(oglrender); + } } static bool screen_opengl_render_init(bContext *C, wmOperator *op) @@ -385,7 +517,6 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); ScrArea *prevsa = CTX_wm_area(C); ARegion *prevar = CTX_wm_region(C); - RenderResult *rr; GPUOffScreen *ofs; OGLRender *oglrender; int sizex, sizey; @@ -458,7 +589,6 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) oglrender->sseq = CTX_wm_space_seq(C); } - oglrender->prevsa = prevsa; oglrender->prevar = prevar; @@ -492,15 +622,17 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op) /* create render result */ RE_InitState(oglrender->re, NULL, &scene->r, NULL, sizex, sizey, NULL); - rr = RE_AcquireResultWrite(oglrender->re); - if (rr->rectf == NULL) - rr->rectf = MEM_callocN(sizeof(float) * 4 * sizex * sizey, "screen_opengl_render_init rect"); - RE_ReleaseResult(oglrender->re); + /* create render views */ + screen_opengl_views_setup(oglrender); /* wm vars */ oglrender->wm = wm; oglrender->win = win; + oglrender->totvideos = 0; + oglrender->mh = NULL; + oglrender->movie_ctx_arr = NULL; + return true; } @@ -508,10 +640,19 @@ static void screen_opengl_render_end(bContext *C, OGLRender *oglrender) { Main *bmain = CTX_data_main(C); Scene *scene = oglrender->scene; + size_t i; if (oglrender->mh) { - if (BKE_imtype_is_movie(scene->r.im_format.imtype)) - oglrender->mh->end_movie(); + if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { + for (i = 0; i < oglrender->totvideos; i++) { + oglrender->mh->end_movie(oglrender->movie_ctx_arr[i]); + oglrender->mh->context_free(oglrender->movie_ctx_arr[i]); + } + } + + if (oglrender->movie_ctx_arr) { + MEM_freeN(oglrender->movie_ctx_arr); + } } if (oglrender->timer) { /* exec will not have a timer */ @@ -552,13 +693,27 @@ static int screen_opengl_render_anim_initialize(bContext *C, wmOperator *op) oglrender = op->customdata; scene = oglrender->scene; + oglrender->totvideos = BKE_scene_multiview_num_videos_get(&scene->r); oglrender->reports = op->reports; - oglrender->mh = BKE_movie_handle_get(scene->r.im_format.imtype); + if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { - if (!oglrender->mh->start_movie(scene, &scene->r, oglrender->sizex, oglrender->sizey, oglrender->reports, PRVRANGEON != 0)) { - screen_opengl_render_end(C, oglrender); - return 0; + size_t i, width, height; + + BKE_scene_multiview_videos_dimensions_get(&scene->r, oglrender->sizex, oglrender->sizey, &width, &height); + oglrender->movie_ctx_arr = MEM_mallocN(sizeof(void *) * oglrender->totvideos, "Movies"); + oglrender->mh = BKE_movie_handle_get(scene->r.im_format.imtype); + + for (i = 0; i < oglrender->totvideos; i++) { + const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, i); + + oglrender->movie_ctx_arr[i] = oglrender->mh->context_create(); + if (!oglrender->mh->start_movie(oglrender->movie_ctx_arr[i], scene, &scene->r, oglrender->sizex, + oglrender->sizey, oglrender->reports, PRVRANGEON != 0, suffix)) + { + screen_opengl_render_end(C, oglrender); + return 0; + } } } @@ -568,18 +723,17 @@ static int screen_opengl_render_anim_initialize(bContext *C, wmOperator *op) return 1; } + static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); OGLRender *oglrender = op->customdata; Scene *scene = oglrender->scene; - ImBuf *ibuf, *ibuf_save = NULL; - void *lock; char name[FILE_MAX]; bool ok = false; const bool view_context = (oglrender->v3d != NULL); - Object *camera = NULL; bool is_movie; + RenderResult *rr; /* go to next frame */ if (CFRA < oglrender->nfra) @@ -599,7 +753,7 @@ static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op) if (!is_movie) { BKE_image_path_from_imformat( name, scene->r.pic, oglrender->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) && BLI_exists(name)) { BKE_reportf(op->reports, RPT_INFO, "Skipping existing frame \"%s\"", name); @@ -619,89 +773,40 @@ static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op) if (BKE_scene_camera_switch_update(scene)) { oglrender->v3d->camera = scene->camera; } - - camera = oglrender->v3d->camera; } } else { BKE_scene_camera_switch_update(scene); - - camera = scene->camera; } /* render into offscreen buffer */ screen_opengl_render_apply(oglrender); /* save to disk */ - ibuf = BKE_image_acquire_ibuf(oglrender->ima, &oglrender->iuser, &lock); - - if (ibuf) { - bool needs_free = false; - - ibuf_save = ibuf; - - if (is_movie || !BKE_imtype_requires_linear_float(scene->r.im_format.imtype)) { - ibuf_save = IMB_colormanagement_imbuf_for_write(ibuf, true, true, &scene->view_settings, - &scene->display_settings, &scene->r.im_format); - - needs_free = true; - } - - /* color -> grayscale */ - /* editing directly would alter the render view */ - if (scene->r.im_format.planes == R_IMF_PLANES_BW) { - ImBuf *ibuf_bw = IMB_dupImBuf(ibuf_save); - IMB_color_to_bw(ibuf_bw); - - if (needs_free) - IMB_freeImBuf(ibuf_save); - - ibuf_save = ibuf_bw; - } - else { - /* this is lightweight & doesnt re-alloc the buffers, only do this - * to save the correct bit depth since the image is always RGBA */ - ImBuf *ibuf_cpy = IMB_allocImBuf(ibuf_save->x, ibuf_save->y, scene->r.im_format.planes, 0); - - ibuf_cpy->rect = ibuf_save->rect; - ibuf_cpy->rect_float = ibuf_save->rect_float; - ibuf_cpy->zbuf_float = ibuf_save->zbuf_float; - - if (needs_free) { - ibuf_cpy->mall = ibuf_save->mall; - ibuf_save->mall = 0; - IMB_freeImBuf(ibuf_save); - } + rr = RE_AcquireResultRead(oglrender->re); - ibuf_save = ibuf_cpy; + if (is_movie) { + ok = RE_WriteRenderViewsMovie(oglrender->reports, rr, scene, &scene->r, oglrender->mh, oglrender->sizex, + oglrender->sizey, oglrender->movie_ctx_arr, oglrender->totvideos); + if (ok) { + printf("Append frame %d", scene->r.cfra); + BKE_reportf(op->reports, RPT_INFO, "Appended frame: %d", scene->r.cfra); } - - if (is_movie) { - ok = oglrender->mh->append_movie(&scene->r, PSFRA, CFRA, (int *)ibuf_save->rect, - oglrender->sizex, oglrender->sizey, oglrender->reports); - if (ok) { - printf("Append frame %d", scene->r.cfra); - BKE_reportf(op->reports, RPT_INFO, "Appended frame: %d", scene->r.cfra); - } + } + else { + ok = RE_WriteRenderViewsImage(op->reports, rr, scene, scene->camera, true, name); + if (ok) { + printf("Saved: %s", name); + BKE_reportf(op->reports, RPT_INFO, "Saved file: %s", name); } else { - ok = BKE_imbuf_write_stamp(scene, camera, ibuf_save, name, &scene->r.im_format); - - if (ok == 0) { - printf("Write error: cannot save %s\n", name); - BKE_reportf(op->reports, RPT_ERROR, "Write error: cannot save %s", name); - } - else { - printf("Saved: %s", name); - BKE_reportf(op->reports, RPT_INFO, "Saved file: %s", name); - } + printf("Write error: cannot save %s\n", name); + BKE_reportf(op->reports, RPT_ERROR, "Write error: cannot save %s", name); } - - if (needs_free) - IMB_freeImBuf(ibuf_save); } - BKE_image_release_ibuf(oglrender->ima, ibuf, lock); + RE_ReleaseResult(oglrender->re); + /* movie stats prints have no line break */ printf("\n"); diff --git a/source/blender/editors/render/render_ops.c b/source/blender/editors/render/render_ops.c index 0d334082a2b..6aaad98f946 100644 --- a/source/blender/editors/render/render_ops.c +++ b/source/blender/editors/render/render_ops.c @@ -58,6 +58,9 @@ void ED_operatortypes_render(void) WM_operatortype_append(SCENE_OT_render_layer_add); WM_operatortype_append(SCENE_OT_render_layer_remove); + WM_operatortype_append(SCENE_OT_render_view_add); + WM_operatortype_append(SCENE_OT_render_view_remove); + #ifdef WITH_FREESTYLE WM_operatortype_append(SCENE_OT_freestyle_module_add); WM_operatortype_append(SCENE_OT_freestyle_module_remove); diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index 9dbd2e88563..e6fbfda3067 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -548,7 +548,9 @@ static bool ed_preview_draw_rect(ScrArea *sa, int split, int first, rcti *rect, /* test if something rendered ok */ re = RE_GetRender(name); - RE_AcquireResultImage(re, &rres); + + /* material preview only needs monoscopy (view 0) */ + RE_AcquireResultImage(re, &rres, 0); if (rres.rectf) { @@ -561,9 +563,11 @@ static bool ed_preview_draw_rect(ScrArea *sa, int split, int first, rcti *rect, unsigned char *rect_byte = MEM_mallocN(rres.rectx * rres.recty * sizeof(int), "ed_preview_draw_rect"); float fx = rect->xmin + offx; float fy = rect->ymin; + + /* material preview only needs monoscopy (view 0) */ if (re) - RE_AcquiredResultGet32(re, &rres, (unsigned int *)rect_byte); - + RE_AcquiredResultGet32(re, &rres, (unsigned int *)rect_byte, 0); + glaDrawPixelsSafe(fx, fy, rres.rectx, rres.recty, rres.rectx, GL_RGBA, GL_UNSIGNED_BYTE, rect_byte); MEM_freeN(rect_byte); diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c index cf712a653e6..3de21536148 100644 --- a/source/blender/editors/render/render_shading.c +++ b/source/blender/editors/render/render_shading.c @@ -605,6 +605,70 @@ void SCENE_OT_render_layer_remove(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; } +/********************** render view operators *********************/ + +static int render_view_remove_poll(bContext *C) +{ + Scene *scene = CTX_data_scene(C); + + /* don't allow user to remove "left" and "right" views */ + return scene->r.actview > 1; +} + +static int render_view_add_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Scene *scene = CTX_data_scene(C); + + BKE_scene_add_render_view(scene, NULL); + scene->r.actview = BLI_listbase_count(&scene->r.views) - 1; + + WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); + + return OPERATOR_FINISHED; +} + +void SCENE_OT_render_view_add(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Add Render View"; + ot->idname = "SCENE_OT_render_view_add"; + ot->description = "Add a render view"; + + /* api callbacks */ + ot->exec = render_view_add_exec; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + +static int render_view_remove_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Scene *scene = CTX_data_scene(C); + SceneRenderView *rv = BLI_findlink(&scene->r.views, scene->r.actview); + + if (!BKE_scene_remove_render_view(scene, rv)) + return OPERATOR_CANCELLED; + + WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); + + return OPERATOR_FINISHED; +} + +void SCENE_OT_render_view_remove(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Remove Render View"; + ot->idname = "SCENE_OT_render_view_remove"; + ot->description = "Remove the selected render view"; + + /* api callbacks */ + ot->exec = render_view_remove_exec; + ot->poll = render_view_remove_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + #ifdef WITH_FREESTYLE static bool freestyle_linestyle_check_report(FreestyleLineSet *lineset, ReportList *reports) diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index be9336cc984..d786c0cfc36 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -493,7 +493,6 @@ void ED_region_do_draw(bContext *C, ARegion *ar) glDisable(GL_BLEND); #endif - ar->do_draw = 0; memset(&ar->drawrct, 0, sizeof(ar->drawrct)); UI_blocklist_free_inactive(C, &ar->uiblocks); diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index e1c414b0031..3d76070f197 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -59,6 +59,7 @@ #include "ED_screen.h" #include "ED_screen_types.h" #include "ED_clip.h" +#include "ED_node.h" #include "ED_render.h" #include "UI_interface.h" @@ -2122,4 +2123,85 @@ void ED_update_for_newframe(Main *bmain, Scene *scene, int UNUSED(mute)) } +/* + * return true if any active area requires to see in 3D + */ +bool ED_screen_stereo3d_required(bScreen *screen) +{ + ScrArea *sa; + Scene *sce = screen->scene; + const bool is_multiview = (sce->r.scemode & R_MULTIVIEW) != 0; + + for (sa = screen->areabase.first; sa; sa = sa->next) { + switch (sa->spacetype) { + case SPACE_VIEW3D: + { + View3D *v3d; + + if (!is_multiview) + continue; + + v3d = sa->spacedata.first; + if (v3d->camera && v3d->stereo3d_camera == STEREO_3D_ID) { + ARegion *ar; + for (ar = sa->regionbase.first; ar; ar = ar->next) { + if (ar->regiondata && ar->regiontype == RGN_TYPE_WINDOW) { + RegionView3D *rv3d = ar->regiondata; + if (rv3d->persp == RV3D_CAMOB) { + return true; + } + } + } + } + break; + } + case SPACE_IMAGE: + { + SpaceImage *sima; + + /* images should always show in stereo, even if + * the file doesn't have views enabled */ + sima = sa->spacedata.first; + if (sima->image && (sima->image->flag & IMA_IS_STEREO) && + (sima->iuser.flag & IMA_SHOW_STEREO)) + { + return true; + } + break; + } + case SPACE_NODE: + { + SpaceNode *snode; + + if (!is_multiview) + continue; + snode = sa->spacedata.first; + if ((snode->flag & SNODE_BACKDRAW) && ED_node_is_compositor(snode)) { + return true; + } + break; + } + case SPACE_SEQ: + { + SpaceSeq *sseq; + + if (!is_multiview) + continue; + + sseq = sa->spacedata.first; + if (ELEM(sseq->view, SEQ_VIEW_PREVIEW, SEQ_VIEW_SEQUENCE_PREVIEW)) { + return true; + } + + if (sseq->draw_flag & SEQ_DRAW_BACKDROP) { + return true; + } + + break; + } + } + } + + return false; +} diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 894a801b677..67c18ac287e 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -990,6 +990,7 @@ static int area_dupli_invoke(bContext *C, wmOperator *op, const wmEvent *event) rect.ymax = rect.ymin + BLI_rcti_size_y(&rect) / U.pixelsize; newwin = WM_window_open(C, &rect); + *newwin->stereo3d_format = *win->stereo3d_format; /* allocs new screen and adds to newly created window, using window size */ newsc = ED_screen_add(newwin, CTX_data_scene(C), sc->id.name + 2); diff --git a/source/blender/editors/screen/screendump.c b/source/blender/editors/screen/screendump.c index 330bc7e3395..4644f0ae0b8 100644 --- a/source/blender/editors/screen/screendump.c +++ b/source/blender/editors/screen/screendump.c @@ -303,6 +303,7 @@ typedef struct ScreenshotJob { const short *stop; const short *do_update; ReportList reports; + void *movie_ctx; } ScreenshotJob; @@ -312,7 +313,10 @@ static void screenshot_freejob(void *sjv) if (sj->dumprect) MEM_freeN(sj->dumprect); - + + if (sj->movie_ctx) + MEM_freeN(sj->movie_ctx); + MEM_freeN(sj); } @@ -337,20 +341,21 @@ static void screenshot_startjob(void *sjv, short *stop, short *do_update, float { ScreenshotJob *sj = sjv; RenderData rd = sj->scene->r; - bMovieHandle *mh = BKE_movie_handle_get(sj->scene->r.im_format.imtype); - + bMovieHandle *mh = NULL; + /* we need this as local variables for renderdata */ rd.frs_sec = U.scrcastfps; rd.frs_sec_base = 1.0f; if (BKE_imtype_is_movie(rd.im_format.imtype)) { - if (!mh->start_movie(sj->scene, &rd, sj->dumpsx, sj->dumpsy, &sj->reports, false)) { + mh = BKE_movie_handle_get(sj->scene->r.im_format.imtype); + sj->movie_ctx = mh->context_create(); + + if (!mh->start_movie(sj->movie_ctx, sj->scene, &rd, sj->dumpsx, sj->dumpsy, &sj->reports, false, "")) { printf("screencast job stopped\n"); return; } } - else - mh = NULL; sj->stop = stop; sj->do_update = do_update; @@ -362,8 +367,8 @@ static void screenshot_startjob(void *sjv, short *stop, short *do_update, float if (sj->dumprect) { if (mh) { - if (mh->append_movie(&rd, rd.sfra, rd.cfra, (int *)sj->dumprect, - sj->dumpsx, sj->dumpsy, &sj->reports)) + if (mh->append_movie(sj->movie_ctx, &rd, rd.sfra, rd.cfra, (int *)sj->dumprect, + sj->dumpsx, sj->dumpsy, "", &sj->reports)) { BKE_reportf(&sj->reports, RPT_INFO, "Appended frame: %d", rd.cfra); printf("Appended frame %d\n", rd.cfra); @@ -379,7 +384,7 @@ static void screenshot_startjob(void *sjv, short *stop, short *do_update, float BKE_image_path_from_imformat( name, rd.pic, sj->bmain->name, rd.cfra, - &rd.im_format, (rd.scemode & R_EXTENSION) != 0, true); + &rd.im_format, (rd.scemode & R_EXTENSION) != 0, true, NULL); ibuf->rect = sj->dumprect; ok = BKE_imbuf_write(ibuf, name, &rd.im_format); @@ -410,8 +415,10 @@ static void screenshot_startjob(void *sjv, short *stop, short *do_update, float PIL_sleep_ms(U.scrcastwait); } - if (mh) - mh->end_movie(); + if (mh) { + mh->end_movie(sj->movie_ctx); + mh->context_free(sj->movie_ctx); + } BKE_report(&sj->reports, RPT_INFO, "Screencast job stopped"); } diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 097ffe3523b..7992767072a 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -499,7 +499,7 @@ void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short te int w = imapaintpartial.x2 - imapaintpartial.x1; int h = imapaintpartial.y2 - imapaintpartial.y1; /* Testing with partial update in uv editor too */ - GPU_paint_update_image(image, imapaintpartial.x1, imapaintpartial.y1, w, h); //!texpaint); + GPU_paint_update_image(image, (sima ? &sima->iuser : NULL), imapaintpartial.x1, imapaintpartial.y1, w, h); //!texpaint); } } diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index 4f71bea7cd2..9d39eea7b96 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -1109,7 +1109,7 @@ static int paint_2d_canvas_set(ImagePaintState *s, Image *ima) if (ima == NULL) { return 0; } - else if (ima->packedfile && ima->rr) { + else if (BKE_image_has_packedfile(ima) && ima->rr) { s->warnpackedfile = ima->id.name + 2; return 0; } diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 9ee15f3a532..b01d315650f 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -5233,7 +5233,7 @@ static int texture_paint_image_from_view_exec(bContext *C, wmOperator *op) if (w > maxsize) w = maxsize; if (h > maxsize) h = maxsize; - ibuf = ED_view3d_draw_offscreen_imbuf(scene, CTX_wm_view3d(C), CTX_wm_region(C), w, h, IB_rect, false, R_ALPHAPREMUL, err_out); + ibuf = ED_view3d_draw_offscreen_imbuf(scene, CTX_wm_view3d(C), CTX_wm_region(C), w, h, IB_rect, false, R_ALPHAPREMUL, NULL, err_out); if (!ibuf) { /* Mostly happens when OpenGL offscreen buffer was failed to create, */ /* but could be other reasons. Should be handled in the future. nazgul */ @@ -5444,7 +5444,7 @@ static Image *proj_paint_image_create(wmOperator *op, Main *bmain) RNA_string_get(op->ptr, "name", imagename); } ima = BKE_image_add_generated(bmain, width, height, imagename, alpha ? 32 : 24, use_float, - gen_type, color); + gen_type, color, false); return ima; } diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c index fb3c140fddf..a02f64d68d8 100644 --- a/source/blender/editors/space_image/image_buttons.c +++ b/source/blender/editors/space_image/image_buttons.c @@ -44,6 +44,7 @@ #include "BKE_image.h" #include "BKE_node.h" #include "BKE_screen.h" +#include "BKE_scene.h" #include "RE_pipeline.h" @@ -82,9 +83,9 @@ static void image_info(Scene *scene, ImageUser *iuser, Image *ima, ImBuf *ibuf, else { if (ima->source == IMA_SRC_MOVIE) { ofs += BLI_strncpy_rlen(str + ofs, IFACE_("Movie"), len - ofs); - if (ima->anim) + if (BKE_image_has_anim(ima)) ofs += BLI_snprintf(str + ofs, len - ofs, IFACE_(" %d frs"), - IMB_anim_get_duration(ima->anim, IMB_TC_RECORD_RUN)); + IMB_anim_get_duration(((ImageAnim *)ima->anims.first)->anim, IMB_TC_RECORD_RUN)); } else { ofs += BLI_strncpy_rlen(str, IFACE_("Image"), len - ofs); @@ -311,10 +312,10 @@ static void ui_imageuser_slot_menu(bContext *UNUSED(C), uiLayout *layout, void * static const char *ui_imageuser_layer_fake_name(RenderResult *rr) { - if (rr->rectf) { + if (RE_RenderViewGetRectf(rr, 0)) { return IFACE_("Composite"); } - else if (rr->rect32) { + else if (RE_RenderViewGetRect32(rr, 0)) { return IFACE_("Sequence"); } else { @@ -375,7 +376,7 @@ final: static const char *ui_imageuser_pass_fake_name(RenderLayer *rl) { - if (rl == NULL || rl->rectf) { + if (rl == NULL) { return IFACE_("Combined"); } else { @@ -383,9 +384,9 @@ static const char *ui_imageuser_pass_fake_name(RenderLayer *rl) } } -static void ui_imageuser_pass_menu(bContext *UNUSED(C), uiLayout *layout, void *ptrpair_p) +static void ui_imageuser_pass_menu(bContext *UNUSED(C), uiLayout *layout, void *rnd_pt) { - void **rnd_data = ptrpair_p; + void **rnd_data = rnd_pt; uiBlock *block = uiLayoutGetBlock(layout); Image *image = rnd_data[0]; ImageUser *iuser = rnd_data[1]; @@ -398,6 +399,7 @@ static void ui_imageuser_pass_menu(bContext *UNUSED(C), uiLayout *layout, void * RenderPass *rpass; const char *fake_name; int nr; + int passflag = 0; /* may have been freed since drawing */ rr = BKE_image_acquire_renderresult(scene, image); @@ -419,15 +421,22 @@ static void ui_imageuser_pass_menu(bContext *UNUSED(C), uiLayout *layout, void * fake_name = ui_imageuser_pass_fake_name(rl); if (fake_name) { - BLI_strncpy(rpass_fake.name, fake_name, sizeof(rpass_fake.name)); + BLI_strncpy(rpass_fake.internal_name, fake_name, sizeof(rpass_fake.internal_name)); nr += 1; } /* rendered results don't have a Combined pass */ for (rpass = rl ? rl->passes.last : NULL; rpass; rpass = rpass->prev, nr--) { + + /* just show one pass of each kind */ + if (passflag & rpass->passtype) + continue; + + passflag |= rpass->passtype; + final: - uiDefButS(block, UI_BTYPE_BUT_MENU, B_NOP, IFACE_(rpass->name), 0, 0, - UI_UNIT_X * 5, UI_UNIT_X, &iuser->pass, (float) nr, 0.0, 0, -1, ""); + uiDefButS(block, UI_BTYPE_BUT_MENU, B_NOP, IFACE_(rpass->internal_name), 0, 0, + UI_UNIT_X * 5, UI_UNIT_X, &iuser->passtype, (float) rpass->passtype, 0.0, 0, -1, ""); } if (fake_name) { @@ -441,21 +450,81 @@ final: BKE_image_release_renderresult(scene, image); } +/**************************** view menus *****************************/ +static void ui_imageuser_view_menu_rr(bContext *UNUSED(C), uiLayout *layout, void *rnd_pt) +{ + void **rnd_data = rnd_pt; + uiBlock *block = uiLayoutGetBlock(layout); + Image *image = rnd_data[0]; + ImageUser *iuser = rnd_data[1]; + RenderResult *rr; + RenderView *rview; + int nr; + Scene *scene = iuser->scene; + + /* may have been freed since drawing */ + rr = BKE_image_acquire_renderresult(scene, image); + if (UNLIKELY(rr == NULL)) { + return; + } + + UI_block_layout_set_current(block, layout); + uiLayoutColumn(layout, false); + + uiDefBut(block, UI_BTYPE_LABEL, 0, IFACE_("View"), + 0, 0, UI_UNIT_X * 5, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, ""); + + uiItemS(layout); + + nr = (rr ? BLI_listbase_count(&rr->views) : 0) - 1; + for (rview = rr ? rr->views.last : NULL; rview; rview = rview->prev, nr--) { + uiDefButS(block, UI_BTYPE_BUT_MENU, B_NOP, IFACE_(rview->name), 0, 0, + UI_UNIT_X * 5, UI_UNIT_X, &iuser->view, (float) nr, 0.0, 0, -1, ""); + } + + BKE_image_release_renderresult(scene, image); +} + +static void ui_imageuser_view_menu_multiview(bContext *UNUSED(C), uiLayout *layout, void *rnd_pt) +{ + void **rnd_data = rnd_pt; + uiBlock *block = uiLayoutGetBlock(layout); + Image *image = rnd_data[0]; + ImageUser *iuser = rnd_data[1]; + int nr; + ImageView *iv; + + UI_block_layout_set_current(block, layout); + uiLayoutColumn(layout, false); + + uiDefBut(block, UI_BTYPE_LABEL, 0, IFACE_("View"), + 0, 0, UI_UNIT_X * 5, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, ""); + + uiItemS(layout); + + nr = BLI_listbase_count(&image->views) - 1; + for (iv = image->views.last; iv; iv = iv->prev, nr--) { + uiDefButS(block, UI_BTYPE_BUT_MENU, B_NOP, IFACE_(iv->name), 0, 0, + UI_UNIT_X * 5, UI_UNIT_X, &iuser->view, (float) nr, 0.0, 0, -1, ""); + } +} + /* 5 layer button callbacks... */ static void image_multi_cb(bContext *C, void *rr_v, void *iuser_v) { ImageUser *iuser = iuser_v; - BKE_image_multilayer_index(rr_v, iuser); + BKE_image_multilayer_index(rr_v, iuser); WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL); } + static void image_multi_inclay_cb(bContext *C, void *rr_v, void *iuser_v) { RenderResult *rr = rr_v; ImageUser *iuser = iuser_v; int tot = BLI_listbase_count(&rr->layers); - if (rr->rectf || rr->rect32) + if (RE_HasFakeLayer(rr)) tot++; /* fake compo/sequencer layer */ if (iuser->layer < tot - 1) { @@ -483,7 +552,7 @@ static void image_multi_incpass_cb(bContext *C, void *rr_v, void *iuser_v) if (rl) { int tot = BLI_listbase_count(&rl->passes); - if (rr->rectf || rr->rect32) + if (RE_HasFakeLayer(rr)) tot++; /* fake compo/sequencer layer */ if (iuser->pass < tot - 1) { @@ -504,6 +573,16 @@ static void image_multi_decpass_cb(bContext *C, void *rr_v, void *iuser_v) } } +/* 5 view button callbacks... */ +static void image_multiview_cb(bContext *C, void *ima_v, void *iuser_v) +{ + Image *ima = ima_v; + ImageUser *iuser = iuser_v; + + BKE_image_multiview_index(ima, iuser); + WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL); +} + #if 0 static void image_freecache_cb(bContext *C, void *ima_v, void *unused) { @@ -523,13 +602,14 @@ static void image_user_change(bContext *C, void *iuser_v, void *unused) static void uiblock_layer_pass_buttons(uiLayout *layout, Image *image, RenderResult *rr, ImageUser *iuser, int w, short *render_slot) { - static void *rnd_pt[3]; /* XXX, workaround */ + static void *rnd_pt[4]; /* XXX, workaround */ uiBlock *block = uiLayoutGetBlock(layout); uiBut *but; RenderLayer *rl = NULL; - int wmenu1, wmenu2, wmenu3; + int wmenu1, wmenu2, wmenu3, wmenu4; const char *fake_name; - const char *display_name; + const char *display_name = ""; + const bool show_stereo = (iuser->flag & IMA_SHOW_STEREO); uiLayoutRow(layout, true); @@ -537,6 +617,7 @@ static void uiblock_layer_pass_buttons(uiLayout *layout, Image *image, RenderRes wmenu1 = (2 * w) / 5; wmenu2 = (3 * w) / 5; wmenu3 = (3 * w) / 6; + wmenu4 = (3 * w) / 6; rnd_pt[0] = image; rnd_pt[1] = iuser; @@ -558,6 +639,7 @@ static void uiblock_layer_pass_buttons(uiLayout *layout, Image *image, RenderRes if (rr) { RenderPass *rpass; + RenderView *rview; int rpass_index; /* layer */ @@ -566,20 +648,50 @@ static void uiblock_layer_pass_buttons(uiLayout *layout, Image *image, RenderRes rl = BLI_findlink(&rr->layers, rpass_index); rnd_pt[2] = SET_INT_IN_POINTER(rpass_index); - display_name = rl ? rl->name : (fake_name ? fake_name : ""); - but = uiDefMenuBut(block, ui_imageuser_layer_menu, rnd_pt, display_name, 0, 0, wmenu2, UI_UNIT_Y, TIP_("Select Layer")); - UI_but_func_set(but, image_multi_cb, rr, iuser); - UI_but_type_set_menu_from_pulldown(but); - + if (RE_layers_have_name(rr)) { + display_name = rl ? rl->name : (fake_name ? fake_name : ""); + but = uiDefMenuBut(block, ui_imageuser_layer_menu, rnd_pt, display_name, 0, 0, wmenu2, UI_UNIT_Y, TIP_("Select Layer")); + UI_but_func_set(but, image_multi_cb, rr, iuser); + UI_but_type_set_menu_from_pulldown(but); + } /* pass */ fake_name = ui_imageuser_pass_fake_name(rl); rpass = (rl ? BLI_findlink(&rl->passes, iuser->pass - (fake_name ? 1 : 0)) : NULL); - display_name = rpass ? rpass->name : (fake_name ? fake_name : ""); + display_name = rpass ? rpass->internal_name : (fake_name ? fake_name : ""); but = uiDefMenuBut(block, ui_imageuser_pass_menu, rnd_pt, display_name, 0, 0, wmenu3, UI_UNIT_Y, TIP_("Select Pass")); UI_but_func_set(but, image_multi_cb, rr, iuser); UI_but_type_set_menu_from_pulldown(but); + + /* view */ + if (BLI_listbase_count_ex(&rr->views, 2) > 1 && !show_stereo) { + rview = BLI_findlink(&rr->views, iuser->view); + display_name = rview ? rview->name : ""; + + but = uiDefMenuBut(block, ui_imageuser_view_menu_rr, rnd_pt, display_name, 0, 0, wmenu4, UI_UNIT_Y, TIP_("Select View")); + UI_but_func_set(but, image_multi_cb, rr, iuser); + UI_but_type_set_menu_from_pulldown(but); + } + } + + /* stereo image */ + else if (((image->flag & IMA_IS_STEREO) && (!show_stereo)) || + ((image->flag & IMA_IS_MULTIVIEW) && ((image->flag & IMA_IS_STEREO) == 0))) + { + ImageView *iv; + int nr = 0; + + for (iv = image->views.first; iv; iv = iv->next) { + if (nr++ == iuser->view) { + display_name = iv->name; + break; + } + } + + but = uiDefMenuBut(block, ui_imageuser_view_menu_multiview, rnd_pt, display_name, 0, 0, wmenu1, UI_UNIT_Y, TIP_("Select View")); + UI_but_func_set(but, image_multiview_cb, image, iuser); + UI_but_type_set_menu_from_pulldown(but); } } @@ -734,13 +846,13 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char if (ima->source != IMA_SRC_GENERATED) { row = uiLayoutRow(layout, true); - if (ima->packedfile) + if (BKE_image_has_packedfile(ima)) uiItemO(row, "", ICON_PACKAGE, "image.unpack"); else uiItemO(row, "", ICON_UGLYPACKAGE, "image.pack"); row = uiLayoutRow(row, true); - uiLayoutSetEnabled(row, ima->packedfile == NULL); + uiLayoutSetEnabled(row, BKE_image_has_packedfile(ima) == false); uiItemR(row, &imaptr, "filepath", 0, "", ICON_NONE); uiItemO(row, "", ICON_FILE_REFRESH, "image.reload"); } @@ -792,6 +904,17 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char uiItemR(row, &imaptr, "alpha_mode", 0, IFACE_("Alpha"), ICON_NONE); } + if ((scene->r.scemode & R_MULTIVIEW) != 0) { + uiItemS(layout); + + col = uiLayoutColumn(layout, false); + uiItemR(col, &imaptr, "use_multiview", 0, NULL, ICON_NONE); + + col = uiLayoutColumn(layout, false); + uiLayoutSetActive(col, RNA_boolean_get(&imaptr, "use_multiview")); + uiTemplateImageViews(col, &imaptr); + } + if (ima->source == IMA_SRC_MOVIE) { col = uiLayoutColumn(layout, false); uiItemR(col, &imaptr, "use_deinterlace", 0, IFACE_("Deinterlace"), ICON_NONE); @@ -962,6 +1085,101 @@ void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr, int color_man } } +void uiTemplateImageStereo3d(uiLayout *layout, PointerRNA *stereo3d_format_ptr) +{ + Stereo3dFormat *stereo3d_format = stereo3d_format_ptr->data; + uiLayout *col; + + col = uiLayoutColumn(layout, false); + uiItemR(col, stereo3d_format_ptr, "display_mode", 0, NULL, ICON_NONE); + + switch (stereo3d_format->display_mode) { + case S3D_DISPLAY_ANAGLYPH: + { + uiItemR(col, stereo3d_format_ptr, "anaglyph_type", 0, NULL, ICON_NONE); + break; + } + case S3D_DISPLAY_INTERLACE: + { + uiItemR(col, stereo3d_format_ptr, "interlace_type", 0, NULL, ICON_NONE); + uiItemR(col, stereo3d_format_ptr, "use_interlace_swap", 0, NULL, ICON_NONE); + break; + } + case S3D_DISPLAY_SIDEBYSIDE: + { + uiItemR(col, stereo3d_format_ptr, "use_sidebyside_crosseyed", 0, NULL, ICON_NONE); + /* fall-through */ + } + case S3D_DISPLAY_TOPBOTTOM: + { + uiItemR(col, stereo3d_format_ptr, "use_squeezed_frame", 0, NULL, ICON_NONE); + break; + } + } +} + +static void uiTemplateViewsFormat(uiLayout *layout, PointerRNA *ptr, PointerRNA *stereo3d_format_ptr) +{ + uiLayout *col, *box; + + col = uiLayoutColumn(layout, false); + + uiItemL(col, IFACE_("Views Format:"), ICON_NONE); + uiItemR(uiLayoutRow(col, false), ptr, "views_format", UI_ITEM_R_EXPAND, NULL, ICON_NONE); + + if (stereo3d_format_ptr) { + box = uiLayoutBox(col); + uiLayoutSetActive(box, RNA_enum_get(ptr, "views_format") == R_IMF_VIEWS_STEREO_3D); + uiTemplateImageStereo3d(box, stereo3d_format_ptr); + } +} + +void uiTemplateImageViews(uiLayout *layout, PointerRNA *imaptr) +{ + Image *ima = imaptr->data; + + if (ima->type != IMA_TYPE_MULTILAYER) { + PropertyRNA *prop; + PointerRNA stereo3d_format_ptr; + + prop = RNA_struct_find_property(imaptr, "stereo_3d_format"); + stereo3d_format_ptr = RNA_property_pointer_get(imaptr, prop); + + uiTemplateViewsFormat(layout, imaptr, &stereo3d_format_ptr); + } + else { + uiTemplateViewsFormat(layout, imaptr, NULL); + } +} + +void uiTemplateImageFormatViews(uiLayout *layout, PointerRNA *imfptr, PointerRNA *ptr) +{ + ImageFormatData *imf = imfptr->data; + uiLayout *col; + + if (ptr) { + uiItemR(layout, ptr, "use_multiview", 0, NULL, ICON_NONE); + col = uiLayoutColumn(layout, false); + uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_multiview")); + } + else { + col = uiLayoutColumn(layout, false); + } + + if (imf->imtype != R_IMF_IMTYPE_MULTILAYER) { + PropertyRNA *prop; + PointerRNA stereo3d_format_ptr; + + prop = RNA_struct_find_property(imfptr, "stereo_3d_format"); + stereo3d_format_ptr = RNA_property_pointer_get(imfptr, prop); + + uiTemplateViewsFormat(col, imfptr, &stereo3d_format_ptr); + } + else { + uiTemplateViewsFormat(col, imfptr, NULL); + } +} + void uiTemplateImageLayers(uiLayout *layout, bContext *C, Image *ima, ImageUser *iuser) { Scene *scene = CTX_data_scene(C); diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index 517cf64214c..5a3c3e90618 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -783,7 +783,7 @@ void draw_image_main(const bContext *C, ARegion *ar) Image *ima; ImBuf *ibuf; float zoomx, zoomy; - bool show_viewer, show_render, show_paint; + bool show_viewer, show_render, show_paint, show_stereo3d, show_multilayer; void *lock; /* XXX can we do this in refresh? */ @@ -813,6 +813,8 @@ void draw_image_main(const bContext *C, ARegion *ar) show_viewer = (ima && ima->source == IMA_SRC_VIEWER) != 0; show_render = (show_viewer && ima->type == IMA_TYPE_R_RESULT) != 0; show_paint = (ima && (sima->mode == SI_MODE_PAINT) && (show_viewer == false) && (show_render == false)); + show_stereo3d = (ima && (ima->flag & IMA_IS_STEREO) && (sima->iuser.flag & IMA_SHOW_STEREO)); + show_multilayer = ima && BKE_image_is_multilayer(ima); if (show_viewer) { /* use locked draw for drawing viewer image buffer since the compositor @@ -823,6 +825,14 @@ void draw_image_main(const bContext *C, ARegion *ar) BLI_lock_thread(LOCK_DRAW_IMAGE); } + if (show_stereo3d) { + if (show_multilayer) + /* update multiindex and pass for the current eye */ + BKE_image_multilayer_index(ima->rr, &sima->iuser); + else + BKE_image_multiview_index(ima, &sima->iuser); + } + ibuf = ED_space_image_acquire_buffer(sima, &lock); /* draw the image or grid */ diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 5890ed61118..6a5462bff45 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -30,14 +30,21 @@ #include <stddef.h> #include <string.h> +#include <fcntl.h> #include <stdlib.h> #include <errno.h> +#ifndef WIN32 +# include <unistd.h> +#else +# include <io.h> +#endif #include "MEM_guardedalloc.h" #include "BLI_math.h" #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLI_string_utf8.h" #include "BLF_translation.h" @@ -60,6 +67,7 @@ #include "BKE_report.h" #include "BKE_screen.h" #include "BKE_sound.h" +#include "BKE_scene.h" #include "GPU_draw.h" #include "GPU_buffers.h" @@ -68,6 +76,7 @@ #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" #include "IMB_moviecache.h" +#include "intern/openexr/openexr_multi.h" #include "RE_pipeline.h" @@ -936,6 +945,7 @@ static void image_filesel(bContext *C, wmOperator *op, const char *path) typedef struct ImageOpenData { PropertyPointerRNA pprop; ImageUser *iuser; + ImageFormatData im_format; } ImageOpenData; typedef struct ImageFrame { @@ -946,7 +956,6 @@ typedef struct ImageFrame { static void image_open_init(bContext *C, wmOperator *op) { ImageOpenData *iod; - op->customdata = iod = MEM_callocN(sizeof(ImageOpenData), __func__); iod->iuser = CTX_data_pointer_get_type(C, "image_user", &RNA_ImageUser).data; UI_context_active_but_prop_get_templateID(C, &iod->pprop.ptr, &iod->pprop.prop); @@ -1048,7 +1057,7 @@ static int image_open_exec(bContext *C, wmOperator *op) Scene *scene = CTX_data_scene(C); Object *obedit = CTX_data_edit_object(C); ImageUser *iuser = NULL; - ImageOpenData *iod; + ImageOpenData *iod = op->customdata; PointerRNA idptr; Image *ima = NULL; char path[FILE_MAX]; @@ -1085,6 +1094,21 @@ static int image_open_exec(bContext *C, wmOperator *op) if (!op->customdata) image_open_init(C, op); + /* handle multiview images */ + if (RNA_boolean_get(op->ptr, "use_multiview")) { + ImageFormatData *imf = &iod->im_format; + + ima->flag |= IMA_USE_VIEWS; + ima->views_format = imf->views_format; + *ima->stereo3d_format = imf->stereo3d_format; + } + else { + ima->flag &= ~IMA_USE_VIEWS; + ima->flag &= ~IMA_IS_STEREO; + ima->flag &= ~IMA_IS_MULTIVIEW; + BKE_image_free_views(ima); + } + /* only image path after save, never ibuf */ if (is_relative_path) { if (!exists) { @@ -1128,6 +1152,8 @@ static int image_open_exec(bContext *C, wmOperator *op) iuser->framenr = 1; iuser->offset = frame_ofs - 1; iuser->fie_ima = 2; + iuser->scene = scene; + BKE_image_init_imageuser(ima, iuser); } /* XXX unpackImage frees image buffers */ @@ -1146,7 +1172,8 @@ static int image_open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED( SpaceImage *sima = CTX_wm_space_image(C); /* XXX other space types can call */ const char *path = U.textudir; Image *ima = NULL; - + Scene *scene = CTX_data_scene(C); + PropertyRNA *prop; if (sima) { ima = sima->image; } @@ -1185,11 +1212,44 @@ static int image_open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED( image_open_init(C, op); + /* show multiview save options only if scene has multiviews */ + prop = RNA_struct_find_property(op->ptr, "show_multiview"); + RNA_property_boolean_set(op->ptr, prop, (scene->r.scemode & R_MULTIVIEW) != 0); + image_filesel(C, op, path); return OPERATOR_RUNNING_MODAL; } +static bool image_open_draw_check_prop(PointerRNA *UNUSED(ptr), PropertyRNA *prop) +{ + const char *prop_id = RNA_property_identifier(prop); + + return !(STREQ(prop_id, "filepath") || + STREQ(prop_id, "directory") || + STREQ(prop_id, "filename") + ); +} + +static void image_open_draw(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *layout = op->layout; + ImageOpenData *iod = op->customdata; + ImageFormatData *imf = &iod->im_format; + PointerRNA imf_ptr, ptr; + + /* main draw call */ + RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); + uiDefAutoButsRNA(layout, &ptr, image_open_draw_check_prop, '\0'); + + /* image template */ + RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &imf_ptr); + + /* multiview template */ + if (RNA_boolean_get(op->ptr, "show_multiview")) + uiTemplateImageFormatViews(layout, &imf_ptr, op->ptr); +} + /* called by other space types too */ void IMAGE_OT_open(wmOperatorType *ot) { @@ -1202,6 +1262,7 @@ void IMAGE_OT_open(wmOperatorType *ot) ot->exec = image_open_exec; ot->invoke = image_open_invoke; ot->cancel = image_open_cancel; + ot->ui = image_open_draw; /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -1233,10 +1294,10 @@ static int image_match_len_exec(bContext *C, wmOperator *UNUSED(op)) } - if (!ima || !iuser || !ima->anim) + if (!ima || !iuser || !BKE_image_has_anim(ima)) return OPERATOR_CANCELLED; - iuser->frames = IMB_anim_get_duration(ima->anim, IMB_TC_RECORD_RUN); + iuser->frames = IMB_anim_get_duration(((ImageAnim *) ima->anims.first)->anim, IMB_TC_RECORD_RUN); BKE_image_user_frame_calc(iuser, scene->r.cfra, 0); return OPERATOR_FINISHED; @@ -1433,6 +1494,10 @@ static int save_image_options_init(SaveImageOptions *simopts, SpaceImage *sima, } } + /* use the multiview image settings as the default */ + simopts->im_format.stereo3d_format = *ima->stereo3d_format; + simopts->im_format.views_format = ima->views_format; + /* color management */ BKE_color_managed_display_settings_copy(&simopts->im_format.display_settings, &scene->display_settings); BKE_color_managed_view_settings_copy(&simopts->im_format.view_settings, &scene->view_settings); @@ -1468,30 +1533,131 @@ static void save_image_options_to_op(SaveImageOptions *simopts, wmOperator *op) RNA_string_set(op->ptr, "filepath", simopts->filepath); } +/* returns the pass index for the view_id */ +static int get_multiview_pass_id(RenderResult *rr, ImageUser *iuser, const int view_id) +{ + RenderLayer *rl; + RenderPass *rpass; + int passtype; + short rl_index = 0, rp_index; + + if (rr == NULL || iuser == NULL) + return 0; + + if (BLI_listbase_count_ex(&rr->views, 2) < 2) + return iuser->pass; + + if (RE_HasFakeLayer(rr)) + rl_index ++; /* fake compo/sequencer layer */ + + rl = BLI_findlink(&rr->layers, rl_index); + if (!rl) return iuser->pass; + + rpass = BLI_findlink(&rl->passes, iuser->pass); + passtype = rpass->passtype; + + rp_index = 0; + for (rpass = rl->passes.first; rpass; rpass = rpass->next, rp_index++) { + if (rpass->passtype == passtype && + rpass->view_id == view_id) + { + return rp_index; + } + } + + return iuser->pass; +} + +static void save_image_post(wmOperator *op, ImBuf *ibuf, Image *ima, int ok, int save_copy, const char *relbase, int relative, int do_newpath, const char *filepath) +{ + if (ok) { + if (!save_copy) { + if (do_newpath) { + BLI_strncpy(ibuf->name, filepath, sizeof(ibuf->name)); + BLI_strncpy(ima->name, filepath, sizeof(ima->name)); + } + + ibuf->userflags &= ~IB_BITMAPDIRTY; + + /* change type? */ + if (ima->type == IMA_TYPE_R_RESULT) { + ima->type = IMA_TYPE_IMAGE; + + /* workaround to ensure the render result buffer is no longer used + * by this image, otherwise can crash when a new render result is + * created. */ + if (ibuf->rect && !(ibuf->mall & IB_rect)) + imb_freerectImBuf(ibuf); + if (ibuf->rect_float && !(ibuf->mall & IB_rectfloat)) + imb_freerectfloatImBuf(ibuf); + if (ibuf->zbuf && !(ibuf->mall & IB_zbuf)) + IMB_freezbufImBuf(ibuf); + if (ibuf->zbuf_float && !(ibuf->mall & IB_zbuffloat)) + IMB_freezbuffloatImBuf(ibuf); + } + if (ELEM(ima->source, IMA_SRC_GENERATED, IMA_SRC_VIEWER)) { + ima->source = IMA_SRC_FILE; + ima->type = IMA_TYPE_IMAGE; + } + + /* only image path, never ibuf */ + if (relative) { + BLI_path_rel(ima->name, relbase); /* only after saving */ + } + + IMB_colormanagment_colorspace_from_ibuf_ftype(&ima->colorspace_settings, ibuf); + + } + } + else { + BKE_reportf(op->reports, RPT_ERROR, "Could not write image %s", filepath); + } +} + +static void save_imbuf_post(ImBuf *ibuf, ImBuf *colormanaged_ibuf) +{ + if (colormanaged_ibuf != ibuf) { + /* This guys might be modified by image buffer write functions, + * need to copy them back from color managed image buffer to an + * original one, so file type of image is being properly updated. + */ + ibuf->ftype = colormanaged_ibuf->ftype; + ibuf->planes = colormanaged_ibuf->planes; + + IMB_freeImBuf(colormanaged_ibuf); + } +} + /** * \return success. * \note ``ima->name`` and ``ibuf->name`` should end up the same. + * \note for multiview the first ``ibuf`` is important to get the settings. */ static bool save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveImageOptions *simopts, bool do_newpath) { Image *ima = ED_space_image(sima); void *lock; ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock); + Scene *scene; + RenderResult *rr = NULL; bool ok = false; + WM_cursor_wait(1); + if (ibuf) { - ImBuf *colormanaged_ibuf; + ImBuf *colormanaged_ibuf = NULL; const char *relbase = ID_BLEND_PATH(CTX_data_main(C), &ima->id); const bool relative = (RNA_struct_find_property(op->ptr, "relative_path") && RNA_boolean_get(op->ptr, "relative_path")); const bool save_copy = (RNA_struct_find_property(op->ptr, "copy") && RNA_boolean_get(op->ptr, "copy")); const bool save_as_render = (RNA_struct_find_property(op->ptr, "save_as_render") && RNA_boolean_get(op->ptr, "save_as_render")); ImageFormatData *imf = &simopts->im_format; + const bool is_multilayer = imf->imtype == R_IMF_IMTYPE_MULTILAYER; + bool is_mono; + /* old global to ensure a 2nd save goes to same dir */ BLI_strncpy(G.ima, simopts->filepath, sizeof(G.ima)); - WM_cursor_wait(1); - if (ima->type == IMA_TYPE_R_RESULT) { /* enforce user setting for RGB or RGBA, but skip BW */ if (simopts->im_format.planes == R_IMF_PLANES_RGBA) { @@ -1512,83 +1678,197 @@ static bool save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveI } } - colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, true, &imf->view_settings, &imf->display_settings, imf); + /* we need renderresult for exr and rendered multiview */ + scene = CTX_data_scene(C); + rr = BKE_image_acquire_renderresult(scene, ima); + is_mono = rr ? BLI_listbase_count_ex(&rr->views, 2) < 2 : (ima->flag & IMA_IS_MULTIVIEW) == 0; - if (simopts->im_format.imtype == R_IMF_IMTYPE_MULTILAYER) { - Scene *scene = CTX_data_scene(C); - RenderResult *rr = BKE_image_acquire_renderresult(scene, ima); - if (rr) { - ok = RE_WriteRenderResult(op->reports, rr, simopts->filepath, simopts->im_format.exr_codec); - } - else { + /* error handling */ + if (!rr) { + if (imf->imtype == R_IMF_IMTYPE_MULTILAYER) { BKE_report(op->reports, RPT_ERROR, "Did not write, no Multilayer Image"); + goto cleanup; } - BKE_image_release_renderresult(scene, ima); } else { - ok = BKE_imbuf_write_as(colormanaged_ibuf, simopts->filepath, &simopts->im_format, save_copy); - } - - if (ok) { - if (!save_copy) { - if (do_newpath) { - BLI_strncpy(ibuf->name, simopts->filepath, sizeof(ibuf->name)); - BLI_strncpy(ima->name, simopts->filepath, sizeof(ima->name)); + if (imf->views_format == R_IMF_VIEWS_STEREO_3D) { + if ((ima->flag & IMA_IS_STEREO) == 0) { + BKE_reportf(op->reports, RPT_ERROR, "Did not write, the image doesn't have a \"%s\" and \"%s\" views", + STEREO_LEFT_NAME, STEREO_RIGHT_NAME); + goto cleanup; } - ibuf->userflags &= ~IB_BITMAPDIRTY; - - /* change type? */ - if (ima->type == IMA_TYPE_R_RESULT) { - ima->type = IMA_TYPE_IMAGE; - - /* workaround to ensure the render result buffer is no longer used - * by this image, otherwise can crash when a new render result is - * created. */ - if (ibuf->rect && !(ibuf->mall & IB_rect)) - imb_freerectImBuf(ibuf); - if (ibuf->rect_float && !(ibuf->mall & IB_rectfloat)) - imb_freerectfloatImBuf(ibuf); - if (ibuf->zbuf && !(ibuf->mall & IB_zbuf)) - IMB_freezbufImBuf(ibuf); - if (ibuf->zbuf_float && !(ibuf->mall & IB_zbuffloat)) - IMB_freezbuffloatImBuf(ibuf); + /* it shouldn't ever happen*/ + if ((BLI_findstring(&rr->views, STEREO_LEFT_NAME, offsetof(RenderView, name)) == NULL) || + (BLI_findstring(&rr->views, STEREO_RIGHT_NAME, offsetof(RenderView, name)) == NULL)) + { + BKE_reportf(op->reports, RPT_ERROR, "Did not write, the image doesn't have a \"%s\" and \"%s\" views", + STEREO_LEFT_NAME, STEREO_RIGHT_NAME); + goto cleanup; } - if (ELEM(ima->source, IMA_SRC_GENERATED, IMA_SRC_VIEWER)) { - ima->source = IMA_SRC_FILE; - ima->type = IMA_TYPE_IMAGE; + } + } + + /* fancy multiview OpenEXR */ + if ((imf->imtype == R_IMF_IMTYPE_MULTILAYER) && (imf->views_format == R_IMF_VIEWS_MULTIVIEW)) { + ok = RE_WriteRenderResult(op->reports, rr, simopts->filepath, imf, true, NULL); + save_image_post(op, ibuf, ima, ok, true, relbase, relative, do_newpath, simopts->filepath); + ED_space_image_release_buffer(sima, ibuf, lock); + } + else if ((imf->imtype == R_IMF_IMTYPE_OPENEXR) && (imf->views_format == R_IMF_VIEWS_MULTIVIEW)) { + /* treat special Openexr case separetely (this is the singlelayer multiview OpenEXR */ + BKE_imbuf_write_prepare(ibuf, imf); + ok = BKE_image_save_openexr_multiview(ima, ibuf, simopts->filepath, (IB_rect | IB_zbuf | IB_zbuffloat | IB_multiview)); + ED_space_image_release_buffer(sima, ibuf, lock); + } + /* regular mono pipeline */ + else if (is_mono) { + if (is_multilayer) { + ok = RE_WriteRenderResult(op->reports, rr, simopts->filepath, imf, false, NULL); + } + else { + colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, true, &imf->view_settings, &imf->display_settings, imf); + ok = BKE_imbuf_write_as(colormanaged_ibuf, simopts->filepath, imf, save_copy); + save_imbuf_post(ibuf, colormanaged_ibuf); + } + save_image_post(op, ibuf, ima, ok, (is_multilayer ? true : save_copy), relbase, relative, do_newpath, simopts->filepath); + ED_space_image_release_buffer(sima, ibuf, lock); + } + /* individual multiview images */ + else if (imf->views_format == R_IMF_VIEWS_INDIVIDUAL) { + size_t i; + unsigned char planes = ibuf->planes; + const size_t totviews = (rr ? BLI_listbase_count(&rr->views) : BLI_listbase_count(&ima->views)); + + if (!is_multilayer) { + ED_space_image_release_buffer(sima, ibuf, lock); + } + + for (i = 0; i < totviews; i++) { + char filepath[FILE_MAX]; + bool ok_view = false; + const char *view = rr ? ((RenderView *) BLI_findlink(&rr->views, i))->name : + ((ImageView *) BLI_findlink(&ima->views, i))->name; + + if (is_multilayer) { + BKE_scene_multiview_view_filepath_get(&scene->r, simopts->filepath, view, filepath); + ok_view = RE_WriteRenderResult(op->reports, rr, filepath, imf, false, view); + save_image_post(op, ibuf, ima, ok_view, true, relbase, relative, do_newpath, filepath); } + else { + /* copy iuser to get the correct ibuf for this view */ + ImageUser iuser = sima->iuser; + iuser.view = i; + iuser.flag &= ~IMA_SHOW_STEREO; + + if (rr) { + iuser.pass = get_multiview_pass_id(rr, &sima->iuser, i); + BKE_image_multilayer_index(rr, &iuser); + } + else { + BKE_image_multiview_index(ima, &iuser); + } + + ibuf = BKE_image_acquire_ibuf(sima->image, &iuser, &lock); + ibuf->planes = planes; - /* only image path, never ibuf */ - if (relative) { - BLI_path_rel(ima->name, relbase); /* only after saving */ + BKE_scene_multiview_view_filepath_get(&scene->r, simopts->filepath, view, filepath); + + colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, true, &imf->view_settings, &imf->display_settings, imf); + ok_view = BKE_imbuf_write_as(colormanaged_ibuf, filepath, &simopts->im_format, save_copy); + save_imbuf_post(ibuf, colormanaged_ibuf); + save_image_post(op, ibuf, ima, ok_view, true, relbase, relative, do_newpath, filepath); + BKE_image_release_ibuf(sima->image, ibuf, lock); } + ok &= ok_view; + } - IMB_colormanagment_colorspace_from_ibuf_ftype(&ima->colorspace_settings, ibuf); + if (is_multilayer) { + ED_space_image_release_buffer(sima, ibuf, lock); } } - else { - BKE_reportf(op->reports, RPT_ERROR, "Could not write image %s", simopts->filepath); - } + /* stereo (multiview) images */ + else if (simopts->im_format.views_format == R_IMF_VIEWS_STEREO_3D) { + if (imf->imtype == R_IMF_IMTYPE_MULTILAYER) { + ok = RE_WriteRenderResult(op->reports, rr, simopts->filepath, imf, false, NULL); + save_image_post(op, ibuf, ima, ok, true, relbase, relative, do_newpath, simopts->filepath); + ED_space_image_release_buffer(sima, ibuf, lock); + } + else { + ImBuf *ibuf_stereo[2] = {NULL}; + unsigned char planes = ibuf->planes; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + int i; - WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, sima->image); + /* we need to get the specific per-view buffers */ + ED_space_image_release_buffer(sima, ibuf, lock); + + for (i = 0; i < 2; i ++) { + ImageUser iuser = sima->iuser; + iuser.flag &= ~IMA_SHOW_STEREO; + + if (rr) { + int id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name)); + iuser.pass = get_multiview_pass_id(rr, &sima->iuser, id); + iuser.view = id; + + BKE_image_multilayer_index(rr, &iuser); + } + else { + iuser.view = i; + BKE_image_multiview_index(ima, &iuser); + } + + ibuf = BKE_image_acquire_ibuf(sima->image, &iuser, &lock); + + if (ibuf == NULL) { + BKE_report(op->reports, RPT_ERROR, "Did not write, unexpected error when saving stereo image"); + goto cleanup; + } - WM_cursor_wait(0); + ibuf->planes = planes; - if (colormanaged_ibuf != ibuf) { - /* This guys might be modified by image buffer write functions, - * need to copy them back from color managed image buffer to an - * original one, so file type of image is being properly updated. - */ - ibuf->ftype = colormanaged_ibuf->ftype; - ibuf->planes = colormanaged_ibuf->planes; + /* color manage the ImBuf leaving it ready for saving */ + colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, true, + &imf->view_settings, &imf->display_settings, imf); - IMB_freeImBuf(colormanaged_ibuf); + BKE_imbuf_write_prepare(colormanaged_ibuf, imf); + IMB_prepare_write_ImBuf(IMB_isfloat(colormanaged_ibuf), colormanaged_ibuf); + + /* duplicate buffer to prevent locker issue when using render result */ + ibuf_stereo[i] = IMB_dupImBuf(colormanaged_ibuf); + + save_imbuf_post(ibuf, colormanaged_ibuf); + BKE_image_release_ibuf(sima->image, ibuf, lock); + } + + ibuf = IMB_stereo3d_ImBuf(imf, ibuf_stereo[0], ibuf_stereo[1]); + + /* save via traditional path */ + ok = BKE_imbuf_write_as(ibuf, simopts->filepath, imf, save_copy); + + IMB_freeImBuf(ibuf); + + for (i = 0; i < 2; i ++) { + IMB_freeImBuf(ibuf_stereo[i]); + } + } } + + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, sima->image); + + } + else { +cleanup: + ED_space_image_release_buffer(sima, ibuf, lock); } - ED_space_image_release_buffer(sima, ibuf, lock); + if (rr) { + BKE_image_release_renderresult(scene, ima); + } + + WM_cursor_wait(0); return ok; } @@ -1636,6 +1916,7 @@ static int image_save_as_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS Image *ima = ED_space_image(sima); Scene *scene = CTX_data_scene(C); SaveImageOptions simopts; + PropertyRNA *prop; const bool save_as_render = ((ima->source == IMA_SRC_VIEWER) || (ima->flag & IMA_VIEW_AS_RENDER)); if (RNA_struct_property_is_set(op->ptr, "filepath")) @@ -1657,6 +1938,12 @@ static int image_save_as_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS op->customdata = MEM_mallocN(sizeof(simopts.im_format), __func__); memcpy(op->customdata, &simopts.im_format, sizeof(simopts.im_format)); + /* show multiview save options only if image has multiviews */ + prop = RNA_struct_find_property(op->ptr, "show_multiview"); + RNA_property_boolean_set(op->ptr, prop, (ima->flag & IMA_IS_MULTIVIEW) != 0); + prop = RNA_struct_find_property(op->ptr, "use_multiview"); + RNA_property_boolean_set(op->ptr, prop, (ima->flag & IMA_IS_MULTIVIEW) != 0); + image_filesel(C, op, simopts.filepath); return OPERATOR_RUNNING_MODAL; @@ -1683,15 +1970,20 @@ static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op) { uiLayout *layout = op->layout; ImageFormatData *imf = op->customdata; - PointerRNA ptr; + PointerRNA imf_ptr, ptr; + const bool is_multiview = RNA_boolean_get(op->ptr, "use_multiview"); /* image template */ - RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &ptr); - uiTemplateImageSettings(layout, &ptr, false); + RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &imf_ptr); + uiTemplateImageSettings(layout, &imf_ptr, false); /* main draw call */ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); uiDefAutoButsRNA(layout, &ptr, image_save_as_draw_check_prop, '\0'); + + /* multiview template */ + if (is_multiview) + uiTemplateImageFormatViews(layout, &imf_ptr, NULL); } static int image_save_as_poll(bContext *C) @@ -1714,8 +2006,6 @@ static int image_save_as_poll(bContext *C) void IMAGE_OT_save_as(wmOperatorType *ot) { -// PropertyRNA *prop; - /* identifiers */ ot->name = "Save As Image"; ot->idname = "IMAGE_OT_save_as"; @@ -1932,6 +2222,7 @@ static int image_new_exec(bContext *C, wmOperator *op) float color[4]; int width, height, floatbuf, gen_type, alpha; int gen_context; + int stereo3d; /* retrieve state */ sima = CTX_wm_space_image(C); @@ -1952,11 +2243,12 @@ static int image_new_exec(bContext *C, wmOperator *op) RNA_float_get_array(op->ptr, "color", color); alpha = RNA_boolean_get(op->ptr, "alpha"); gen_context = RNA_enum_get(op->ptr, "gen_context"); + stereo3d = RNA_boolean_get(op->ptr, "use_stereo_3d"); if (!alpha) color[3] = 1.0f; - ima = BKE_image_add_generated(bmain, width, height, name, alpha ? 32 : 24, floatbuf, gen_type, color); + ima = BKE_image_add_generated(bmain, width, height, name, alpha ? 32 : 24, floatbuf, gen_type, color, stereo3d); if (!ima) return OPERATOR_CANCELLED; @@ -2036,6 +2328,53 @@ static int image_new_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e return WM_operator_props_dialog_popup(C, op, 15 * UI_UNIT_X, 5 * UI_UNIT_Y); } +static void image_new_draw(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *split, *col[2]; + uiLayout *layout = op->layout; + PointerRNA ptr; +#if 0 + Scene *scene = CTX_data_scene(C); + const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0; +#endif + + RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); + + /* copy of WM_operator_props_dialog_popup() layout */ + + split = uiLayoutSplit(layout, 0.5f, false); + col[0] = uiLayoutColumn(split, false); + col[1] = uiLayoutColumn(split, false); + + uiItemL(col[0], IFACE_("Name"), ICON_NONE); + uiItemR(col[1], &ptr, "name", 0, "", ICON_NONE); + + uiItemL(col[0], IFACE_("Width"), ICON_NONE); + uiItemR(col[1], &ptr, "width", 0, "", ICON_NONE); + + uiItemL(col[0], IFACE_("Height"), ICON_NONE); + uiItemR(col[1], &ptr, "height", 0, "", ICON_NONE); + + uiItemL(col[0], IFACE_("Color"), ICON_NONE); + uiItemR(col[1], &ptr, "color", 0, "", ICON_NONE); + + uiItemL(col[0], "", ICON_NONE); + uiItemR(col[1], &ptr, "alpha", 0, NULL, ICON_NONE); + + uiItemL(col[0], IFACE_("Generated Type"), ICON_NONE); + uiItemR(col[1], &ptr, "generated_type", 0, "", ICON_NONE); + + uiItemL(col[0], "", ICON_NONE); + uiItemR(col[1], &ptr, "float", 0, NULL, ICON_NONE); + +#if 0 + if (is_multiview) { + uiItemL(col[0], "", ICON_NONE); + uiItemR(col[1], &ptr, "use_stereo_3d", 0, NULL, ICON_NONE); + } +#endif +} + void IMAGE_OT_new(wmOperatorType *ot) { PropertyRNA *prop; @@ -2056,6 +2395,7 @@ void IMAGE_OT_new(wmOperatorType *ot) /* api callbacks */ ot->exec = image_new_exec; ot->invoke = image_new_invoke; + ot->ui = image_new_draw; /* flags */ ot->flag = OPTYPE_UNDO; @@ -2075,7 +2415,8 @@ void IMAGE_OT_new(wmOperatorType *ot) RNA_def_boolean(ot->srna, "float", 0, "32 bit Float", "Create image with 32 bit floating point bit depth"); prop = RNA_def_enum(ot->srna, "gen_context", gen_context_items, 0, "Gen Context", "Generation context"); RNA_def_property_flag(prop, PROP_HIDDEN); - + prop = RNA_def_boolean(ot->srna, "use_stereo_3d", 0, "Stereo 3D", "Create an image with left and right views"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN); } #undef IMA_DEF_NAME @@ -2200,7 +2541,7 @@ static bool image_pack_test(bContext *C, wmOperator *op) if (!ima) return 0; - if (!as_png && ima->packedfile) + if (!as_png && BKE_image_has_packedfile(ima)) return 0; if (ima->source == IMA_SRC_SEQUENCE || ima->source == IMA_SRC_MOVIE) { @@ -2229,7 +2570,7 @@ static int image_pack_exec(bContext *C, wmOperator *op) if (as_png) BKE_image_memorypack(ima); else - ima->packedfile = newPackedFile(op->reports, ima->name, ID_BLEND_PATH(bmain, &ima->id)); + BKE_image_packfiles(op->reports, ima, ID_BLEND_PATH(bmain, &ima->id)); WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); @@ -2301,7 +2642,7 @@ static int image_unpack_exec(bContext *C, wmOperator *op) if (!ima) ima = CTX_data_edit_image(C); } - if (!ima || !ima->packedfile) + if (!ima || !BKE_image_has_packedfile(ima)) return OPERATOR_CANCELLED; if (ima->source == IMA_SRC_SEQUENCE || ima->source == IMA_SRC_MOVIE) { @@ -2329,7 +2670,7 @@ static int image_unpack_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE if (RNA_struct_property_is_set(op->ptr, "id")) return image_unpack_exec(C, op); - if (!ima || !ima->packedfile) + if (!ima || !BKE_image_has_packedfile(ima)) return OPERATOR_CANCELLED; if (ima->source == IMA_SRC_SEQUENCE || ima->source == IMA_SRC_MOVIE) { @@ -2340,7 +2681,7 @@ static int image_unpack_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE if (G.fileflags & G_AUTOPACK) BKE_report(op->reports, RPT_WARNING, "AutoPack is enabled, so image will be packed again on file save"); - unpack_menu(C, "IMAGE_OT_unpack", ima->id.name + 2, ima->name, "textures", ima->packedfile); + unpack_menu(C, "IMAGE_OT_unpack", ima->id.name + 2, ima->name, "textures", BKE_image_has_packedfile(ima) ? ((ImagePackedFile *)ima->packedfiles.first)->packedfile : NULL); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index bd3d76bbaba..1023f9641b3 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -156,7 +156,8 @@ static SpaceLink *image_new(const bContext *UNUSED(C)) simage->iuser.ok = true; simage->iuser.fie_ima = 2; simage->iuser.frames = 100; - + simage->iuser.flag = IMA_SHOW_STEREO; + scopes_new(&simage->scopes); simage->sample_line_hist.height = 100; diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 6fa164a483f..d08a8bacd29 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -703,7 +703,9 @@ static void node_buts_image_user(uiLayout *layout, bContext *C, PointerRNA *ptr, uiItemR(col, ptr, "use_auto_refresh", 0, NULL, ICON_NONE); } - if (RNA_enum_get(imaptr, "type") == IMA_TYPE_MULTILAYER) { + if (RNA_enum_get(imaptr, "type") == IMA_TYPE_MULTILAYER && + RNA_boolean_get(ptr, "has_layers")) + { col = uiLayoutColumn(layout, false); uiItemR(col, ptr, "layer", 0, NULL, ICON_NONE); } @@ -1214,6 +1216,24 @@ static void node_shader_set_butfunc(bNodeType *ntype) /* ****************** BUTTON CALLBACKS FOR COMPOSITE NODES ***************** */ +static void node_buts_image_views(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr, + PointerRNA *imaptr) +{ + uiLayout *col; + + if (!imaptr->data) + return; + + col = uiLayoutColumn(layout, false); + + if (RNA_boolean_get(ptr, "has_views")) { + if (RNA_enum_get(ptr, "view") == 0) + uiItemR(col, ptr, "view", 0, NULL, ICON_CAMERA_STEREO); + else + uiItemR(col, ptr, "view", 0, NULL, ICON_SCENE); + } +} + static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA *ptr) { bNode *node = ptr->data; @@ -1227,6 +1247,8 @@ static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA * imaptr = RNA_pointer_get(ptr, "image"); node_buts_image_user(layout, C, ptr, &imaptr, &iuserptr); + + node_buts_image_views(layout, C, ptr, &imaptr); } static void node_composit_buts_image_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) @@ -1717,8 +1739,8 @@ static void node_composit_buts_id_mask(uiLayout *layout, bContext *UNUSED(C), Po static void node_composit_buts_file_output(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { PointerRNA imfptr = RNA_pointer_get(ptr, "format"); - int multilayer = (RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER); - + const bool multilayer = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER; + if (multilayer) uiItemL(layout, IFACE_("Path:"), ICON_NONE); else @@ -1727,15 +1749,22 @@ static void node_composit_buts_file_output(uiLayout *layout, bContext *UNUSED(C) } static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, PointerRNA *ptr) { + Scene *scene = CTX_data_scene(C); PointerRNA imfptr = RNA_pointer_get(ptr, "format"); PointerRNA active_input_ptr, op_ptr; uiLayout *row, *col; int active_index; - int multilayer = (RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER); + const bool multilayer = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER; + const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0; node_composit_buts_file_output(layout, C, ptr); uiTemplateImageSettings(layout, &imfptr, false); + /* disable stereo output for multilayer, too much work for something that no one will use */ + /* if someone asks for that we can implement it */ + if (is_multiview) + uiTemplateImageFormatViews(layout, &imfptr, NULL); + uiItemS(layout); uiItemO(layout, IFACE_("Add Input"), ICON_ZOOMIN, "NODE_OT_output_file_add_socket"); @@ -1797,6 +1826,9 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi col = uiLayoutColumn(layout, false); uiLayoutSetActive(col, RNA_boolean_get(&active_input_ptr, "use_node_format") == false); uiTemplateImageSettings(col, &imfptr, false); + + if (is_multiview) + uiTemplateImageFormatViews(layout, &imfptr, NULL); } } } @@ -2090,6 +2122,18 @@ static void node_composit_buts_switch(uiLayout *layout, bContext *UNUSED(C), Poi uiItemR(layout, ptr, "check", 0, NULL, ICON_NONE); } +static void node_composit_buts_switch_view_ex(uiLayout *layout, bContext *UNUSED(C), PointerRNA *UNUSED(ptr)) +{ + PointerRNA op_ptr; + wmOperatorType *ot = WM_operatortype_find("NODE_OT_switch_view_update", 1); + + BLI_assert(ot != 0); + + WM_operator_properties_create_ptr(&op_ptr, ot); + + uiItemFullO_ptr(layout, ot, "Update Views", ICON_FILE_REFRESH, op_ptr.data, WM_OP_INVOKE_DEFAULT, 0); +} + static void node_composit_buts_boxmask(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { uiLayout *row; @@ -2587,6 +2631,9 @@ static void node_composit_set_butfunc(bNodeType *ntype) case CMP_NODE_SWITCH: ntype->draw_buttons = node_composit_buts_switch; break; + case CMP_NODE_SWITCH_VIEW: + ntype->draw_buttons_ex = node_composit_buts_switch_view_ex; + break; case CMP_NODE_MASK_BOX: ntype->draw_buttons = node_composit_buts_boxmask; ntype->draw_backdrop = node_composit_backdrop_boxmask; @@ -2959,6 +3006,7 @@ static void node_file_output_socket_draw(bContext *C, uiLayout *layout, PointerR imfptr = RNA_pointer_get(node_ptr, "format"); imtype = RNA_enum_get(&imfptr, "file_format"); + if (imtype == R_IMF_IMTYPE_MULTILAYER) { NodeImageMultiFileSocket *input = sock->storage; RNA_pointer_create(&ntree->id, &RNA_NodeOutputFileSlotLayer, input, &inputptr); diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index 27af4e7490b..80ecab09442 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -212,13 +212,13 @@ static void compo_progressjob(void *cjv, float progress) *(cj->progress) = progress; } - /* only this runs inside thread */ static void compo_startjob(void *cjv, short *stop, short *do_update, float *progress) { CompoJob *cj = cjv; bNodeTree *ntree = cj->localtree; Scene *scene = cj->scene; + SceneRenderView *srv; if (scene->use_nodes == false) return; @@ -238,7 +238,11 @@ static void compo_startjob(void *cjv, short *stop, short *do_update, float *prog // XXX BIF_store_spare(); /* 1 is do_previews */ - ntreeCompositExecTree(cj->scene, ntree, &cj->scene->r, false, true, &scene->view_settings, &scene->display_settings); + + for (srv = scene->r.views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(&scene->r, srv) == false) continue; + ntreeCompositExecTree(cj->scene, ntree, &cj->scene->r, false, true, &scene->view_settings, &scene->display_settings, srv->name); + } ntree->test_break = NULL; ntree->stats_draw = NULL; @@ -1660,6 +1664,54 @@ void NODE_OT_delete(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } +/* ****************** Switch View ******************* */ + +static int node_switch_view_poll(bContext *C) +{ + SpaceNode *snode = CTX_wm_space_node(C); + + if (snode && snode->edittree) + return true; + + return false; +} + +static int node_switch_view_exec(bContext *C, wmOperator *UNUSED(op)) +{ + SpaceNode *snode = CTX_wm_space_node(C); + bNode *node, *next; + + for (node = snode->edittree->nodes.first; node; node = next) { + next = node->next; + if (node->flag & SELECT) { + /* call the update function from the Switch View node */ + node->update = NODE_UPDATE_OPERATOR; + } + } + + ntreeUpdateTree(CTX_data_main(C), snode->edittree); + + snode_notify(C, snode); + snode_dag_update(C, snode); + + return OPERATOR_FINISHED; +} + +void NODE_OT_switch_view_update(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Update Views"; + ot->description = "Update views of selected node"; + ot->idname = "NODE_OT_switch_view_update"; + + /* api callbacks */ + ot->exec = node_switch_view_exec; + ot->poll = node_switch_view_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + /* ****************** Delete with reconnect ******************* */ static int node_delete_reconnect_exec(bContext *C, wmOperator *UNUSED(op)) { diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index d49d37c651e..b15e9025a82 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -200,6 +200,8 @@ void NODE_OT_output_file_add_socket(struct wmOperatorType *ot); void NODE_OT_output_file_remove_active_socket(struct wmOperatorType *ot); void NODE_OT_output_file_move_active_socket(struct wmOperatorType *ot); +void NODE_OT_switch_view_update (struct wmOperatorType *ot); + /* Note: clipboard_cut is a simple macro of copy + delete */ void NODE_OT_clipboard_copy(struct wmOperatorType *ot); void NODE_OT_clipboard_paste(struct wmOperatorType *ot); diff --git a/source/blender/editors/space_node/node_ops.c b/source/blender/editors/space_node/node_ops.c index e2d83c243a2..474ad4db4af 100644 --- a/source/blender/editors/space_node/node_ops.c +++ b/source/blender/editors/space_node/node_ops.c @@ -123,6 +123,8 @@ void node_operatortypes(void) WM_operatortype_append(NODE_OT_viewer_border); WM_operatortype_append(NODE_OT_clear_viewer_border); + WM_operatortype_append(NODE_OT_switch_view_update); + WM_operatortype_append(NODE_OT_tree_socket_add); WM_operatortype_append(NODE_OT_tree_socket_remove); WM_operatortype_append(NODE_OT_tree_socket_move); diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 329e6db4cd0..373095d68f9 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -60,6 +60,7 @@ #include "ED_screen.h" #include "ED_sequencer.h" +#include "UI_interface.h" #include "BKE_sound.h" @@ -70,6 +71,10 @@ /* own include */ #include "sequencer_intern.h" +typedef struct SequencerAddData { + ImageFormatData im_format; +} SequencerAddData; + /* Generic functions, reused by add strip operators */ /* avoid passing multiple args and be more verbose */ @@ -222,6 +227,19 @@ static void seq_load_operator_info(SeqLoadInfo *seq_load, wmOperator *op) } RNA_PROP_END; } + + if ((prop = RNA_struct_find_property(op->ptr, "use_multiview")) && RNA_property_boolean_get(op->ptr, prop)) { + if (op->customdata) { + SequencerAddData *sad = op->customdata; + ImageFormatData *imf = &sad->im_format; + + seq_load->views_format = imf->views_format; + seq_load->flag |= SEQ_USE_VIEWS; + + /* operator custom data is always released after the SeqLoadInfo, no need to handle the memory here */ + seq_load->stereo3d_format = &imf->stereo3d_format; + } + } } /** @@ -573,6 +591,9 @@ static int sequencer_add_generic_strip_exec(bContext *C, wmOperator *op, SeqLoad return OPERATOR_CANCELLED; } + if (op->customdata) + MEM_freeN(op->customdata); + BKE_sequencer_sort(scene); BKE_sequencer_update_muting(ed); @@ -581,15 +602,40 @@ static int sequencer_add_generic_strip_exec(bContext *C, wmOperator *op, SeqLoad return OPERATOR_FINISHED; } +/* add sequencer operators */ +static void sequencer_add_init(bContext *UNUSED(C), wmOperator *op) +{ + op->customdata = MEM_callocN(sizeof(SequencerAddData), __func__); +} + +static void sequencer_add_cancel(bContext *UNUSED(C), wmOperator *op) +{ + if (op->customdata) + MEM_freeN(op->customdata); + op->customdata = NULL; +} + +static bool sequencer_add_draw_check_prop(PointerRNA *UNUSED(ptr), PropertyRNA *prop) +{ + const char *prop_id = RNA_property_identifier(prop); + + return !(STREQ(prop_id, "filepath") || + STREQ(prop_id, "directory") || + STREQ(prop_id, "filename") + ); +} + /* add movie operator */ static int sequencer_add_movie_strip_exec(bContext *C, wmOperator *op) { return sequencer_add_generic_strip_exec(C, op, BKE_sequencer_add_movie_strip); } - static int sequencer_add_movie_strip_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { + PropertyRNA *prop; + Scene *scene = CTX_data_scene(C); + /* This is for drag and drop */ if ((RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) || RNA_struct_property_is_set(op->ptr, "filepath")) @@ -599,13 +645,37 @@ static int sequencer_add_movie_strip_invoke(bContext *C, wmOperator *op, const w } sequencer_generic_invoke_xy__internal(C, op, 0, SEQ_TYPE_MOVIE); - + + sequencer_add_init(C, op); + + /* show multiview save options only if scene has multiviews */ + prop = RNA_struct_find_property(op->ptr, "show_multiview"); + RNA_property_boolean_set(op->ptr, prop, (scene->r.scemode & R_MULTIVIEW) != 0); + WM_event_add_fileselect(C, op); return OPERATOR_RUNNING_MODAL; //return sequencer_add_movie_strip_exec(C, op); } +static void sequencer_add_draw(bContext *UNUSED(C), wmOperator *op) +{ + uiLayout *layout = op->layout; + SequencerAddData *sad = op->customdata; + ImageFormatData *imf = &sad->im_format; + PointerRNA imf_ptr, ptr; + + /* main draw call */ + RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr); + uiDefAutoButsRNA(layout, &ptr, sequencer_add_draw_check_prop, '\0'); + + /* image template */ + RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &imf_ptr); + + /* multiview template */ + if (RNA_boolean_get(op->ptr, "show_multiview")) + uiTemplateImageFormatViews(layout, &imf_ptr, op->ptr); +} void SEQUENCER_OT_movie_strip_add(struct wmOperatorType *ot) { @@ -618,9 +688,11 @@ void SEQUENCER_OT_movie_strip_add(struct wmOperatorType *ot) /* api callbacks */ ot->invoke = sequencer_add_movie_strip_invoke; ot->exec = sequencer_add_movie_strip_exec; + ot->cancel = sequencer_add_cancel; + ot->ui = sequencer_add_draw; ot->poll = ED_operator_sequencer_active_editable; - + /* flags */ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; @@ -735,6 +807,9 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op) sequencer_add_apply_overlap(C, op, seq); + if (op->customdata) + MEM_freeN(op->customdata); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); return OPERATOR_FINISHED; @@ -742,6 +817,9 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op) static int sequencer_add_image_strip_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { + PropertyRNA *prop; + Scene *scene = CTX_data_scene(C); + /* drag drop has set the names */ if (RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) { sequencer_generic_invoke_xy__internal(C, op, SEQPROP_ENDFRAME | SEQPROP_NOPATHS, SEQ_TYPE_IMAGE); @@ -750,6 +828,12 @@ static int sequencer_add_image_strip_invoke(bContext *C, wmOperator *op, const w sequencer_generic_invoke_xy__internal(C, op, SEQPROP_ENDFRAME, SEQ_TYPE_IMAGE); + sequencer_add_init(C, op); + + /* show multiview save options only if scene has multiviews */ + prop = RNA_struct_find_property(op->ptr, "show_multiview"); + RNA_property_boolean_set(op->ptr, prop, (scene->r.scemode & R_MULTIVIEW) != 0); + WM_event_add_fileselect(C, op); return OPERATOR_RUNNING_MODAL; } @@ -766,6 +850,8 @@ void SEQUENCER_OT_image_strip_add(struct wmOperatorType *ot) /* api callbacks */ ot->invoke = sequencer_add_image_strip_invoke; ot->exec = sequencer_add_image_strip_exec; + ot->cancel = sequencer_add_cancel; + ot->ui = sequencer_add_draw; ot->poll = ED_operator_sequencer_active_editable; diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index ec4ae5a3a62..baadc28cd65 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -50,6 +50,7 @@ #include "BKE_main.h" #include "BKE_sequencer.h" #include "BKE_sound.h" +#include "BKE_scene.h" #include "IMB_colormanagement.h" #include "IMB_imbuf.h" @@ -860,7 +861,7 @@ void ED_sequencer_special_preview_clear(void) sequencer_special_update_set(NULL); } -ImBuf *sequencer_ibuf_get(struct Main *bmain, Scene *scene, SpaceSeq *sseq, int cfra, int frame_ofs) +ImBuf *sequencer_ibuf_get(struct Main *bmain, Scene *scene, SpaceSeq *sseq, int cfra, int frame_ofs, const char *viewname) { SeqRenderData context; ImBuf *ibuf; @@ -888,6 +889,7 @@ ImBuf *sequencer_ibuf_get(struct Main *bmain, Scene *scene, SpaceSeq *sseq, int bmain->eval_ctx, bmain, scene, rectx, recty, proxy_size, &context); + context.view_id = BKE_scene_multiview_view_id_get(&scene->r, viewname); /* sequencer could start rendering, in this case we need to be sure it wouldn't be canceled * by Esc pressed somewhere in the past @@ -994,6 +996,7 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq const bool is_imbuf = ED_space_sequencer_check_show_imbuf(sseq); int format, type; bool glsl_used = false; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; if (G.is_rendering == false && (scene->r.seq_flag & R_SEQ_GL_PREV) == 0) { /* stop all running jobs, except screen one. currently previews frustrate Render @@ -1023,8 +1026,9 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq return; } - ibuf = sequencer_ibuf_get(bmain, scene, sseq, cfra, frame_ofs); - + /* for now we only support Left/Right */ + ibuf = sequencer_ibuf_get(bmain, scene, sseq, cfra, frame_ofs, names[sseq->multiview_eye]); + if (ibuf == NULL) return; diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index ad38cb66946..138d9b943fc 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -180,8 +180,6 @@ static void seq_proxy_build_job(const bContext *C) Scene *scene = CTX_data_scene(C); Editing *ed = BKE_sequencer_editing_get(scene, false); ScrArea *sa = CTX_wm_area(C); - struct SeqIndexBuildContext *context; - LinkData *link; Sequence *seq; GSet *file_list; @@ -209,9 +207,7 @@ static void seq_proxy_build_job(const bContext *C) SEQP_BEGIN (ed, seq) { if ((seq->flag & SELECT)) { - context = BKE_sequencer_proxy_rebuild_context(pj->main, pj->scene, seq, file_list); - link = BLI_genericNodeN(context); - BLI_addtail(&pj->queue, link); + BKE_sequencer_proxy_rebuild_context(pj->main, pj->scene, seq, file_list, &pj->queue); } } SEQ_END @@ -3442,12 +3438,18 @@ static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op)) SEQP_BEGIN(ed, seq) { if ((seq->flag & SELECT)) { - struct SeqIndexBuildContext *context; + ListBase queue = {NULL, NULL}; + LinkData *link; short stop = 0, do_update; float progress; - context = BKE_sequencer_proxy_rebuild_context(bmain, scene, seq, file_list); - BKE_sequencer_proxy_rebuild(context, &stop, &do_update, &progress); - BKE_sequencer_proxy_rebuild_finish(context, 0); + + BKE_sequencer_proxy_rebuild_context(bmain, scene, seq, file_list, &queue); + + for (link = queue.first; link; link = link->next) { + struct SeqIndexBuildContext *context = link->data; + BKE_sequencer_proxy_rebuild(context, &stop, &do_update, &progress); + BKE_sequencer_proxy_rebuild_finish(context, 0); + } BKE_sequencer_free_imbuf(scene, &ed->seqbase, false); } } diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index b02e254370a..78255507321 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -62,7 +62,7 @@ void sequencer_special_update_set(Sequence *seq); /* UNUSED */ // void seq_reset_imageofs(struct SpaceSeq *sseq); -struct ImBuf *sequencer_ibuf_get(struct Main *bmain, struct Scene *scene, struct SpaceSeq *sseq, int cfra, int frame_ofs); +struct ImBuf *sequencer_ibuf_get(struct Main *bmain, struct Scene *scene, struct SpaceSeq *sseq, int cfra, int frame_ofs, const char *viewname); /* sequencer_edit.c */ struct View2D; diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c index 7fdbc9cc7de..4eafd2f86a8 100644 --- a/source/blender/editors/space_sequencer/sequencer_view.c +++ b/source/blender/editors/space_sequencer/sequencer_view.c @@ -94,7 +94,7 @@ static void sample_apply(bContext *C, wmOperator *op, const wmEvent *event) Scene *scene = CTX_data_scene(C); SpaceSeq *sseq = (SpaceSeq *) CTX_wm_space_data(C); ARegion *ar = CTX_wm_region(C); - ImBuf *ibuf = sequencer_ibuf_get(bmain, scene, sseq, CFRA, 0); + ImBuf *ibuf = sequencer_ibuf_get(bmain, scene, sseq, CFRA, 0, NULL); ImageSampleInfo *info = op->customdata; float fx, fy; diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c index f14c19dc07e..f0b78649307 100644 --- a/source/blender/editors/space_view3d/drawobject.c +++ b/source/blender/editors/space_view3d/drawobject.c @@ -1741,6 +1741,238 @@ static void draw_viewport_reconstruction(Scene *scene, Base *base, View3D *v3d, GPU_select_load_id(base->selcol); } +static void drawcamera_volume(float near_plane[4][3], float far_plane[4][3], const GLenum mode) +{ + glBegin(mode); + glVertex3fv(near_plane[0]); + glVertex3fv(far_plane[0]); + glVertex3fv(far_plane[1]); + glVertex3fv(near_plane[1]); + glEnd(); + + glBegin(mode); + glVertex3fv(near_plane[1]); + glVertex3fv(far_plane[1]); + glVertex3fv(far_plane[2]); + glVertex3fv(near_plane[2]); + glEnd(); + + glBegin(mode); + glVertex3fv(near_plane[2]); + glVertex3fv(near_plane[1]); + glVertex3fv(far_plane[1]); + glVertex3fv(far_plane[2]); + glEnd(); + + glBegin(mode); + glVertex3fv(far_plane[0]); + glVertex3fv(near_plane[0]); + glVertex3fv(near_plane[3]); + glVertex3fv(far_plane[3]); + glEnd(); +} + +/* camera frame */ +static void drawcamera_frame(float vec[4][3], const GLenum mode) +{ + glBegin(mode); + glVertex3fv(vec[0]); + glVertex3fv(vec[1]); + glVertex3fv(vec[2]); + glVertex3fv(vec[3]); + glEnd(); +} + +/* center point to camera frame */ +static void drawcamera_framelines(float vec[4][3], float origin[3]) +{ + glBegin(GL_LINE_STRIP); + glVertex3fv(vec[1]); + glVertex3fv(origin); + glVertex3fv(vec[0]); + glVertex3fv(vec[3]); + glVertex3fv(origin); + glVertex3fv(vec[2]); + glEnd(); +} + +static bool drawcamera_is_stereo3d(Scene *scene, View3D *v3d, Object *ob) +{ + return (ob == v3d->camera) && + (scene->r.scemode & R_MULTIVIEW) != 0 && + (v3d->stereo3d_flag); +} + +static void drawcamera_stereo3d( + Scene *scene, View3D *v3d, RegionView3D *rv3d, Object *ob, const Camera *cam, + float vec[4][3], float drawsize, const float scale[3]) +{ + int i, j; + float obmat[4][4]; + float vec_lr[2][4][3]; + const float fac = (cam->stereo.pivot == CAM_S3D_PIVOT_CENTER) ? 2.0f : 1.0f; + float origin[2][3] = {{0}}; + float tvec[3]; + const Camera *cam_lr[2]; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + + const bool is_stereo3d_cameras = (v3d->stereo3d_flag & V3D_S3D_DISPCAMERAS) && (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D); + const bool is_stereo3d_plane = (v3d->stereo3d_flag & V3D_S3D_DISPPLANE) && (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D); + const bool is_stereo3d_volume = (v3d->stereo3d_flag & V3D_S3D_DISPVOLUME); + + zero_v3(tvec); + + glPushMatrix(); + + for (i = 0; i < 2; i++) { + ob = BKE_camera_multiview_render(scene, ob, names[i]); + cam_lr[i] = ob->data; + + glLoadMatrixf(rv3d->viewmat); + BKE_camera_multiview_model_matrix(&scene->r, ob, names[i], obmat); + glMultMatrixf(obmat); + + copy_m3_m3(vec_lr[i], vec); + copy_v3_v3(vec_lr[i][3], vec[3]); + + if (cam->stereo.convergence_mode == CAM_S3D_OFFAXIS) { + const float shift_x = + ((BKE_camera_multiview_shift_x(&scene->r, ob, names[i]) - cam->shiftx) * + (drawsize * scale[0] * fac)); + + for (j = 0; j < 4; j++) { + vec_lr[i][j][0] += shift_x; + } + } + + if (is_stereo3d_cameras) { + /* camera frame */ + drawcamera_frame(vec_lr[i], GL_LINE_LOOP); + + /* center point to camera frame */ + drawcamera_framelines(vec_lr[i], tvec); + } + + /* connecting line */ + mul_m4_v3(obmat, origin[i]); + + /* convergence plane */ + if (is_stereo3d_plane || is_stereo3d_volume) { + for (j = 0; j < 4; j++) { + mul_m4_v3(obmat, vec_lr[i][j]); + } + } + } + + + /* the remaining drawing takes place in the view space */ + glLoadMatrixf(rv3d->viewmat); + + if (is_stereo3d_cameras) { + /* draw connecting lines */ + glPushAttrib(GL_ENABLE_BIT); + + glLineStipple(2, 0xAAAA); + glEnable(GL_LINE_STIPPLE); + + glBegin(GL_LINES); + glVertex3fv(origin[0]); + glVertex3fv(origin[1]); + glEnd(); + glPopAttrib(); + } + + /* draw convergence plane*/ + if (is_stereo3d_plane) { + float axis_center[3], screen_center[3]; + float world_plane[4][3]; + float local_plane[4][3]; + float offset; + + mid_v3_v3v3(axis_center, origin[0], origin[1]); + + for (i = 0; i < 4; i++) { + mid_v3_v3v3(world_plane[i], vec_lr[0][i], vec_lr[1][i]); + sub_v3_v3v3(local_plane[i], world_plane[i], axis_center); + } + + mid_v3_v3v3(screen_center, world_plane[0], world_plane[2]); + offset = cam->stereo.convergence_distance / len_v3v3(screen_center, axis_center); + + for (i = 0; i < 4; i++) { + mul_v3_fl(local_plane[i], offset); + add_v3_v3(local_plane[i], axis_center); + } + + glColor3f(0.0f, 0.0f, 0.0f); + + /* camera frame */ + drawcamera_frame(local_plane, GL_LINE_LOOP); + + if (v3d->stereo3d_convergence_alpha > 0.0f) { + glEnable(GL_BLEND); + glDepthMask(0); /* disable write in zbuffer, needed for nice transp */ + + glColor4f(0.0f, 0.0f, 0.0f, v3d->stereo3d_convergence_alpha); + + drawcamera_frame(local_plane, GL_QUADS); + + glDisable(GL_BLEND); + glDepthMask(1); /* restore write in zbuffer */ + } + } + + /* draw convergence plane*/ + if (is_stereo3d_volume) { + float screen_center[3]; + float near_plane[4][3], far_plane[4][3]; + float offset; + int j; + + for (i = 0; i < 2; i++) { + mid_v3_v3v3(screen_center, vec_lr[i][0], vec_lr[i][2]); + + offset = len_v3v3(screen_center, origin[i]); + + for (j = 0; j < 4; j++) { + sub_v3_v3v3(near_plane[j], vec_lr[i][j], origin[i]); + mul_v3_fl(near_plane[j], cam_lr[i]->clipsta / offset); + add_v3_v3(near_plane[j], origin[i]); + + sub_v3_v3v3(far_plane[j], vec_lr[i][j], origin[i]); + mul_v3_fl(far_plane[j], cam_lr[i]->clipend / offset); + add_v3_v3(far_plane[j], origin[i]); + } + + /* camera frame */ + glColor3f(0.0f, 0.0f, 0.0f); + + drawcamera_frame(near_plane, GL_LINE_LOOP); + drawcamera_frame(far_plane, GL_LINE_LOOP); + drawcamera_volume(near_plane, far_plane, GL_LINE_LOOP); + + if (v3d->stereo3d_volume_alpha > 0.0f) { + glEnable(GL_BLEND); + glDepthMask(0); /* disable write in zbuffer, needed for nice transp */ + + if (i == 0) + glColor4f(0.0f, 1.0f, 1.0f, v3d->stereo3d_volume_alpha); + else + glColor4f(1.0f, 0.0f, 0.0f, v3d->stereo3d_volume_alpha); + + drawcamera_frame(near_plane, GL_QUADS); + drawcamera_frame(far_plane, GL_QUADS); + drawcamera_volume(near_plane, far_plane, GL_QUADS); + + glDisable(GL_BLEND); + glDepthMask(1); /* restore write in zbuffer */ + } + } + } + + glPopMatrix(); +} + /* flag similar to draw_object() */ static void drawcamera(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base, const short dflag, const unsigned char ob_wire_col[4]) @@ -1755,6 +1987,12 @@ static void drawcamera(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base const bool is_view = (rv3d->persp == RV3D_CAMOB && ob == v3d->camera); MovieClip *clip = BKE_object_movieclip_get(scene, base->object, false); + const bool is_stereo3d = drawcamera_is_stereo3d(scene, v3d, ob); + const bool is_stereo3d_cameras = (ob == scene->camera) && + (scene->r.scemode & R_MULTIVIEW) && + (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D) && + (v3d->stereo3d_flag & V3D_S3D_DISPCAMERAS); + /* draw data for movie clip set as active for scene */ if (clip) { draw_viewport_reconstruction(scene, base, v3d, clip, dflag, ob_wire_col, false); @@ -1791,12 +2029,8 @@ static void drawcamera(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base glDisable(GL_CULL_FACE); /* camera frame */ - glBegin(GL_LINE_LOOP); - glVertex3fv(vec[0]); - glVertex3fv(vec[1]); - glVertex3fv(vec[2]); - glVertex3fv(vec[3]); - glEnd(); + if (!is_stereo3d_cameras) + drawcamera_frame(vec, GL_LINE_LOOP); if (is_view) return; @@ -1804,20 +2038,12 @@ static void drawcamera(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base zero_v3(tvec); /* center point to camera frame */ - glBegin(GL_LINE_STRIP); - glVertex3fv(vec[1]); - glVertex3fv(tvec); - glVertex3fv(vec[0]); - glVertex3fv(vec[3]); - glVertex3fv(tvec); - glVertex3fv(vec[2]); - glEnd(); - + if (!is_stereo3d_cameras) + drawcamera_framelines(vec, tvec); /* arrow on top */ tvec[2] = vec[1][2]; /* copy the depth */ - /* draw an outline arrow for inactive cameras and filled * for active cameras. We actually draw both outline+filled * for active cameras so the wire can be seen side-on */ @@ -1867,6 +2093,11 @@ static void drawcamera(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base glPopMatrix(); } } + + /* stereo cameras drawing */ + if (is_stereo3d) { + drawcamera_stereo3d(scene, v3d, rv3d, ob, cam, vec, drawsize, scale); + } } /* flag similar to draw_object() */ diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index ddb3e2f20c5..f8345e91afd 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -344,7 +344,13 @@ static SpaceLink *view3d_new(const bContext *C) v3d->bundle_size = 0.2f; v3d->bundle_drawtype = OB_PLAINAXES; - + + /* stereo */ + v3d->stereo3d_camera = STEREO_3D_ID; + v3d->stereo3d_flag |= V3D_S3D_DISPPLANE; + v3d->stereo3d_convergence_alpha = 0.15f; + v3d->stereo3d_volume_alpha = 0.05f; + /* header */ ar = MEM_callocN(sizeof(ARegion), "header for view3d"); diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c index e480593b1e5..95a1ab87348 100644 --- a/source/blender/editors/space_view3d/view3d_draw.c +++ b/source/blender/editors/space_view3d/view3d_draw.c @@ -50,6 +50,7 @@ #include "BLI_math.h" #include "BLI_utildefines.h" #include "BLI_endian_switch.h" +#include "BLI_threads.h" #include "BKE_anim.h" #include "BKE_camera.h" @@ -100,6 +101,11 @@ #include "view3d_intern.h" /* own include */ +/* prototypes */ +static void view3d_stereo3d_setup(Scene *scene, View3D *v3d, ARegion *ar); +static void view3d_stereo3d_setup_offscreen(Scene *scene, View3D *v3d, ARegion *ar, + float winmat[4][4], const char *viewname); + /* handy utility for drawing shapes in the viewport for arbitrary code. * could add lines and points too */ // #define DEBUG_DRAW @@ -1603,6 +1609,24 @@ exit: /* ************************************************************* */ +static void view3d_stereo_bgpic_setup(Scene *scene, View3D *v3d, Image *ima, ImageUser *iuser) +{ + if ((ima->flag & IMA_IS_STEREO)) { + iuser->flag |= IMA_SHOW_STEREO; + + if ((scene->r.scemode & R_MULTIVIEW) == 0) + iuser->multiview_eye = STEREO_LEFT_ID; + + /* show only left or right camera */ + else if (v3d->stereo3d_camera != STEREO_3D_ID) + iuser->multiview_eye = v3d->stereo3d_camera; + + BKE_image_multiview_index(ima, iuser); + } + else + iuser->flag &= ~IMA_SHOW_STEREO; +} + static void view3d_draw_bgpic(Scene *scene, ARegion *ar, View3D *v3d, const bool do_foreground, const bool do_camera_frame) { @@ -1646,6 +1670,7 @@ static void view3d_draw_bgpic(Scene *scene, ARegion *ar, View3D *v3d, ibuf = NULL; /* frame is out of range, dont show */ } else { + view3d_stereo_bgpic_setup(scene, v3d, ima, &bgpic->iuser); ibuf = BKE_image_acquire_ibuf(ima, &bgpic->iuser, &lock); releaseibuf = ibuf; } @@ -2551,7 +2576,7 @@ static void gpu_update_lamps_shadows_world(Scene *scene, View3D *v3d) ED_view3d_draw_offscreen( scene, v3d, &ar, winsize, winsize, viewmat, winmat, false, false, true, - NULL, NULL, NULL); + NULL, NULL, NULL, NULL); GPU_lamp_shadow_buffer_unbind(shadow->lamp); v3d->drawtype = drawtype; @@ -3097,7 +3122,8 @@ void ED_view3d_draw_offscreen( float viewmat[4][4], float winmat[4][4], bool do_bgpic, bool do_sky, bool is_persp, GPUOffScreen *ofs, - GPUFX *fx, GPUFXSettings *fx_settings) + GPUFX *fx, GPUFXSettings *fx_settings, + const char *viewname) { struct bThemeState theme_state; int bwinx, bwiny; @@ -3132,7 +3158,10 @@ void ED_view3d_draw_offscreen( } /* setup view matrices before fx or unbinding the offscreen buffers will cause issues */ - view3d_main_area_setup_view(scene, v3d, ar, viewmat, winmat); + if ((viewname != NULL && viewname[0] != '\0') && (viewmat == NULL)) + view3d_stereo3d_setup_offscreen(scene, v3d, ar, winmat, viewname); + else + view3d_main_area_setup_view(scene, v3d, ar, viewmat, winmat); /* framebuffer fx needed, we need to draw offscreen first */ if (v3d->fx_settings.fx_flag && fx) { @@ -3196,7 +3225,7 @@ void ED_view3d_draw_offscreen( /* utility func for ED_view3d_draw_offscreen */ ImBuf *ED_view3d_draw_offscreen_imbuf(Scene *scene, View3D *v3d, ARegion *ar, int sizex, int sizey, unsigned int flag, - bool draw_background, int alpha_mode, char err_out[256]) + bool draw_background, int alpha_mode, const char *viewname, char err_out[256]) { RegionView3D *rv3d = ar->regiondata; ImBuf *ibuf; @@ -3221,28 +3250,29 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Scene *scene, View3D *v3d, ARegion *ar, in if (rv3d->persp == RV3D_CAMOB && v3d->camera) { CameraParams params; GPUFXSettings fx_settings = {NULL}; - Object *camera = v3d->camera; + Object *camera = BKE_camera_multiview_render(scene, v3d->camera, viewname); BKE_camera_params_init(¶ms); /* fallback for non camera objects */ params.clipsta = v3d->near; params.clipend = v3d->far; - BKE_camera_params_from_object(¶ms, v3d->camera); + BKE_camera_params_from_object(¶ms, camera); + BKE_camera_multiview_params(&scene->r, ¶ms, camera, viewname); BKE_camera_params_compute_viewplane(¶ms, sizex, sizey, scene->r.xasp, scene->r.yasp); BKE_camera_params_compute_matrix(¶ms); BKE_camera_to_gpu_dof(camera, &fx_settings); ED_view3d_draw_offscreen( - scene, v3d, ar, sizex, sizey, NULL, params.winmat, + scene, v3d, ar, sizex, sizey, NULL, params.winmat, draw_background, draw_sky, !params.is_ortho, - ofs, NULL, &fx_settings); + ofs, NULL, &fx_settings, viewname); } else { ED_view3d_draw_offscreen( scene, v3d, ar, sizex, sizey, NULL, NULL, draw_background, draw_sky, true, - ofs, NULL, NULL); + ofs, NULL, NULL, viewname); } /* read in pixels & stamp */ @@ -3267,7 +3297,8 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Scene *scene, View3D *v3d, ARegion *ar, in /* creates own 3d views, used by the sequencer */ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Scene *scene, Object *camera, int width, int height, unsigned int flag, int drawtype, - bool use_solid_tex, bool use_gpencil, bool draw_background, int alpha_mode, char err_out[256]) + bool use_solid_tex, bool use_gpencil, bool draw_background, int alpha_mode, + const char *viewname, char err_out[256]) { View3D v3d = {NULL}; ARegion ar = {NULL}; @@ -3297,9 +3328,11 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Scene *scene, Object *camera, int w { CameraParams params; + Object *camera = BKE_camera_multiview_render(scene, v3d.camera, viewname); BKE_camera_params_init(¶ms); - BKE_camera_params_from_object(¶ms, v3d.camera); + BKE_camera_params_from_object(¶ms, camera); + BKE_camera_multiview_params(&scene->r, ¶ms, camera, viewname); BKE_camera_params_compute_viewplane(¶ms, width, height, scene->r.xasp, scene->r.yasp); BKE_camera_params_compute_matrix(¶ms); @@ -3313,7 +3346,7 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Scene *scene, Object *camera, int w invert_m4_m4(rv3d.persinv, rv3d.viewinv); return ED_view3d_draw_offscreen_imbuf(scene, &v3d, &ar, width, height, flag, - draw_background, alpha_mode, err_out); + draw_background, alpha_mode, viewname, err_out); // seq_view3d_cb(scene, cfra, render_size, seqrectx, seqrecty); } @@ -3518,6 +3551,104 @@ static void view3d_main_area_draw_engine_info(View3D *v3d, RegionView3D *rv3d, A ED_region_info_draw(ar, rv3d->render_engine->text, 1, fill_color); } +static bool view3d_stereo3d_active(const bContext *C, Scene *scene, View3D *v3d, RegionView3D *rv3d) +{ + wmWindow *win = CTX_wm_window(C); + + if ((scene->r.scemode & R_MULTIVIEW) == 0) + return false; + + if (WM_stereo3d_enabled(win, true) == false) + return false; + + if ((v3d->camera == NULL) || rv3d->persp != RV3D_CAMOB) + return false; + + if (scene->r.views_format & SCE_VIEWS_FORMAT_MULTIVIEW) { + if (v3d->stereo3d_camera == STEREO_MONO_ID) + return false; + + return BKE_scene_multiview_is_stereo3d(&scene->r); + } + + return true; +} + +/* setup the view and win matrices for the multiview cameras + * + * unlike view3d_stereo3d_setup_offscreen, when view3d_stereo3d_setup is called + * we have no winmatrix (i.e., projection matrix) defined at that time. + * Since the camera and the camera shift are needed for the winmat calculation + * we do a small hack to replace it temporarily so we don't need to change the + * view3d)main_area_setup_view() code to account for that. + */ +static void view3d_stereo3d_setup(Scene *scene, View3D *v3d, ARegion *ar) +{ + bool is_left; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + const char *viewname; + + /* show only left or right camera */ + if (v3d->stereo3d_camera != STEREO_3D_ID) + v3d->multiview_eye = v3d->stereo3d_camera; + + is_left = v3d->multiview_eye == STEREO_LEFT_ID; + viewname = names[is_left ? STEREO_LEFT_ID : STEREO_RIGHT_ID]; + + /* update the viewport matrices with the new camera */ + if (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D) { + Camera *data; + float viewmat[4][4]; + float shiftx; + + data = (Camera *)v3d->camera->data; + shiftx = data->shiftx; + + BLI_lock_thread(LOCK_VIEW3D); + data->shiftx = BKE_camera_multiview_shift_x(&scene->r, v3d->camera, viewname); + + BKE_camera_multiview_view_matrix(&scene->r, v3d->camera, is_left, viewmat); + view3d_main_area_setup_view(scene, v3d, ar, viewmat, NULL); + + data->shiftx = shiftx; + BLI_unlock_thread(LOCK_VIEW3D); + } + else { /* SCE_VIEWS_FORMAT_MULTIVIEW */ + float viewmat[4][4]; + Object *view_ob = v3d->camera; + Object *camera = BKE_camera_multiview_render(scene, v3d->camera, viewname); + + BLI_lock_thread(LOCK_VIEW3D); + v3d->camera = camera; + + BKE_camera_multiview_view_matrix(&scene->r, camera, false, viewmat); + view3d_main_area_setup_view(scene, v3d, ar, viewmat, NULL); + + v3d->camera = view_ob; + BLI_unlock_thread(LOCK_VIEW3D); + } +} + +static void view3d_stereo3d_setup_offscreen(Scene *scene, View3D *v3d, ARegion *ar, + float winmat[4][4], const char *viewname) +{ + /* update the viewport matrices with the new camera */ + if (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D) { + float viewmat[4][4]; + const bool is_left = STREQ(viewname, STEREO_LEFT_NAME); + + BKE_camera_multiview_view_matrix(&scene->r, v3d->camera, is_left, viewmat); + view3d_main_area_setup_view(scene, v3d, ar, viewmat, winmat); + } + else { /* SCE_VIEWS_FORMAT_MULTIVIEW */ + float viewmat[4][4]; + Object *camera = BKE_camera_multiview_render(scene, v3d->camera, viewname); + + BKE_camera_multiview_view_matrix(&scene->r, camera, false, viewmat); + view3d_main_area_setup_view(scene, v3d, ar, viewmat, winmat); + } +} + #ifdef WITH_GAMEENGINE static void update_lods(Scene *scene, float camera_pos[3]) { @@ -3551,8 +3682,11 @@ static void view3d_main_area_draw_objects(const bContext *C, Scene *scene, View3 GPU_default_lights(); } - /* setup view matrices */ - view3d_main_area_setup_view(scene, v3d, ar, NULL, NULL); + /* setup the view matrix */ + if (view3d_stereo3d_active(C, scene, v3d, rv3d)) + view3d_stereo3d_setup(scene, v3d, ar); + else + view3d_main_area_setup_view(scene, v3d, ar, NULL, NULL); rv3d->rflag &= ~RV3D_IS_GAME_ENGINE; #ifdef WITH_GAMEENGINE |