diff options
Diffstat (limited to 'source')
163 files changed, 11638 insertions, 2101 deletions
diff --git a/source/blender/blenkernel/BKE_blender.h b/source/blender/blenkernel/BKE_blender.h index a790715ac98..86576f9a11b 100644 --- a/source/blender/blenkernel/BKE_blender.h +++ b/source/blender/blenkernel/BKE_blender.h @@ -42,7 +42,7 @@ extern "C" { * and keep comment above the defines. * Use STRINGIFY() rather than defining with quotes */ #define BLENDER_VERSION 274 -#define BLENDER_SUBVERSION 3 +#define BLENDER_SUBVERSION 4 /* Several breakages with 270, e.g. constraint deg vs rad */ #define BLENDER_MINVERSION 270 #define BLENDER_MINSUBVERSION 5 diff --git a/source/blender/blenkernel/BKE_camera.h b/source/blender/blenkernel/BKE_camera.h index 4849c66df1b..aacb7a4066b 100644 --- a/source/blender/blenkernel/BKE_camera.h +++ b/source/blender/blenkernel/BKE_camera.h @@ -136,6 +136,14 @@ bool BKE_camera_view_frame_fit_to_coords( void BKE_camera_to_gpu_dof(struct Object *camera, struct GPUFXSettings *r_fx_settings); +/* Camera multi-view API */ + +struct Object *BKE_camera_multiview_render(struct Scene *scene, struct Object *camera, const char *viewname); +void BKE_camera_multiview_view_matrix(struct RenderData *rd, struct Object *camera, const bool is_left, float r_viewmat[4][4]); +void BKE_camera_multiview_model_matrix(struct RenderData *rd, struct Object *camera, const char *viewname, float r_modelmat[4][4]); +float BKE_camera_multiview_shift_x(struct RenderData *rd, struct Object *camera, const char *viewname); +void BKE_camera_multiview_params(struct RenderData *rd, struct CameraParams *params, struct Object *camera, const char *viewname); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 3b7ba24d20b..ad191968a40 100644 --- a/source/blender/blenkernel/BKE_image.h +++ b/source/blender/blenkernel/BKE_image.h @@ -45,34 +45,34 @@ struct Object; struct ImageFormatData; struct ImagePool; struct Main; +struct ReportList; #define IMA_MAX_SPACE 64 void BKE_images_init(void); void BKE_images_exit(void); +void BKE_image_free_packedfiles(struct Image *image); +void BKE_image_free_views(struct Image *image); void BKE_image_free_buffers(struct Image *image); /* call from library */ void BKE_image_free(struct Image *image); void BKE_imbuf_stamp_info(struct Scene *scene, struct Object *camera, struct ImBuf *ibuf); -void BKE_image_stamp_buf( - struct Scene *scene, struct Object *camera, - unsigned char *rect, float *rectf, int width, int height, int channels); +void BKE_image_stamp_buf(struct Scene *scene, struct Object *camera, unsigned char *rect, float *rectf, int width, int height, int channels); bool BKE_imbuf_alpha_test(struct ImBuf *ibuf); int BKE_imbuf_write_stamp(struct Scene *scene, struct Object *camera, struct ImBuf *ibuf, const char *name, struct ImageFormatData *imf); +void BKE_imbuf_write_prepare(struct ImBuf *ibuf, struct ImageFormatData *imf); int BKE_imbuf_write(struct ImBuf *ibuf, const char *name, struct ImageFormatData *imf); int BKE_imbuf_write_as(struct ImBuf *ibuf, const char *name, struct ImageFormatData *imf, const bool is_copy); - void BKE_image_path_from_imformat( char *string, const char *base, const char *relbase, int frame, - const struct ImageFormatData *im_format, const bool use_ext, const bool use_frames); + const struct ImageFormatData *im_format, const bool use_ext, const bool use_frames, const char *suffix); void BKE_image_path_from_imtype( char *string, const char *base, const char *relbase, int frame, - const char imtype, const bool use_ext, const bool use_frames); - -bool BKE_image_path_ensure_ext_from_imformat(char *string, const struct ImageFormatData *im_format); -bool BKE_image_path_ensure_ext_from_imtype(char *string, const char imtype); + const char imtype, const bool use_ext, const bool use_frames, const char *suffix); +int BKE_image_path_ensure_ext_from_imformat(char *string, const struct ImageFormatData *im_format); +int BKE_image_path_ensure_ext_from_imtype(char *string, const char imtype); char BKE_image_ftype_to_imtype(const int ftype); int BKE_image_imtype_to_ftype(const char imtype); @@ -103,6 +103,7 @@ void BKE_image_tag_time(struct Image *ima); /* ImageUser is in Texture, in Nodes, Background Image, Image Window, .... */ /* should be used in conjunction with an ID * to Image. */ struct ImageUser; +struct RenderData; struct RenderPass; struct RenderResult; @@ -172,11 +173,12 @@ struct Image *BKE_image_load_exists(const char *filepath); /* adds image, adds ibuf, generates color or pattern */ struct Image *BKE_image_add_generated( - struct Main *bmain, unsigned int width, unsigned int height, const char *name, int depth, int floatbuf, short gen_type, const float color[4]); + struct Main *bmain, unsigned int width, unsigned int height, const char *name, int depth, int floatbuf, short gen_type, const float color[4], const bool stereo3d); /* adds image from imbuf, owns imbuf */ struct Image *BKE_image_add_from_imbuf(struct ImBuf *ibuf); /* for reload, refresh, pack */ +void BKE_image_init_imageuser(struct Image *ima, struct ImageUser *iuser); void BKE_image_signal(struct Image *ima, struct ImageUser *iuser, int signal); void BKE_image_walk_all_users(const struct Main *mainp, void *customdata, @@ -184,6 +186,8 @@ void BKE_image_walk_all_users(const struct Main *mainp, void *customdata, /* ensures an Image exists for viewing nodes or render */ struct Image *BKE_image_verify_viewer(int type, const char *name); +/* ensures the view node cache is compatible with the scene views */ +void BKE_image_verify_viewer_views(const struct RenderData *rd, struct Image *ima, struct ImageUser *iuser); /* called on frame change or before render */ void BKE_image_user_frame_calc(struct ImageUser *iuser, int cfra, int fieldnr); @@ -195,13 +199,23 @@ void BKE_image_update_frame(const struct Main *bmain, int cfra); /* sets index offset for multilayer files */ struct RenderPass *BKE_image_multilayer_index(struct RenderResult *rr, struct ImageUser *iuser); +/* sets index offset for multiview files */ +void BKE_image_multiview_index(struct Image *ima, struct ImageUser *iuser); + /* for multilayer images as well as for render-viewer */ +bool BKE_image_is_multilayer(struct Image *ima); struct RenderResult *BKE_image_acquire_renderresult(struct Scene *scene, struct Image *ima); void BKE_image_release_renderresult(struct Scene *scene, struct Image *ima); +/* for multilayer images as well as for singlelayer */ +bool BKE_image_is_openexr(struct Image *ima); + /* for multiple slot render, call this before render */ void BKE_image_backup_render(struct Scene *scene, struct Image *ima); - + +/* for singlelayer openexr saving */ +bool BKE_image_save_openexr_multiview(struct Image *ima, struct ImBuf *ibuf, const char *filepath, const int flags); + /* goes over all textures that use images */ void BKE_image_free_all_textures(void); @@ -212,6 +226,7 @@ void BKE_image_free_anim_ibufs(struct Image *ima, int except_frame); void BKE_image_all_free_anim_ibufs(int except_frame); void BKE_image_memorypack(struct Image *ima); +void BKE_image_packfiles(struct ReportList *reports, struct Image *ima, const char *basepath); /* prints memory statistics for images */ void BKE_image_print_memlist(void); @@ -243,7 +258,8 @@ float *BKE_image_get_float_pixels_for_frame(struct Image *image, int frame); /* Guess offset for the first frame in the sequence */ int BKE_image_sequence_guess_offset(struct Image *image); - +bool BKE_image_has_anim(struct Image *image); +bool BKE_image_has_packedfile(struct Image *image); bool BKE_image_is_animated(struct Image *image); bool BKE_image_is_dirty(struct Image *image); void BKE_image_file_format_set(struct Image *image, int ftype); diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h index ee63771406e..eca15a62c70 100644 --- a/source/blender/blenkernel/BKE_node.h +++ b/source/blender/blenkernel/BKE_node.h @@ -941,6 +941,7 @@ void ntreeGPUMaterialNodes(struct bNodeTree *ntree, struct GPUMateria #define CMP_NODE_MAP_RANGE 319 #define CMP_NODE_PLANETRACKDEFORM 320 #define CMP_NODE_CORNERPIN 321 +#define CMP_NODE_SWITCH_VIEW 322 /* channel toggles */ #define CMP_CHAN_RGB 1 @@ -975,7 +976,8 @@ void ntreeGPUMaterialNodes(struct bNodeTree *ntree, struct GPUMateria /* API */ void ntreeCompositExecTree(struct Scene *scene, struct bNodeTree *ntree, struct RenderData *rd, int rendering, int do_previews, - const struct ColorManagedViewSettings *view_settings, const struct ColorManagedDisplaySettings *display_settings); + const struct ColorManagedViewSettings *view_settings, const struct ColorManagedDisplaySettings *display_settings, + const char *view_name); void ntreeCompositTagRender(struct Scene *sce); int ntreeCompositTagAnimated(struct bNodeTree *ntree); void ntreeCompositTagGenerators(struct bNodeTree *ntree); diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h index 27c89dd3a09..79778d504a5 100644 --- a/source/blender/blenkernel/BKE_scene.h +++ b/source/blender/blenkernel/BKE_scene.h @@ -70,6 +70,7 @@ void BKE_scene_free(struct Scene *sce); struct Scene *BKE_scene_add(struct Main *bmain, const char *name); /* base functions */ +struct Base *BKE_scene_base_find_by_name(struct Scene *scene, const char *name); struct Base *BKE_scene_base_find(struct Scene *scene, struct Object *ob); struct Base *BKE_scene_base_add(struct Scene *sce, struct Object *ob); void BKE_scene_base_unlink(struct Scene *sce, struct Base *base); @@ -122,6 +123,9 @@ void BKE_scene_update_for_newframe_ex(struct EvaluationContext *eval_ctx, struct struct SceneRenderLayer *BKE_scene_add_render_layer(struct Scene *sce, const char *name); bool BKE_scene_remove_render_layer(struct Main *main, struct Scene *scene, struct SceneRenderLayer *srl); +struct SceneRenderView *BKE_scene_add_render_view(struct Scene *sce, const char *name); +bool BKE_scene_remove_render_view(struct Scene *scene, struct SceneRenderView *srv); + /* render profile */ int get_render_subsurf_level(const struct RenderData *r, int level); int get_render_child_particle_number(const struct RenderData *r, int num); @@ -142,6 +146,23 @@ int BKE_render_num_threads(const struct RenderData *r); double BKE_scene_unit_scale(const struct UnitSettings *unit, const int unit_type, double value); +/* multiview */ +bool BKE_scene_multiview_is_stereo3d(const struct RenderData *rd); +bool BKE_scene_multiview_is_render_view_active(const struct RenderData *rd, const struct SceneRenderView *srv); +bool BKE_scene_multiview_is_render_view_first(const struct RenderData *rd, const char *viewname); +bool BKE_scene_multiview_is_render_view_last(const struct RenderData *rd, const char *viewname); +size_t BKE_scene_multiview_num_views_get(const struct RenderData *rd); +struct SceneRenderView *BKE_scene_multiview_render_view_findindex(const struct RenderData *rd, const int view_id); +const char *BKE_scene_multiview_render_view_name_get(const struct RenderData *rd, const int view_id); +size_t BKE_scene_multiview_view_id_get(const struct RenderData *rd, const char *viewname); +void BKE_scene_multiview_filepath_get(struct SceneRenderView *srv, const char *filepath, char *r_filepath); +void BKE_scene_multiview_view_filepath_get(const struct RenderData *rd, const char *filepath, const char *view, char *r_filepath); +const char *BKE_scene_multiview_view_suffix_get(const struct RenderData *rd, const char *viewname); +const char *BKE_scene_multiview_view_id_suffix_get(const struct RenderData *rd, const size_t view_id); +void BKE_scene_multiview_view_prefix_get(struct Scene *scene, const char *name, char *rprefix, char **rext); +void BKE_scene_multiview_videos_dimensions_get(const struct RenderData *rd, const size_t width, const size_t height, size_t *r_width, size_t *r_height); +size_t BKE_scene_multiview_num_videos_get(const struct RenderData *rd); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h index 84818276e7b..4a45489933c 100644 --- a/source/blender/blenkernel/BKE_sequencer.h +++ b/source/blender/blenkernel/BKE_sequencer.h @@ -41,6 +41,7 @@ struct Mask; struct Scene; struct Sequence; struct SequenceModifierData; +struct Stereo3dFormat; struct StripElem; struct bSound; @@ -99,6 +100,7 @@ typedef struct SeqRenderData { float motion_blur_shutter; bool skip_cache; bool is_proxy_render; + size_t view_id; } SeqRenderData; void BKE_sequencer_new_render_data( @@ -223,6 +225,7 @@ void BKE_sequencer_base_clipboard_pointers_store(struct ListBase *seqbase); void BKE_sequencer_base_clipboard_pointers_restore(struct ListBase *seqbase, struct Main *bmain); void BKE_sequence_free(struct Scene *scene, struct Sequence *seq); +void BKE_sequence_free_anim(struct Sequence *seq); const char *BKE_sequence_give_name(struct Sequence *seq); void BKE_sequence_calc(struct Scene *scene, struct Sequence *seq); void BKE_sequence_calc_disp(struct Scene *scene, struct Sequence *seq); @@ -235,7 +238,7 @@ struct StripElem *BKE_sequencer_give_stripelem(struct Sequence *seq, int cfra); void BKE_sequencer_update_changed_seq_and_deps(struct Scene *scene, struct Sequence *changed_seq, int len_change, int ibuf_change); bool BKE_sequencer_input_have_to_preprocess(const SeqRenderData *context, struct Sequence *seq, float cfra); -struct SeqIndexBuildContext *BKE_sequencer_proxy_rebuild_context(struct Main *bmain, struct Scene *scene, struct Sequence *seq, struct GSet *file_list); +void BKE_sequencer_proxy_rebuild_context(struct Main *bmain, struct Scene *scene, struct Sequence *seq, struct GSet *file_list, ListBase *queue); void BKE_sequencer_proxy_rebuild(struct SeqIndexBuildContext *context, short *stop, short *do_update, float *progress); void BKE_sequencer_proxy_rebuild_finish(struct SeqIndexBuildContext *context, bool stop); @@ -355,6 +358,10 @@ typedef struct SeqLoadInfo { int len; /* only for image strips */ char path[1024]; /* 1024 = FILE_MAX */ + /* multiview */ + char views_format; + struct Stereo3dFormat *stereo3d_format; + /* return values */ char name[64]; struct Sequence *seq_sound; /* for movie's */ @@ -399,7 +406,7 @@ struct Sequence *BKE_sequencer_add_sound_strip(struct bContext *C, ListBase *seq struct Sequence *BKE_sequencer_add_movie_strip(struct bContext *C, ListBase *seqbasep, struct SeqLoadInfo *seq_load); /* view3d draw callback, run when not in background view */ -typedef struct ImBuf *(*SequencerDrawView)(struct Scene *, struct Object *, int, int, unsigned int, int, bool, bool, bool, int, char[256]); +typedef struct ImBuf *(*SequencerDrawView)(struct Scene *, struct Object *, int, int, unsigned int, int, bool, bool, bool, int, const char *, char[256]); extern SequencerDrawView sequencer_view3d_cb; /* copy/paste */ diff --git a/source/blender/blenkernel/BKE_writeavi.h b/source/blender/blenkernel/BKE_writeavi.h index bc06be0295a..ca295c51f5d 100644 --- a/source/blender/blenkernel/BKE_writeavi.h +++ b/source/blender/blenkernel/BKE_writeavi.h @@ -43,16 +43,20 @@ struct ReportList; struct Scene; typedef struct bMovieHandle { - int (*start_movie)(struct Scene *scene, struct RenderData *rd, int rectx, int recty, struct ReportList *reports, bool preview); - int (*append_movie)(struct RenderData *rd, int start_frame, int frame, int *pixels, - int rectx, int recty, struct ReportList *reports); - void (*end_movie)(void); - int (*get_next_frame)(struct RenderData *rd, struct ReportList *reports); /* optional */ - void (*get_movie_path)(char *string, struct RenderData *rd, bool preview); /* optional */ + int (*start_movie)(void *context_v, struct Scene *scene, struct RenderData *rd, int rectx, int recty, + struct ReportList *reports, bool preview, const char *suffix); + int (*append_movie)(void *context_v, struct RenderData *rd, int start_frame, int frame, int *pixels, + int rectx, int recty, const char *suffix, struct ReportList *reports); + void (*end_movie)(void *context_v); + int (*get_next_frame)(void *context_v, struct RenderData *rd, struct ReportList *reports); /* optional */ + void (*get_movie_path)(char *string, struct RenderData *rd, bool preview, const char *suffix); /* optional */ + void *(*context_create)(void); + void (*context_free)(void *context_v); } bMovieHandle; bMovieHandle *BKE_movie_handle_get(const char imtype); -void BKE_movie_filepath_get(char *string, struct RenderData *rd, bool preview); +void BKE_movie_filepath_get(char *string, struct RenderData *rd, bool preview, const char *suffix); +void BKE_context_create(bMovieHandle *mh); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/BKE_writeffmpeg.h b/source/blender/blenkernel/BKE_writeffmpeg.h index 951fa501f9c..a40c31022e3 100644 --- a/source/blender/blenkernel/BKE_writeffmpeg.h +++ b/source/blender/blenkernel/BKE_writeffmpeg.h @@ -68,11 +68,11 @@ struct RenderData; struct ReportList; struct Scene; -int BKE_ffmpeg_start(struct Scene *scene, struct RenderData *rd, int rectx, int recty, struct ReportList *reports, bool preview); -void BKE_ffmpeg_end(void); -int BKE_ffmpeg_append(struct RenderData *rd, int start_frame, int frame, int *pixels, - int rectx, int recty, struct ReportList *reports); -void BKE_ffmpeg_filepath_get(char *string, struct RenderData *rd, bool preview); +int BKE_ffmpeg_start(void *context_v, struct Scene *scene, struct RenderData *rd, int rectx, int recty, struct ReportList *reports, bool preview, const char *suffix); +void BKE_ffmpeg_end(void *context_v); +int BKE_ffmpeg_append(void *context_v, struct RenderData *rd, int start_frame, int frame, int *pixels, + int rectx, int recty, const char *suffix, struct ReportList *reports); +void BKE_ffmpeg_filepath_get(char *string, struct RenderData *rd, bool preview, const char *suffix); void BKE_ffmpeg_preset_set(struct RenderData *rd, int preset); void BKE_ffmpeg_image_type_verify(struct RenderData *rd, struct ImageFormatData *imf); @@ -82,6 +82,9 @@ bool BKE_ffmpeg_alpha_channel_is_supported(struct RenderData *rd); int BKE_ffmpeg_property_add_string(struct RenderData *rd, const char *type, const char *str); void BKE_ffmpeg_property_del(struct RenderData *rd, void *type, void *prop_); +void *BKE_ffmpeg_context_create(void); +void BKE_ffmpeg_context_free(void *context_v); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_writeframeserver.h b/source/blender/blenkernel/BKE_writeframeserver.h index b7d601ea769..2f8bce31e41 100644 --- a/source/blender/blenkernel/BKE_writeframeserver.h +++ b/source/blender/blenkernel/BKE_writeframeserver.h @@ -40,11 +40,14 @@ struct RenderData; struct ReportList; struct Scene; -int BKE_frameserver_start(struct Scene *scene, struct RenderData *rd, int rectx, int recty, struct ReportList *reports, bool preview); -void BKE_frameserver_end(void); -int BKE_frameserver_append(struct RenderData *rd, int start_frame, int frame, int *pixels, - int rectx, int recty, struct ReportList *reports); -int BKE_frameserver_loop(struct RenderData *rd, struct ReportList *reports); +int BKE_frameserver_start(void *context_v, struct Scene *scene, struct RenderData *rd, int rectx, int recty, + struct ReportList *reports, bool preview, const char *suffix); +void BKE_frameserver_end(void *context_v); +int BKE_frameserver_append(void *context_v, struct RenderData *rd, int start_frame, int frame, int *pixels, + int rectx, int recty, const char*suffix, struct ReportList *reports); +int BKE_frameserver_loop(void *context_v, struct RenderData *rd, struct ReportList *reports); +void *BKE_frameserver_context_create(void); +void BKE_frameserver_context_free(void *context_v); #ifdef __cplusplus } diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c index e94f30da0c5..bb3731218ed 100644 --- a/source/blender/blenkernel/intern/bpath.c +++ b/source/blender/blenkernel/intern/bpath.c @@ -423,10 +423,10 @@ void BKE_bpath_traverse_id(Main *bmain, ID *id, BPathVisitor visit_cb, const int { Image *ima; ima = (Image *)id; - if (ima->packedfile == NULL || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) { + if (BKE_image_has_packedfile(ima) == false || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) { if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE)) { if (rewrite_path_fixed(ima->name, visit_cb, absbase, bpath_user_data)) { - if (!ima->packedfile) { + if (!BKE_image_has_packedfile(ima)) { BKE_image_signal(ima, NULL, IMA_SIGNAL_RELOAD); BKE_image_walk_all_users(bmain, ima, bpath_traverse_image_user_cb); } diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c index 5b4d7ba8214..fec6542ac47 100644 --- a/source/blender/blenkernel/intern/camera.c +++ b/source/blender/blenkernel/intern/camera.c @@ -30,16 +30,19 @@ */ #include <stdlib.h> +#include <stddef.h> #include "DNA_camera_types.h" #include "DNA_lamp_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_view3d_types.h" +#include "DNA_ID.h" #include "BLI_math.h" -#include "BLI_utildefines.h" #include "BLI_rect.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" #include "BKE_animsys.h" #include "BKE_camera.h" @@ -47,6 +50,7 @@ #include "BKE_global.h" #include "BKE_library.h" #include "BKE_main.h" +#include "BKE_scene.h" #include "BKE_screen.h" #include "GPU_compositing.h" @@ -71,6 +75,10 @@ void *BKE_camera_add(Main *bmain, const char *name) GPU_fx_compositor_init_dof_settings(&cam->gpu_dof); + /* stereoscopy 3d */ + cam->stereo.interocular_distance = 0.065f; + cam->stereo.convergence_distance = 30.f * 0.065f; + return cam; } @@ -695,6 +703,250 @@ bool BKE_camera_view_frame_fit_to_coords( return camera_frame_fit_calc_from_data(¶ms, &data_cb, r_co, r_scale); } +/******************* multiview matrix functions ***********************/ + +static void camera_model_matrix(Object *camera, float r_modelmat[4][4]) +{ + copy_m4_m4(r_modelmat, camera->obmat); +} + +static void camera_stereo3d_model_matrix(Object *camera, const bool is_left, float r_modelmat[4][4]) +{ + Camera *data = (Camera *)camera->data; + float interocular_distance, convergence_distance; + short convergence_mode, pivot; + float sizemat[4][4]; + + float fac = 1.0f; + float fac_signed; + + interocular_distance = data->stereo.interocular_distance; + convergence_distance = data->stereo.convergence_distance; + convergence_mode = data->stereo.convergence_mode; + pivot = data->stereo.pivot; + + if (((pivot == CAM_S3D_PIVOT_LEFT) && is_left) || + ((pivot == CAM_S3D_PIVOT_RIGHT) && !is_left)) + { + return camera_model_matrix(camera, r_modelmat); + } + else { + float size[3]; + mat4_to_size(size, camera->obmat); + size_to_mat4(sizemat, size); + } + + if (pivot == CAM_S3D_PIVOT_CENTER) + fac = 0.5f; + + fac_signed = is_left ? fac : -fac; + + /* rotation */ + if (convergence_mode == CAM_S3D_TOE) { + float angle; + float angle_sin, angle_cos; + float toeinmat[4][4]; + float rotmat[4][4]; + + unit_m4(rotmat); + + if (pivot == CAM_S3D_PIVOT_CENTER) { + fac = -fac; + fac_signed = -fac_signed; + } + + angle = atanf((interocular_distance * 0.5f) / convergence_distance) / fac; + + angle_cos = cosf(angle * fac_signed); + angle_sin = sinf(angle * fac_signed); + + rotmat[0][0] = angle_cos; + rotmat[2][0] = -angle_sin; + rotmat[0][2] = angle_sin; + rotmat[2][2] = angle_cos; + + if (pivot == CAM_S3D_PIVOT_CENTER) { + /* set the rotation */ + copy_m4_m4(toeinmat, rotmat); + /* set the translation */ + toeinmat[3][0] = interocular_distance * fac_signed; + + /* transform */ + normalize_m4_m4(r_modelmat, camera->obmat); + mul_m4_m4m4(r_modelmat, r_modelmat, toeinmat); + + /* scale back to the original size */ + mul_m4_m4m4(r_modelmat, r_modelmat, sizemat); + } + else { /* CAM_S3D_PIVOT_LEFT, CAM_S3D_PIVOT_RIGHT */ + /* rotate perpendicular to the interocular line */ + normalize_m4_m4(r_modelmat, camera->obmat); + mul_m4_m4m4(r_modelmat, r_modelmat, rotmat); + + /* translate along the interocular line */ + unit_m4(toeinmat); + toeinmat[3][0] = -interocular_distance * fac_signed; + mul_m4_m4m4(r_modelmat, r_modelmat, toeinmat); + + /* rotate to toe-in angle */ + mul_m4_m4m4(r_modelmat, r_modelmat, rotmat); + + /* scale back to the original size */ + mul_m4_m4m4(r_modelmat, r_modelmat, sizemat); + } + } + else { + normalize_m4_m4(r_modelmat, camera->obmat); + + /* translate - no rotation in CAM_S3D_OFFAXIS, CAM_S3D_PARALLEL */ + translate_m4(r_modelmat, -interocular_distance * fac_signed, 0.0f, 0.0f); + + /* scale back to the original size */ + mul_m4_m4m4(r_modelmat, r_modelmat, sizemat); + } +} + +/* the view matrix is used by the viewport drawing, it is basically the inverted model matrix */ +void BKE_camera_multiview_view_matrix(RenderData *rd, Object *camera, const bool is_left, float r_viewmat[4][4]) +{ + BKE_camera_multiview_model_matrix(rd, camera, is_left ? STEREO_LEFT_NAME : STEREO_RIGHT_NAME, r_viewmat); + invert_m4(r_viewmat); +} + +/* left is the default */ +static bool camera_is_left(const char *viewname) +{ + if (viewname && viewname[0] != '\0') { + return !STREQ(viewname, STEREO_RIGHT_NAME); + } + return true; +} + +void BKE_camera_multiview_model_matrix(RenderData *rd, Object *camera, const char *viewname, float r_modelmat[4][4]) +{ + const bool is_multiview = (rd && rd->scemode & R_MULTIVIEW) != 0; + + if (!is_multiview) { + camera_model_matrix(camera, r_modelmat); + } + else if (rd->views_format == SCE_VIEWS_FORMAT_MULTIVIEW) { + camera_model_matrix(camera, r_modelmat); + } + else { /* SCE_VIEWS_SETUP_BASIC */ + const bool is_left = camera_is_left(viewname); + camera_stereo3d_model_matrix(camera, is_left, r_modelmat); + } + normalize_m4(r_modelmat); +} + +static Object *camera_multiview_advanced(Scene *scene, Object *camera, const char *suffix) +{ + SceneRenderView *srv; + char name[MAX_NAME]; + const char *camera_name = camera->id.name + 2; + const int len_name = strlen(camera_name); + + name[0] = '\0'; + + for (srv = scene->r.views.first; srv; srv = srv->next) { + const int len_suffix = strlen(srv->suffix); + + if (len_name < len_suffix) + continue; + + if (STREQ(camera_name + (len_name - len_suffix), srv->suffix)) { + BLI_snprintf(name, sizeof(name), "%.*s%s", (len_name - len_suffix), camera_name, suffix); + break; + } + } + + if (name[0] != '\0') { + Base *base = BKE_scene_base_find_by_name(scene, name); + if (base) { + return base->object; + } + } + + return camera; +} + +/* returns the camera to be used for render */ +Object *BKE_camera_multiview_render(Scene *scene, Object *camera, const char *viewname) +{ + const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0; + + if (!is_multiview) { + return camera; + } + else if (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D) { + return camera; + } + else { /* SCE_VIEWS_FORMAT_MULTIVIEW */ + const char *suffix = BKE_scene_multiview_view_suffix_get(&scene->r, viewname); + return camera_multiview_advanced(scene, camera, suffix); + } +} + +static float camera_stereo3d_shift_x(Object *camera, const char *viewname) +{ + Camera *data = camera->data; + float shift = data->shiftx; + float interocular_distance, convergence_distance; + short convergence_mode, pivot; + bool is_left = true; + + float fac = 1.0f; + float fac_signed; + + if (viewname && viewname[0]) { + is_left = STREQ(viewname, STEREO_LEFT_NAME); + } + + interocular_distance = data->stereo.interocular_distance; + convergence_distance = data->stereo.convergence_distance; + convergence_mode = data->stereo.convergence_mode; + pivot = data->stereo.pivot; + + if (((pivot == CAM_S3D_PIVOT_LEFT) && is_left) || + ((pivot == CAM_S3D_PIVOT_RIGHT) && !is_left)) + { + return shift; + } + + if (pivot == CAM_S3D_PIVOT_CENTER) + fac = 0.5f; + + fac_signed = is_left ? fac : -fac; + + /* Note: in viewport, parallel renders as offaxis, but in render it does parallel */ + if (ELEM(convergence_mode, CAM_S3D_OFFAXIS, CAM_S3D_PARALLEL)) { + shift += ((interocular_distance / data->sensor_x) * (data->lens / convergence_distance)) * fac_signed; + } + + return shift; +} + +float BKE_camera_multiview_shift_x(RenderData *rd, Object *camera, const char *viewname) +{ + const bool is_multiview = (rd && rd->scemode & R_MULTIVIEW) != 0; + Camera *data = camera->data; + + if (!is_multiview) { + return data->shiftx; + } + else if (rd->views_format == SCE_VIEWS_FORMAT_MULTIVIEW) { + return data->shiftx; + } + else { /* SCE_VIEWS_SETUP_BASIC */ + return camera_stereo3d_shift_x(camera, viewname); + } +} + +void BKE_camera_multiview_params(RenderData *rd, CameraParams *params, Object *camera, const char *viewname) +{ + params->shiftx = BKE_camera_multiview_shift_x(rd, camera, viewname); +} + void BKE_camera_to_gpu_dof(struct Object *camera, struct GPUFXSettings *r_fx_settings) { if (camera->type == OB_CAMERA) { diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c index 9b00a0150ad..f852d886ae9 100644 --- a/source/blender/blenkernel/intern/image.c +++ b/source/blender/blenkernel/intern/image.c @@ -99,6 +99,12 @@ static SpinLock image_spin; +/* prototypes */ +static size_t image_num_files(struct Image *ima); +static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **lock_r); +static void image_update_views_format(Image *ima, ImageUser *iuser); +static void image_add_view(Image *ima, const char *viewname, const char *filepath); + /* max int, to indicate we don't store sequences in ibuf */ #define IMA_NO_INDEX 0x7FEFEFEF @@ -257,6 +263,46 @@ static void image_free_cached_frames(Image *image) } } +static void image_free_packedfiles(Image *ima) +{ + while (ima->packedfiles.last) { + ImagePackedFile *imapf = ima->packedfiles.last; + if (imapf->packedfile) { + freePackedFile(imapf->packedfile); + } + BLI_remlink(&ima->packedfiles, imapf); + MEM_freeN(imapf); + } +} + +void BKE_image_free_packedfiles(Image *ima) +{ + image_free_packedfiles(ima); +} + +static void image_free_views(Image *ima) +{ + BLI_freelistN(&ima->views); +} + +void BKE_image_free_views(Image *image) +{ + image_free_views(image); +} + +static void image_free_anims(Image *ima) +{ + while (ima->anims.last) { + ImageAnim *ia = ima->anims.last; + if (ia->anim) { + IMB_free_anim(ia->anim); + ia->anim = NULL; + } + BLI_remlink(&ima->anims, ia); + MEM_freeN(ia); + } +} + /** * Simply free the image data from memory, * on display the image can load again (except for render buffers). @@ -265,8 +311,7 @@ void BKE_image_free_buffers(Image *ima) { image_free_cached_frames(ima); - if (ima->anim) IMB_free_anim(ima->anim); - ima->anim = NULL; + image_free_anims(ima); if (ima->rr) { RE_FreeRenderResult(ima->rr); @@ -290,10 +335,9 @@ void BKE_image_free(Image *ima) int a; BKE_image_free_buffers(ima); - if (ima->packedfile) { - freePackedFile(ima->packedfile); - ima->packedfile = NULL; - } + + image_free_packedfiles(ima); + BKE_icon_delete(&ima->id); ima->id.icon_id = 0; @@ -305,6 +349,9 @@ void BKE_image_free(Image *ima) ima->renders[a] = NULL; } } + + image_free_views(ima); + MEM_freeN(ima->stereo3d_format); } /* only image block itself */ @@ -328,7 +375,9 @@ static Image *image_alloc(Main *bmain, const char *name, short source, short typ ima->flag |= IMA_VIEW_AS_RENDER; BKE_color_managed_colorspace_settings_init(&ima->colorspace_settings); + ima->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Image Stereo Format"); } + return ima; } @@ -359,6 +408,22 @@ static void image_assign_ibuf(Image *ima, ImBuf *ibuf, int index, int frame) } } +static void copy_image_packedfiles(ListBase *lb_dst, const ListBase *lb_src) +{ + const ImagePackedFile *imapf_src; + + BLI_listbase_clear(lb_dst); + for (imapf_src = lb_src->first; imapf_src; imapf_src = imapf_src->next) { + ImagePackedFile *imapf_dst = MEM_mallocN(sizeof(ImagePackedFile), "Image Packed Files (copy)"); + BLI_strncpy(imapf_dst->filepath, imapf_src->filepath, sizeof(imapf_dst->filepath)); + + if (imapf_src->packedfile) + imapf_dst->packedfile = dupPackedFile(imapf_src->packedfile); + + BLI_addtail(lb_dst, imapf_dst); + } +} + /* empty image block, of similar type and filename */ Image *BKE_image_copy(Main *bmain, Image *ima) { @@ -381,8 +446,10 @@ Image *BKE_image_copy(Main *bmain, Image *ima) BKE_color_managed_colorspace_settings_copy(&nima->colorspace_settings, &ima->colorspace_settings); - if (ima->packedfile) - nima->packedfile = dupPackedFile(ima->packedfile); + copy_image_packedfiles(&nima->packedfiles, &ima->packedfiles); + + nima->stereo3d_format = MEM_dupallocN(ima->stereo3d_format); + BLI_duplicatelist(&nima->views, &ima->views); if (ima->id.lib) { BKE_id_lib_local_paths(bmain, ima->id.lib, &nima->id); @@ -686,7 +753,9 @@ Image *BKE_image_load_exists_ex(const char *filepath, bool *r_exists) BLI_path_abs(strtest, ID_BLEND_PATH(G.main, &ima->id)); if (BLI_path_cmp(strtest, str) == 0) { - if (ima->anim == NULL || ima->id.us == 0) { + if ((BKE_image_has_anim(ima) == false) || + (ima->id.us == 0)) + { ima->id.us++; /* officially should not, it doesn't link here! */ if (ima->ok == 0) ima->ok = IMA_OK; @@ -774,13 +843,14 @@ static ImBuf *add_ibuf_size(unsigned int width, unsigned int height, const char } /* adds new image block, creates ImBuf and initializes color */ -Image *BKE_image_add_generated(Main *bmain, unsigned int width, unsigned int height, const char *name, int depth, int floatbuf, short gen_type, const float color[4]) +Image *BKE_image_add_generated(Main *bmain, unsigned int width, unsigned int height, const char *name, int depth, int floatbuf, short gen_type, const float color[4], const bool stereo3d) { /* on save, type is changed to FILE in editsima.c */ Image *ima = image_alloc(bmain, name, IMA_SRC_GENERATED, IMA_TYPE_UV_TEST); if (ima) { - ImBuf *ibuf; + size_t view_id; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; /* BLI_strncpy(ima->name, name, FILE_MAX); */ /* don't do this, this writes in ain invalid filepath! */ ima->gen_x = width; @@ -790,13 +860,21 @@ Image *BKE_image_add_generated(Main *bmain, unsigned int width, unsigned int hei ima->gen_depth = depth; copy_v4_v4(ima->gen_color, color); - ibuf = add_ibuf_size(width, height, ima->name, depth, floatbuf, gen_type, color, &ima->colorspace_settings); - image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); + for (view_id = 0; view_id < 2; view_id++) { + ImBuf *ibuf; + ibuf = add_ibuf_size(width, height, ima->name, depth, floatbuf, gen_type, color, &ima->colorspace_settings); + image_assign_ibuf(ima, ibuf, stereo3d ? view_id : IMA_NO_INDEX, 0); - /* image_assign_ibuf puts buffer to the cache, which increments user counter. */ - IMB_freeImBuf(ibuf); + /* image_assign_ibuf puts buffer to the cache, which increments user counter. */ + IMB_freeImBuf(ibuf); + if (!stereo3d) break; + + image_add_view(ima, names[view_id], ""); + } ima->ok = IMA_OK_LOADED; + if (stereo3d) + ima->flag |= IMA_IS_STEREO | IMA_IS_MULTIVIEW; } return ima; @@ -821,17 +899,76 @@ Image *BKE_image_add_from_imbuf(ImBuf *ibuf) return ima; } +/* packs rects from memory as PNG + * convert multiview images to R_IMF_VIEWS_INDIVIDUAL + */ +static void image_memorypack_multiview(Image *ima) +{ + ImageView *iv; + size_t i; + + image_free_packedfiles(ima); + + for (i = 0, iv = ima->views.first; iv; iv = iv->next, i++) { + ImBuf *ibuf = image_get_cached_ibuf_for_index_frame(ima, i, 0); + + ibuf->ftype = PNG; + ibuf->planes = R_IMF_PLANES_RGBA; + + /* if the image was a R_IMF_VIEWS_STEREO_3D we force _L, _R suffices */ + if (ima->views_format == R_IMF_VIEWS_STEREO_3D) { + const char *suffix[2] = {STEREO_LEFT_SUFFIX, STEREO_RIGHT_SUFFIX}; + BLI_path_suffix(iv->filepath, FILE_MAX, suffix[i], ""); + } + + IMB_saveiff(ibuf, iv->filepath, IB_rect | IB_mem); + + if (ibuf->encodedbuffer == NULL) { + printf("memory save for pack error\n"); + IMB_freeImBuf(ibuf); + image_free_packedfiles(ima); + return; + } + else { + ImagePackedFile *imapf; + PackedFile *pf = MEM_callocN(sizeof(*pf), "PackedFile"); + + pf->data = ibuf->encodedbuffer; + pf->size = ibuf->encodedsize; + + imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image PackedFile"); + BLI_strncpy(imapf->filepath, iv->filepath, sizeof(imapf->filepath)); + imapf->packedfile = pf; + BLI_addtail(&ima->packedfiles, imapf); + + ibuf->encodedbuffer = NULL; + ibuf->encodedsize = 0; + ibuf->userflags &= ~IB_BITMAPDIRTY; + } + IMB_freeImBuf(ibuf); + } + + if (ima->source == IMA_SRC_GENERATED) { + ima->source = IMA_SRC_FILE; + ima->type = IMA_TYPE_IMAGE; + } + ima->views_format = R_IMF_VIEWS_INDIVIDUAL; +} + /* packs rect from memory as PNG */ void BKE_image_memorypack(Image *ima) { - ImBuf *ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0); + ImBuf *ibuf; + + if ((ima->flag & IMA_IS_MULTIVIEW)) + return image_memorypack_multiview(ima); + + ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0); if (ibuf == NULL) return; - if (ima->packedfile) { - freePackedFile(ima->packedfile); - ima->packedfile = NULL; - } + + image_free_packedfiles(ima); ibuf->ftype = PNG; ibuf->planes = R_IMF_PLANES_RGBA; @@ -841,11 +978,17 @@ void BKE_image_memorypack(Image *ima) printf("memory save for pack error\n"); } else { + ImagePackedFile *imapf; PackedFile *pf = MEM_callocN(sizeof(*pf), "PackedFile"); pf->data = ibuf->encodedbuffer; pf->size = ibuf->encodedsize; - ima->packedfile = pf; + + imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image PackedFile"); + BLI_strncpy(imapf->filepath, ima->name, sizeof(imapf->filepath)); + imapf->packedfile = pf; + BLI_addtail(&ima->packedfiles, imapf); + ibuf->encodedbuffer = NULL; ibuf->encodedsize = 0; ibuf->userflags &= ~IB_BITMAPDIRTY; @@ -859,6 +1002,28 @@ void BKE_image_memorypack(Image *ima) IMB_freeImBuf(ibuf); } +void BKE_image_packfiles(ReportList *reports, Image *ima, const char *basepath) +{ + const size_t totfiles = image_num_files(ima); + + if (totfiles == 1) { + ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image packed file"); + BLI_addtail(&ima->packedfiles, imapf); + imapf->packedfile = newPackedFile(reports, ima->name, basepath); + BLI_strncpy(imapf->filepath, ima->name, sizeof(imapf->filepath)); + } + else { + ImageView *iv; + for (iv = ima->views.first; iv; iv = iv->next) { + ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image packed file"); + BLI_addtail(&ima->packedfiles, imapf); + + imapf->packedfile = newPackedFile(reports, iv->filepath, basepath); + BLI_strncpy(imapf->filepath, iv->filepath, sizeof(imapf->filepath)); + } + } +} + void BKE_image_tag_time(Image *ima) { ima->lastused = PIL_check_seconds_timer_i(); @@ -1259,7 +1424,7 @@ char BKE_imtype_from_arg(const char *imtype_arg) else return R_IMF_IMTYPE_INVALID; } -static bool image_path_ensure_ext(char *string, const char imtype, const ImageFormatData *im_format) +static bool do_add_image_extension(char *string, const char imtype, const ImageFormatData *im_format) { const char *extension = NULL; const char *extension_test; @@ -1311,7 +1476,7 @@ static bool image_path_ensure_ext(char *string, const char imtype, const ImageFo } #endif #ifdef WITH_OPENEXR - else if (ELEM(imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) { + else if (imtype == R_IMF_IMTYPE_OPENEXR || imtype == R_IMF_IMTYPE_MULTILAYER) { if (!BLI_testextensie(string, extension_test = ".exr")) extension = extension_test; } @@ -1369,14 +1534,14 @@ static bool image_path_ensure_ext(char *string, const char imtype, const ImageFo } } -bool BKE_image_path_ensure_ext_from_imformat(char *string, const ImageFormatData *im_format) +int BKE_image_path_ensure_ext_from_imformat(char *string, const ImageFormatData *im_format) { - return image_path_ensure_ext(string, im_format->imtype, im_format); + return do_add_image_extension(string, im_format->imtype, im_format); } -bool BKE_image_path_ensure_ext_from_imtype(char *string, const char imtype) +int BKE_image_path_ensure_ext_from_imtype(char *string, const char imtype) { - return image_path_ensure_ext(string, imtype, NULL); + return do_add_image_extension(string, imtype, NULL); } void BKE_imformat_defaults(ImageFormatData *im_format) @@ -1908,14 +2073,12 @@ bool BKE_imbuf_alpha_test(ImBuf *ibuf) /* note: imf->planes is ignored here, its assumed the image channels * are already set */ -int BKE_imbuf_write(ImBuf *ibuf, const char *name, ImageFormatData *imf) +void BKE_imbuf_write_prepare(ImBuf *ibuf, ImageFormatData *imf) { char imtype = imf->imtype; char compress = imf->compress; char quality = imf->quality; - int ok; - if (imtype == R_IMF_IMTYPE_IRIS) { ibuf->ftype = IMAGIC; } @@ -1952,7 +2115,7 @@ int BKE_imbuf_write(ImBuf *ibuf, const char *name, ImageFormatData *imf) } #endif #ifdef WITH_OPENEXR - else if (imtype == R_IMF_IMTYPE_OPENEXR || imtype == R_IMF_IMTYPE_MULTILAYER) { + else if (ELEM(imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) { ibuf->ftype = OPENEXR; if (imf->depth == R_IMF_CHAN_DEPTH_16) ibuf->ftype |= OPENEXR_HALF; @@ -2036,6 +2199,13 @@ int BKE_imbuf_write(ImBuf *ibuf, const char *name, ImageFormatData *imf) if (quality < 10) quality = 90; ibuf->ftype = JPG | quality; } +} + +int BKE_imbuf_write(ImBuf *ibuf, const char *name, ImageFormatData *imf) +{ + int ok; + + BKE_imbuf_write_prepare(ibuf, imf); BLI_make_existing_file(name); @@ -2079,9 +2249,10 @@ int BKE_imbuf_write_stamp(Scene *scene, struct Object *camera, ImBuf *ibuf, cons } -static void image_path_makepicstring( +static void do_makepicstring( char *string, const char *base, const char *relbase, int frame, const char imtype, - const ImageFormatData *im_format, const short use_ext, const short use_frames) + const ImageFormatData *im_format, const short use_ext, const short use_frames, + const char *suffix) { if (string == NULL) return; BLI_strncpy(string, base, FILE_MAX - 10); /* weak assumption */ @@ -2090,22 +2261,25 @@ static void image_path_makepicstring( if (use_frames) BLI_path_frame(string, frame, 4); + if (suffix) + BLI_path_suffix(string, FILE_MAX, suffix, ""); + if (use_ext) - image_path_ensure_ext(string, imtype, im_format); + do_add_image_extension(string, imtype, im_format); } void BKE_image_path_from_imformat( char *string, const char *base, const char *relbase, int frame, - const ImageFormatData *im_format, const bool use_ext, const bool use_frames) + const ImageFormatData *im_format, const bool use_ext, const bool use_frames, const char *suffix) { - image_path_makepicstring(string, base, relbase, frame, im_format->imtype, im_format, use_ext, use_frames); + do_makepicstring(string, base, relbase, frame, im_format->imtype, im_format, use_ext, use_frames, suffix); } void BKE_image_path_from_imtype( char *string, const char *base, const char *relbase, int frame, - const char imtype, const bool use_ext, const bool use_frames) + const char imtype, const bool use_ext, const bool use_frames, const char *view) { - image_path_makepicstring(string, base, relbase, frame, imtype, NULL, use_ext, use_frames); + do_makepicstring(string, base, relbase, frame, imtype, NULL, use_ext, use_frames, view); } struct anim *openanim_noload(const char *name, int flags, int streamindex, char colorspace[IMA_MAX_SPACE]) @@ -2181,6 +2355,59 @@ Image *BKE_image_verify_viewer(int type, const char *name) return ima; } +static void image_viewer_create_views(const RenderData *rd, Image *ima) +{ + SceneRenderView *srv; + for (srv = rd->views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(rd, srv) == false) + continue; + image_add_view(ima, srv->name, ""); + } +} + +/* Reset the image cache and views when the Viewer Nodes views don't match the scene views */ +void BKE_image_verify_viewer_views(const RenderData *rd, Image *ima, ImageUser *iuser) +{ + bool do_reset; + + BLI_lock_thread(LOCK_DRAW_IMAGE); + + if (BKE_scene_multiview_is_stereo3d(rd)) { + ima->flag |= IMA_IS_STEREO; + ima->flag |= IMA_IS_MULTIVIEW; + } + else { + ima->flag &= ~IMA_IS_STEREO; + ima->flag &= ~IMA_IS_MULTIVIEW; + iuser->flag &= ~IMA_SHOW_STEREO; + } + + /* see if all scene render views are in the image view list */ + do_reset = (BKE_scene_multiview_num_views_get(rd) != BLI_listbase_count(&ima->views)); + if (!do_reset) { + SceneRenderView *srv; + ImageView *iv; + + for (iv = ima->views.first; iv; iv = iv->next) { + srv = BLI_findstring(&rd->views, iv->name, offsetof(SceneRenderView, name)); + if ((srv == NULL) || (BKE_scene_multiview_is_render_view_active(rd, srv) == false)) { + do_reset = true; + break; + } + } + } + + if (do_reset) { + image_free_cached_frames(ima); + BKE_image_free_views(ima); + + /* add new views */ + image_viewer_create_views(rd, ima); + } + + BLI_unlock_thread(LOCK_DRAW_IMAGE); +} + void BKE_image_walk_all_users(const Main *mainp, void *customdata, void callback(Image *ima, ImageUser *iuser, void *customdata)) { @@ -2238,6 +2465,33 @@ static void image_tag_frame_recalc(Image *ima, ImageUser *iuser, void *customdat } } +static void image_init_imageuser(Image *ima, ImageUser *iuser) +{ + RenderResult *rr = ima->rr; + + iuser->multi_index = 0; + iuser->layer = iuser->pass = iuser->view = 0; + iuser->passtype = SCE_PASS_COMBINED; + + if (rr) { + RenderLayer *rl = rr->layers.first; + + if (rl) { + RenderPass *rp = rl->passes.first; + + if (rp) + iuser->passtype = rp->passtype; + } + + BKE_image_multilayer_index(rr, iuser); + } +} + +void BKE_image_init_imageuser(Image *ima, ImageUser *iuser) +{ + return image_init_imageuser(ima, iuser); +} + void BKE_image_signal(Image *ima, ImageUser *iuser, int signal) { if (ima == NULL) @@ -2248,8 +2502,13 @@ void BKE_image_signal(Image *ima, ImageUser *iuser, int signal) switch (signal) { case IMA_SIGNAL_FREE: BKE_image_free_buffers(ima); - if (iuser) + + if (iuser) { iuser->ok = 1; + if (iuser->scene) { + image_update_views_format(ima, iuser); + } + } break; case IMA_SIGNAL_SRC_CHANGE: if (ima->type == IMA_TYPE_UV_TEST) @@ -2301,23 +2560,41 @@ void BKE_image_signal(Image *ima, ImageUser *iuser, int signal) case IMA_SIGNAL_RELOAD: /* try to repack file */ - if (ima->packedfile) { - PackedFile *pf; - pf = newPackedFile(NULL, ima->name, ID_BLEND_PATH(G.main, &ima->id)); - if (pf) { - freePackedFile(ima->packedfile); - ima->packedfile = pf; - BKE_image_free_buffers(ima); + if (BKE_image_has_packedfile(ima)) { + const size_t totfiles = image_num_files(ima); + + if (totfiles != BLI_listbase_count_ex(&ima->packedfiles, totfiles + 1)) { + /* in case there are new available files to be loaded */ + image_free_packedfiles(ima); + BKE_image_packfiles(NULL, ima, ID_BLEND_PATH(G.main, &ima->id)); } else { - printf("ERROR: Image not available. Keeping packed image\n"); + ImagePackedFile *imapf; + for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) { + PackedFile *pf; + pf = newPackedFile(NULL, imapf->filepath, ID_BLEND_PATH(G.main, &ima->id)); + if (pf) { + freePackedFile(imapf->packedfile); + imapf->packedfile = pf; + } + else { + printf("ERROR: Image \"%s\" not available. Keeping packed image\n", imapf->filepath); + } + } } + + if (BKE_image_has_packedfile(ima)) + BKE_image_free_buffers(ima); } else BKE_image_free_buffers(ima); - if (iuser) + if (iuser) { iuser->ok = 1; + if (iuser->scene) { + image_update_views_format(ima, iuser); + } + } break; case IMA_SIGNAL_USER_NEW_IMAGE: @@ -2325,8 +2602,7 @@ void BKE_image_signal(Image *ima, ImageUser *iuser, int signal) iuser->ok = 1; if (ima->source == IMA_SRC_FILE || ima->source == IMA_SRC_SEQUENCE) { if (ima->type == IMA_TYPE_MULTILAYER) { - iuser->multi_index = 0; - iuser->layer = iuser->pass = 0; + image_init_imageuser(ima, iuser); } } } @@ -2368,21 +2644,34 @@ RenderPass *BKE_image_multilayer_index(RenderResult *rr, ImageUser *iuser) return NULL; if (iuser) { - short index = 0, rl_index = 0, rp_index; + short index = 0, rv_index, rl_index = 0, rp_index; + bool is_stereo = (iuser->flag & IMA_SHOW_STEREO) && RE_RenderResult_is_stereo(rr); + + rv_index = is_stereo ? iuser->multiview_eye : iuser->view; for (rl = rr->layers.first; rl; rl = rl->next, rl_index++) { rp_index = 0; - for (rpass = rl->passes.first; rpass; rpass = rpass->next, index++, rp_index++) - if (iuser->layer == rl_index && iuser->pass == rp_index) + + for (rpass = rl->passes.first; rpass; rpass = rpass->next, index++, rp_index++) { + if (iuser->layer == rl_index && + iuser->passtype == rpass->passtype && + rv_index == rpass->view_id) + { break; + } + } if (rpass) break; } - if (rpass) + if (rpass) { iuser->multi_index = index; - else + iuser->pass = rp_index; + } + else { iuser->multi_index = 0; + iuser->pass = 0; + } } if (rpass == NULL) { rl = rr->layers.first; @@ -2393,19 +2682,80 @@ RenderPass *BKE_image_multilayer_index(RenderResult *rr, ImageUser *iuser) return rpass; } +void BKE_image_multiview_index(Image *ima, ImageUser *iuser) +{ + if (iuser) { + bool is_stereo = (ima->flag & IMA_IS_STEREO) && (iuser->flag & IMA_SHOW_STEREO); + if (is_stereo) { + iuser->multi_index = iuser->multiview_eye; + } + else { + if ((iuser->view < 0) || (iuser->view >= BLI_listbase_count_ex(&ima->views, iuser->view + 1))) { + iuser->multi_index = iuser->view = 0; + } + else { + iuser->multi_index = iuser->view; + } + } + } +} + +/* if layer or pass changes, we need an index for the imbufs list */ +/* note it is called for rendered results, but it doesnt use the index! */ +/* and because rendered results use fake layer/passes, don't correct for wrong indices here */ +bool BKE_image_is_multilayer(Image *ima) +{ + if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE)) { + if (ima->type == IMA_TYPE_MULTILAYER) { + return true; + } + } + else if (ima->source == IMA_SRC_VIEWER) { + if (ima->type == IMA_TYPE_R_RESULT) { + return true; + } + } + return false; +} + +static void image_init_multilayer_multiview_flag(Image *ima, RenderResult *rr) +{ + if (rr) { + if (RE_RenderResult_is_stereo(rr)) { + ima->flag |= IMA_IS_STEREO; + ima->flag |= IMA_IS_MULTIVIEW; + } + else { + ima->flag &= ~IMA_IS_STEREO; + if (BLI_listbase_count_ex(&rr->views, 2) > 1) + ima->flag |= IMA_IS_MULTIVIEW; + else + ima->flag &= ~IMA_IS_MULTIVIEW; + } + } + else { + ima->flag &= ~IMA_IS_STEREO; + ima->flag &= ~IMA_IS_MULTIVIEW; + } +} + RenderResult *BKE_image_acquire_renderresult(Scene *scene, Image *ima) { + RenderResult *rr = NULL; if (ima->rr) { - return ima->rr; + rr = ima->rr; } else if (ima->type == IMA_TYPE_R_RESULT) { if (ima->render_slot == ima->last_render_slot) - return RE_AcquireResultRead(RE_GetRender(scene->id.name)); + rr = RE_AcquireResultRead(RE_GetRender(scene->id.name)); else - return ima->renders[ima->render_slot]; + rr = ima->renders[ima->render_slot]; + + /* set proper multiview flag */ + image_init_multilayer_multiview_flag(ima, rr); } - else - return NULL; + + return rr; } void BKE_image_release_renderresult(Scene *scene, Image *ima) @@ -2419,6 +2769,18 @@ void BKE_image_release_renderresult(Scene *scene, Image *ima) } } +bool BKE_image_is_openexr(struct Image *ima) +{ +#ifdef WITH_OPENEXR + if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE)) { + return BLI_testextensie(ima->name, ".exr"); + } +#else + UNUSED_VARS(ima); +#endif + return false; +} + void BKE_image_backup_render(Scene *scene, Image *ima) { /* called right before rendering, ima->renders contains render @@ -2439,8 +2801,155 @@ void BKE_image_backup_render(Scene *scene, Image *ima) ima->last_render_slot = slot; } +/**************************** multiview save openexr *********************************/ +#ifdef WITH_OPENEXR +static const char *image_get_view_cb(void *base, const size_t view_id) +{ + Image *ima = base; + ImageView *iv = BLI_findlink(&ima->views, view_id); + return iv ? iv->name : ""; +} +#endif /* WITH_OPENEXR */ + +#ifdef WITH_OPENEXR +static ImBuf *image_get_buffer_cb(void *base, const size_t view_id) +{ + Image *ima = base; + ImageUser iuser = {0}; + + iuser.view = view_id; + iuser.ok = 1; + + BKE_image_multiview_index(ima, &iuser); + + return image_acquire_ibuf(ima, &iuser, NULL); +} +#endif /* WITH_OPENEXR */ + +bool BKE_image_save_openexr_multiview(Image *ima, ImBuf *ibuf, const char *filepath, const int flags) +{ +#ifdef WITH_OPENEXR + char name[FILE_MAX]; + bool ok; + + BLI_strncpy(name, filepath, sizeof(name)); + BLI_path_abs(name, G.main->name); + + ibuf->userdata = ima; + ok = IMB_exr_multiview_save(ibuf, name, flags, BLI_listbase_count(&ima->views), image_get_view_cb, image_get_buffer_cb); + ibuf->userdata = NULL; + + return ok; +#else + UNUSED_VARS(ima, ibuf, filepath, flags); + return false; +#endif +} + +/**************************** multiview load openexr *********************************/ + +static void image_add_view(Image *ima, const char *viewname, const char *filepath) +{ + ImageView *iv; + + iv = MEM_mallocN(sizeof(ImageView), "Viewer Image View"); + BLI_strncpy(iv->name, viewname, sizeof(iv->name)); + BLI_strncpy(iv->filepath, filepath, sizeof(iv->filepath)); + + /* For stereo drawing we need to ensure: + * STEREO_LEFT_NAME == STEREO_LEFT_ID and + * STEREO_RIGHT_NAME == STEREO_RIGHT_ID */ + + if (STREQ(viewname, STEREO_LEFT_NAME)) { + BLI_addhead(&ima->views, iv); + } + else if (STREQ(viewname, STEREO_RIGHT_NAME)) { + ImageView *left_iv = BLI_findstring(&ima->views, STEREO_LEFT_NAME, offsetof(ImageView, name)); + + if (left_iv == NULL) { + BLI_addhead(&ima->views, iv); + } + else { + BLI_insertlinkafter(&ima->views, left_iv, iv); + } + } + else { + BLI_addtail(&ima->views, iv); + } +} + +#ifdef WITH_OPENEXR +static void image_add_view_cb(void *base, const char *str) +{ + Image *ima = base; + image_add_view(ima, str, ima->name); +} + +static void image_add_buffer_cb(void *base, const char *str, ImBuf *ibuf, const int frame) +{ + Image *ima = base; + size_t id; + bool predivide = (ima->alpha_mode == IMA_ALPHA_PREMUL); + const char *colorspace = ima->colorspace_settings.name; + const char *to_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR); + + if (ibuf == NULL) + return; + + id = BLI_findstringindex(&ima->views, str, offsetof(ImageView, name)); + + if (id == -1) + return; + + if (ibuf->channels >= 3) + IMB_colormanagement_transform(ibuf->rect_float, ibuf->x, ibuf->y, ibuf->channels, + colorspace, to_colorspace, predivide); + + image_assign_ibuf(ima, ibuf, id, frame); + IMB_freeImBuf(ibuf); +} +#endif /* WITH_OPENEXR */ + +#ifdef WITH_OPENEXR +static void image_update_multiview_flags(Image *ima) +{ + if (BLI_listbase_count_ex(&ima->views, 2) > 1) { + ima->flag |= IMA_IS_MULTIVIEW; + + if (BLI_findstring(&ima->views, STEREO_LEFT_NAME, offsetof(ImageView, name)) && + BLI_findstring(&ima->views, STEREO_RIGHT_NAME, offsetof(ImageView, name))) + { + ima->flag |= IMA_IS_STEREO; + } + else { + ima->flag &= ~IMA_IS_STEREO; + } + } + else { + ima->flag &= ~IMA_IS_STEREO; + ima->flag &= ~IMA_IS_MULTIVIEW; + } +} +#endif /* WITH_OPENEXR */ + /* after imbuf load, openexr type can return with a exrhandle open */ /* in that case we have to build a render-result */ +#ifdef WITH_OPENEXR +static void image_create_multiview(Image *ima, ImBuf *ibuf, const int frame) +{ + image_free_views(ima); + + IMB_exr_multiview_convert(ibuf->userdata, ima, image_add_view_cb, image_add_buffer_cb, frame); + + image_update_multiview_flags(ima); + + IMB_exr_close(ibuf->userdata); +} +#endif /* WITH_OPENEXR */ + +/* after imbuf load, openexr type can return with a exrhandle open */ +/* in that case we have to build a render-result */ +#ifdef WITH_OPENEXR static void image_create_multilayer(Image *ima, ImBuf *ibuf, int framenr) { const char *colorspace = ima->colorspace_settings.name; @@ -2448,14 +2957,16 @@ static void image_create_multilayer(Image *ima, ImBuf *ibuf, int framenr) ima->rr = RE_MultilayerConvert(ibuf->userdata, colorspace, predivide, ibuf->x, ibuf->y); -#ifdef WITH_OPENEXR IMB_exr_close(ibuf->userdata); -#endif ibuf->userdata = NULL; if (ima->rr) ima->rr->framenr = framenr; + + /* set proper multiview flag */ + image_init_multilayer_multiview_flag(ima, ima->rr); } +#endif /* WITH_OPENEXR */ /* common stuff to do with images after loading */ static void image_initialize_after_load(Image *ima, ImBuf *ibuf) @@ -2488,18 +2999,41 @@ static int imbuf_alpha_flags_for_image(Image *ima) return flag; } -static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int frame) +/* the number of files will vary according to the stereo format */ +static size_t image_num_files(Image *ima) +{ + const bool is_multiview = (ima->flag & IMA_IS_MULTIVIEW) != 0; + + if (!is_multiview) { + return 1; + } + else if (ima->views_format == R_IMF_VIEWS_STEREO_3D) { + return 1; + } + /* R_IMF_VIEWS_INDIVIDUAL */ + else { + return BLI_listbase_count(&ima->views); + } +} + +static ImBuf *load_sequence_single(Image *ima, ImageUser *iuser, int frame, const size_t view_id, bool *r_assign) { struct ImBuf *ibuf; char name[FILE_MAX]; int flag; + ImageUser iuser_t; /* XXX temp stuff? */ if (ima->lastframe != frame) ima->tpageflag |= IMA_TPAGE_REFRESH; ima->lastframe = frame; - BKE_image_user_file_path(iuser, ima, name); + + if (iuser) + iuser_t = *iuser; + + iuser_t.view = view_id; + BKE_image_user_file_path(&iuser_t, ima, name); flag = IB_rect | IB_multilayer; flag |= imbuf_alpha_flags_for_image(ima); @@ -2520,25 +3054,78 @@ static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int frame) #ifdef WITH_OPENEXR /* handle multilayer case, don't assign ibuf. will be handled in BKE_image_acquire_ibuf */ if (ibuf->ftype == OPENEXR && ibuf->userdata) { - image_create_multilayer(ima, ibuf, frame); - ima->type = IMA_TYPE_MULTILAYER; - IMB_freeImBuf(ibuf); - ibuf = NULL; + /* handle singlelayer multiview case assign ibuf based on available views */ + if (IMB_exr_has_singlelayer_multiview(ibuf->userdata)) { + image_create_multiview(ima, ibuf, frame); + IMB_freeImBuf(ibuf); + ibuf = NULL; + } + else if (IMB_exr_has_multilayer(ibuf->userdata)) { + /* handle multilayer case, don't assign ibuf. will be handled in BKE_image_acquire_ibuf */ + image_create_multilayer(ima, ibuf, frame); + ima->type = IMA_TYPE_MULTILAYER; + IMB_freeImBuf(ibuf); + ibuf = NULL; + } } else { image_initialize_after_load(ima, ibuf); - image_assign_ibuf(ima, ibuf, 0, frame); + *r_assign = true; } #else image_initialize_after_load(ima, ibuf); - image_assign_ibuf(ima, ibuf, 0, frame); + *r_assign = true; #endif } - else - ima->ok = 0; - if (iuser) - iuser->ok = ima->ok; + return ibuf; +} + +static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int frame) +{ + struct ImBuf *ibuf = NULL; + const bool is_multiview = (ima->flag & IMA_IS_MULTIVIEW) != 0; + const size_t totfiles = image_num_files(ima); + bool assign = false; + + if (!is_multiview) { + ibuf = load_sequence_single(ima, iuser, frame, 0, &assign); + if (assign) { + image_assign_ibuf(ima, ibuf, 0, frame); + } + } + else { + size_t i; + struct ImBuf **ibuf_arr; + const size_t totviews = BLI_listbase_count(&ima->views); + + ibuf_arr = MEM_mallocN(sizeof(ImBuf *) * totviews, "Image Views Imbufs"); + + for (i = 0; i < totfiles; i++) + ibuf_arr[i] = load_sequence_single(ima, iuser, frame, i, &assign); + + if ((ima->flag & IMA_IS_STEREO) && ima->views_format == R_IMF_VIEWS_STEREO_3D) + IMB_ImBufFromStereo3d(ima->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]); + + /* return the original requested ImBuf */ + ibuf = ibuf_arr[(iuser ? iuser->multi_index : 0)]; + + if (assign) { + for (i = 0; i < totviews; i++) { + image_assign_ibuf(ima, ibuf_arr[i], i, frame); + } + } + + /* "remove" the others (decrease their refcount) */ + for (i = 0; i < totviews; i++) { + if (ibuf_arr[i] != ibuf) { + IMB_freeImBuf(ibuf_arr[i]); + } + } + + /* cleanup */ + MEM_freeN(ibuf_arr); + } return ibuf; } @@ -2595,46 +3182,52 @@ static ImBuf *image_load_sequence_multilayer(Image *ima, ImageUser *iuser, int f return ibuf; } - -static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame) +static ImBuf *load_movie_single(Image *ima, ImageUser *iuser, int frame, const size_t view_id) { struct ImBuf *ibuf = NULL; + ImageAnim *ia; - ima->lastframe = frame; + ia = BLI_findlink(&ima->anims, view_id); - if (ima->anim == NULL) { + if (ia->anim == NULL) { char str[FILE_MAX]; int flags = IB_rect; + ImageUser iuser_t; + if (ima->flag & IMA_DEINTERLACE) { flags |= IB_animdeinterlace; } - BKE_image_user_file_path(iuser, ima, str); + if (iuser) + iuser_t = *iuser; + + iuser_t.view = view_id; + + BKE_image_user_file_path(&iuser_t, ima, str); /* FIXME: make several stream accessible in image editor, too*/ - ima->anim = openanim(str, flags, 0, ima->colorspace_settings.name); + ia->anim = openanim(str, flags, 0, ima->colorspace_settings.name); /* let's initialize this user */ - if (ima->anim && iuser && iuser->frames == 0) - iuser->frames = IMB_anim_get_duration(ima->anim, + if (ia->anim && iuser && iuser->frames == 0) + iuser->frames = IMB_anim_get_duration(ia->anim, IMB_TC_RECORD_RUN); } - if (ima->anim) { - int dur = IMB_anim_get_duration(ima->anim, + if (ia->anim) { + int dur = IMB_anim_get_duration(ia->anim, IMB_TC_RECORD_RUN); int fra = frame - 1; if (fra < 0) fra = 0; if (fra > (dur - 1)) fra = dur - 1; ibuf = IMB_makeSingleUser( - IMB_anim_absolute(ima->anim, fra, + IMB_anim_absolute(ia->anim, fra, IMB_TC_RECORD_RUN, IMB_PROXY_NONE)); if (ibuf) { image_initialize_after_load(ima, ibuf); - image_assign_ibuf(ima, ibuf, 0, frame); } else ima->ok = 0; @@ -2642,67 +3235,223 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame) else ima->ok = 0; + return ibuf; +} + +static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame) +{ + struct ImBuf *ibuf = NULL; + const bool is_multiview = (ima->flag & IMA_IS_MULTIVIEW) != 0; + const size_t totfiles = image_num_files(ima); + size_t i; + + if (totfiles != BLI_listbase_count_ex(&ima->anims, totfiles + 1)) { + image_free_anims(ima); + + for (i = 0; i < totfiles; i++) { + /* allocate the ImageAnim */ + ImageAnim *ia = MEM_callocN(sizeof(ImageAnim), "Image Anim"); + BLI_addtail(&ima->anims, ia); + } + } + + if (!is_multiview) { + ibuf = load_movie_single(ima, iuser, frame, 0); + image_assign_ibuf(ima, ibuf, 0, frame); + } + else { + struct ImBuf **ibuf_arr; + const size_t totviews = BLI_listbase_count(&ima->views); + + ibuf_arr = MEM_mallocN(sizeof(ImBuf *) * totviews, "Image Views (movie) Imbufs"); + + for (i = 0; i < totfiles; i++) { + ibuf_arr[i] = load_movie_single(ima, iuser, frame, i); + } + + if ((ima->flag & IMA_IS_STEREO) && ima->views_format == R_IMF_VIEWS_STEREO_3D) + IMB_ImBufFromStereo3d(ima->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]); + + for (i = 0; i < totviews; i++) { + if (ibuf_arr[i]) { + image_assign_ibuf(ima, ibuf_arr[i], i, frame); + } + else { + ima->ok = 0; + } + } + + /* return the original requested ImBuf */ + ibuf = ibuf_arr[(iuser ? iuser->multi_index : 0)]; + + /* "remove" the others (decrease their refcount) */ + for (i = 0; i < totviews; i++) { + if (ibuf_arr[i] != ibuf) { + IMB_freeImBuf(ibuf_arr[i]); + } + } + + /* cleanup */ + MEM_freeN(ibuf_arr); + } + if (iuser) iuser->ok = ima->ok; return ibuf; } -/* warning, 'iuser' can be NULL */ -static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra) +static ImBuf *load_image_single( + Image *ima, ImageUser *iuser, int cfra, + const size_t view_id, + const bool has_packed, + bool *r_assign) { - struct ImBuf *ibuf; - char str[FILE_MAX]; - int assign = 0, flag; - - /* always ensure clean ima */ - BKE_image_free_buffers(ima); + char filepath[FILE_MAX]; + struct ImBuf *ibuf = NULL; + int flag; /* is there a PackedFile with this image ? */ - if (ima->packedfile) { + if (has_packed) { + ImagePackedFile *imapf; + flag = IB_rect | IB_multilayer; flag |= imbuf_alpha_flags_for_image(ima); - ibuf = IMB_ibImageFromMemory((unsigned char *)ima->packedfile->data, ima->packedfile->size, flag, - ima->colorspace_settings.name, "<packed data>"); + imapf = BLI_findlink(&ima->packedfiles, view_id); + ibuf = IMB_ibImageFromMemory( + (unsigned char *)imapf->packedfile->data, imapf->packedfile->size, flag, + ima->colorspace_settings.name, "<packed data>"); } else { + ImageUser iuser_t; + flag = IB_rect | IB_multilayer | IB_metadata; flag |= imbuf_alpha_flags_for_image(ima); - /* get the right string */ + /* get the correct filepath */ BKE_image_user_frame_calc(iuser, cfra, 0); - BKE_image_user_file_path(iuser, ima, str); + + if (iuser) + iuser_t = *iuser; + else + iuser_t.framenr = ima->lastframe; + + iuser_t.view = view_id; + + BKE_image_user_file_path(&iuser_t, ima, filepath); /* read ibuf */ - ibuf = IMB_loadiffname(str, flag, ima->colorspace_settings.name); + ibuf = IMB_loadiffname(filepath, flag, ima->colorspace_settings.name); } if (ibuf) { - /* handle multilayer case, don't assign ibuf. will be handled in BKE_image_acquire_ibuf */ +#ifdef WITH_OPENEXR if (ibuf->ftype == OPENEXR && ibuf->userdata) { - image_create_multilayer(ima, ibuf, cfra); - ima->type = IMA_TYPE_MULTILAYER; - IMB_freeImBuf(ibuf); - ibuf = NULL; + if (IMB_exr_has_singlelayer_multiview(ibuf->userdata)) { + /* handle singlelayer multiview case assign ibuf based on available views */ + image_create_multiview(ima, ibuf, cfra); + IMB_freeImBuf(ibuf); + ibuf = NULL; + } + else if (IMB_exr_has_multilayer(ibuf->userdata)) { + /* handle multilayer case, don't assign ibuf. will be handled in BKE_image_acquire_ibuf */ + image_create_multilayer(ima, ibuf, cfra); + ima->type = IMA_TYPE_MULTILAYER; + IMB_freeImBuf(ibuf); + ibuf = NULL; + } } else { image_initialize_after_load(ima, ibuf); - assign = 1; + *r_assign = true; /* check if the image is a font image... */ detectBitmapFont(ibuf); /* make packed file for autopack */ - if ((ima->packedfile == NULL) && (G.fileflags & G_AUTOPACK)) - ima->packedfile = newPackedFile(NULL, str, ID_BLEND_PATH(G.main, &ima->id)); + if ((has_packed == false) && (G.fileflags & G_AUTOPACK)) { + ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image Packefile"); + BLI_addtail(&ima->packedfiles, imapf); + + BLI_strncpy(imapf->filepath, filepath, sizeof(imapf->filepath)); + imapf->packedfile = newPackedFile(NULL, filepath, ID_BLEND_PATH(G.main, &ima->id)); + } } +#else + image_initialize_after_load(ima, ibuf); + *r_assign = true; +#endif } - else + else { ima->ok = 0; + } - if (assign) - image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); + return ibuf; +} + +/* warning, 'iuser' can be NULL + * note: Image->views was already populated (in image_update_views_format) + */ +static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra) +{ + struct ImBuf *ibuf = NULL; + bool assign = false; + const bool is_multiview = (ima->flag & IMA_IS_MULTIVIEW) != 0; + const size_t totfiles = image_num_files(ima); + bool has_packed = BKE_image_has_packedfile(ima); + + /* always ensure clean ima */ + BKE_image_free_buffers(ima); + + /* this should never happen, but just playing safe */ + if (has_packed) { + if (totfiles != BLI_listbase_count_ex(&ima->packedfiles, totfiles + 1)) { + image_free_packedfiles(ima); + has_packed = false; + } + } + + if (!is_multiview) { + ibuf = load_image_single(ima, iuser, cfra, 0, has_packed, &assign); + if (assign) { + image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); + } + } + else { + size_t i; + struct ImBuf **ibuf_arr; + const size_t totviews = BLI_listbase_count(&ima->views); + BLI_assert(totviews > 0); + + ibuf_arr = MEM_mallocN(sizeof(ImBuf *) * totviews, "Image Views Imbufs"); + + for (i = 0; i < totfiles; i++) + ibuf_arr[i] = load_image_single(ima, iuser, cfra, i, has_packed, &assign); + + if ((ima->flag & IMA_IS_STEREO) && ima->views_format == R_IMF_VIEWS_STEREO_3D) + IMB_ImBufFromStereo3d(ima->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]); + + /* return the original requested ImBuf */ + i = iuser && iuser->multi_index < totviews ? iuser->multi_index : 0; + ibuf = ibuf_arr[i]; + + if (assign) { + for (i = 0; i < totviews; i++) { + image_assign_ibuf(ima, ibuf_arr[i], i, 0); + } + } + + /* "remove" the others (decrease their refcount) */ + for (i = 0; i < totviews; i++) { + if (ibuf_arr[i] != ibuf) { + IMB_freeImBuf(ibuf_arr[i]); + } + } + + /* cleanup */ + MEM_freeN(ibuf_arr); + } if (iuser) iuser->ok = ima->ok; @@ -2756,9 +3505,10 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **lock_ float *rectf, *rectz; unsigned int *rect; float dither; - int channels, layer, pass; + int channels, layer, passtype; ImBuf *ibuf; int from_render = (ima->render_slot == ima->last_render_slot); + int actview; bool byte_buffer_in_display_space = false; if (!(iuser && iuser->scene)) @@ -2772,14 +3522,18 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **lock_ channels = 4; layer = iuser->layer; - pass = iuser->pass; + passtype = iuser->passtype; + actview = iuser->view; + + if ((ima->flag & IMA_IS_STEREO) && (iuser->flag & IMA_SHOW_STEREO)) + actview = iuser->multiview_eye; if (from_render) { - RE_AcquireResultImage(re, &rres); + RE_AcquireResultImage(re, &rres, actview); } else if (ima->renders[ima->render_slot]) { rres = *(ima->renders[ima->render_slot]); - rres.have_combined = rres.rectf != NULL; + rres.have_combined = RE_RenderViewGetRectf(&rres, actview) != NULL; } else memset(&rres, 0, sizeof(RenderResult)); @@ -2819,24 +3573,18 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **lock_ if (rl) { RenderPass *rpass; - /* there's no combined pass, is in renderlayer itself */ - if (pass == 0) { - rectf = rl->rectf; - if (rectf == NULL) { - /* Happens when Save Buffers is enabled. - * Use display buffer stored in the render layer. - */ - rect = (unsigned int *) rl->display_buffer; - byte_buffer_in_display_space = true; + for (rpass = rl->passes.first; rpass; rpass = rpass->next) { + if (passtype == rpass->passtype && + actview == rpass->view_id) + { + break; } } - else { - rpass = BLI_findlink(&rl->passes, pass - 1); - if (rpass) { - channels = rpass->channels; - rectf = rpass->rect; - dither = 0.0f; /* don't dither passes */ - } + + if (rpass) { + channels = rpass->channels; + rectf = rpass->rect; + dither = 0.0f; /* don't dither passes */ } for (rpass = rl->passes.first; rpass; rpass = rpass->next) @@ -2925,9 +3673,31 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **lock_ return ibuf; } +static size_t image_get_multiview_index(Image *ima, ImageUser *iuser) +{ + const bool is_multilayer = BKE_image_is_multilayer(ima); + const bool is_backdrop = (ima->source == IMA_SRC_VIEWER) && (ima->type == IMA_TYPE_COMPOSITE) && (iuser == NULL); + + if (is_multilayer) { + return iuser ? iuser->multi_index : IMA_NO_INDEX; + } + else if (is_backdrop) { + if ((ima->flag & IMA_IS_STEREO)) { + /* backdrop hackaround (since there is no iuser */ + return ima->eye; + } + } + else if ((ima->flag & IMA_IS_MULTIVIEW)) { + return iuser ? iuser->multi_index : 0; + } + + return IMA_NO_INDEX; +} + static void image_get_frame_and_index(Image *ima, ImageUser *iuser, int *r_frame, int *r_index) { int frame = 0, index = 0; + index = image_get_multiview_index(ima, iuser); /* see if we already have an appropriate ibuf, with image source and type */ if (ima->source == IMA_SRC_MOVIE) { @@ -2939,7 +3709,6 @@ static void image_get_frame_and_index(Image *ima, ImageUser *iuser, int *r_frame } else if (ima->type == IMA_TYPE_MULTILAYER) { frame = iuser ? iuser->framenr : ima->lastframe; - index = iuser ? iuser->multi_index : IMA_NO_INDEX; } } @@ -2958,10 +3727,12 @@ static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_frame, ImBuf *ibuf = NULL; int frame = 0, index = 0; + index = image_get_multiview_index(ima, iuser); + /* see if we already have an appropriate ibuf, with image source and type */ if (ima->source == IMA_SRC_MOVIE) { frame = iuser ? iuser->framenr : ima->lastframe; - ibuf = image_get_cached_ibuf_for_index_frame(ima, 0, frame); + ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame); /* XXX temp stuff? */ if (ima->lastframe != frame) ima->tpageflag |= IMA_TPAGE_REFRESH; @@ -2970,7 +3741,7 @@ static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_frame, else if (ima->source == IMA_SRC_SEQUENCE) { if (ima->type == IMA_TYPE_IMAGE) { frame = iuser ? iuser->framenr : ima->lastframe; - ibuf = image_get_cached_ibuf_for_index_frame(ima, 0, frame); + ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame); /* XXX temp stuff? */ if (ima->lastframe != frame) { @@ -2990,18 +3761,17 @@ static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_frame, } else if (ima->type == IMA_TYPE_MULTILAYER) { frame = iuser ? iuser->framenr : ima->lastframe; - index = iuser ? iuser->multi_index : IMA_NO_INDEX; ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame); } } else if (ima->source == IMA_SRC_FILE) { if (ima->type == IMA_TYPE_IMAGE) - ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0); + ibuf = image_get_cached_ibuf_for_index_frame(ima, index, 0); else if (ima->type == IMA_TYPE_MULTILAYER) - ibuf = image_get_cached_ibuf_for_index_frame(ima, iuser ? iuser->multi_index : IMA_NO_INDEX, 0); + ibuf = image_get_cached_ibuf_for_index_frame(ima, index, 0); } else if (ima->source == IMA_SRC_GENERATED) { - ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0); + ibuf = image_get_cached_ibuf_for_index_frame(ima, index, 0); } else if (ima->source == IMA_SRC_VIEWER) { /* always verify entirely, not that this shouldn't happen @@ -3085,7 +3855,7 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **lock_r) if (ima->gen_depth == 0) ima->gen_depth = 24; ibuf = add_ibuf_size(ima->gen_x, ima->gen_y, ima->name, ima->gen_depth, (ima->gen_flag & IMA_GEN_FLOAT) != 0, ima->gen_type, ima->gen_color, &ima->colorspace_settings); - image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0); + image_assign_ibuf(ima, ibuf, index, 0); ima->ok = IMA_OK_LOADED; } else if (ima->source == IMA_SRC_VIEWER) { @@ -3103,13 +3873,13 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **lock_r) /* XXX anim play for viewer nodes not yet supported */ frame = 0; // XXX iuser ? iuser->framenr : 0; - ibuf = image_get_cached_ibuf_for_index_frame(ima, 0, frame); + ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame); if (!ibuf) { /* Composite Viewer, all handled in compositor */ /* fake ibuf, will be filled in compositor */ - ibuf = IMB_allocImBuf(256, 256, 32, IB_rect); - image_assign_ibuf(ima, ibuf, 0, frame); + ibuf = IMB_allocImBuf(256, 256, 32, IB_rect | IB_rectfloat); + image_assign_ibuf(ima, ibuf, index, frame); } } } @@ -3409,7 +4179,13 @@ void BKE_image_update_frame(const Main *bmain, int cfra) void BKE_image_user_file_path(ImageUser *iuser, Image *ima, char *filepath) { - BLI_strncpy(filepath, ima->name, FILE_MAX); + if ((ima->flag & IMA_IS_MULTIVIEW) && (ima->rr == NULL)) { + ImageView *iv = BLI_findlink(&ima->views, iuser->view); + BLI_strncpy(filepath, iv->filepath, FILE_MAX); + } + else { + BLI_strncpy(filepath, ima->name, FILE_MAX); + } if (ima->source == IMA_SRC_SEQUENCE) { char head[FILE_MAX], tail[FILE_MAX]; @@ -3545,6 +4321,16 @@ int BKE_image_sequence_guess_offset(Image *image) return atoi(num); } +bool BKE_image_has_anim(Image *ima) +{ + return (BLI_listbase_is_empty(&ima->anims) == false); +} + +bool BKE_image_has_packedfile(Image *ima) +{ + return (BLI_listbase_is_empty(&ima->packedfiles) == false); +} + /** * Checks the image buffer changes (not keyframed values) * @@ -3675,3 +4461,88 @@ ImBuf *BKE_image_get_first_ibuf(Image *image) return ibuf; } + +static void image_update_views_format(Image *ima, ImageUser *iuser) +{ + SceneRenderView *srv; + ImageView *iv; + Scene *scene = iuser->scene; + const bool is_multiview = ((scene->r.scemode & R_MULTIVIEW) != 0) && + ((ima->flag & IMA_USE_VIEWS) != 0); + + /* reset the image views */ + BKE_image_free_views(ima); + + if (!is_multiview) { + goto monoview; + } + else if (ima->views_format == R_IMF_VIEWS_STEREO_3D) { + size_t i; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + + ima->flag |= IMA_IS_MULTIVIEW; + ima->flag |= IMA_IS_STEREO; + + for (i = 0; i < 2; i++) { + image_add_view(ima, names[i], ima->name); + } + return; + } + else { + /* R_IMF_VIEWS_INDIVIDUAL */ + char prefix[FILE_MAX] = {'\0'}; + char *name = ima->name; + char *ext = NULL; + + BKE_scene_multiview_view_prefix_get(scene, name, prefix, &ext); + + if (prefix[0] == '\0') { + goto monoview; + } + + /* create all the image views */ + for (srv = scene->r.views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(&scene->r, srv)) { + char filepath[FILE_MAX]; + BLI_snprintf(filepath, sizeof(filepath), "%s%s%s", prefix, srv->suffix, ext); + image_add_view(ima, srv->name, filepath); + } + } + + /* check if the files are all available */ + iv = ima->views.last; + while (iv) { + int file; + char str[FILE_MAX]; + + BLI_strncpy(str, iv->filepath, sizeof(str)); + BLI_path_abs(str, G.main->name); + + /* exists? */ + file = BLI_open(str, O_BINARY | O_RDONLY, 0); + if (file == -1) { + ImageView *iv_del = iv; + iv = iv->prev; + BLI_remlink(&ima->views, iv_del); + MEM_freeN(iv_del); + } + else { + iv = iv->prev; + } + close(file); + } + + /* all good */ + if (BLI_listbase_count_ex(&ima->views, 2) > 1) { + ima->flag |= IMA_IS_MULTIVIEW; + if (BKE_scene_multiview_is_stereo3d(&scene->r)) + ima->flag |= IMA_IS_STEREO; + } + else { +monoview: + ima->flag &= ~IMA_IS_STEREO; + ima->flag &= ~IMA_IS_MULTIVIEW; + BKE_image_free_views(ima); + } + } +} diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c index 7f3db700534..0d375b0dbeb 100644 --- a/source/blender/blenkernel/intern/node.c +++ b/source/blender/blenkernel/intern/node.c @@ -3475,6 +3475,7 @@ static void registerCompositNodes(void) register_node_type_cmp_bokehimage(); register_node_type_cmp_bokehblur(); register_node_type_cmp_switch(); + register_node_type_cmp_switch_view(); register_node_type_cmp_pixelate(); register_node_type_cmp_mask(); diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c index 81460ecdcc9..b89b5ab4bbc 100644 --- a/source/blender/blenkernel/intern/ocean.c +++ b/source/blender/blenkernel/intern/ocean.c @@ -1002,7 +1002,7 @@ static void cache_filename(char *string, const char *path, const char *relbase, BLI_join_dirfile(cachepath, sizeof(cachepath), path, fname); - BKE_image_path_from_imtype(string, cachepath, relbase, frame, R_IMF_IMTYPE_OPENEXR, true, true); + BKE_image_path_from_imtype(string, cachepath, relbase, frame, R_IMF_IMTYPE_OPENEXR, true, true, ""); } /* silly functions but useful to inline when the args do a lot of indirections */ diff --git a/source/blender/blenkernel/intern/packedFile.c b/source/blender/blenkernel/intern/packedFile.c index 9b429b356b4..61e39d0ad47 100644 --- a/source/blender/blenkernel/intern/packedFile.c +++ b/source/blender/blenkernel/intern/packedFile.c @@ -128,8 +128,8 @@ int countPackedFiles(Main *bmain) /* let's check if there are packed files... */ for (ima = bmain->image.first; ima; ima = ima->id.next) - if (ima->packedfile) - count++; + if (BKE_image_has_packedfile(ima)) + count ++; for (vf = bmain->vfont.first; vf; vf = vf->id.next) if (vf->packedfile) @@ -232,9 +232,9 @@ void packAll(Main *bmain, ReportList *reports) int tot = 0; for (ima = bmain->image.first; ima; ima = ima->id.next) { - if (ima->packedfile == NULL && ima->id.lib == NULL) { + if (BKE_image_has_packedfile(ima) == false && ima->id.lib == NULL) { if (ima->source == IMA_SRC_FILE) { - ima->packedfile = newPackedFile(reports, ima->name, ID_BLEND_PATH(bmain, &ima->id)); + BKE_image_packfiles(reports, ima, ID_BLEND_PATH(bmain, &ima->id)); tot ++; } else if (BKE_image_is_animated(ima)) { @@ -564,23 +564,47 @@ int unpackSound(Main *bmain, ReportList *reports, bSound *sound, int how) int unpackImage(ReportList *reports, Image *ima, int how) { - char localname[FILE_MAX], absname[FILE_MAX]; - char *newname; int ret_value = RET_ERROR; - + if (ima != NULL && ima->name[0]) { - unpack_generate_paths(ima->name, (ID *)ima, absname, localname, sizeof(absname), sizeof(localname)); - newname = unpackFile(reports, absname, localname, ima->packedfile, how); - if (newname != NULL) { - ret_value = RET_OK; - freePackedFile(ima->packedfile); - ima->packedfile = NULL; - BLI_strncpy(ima->name, newname, sizeof(ima->name)); - MEM_freeN(newname); - BKE_image_signal(ima, NULL, IMA_SIGNAL_RELOAD); + while (ima->packedfiles.last) { + char localname[FILE_MAX], absname[FILE_MAX]; + char *newname; + ImagePackedFile *imapf = ima->packedfiles.last; + + unpack_generate_paths(imapf->filepath, (ID *)ima, absname, localname, sizeof(absname), sizeof(localname)); + newname = unpackFile(reports, absname, localname, imapf->packedfile, how); + + if (newname != NULL) { + ImageView *iv; + + ret_value = ret_value == RET_ERROR ? RET_ERROR : RET_OK; + freePackedFile(imapf->packedfile); + imapf->packedfile = NULL; + + /* update the new corresponding view filepath */ + iv = BLI_findstring(&ima->views, imapf->filepath, offsetof(ImageView, filepath)); + if (iv) { + BLI_strncpy(iv->filepath, newname, sizeof(imapf->filepath)); + } + + /* keep the new name in the image for non-pack specific reasons */ + BLI_strncpy(ima->name, newname, sizeof(imapf->filepath)); + MEM_freeN(newname); + } + else { + ret_value = RET_ERROR; + } + + BLI_remlink(&ima->packedfiles, imapf); + MEM_freeN(imapf); } } - + + if (ret_value == RET_OK) { + BKE_image_signal(ima, NULL, IMA_SIGNAL_RELOAD); + } + return(ret_value); } @@ -636,7 +660,7 @@ void unpackAll(Main *bmain, ReportList *reports, int how) bSound *sound; for (ima = bmain->image.first; ima; ima = ima->id.next) - if (ima->packedfile) + if (BKE_image_has_packedfile(ima)) unpackImage(reports, ima, how); for (vf = bmain->vfont.first; vf; vf = vf->id.next) @@ -655,7 +679,7 @@ bool BKE_pack_check(ID *id) case ID_IM: { Image *ima = (Image *)id; - return ima->packedfile != NULL; + return BKE_image_has_packedfile(ima); } case ID_VF: { @@ -683,7 +707,7 @@ void BKE_unpack_id(Main *bmain, ID *id, ReportList *reports, int how) case ID_IM: { Image *ima = (Image *)id; - if (ima->packedfile) { + if (BKE_image_has_packedfile(ima)) { unpackImage(reports, ima, how); } break; diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 3b48de13553..443671f5a61 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -92,6 +92,7 @@ #include "PIL_time.h" #include "IMB_colormanagement.h" +#include "IMB_imbuf.h" #include "bmesh.h" @@ -157,14 +158,16 @@ Scene *BKE_scene_copy(Scene *sce, int type) Base *base, *obase; if (type == SCE_COPY_EMPTY) { - ListBase lb; + ListBase rl, rv; /* XXX. main should become an arg */ scen = BKE_scene_add(G.main, sce->id.name + 2); - lb = scen->r.layers; + rl = scen->r.layers; + rv = scen->r.views; scen->r = sce->r; - scen->r.layers = lb; + scen->r.layers = rl; scen->r.actlay = 0; + scen->r.views = rv; scen->unit = sce->unit; scen->physics_settings = sce->physics_settings; scen->gm = sce->gm; @@ -197,6 +200,7 @@ Scene *BKE_scene_copy(Scene *sce, int type) BLI_duplicatelist(&(scen->markers), &(sce->markers)); BLI_duplicatelist(&(scen->transform_spaces), &(sce->transform_spaces)); BLI_duplicatelist(&(scen->r.layers), &(sce->r.layers)); + BLI_duplicatelist(&(scen->r.views), &(sce->r.views)); BKE_keyingsets_copy(&(scen->keyingsets), &(sce->keyingsets)); if (sce->nodetree) { @@ -390,6 +394,7 @@ void BKE_scene_free(Scene *sce) BLI_freelistN(&sce->markers); BLI_freelistN(&sce->transform_spaces); BLI_freelistN(&sce->r.layers); + BLI_freelistN(&sce->r.views); if (sce->toolsettings) { if (sce->toolsettings->vpaint) { @@ -437,6 +442,7 @@ Scene *BKE_scene_add(Main *bmain, const char *name) ParticleEditSettings *pset; int a; const char *colorspace_name; + SceneRenderView *srv; sce = BKE_libblock_alloc(bmain, ID_SCE, name); sce->lay = sce->layact = 1; @@ -628,7 +634,16 @@ Scene *BKE_scene_add(Main *bmain, const char *name) /* note; in header_info.c the scene copy happens..., if you add more to renderdata it has to be checked there */ BKE_scene_add_render_layer(sce, NULL); - + + /* multiview - stereo */ + BKE_scene_add_render_view(sce, STEREO_LEFT_NAME); + srv = sce->r.views.first; + BLI_strncpy(srv->suffix, STEREO_LEFT_SUFFIX, sizeof(srv->suffix)); + + BKE_scene_add_render_view(sce, STEREO_RIGHT_NAME); + srv = sce->r.views.last; + BLI_strncpy(srv->suffix, STEREO_RIGHT_SUFFIX, sizeof(srv->suffix)); + /* game data */ sce->gm.stereoflag = STEREO_NOSTEREO; sce->gm.stereomode = STEREO_ANAGLYPH; @@ -701,6 +716,19 @@ Scene *BKE_scene_add(Main *bmain, const char *name) return sce; } +Base *BKE_scene_base_find_by_name(struct Scene *scene, const char *name) +{ + Base *base; + + for (base = scene->base.first; base; base = base->next) { + if (STREQ(base->object->id.name + 2, name)) { + break; + } + } + + return base; +} + Base *BKE_scene_base_find(Scene *scene, Object *ob) { return BLI_findptr(&scene->base, ob, offsetof(Base, object)); @@ -1892,6 +1920,42 @@ bool BKE_scene_remove_render_layer(Main *bmain, Scene *scene, SceneRenderLayer * return true; } +/* return default view */ +SceneRenderView *BKE_scene_add_render_view(Scene *sce, const char *name) +{ + SceneRenderView *srv; + + if (!name) + name = DATA_("RenderView"); + + srv = MEM_callocN(sizeof(SceneRenderView), "new render view"); + BLI_strncpy(srv->name, name, sizeof(srv->name)); + BLI_uniquename(&sce->r.views, srv, DATA_("RenderView"), '.', offsetof(SceneRenderView, name), sizeof(srv->name)); + BLI_addtail(&sce->r.views, srv); + + return srv; +} + +bool BKE_scene_remove_render_view(Scene *scene, SceneRenderView *srv) +{ + const int act = BLI_findindex(&scene->r.views, srv); + + if (act == -1) { + return false; + } + else if (scene->r.views.first == scene->r.views.last) { + /* ensure 1 view is kept */ + return false; + } + + BLI_remlink(&scene->r.views, srv); + MEM_freeN(srv); + + scene->r.actview = 0; + + return true; +} + /* render simplification */ int get_render_subsurf_level(const RenderData *r, int lvl) @@ -2062,3 +2126,281 @@ double BKE_scene_unit_scale(const UnitSettings *unit, const int unit_type, doubl return value; } } + +/******************** multiview *************************/ + +size_t BKE_scene_multiview_num_views_get(const RenderData *rd) +{ + SceneRenderView *srv; + size_t totviews = 0; + + if ((rd->scemode & R_MULTIVIEW) == 0) + return 1; + + if (rd->views_format == SCE_VIEWS_FORMAT_STEREO_3D) { + if (BLI_findstring(&rd->views, STEREO_LEFT_NAME, offsetof(SceneRenderView, name))) { + totviews++; + } + + if (BLI_findstring(&rd->views, STEREO_RIGHT_NAME, offsetof(SceneRenderView, name))) { + totviews++; + } + } + else { + for (srv = rd->views.first; srv; srv = srv->next) { + if ((srv->viewflag & SCE_VIEW_DISABLE) == 0) { + totviews++; + } + } + } + return totviews; +} + +bool BKE_scene_multiview_is_stereo3d(const RenderData *rd) +{ + SceneRenderView *srv[2]; + + if ((rd->scemode & R_MULTIVIEW) == 0) + return false; + + srv[0] = (SceneRenderView *)BLI_findstring(&rd->views, STEREO_LEFT_NAME, offsetof(SceneRenderView, name)); + srv[1] = (SceneRenderView *)BLI_findstring(&rd->views, STEREO_RIGHT_NAME, offsetof(SceneRenderView, name)); + + return (srv[0] && ((srv[0]->viewflag & SCE_VIEW_DISABLE) == 0) && + srv[1] && ((srv[1]->viewflag & SCE_VIEW_DISABLE) == 0)); +} + +/* return whether to render this SceneRenderView */ +bool BKE_scene_multiview_is_render_view_active(const RenderData *rd, const SceneRenderView *srv) +{ + if (srv == NULL) + return false; + + if ((rd->scemode & R_MULTIVIEW) == 0) + return false; + + if ((srv->viewflag & SCE_VIEW_DISABLE)) + return false; + + if (rd->views_format == SCE_VIEWS_FORMAT_MULTIVIEW) + return true; + + /* SCE_VIEWS_SETUP_BASIC */ + if (STREQ(srv->name, STEREO_LEFT_NAME) || + STREQ(srv->name, STEREO_RIGHT_NAME)) + { + return true; + } + + return false; +} + +/* return true if viewname is the first or if the name is NULL or not found */ +bool BKE_scene_multiview_is_render_view_first(const RenderData *rd, const char *viewname) +{ + SceneRenderView *srv; + + if ((rd->scemode & R_MULTIVIEW) == 0) + return true; + + if ((!viewname) || (!viewname[0])) + return true; + + for (srv = rd->views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(rd, srv)) { + return STREQ(viewname, srv->name); + } + } + + return true; +} + +/* return true if viewname is the last or if the name is NULL or not found */ +bool BKE_scene_multiview_is_render_view_last(const RenderData *rd, const char *viewname) +{ + SceneRenderView *srv; + + if ((rd->scemode & R_MULTIVIEW) == 0) + return true; + + if ((!viewname) || (!viewname[0])) + return true; + + for (srv = rd->views.last; srv; srv = srv->prev) { + if (BKE_scene_multiview_is_render_view_active(rd, srv)) { + return STREQ(viewname, srv->name); + } + } + + return true; +} + +SceneRenderView *BKE_scene_multiview_render_view_findindex(const RenderData *rd, const int view_id) +{ + SceneRenderView *srv; + size_t nr; + + if ((rd->scemode & R_MULTIVIEW) == 0) + return NULL; + + nr = 0; + for (srv = rd->views.first, nr = 0; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(rd, srv)) { + if (nr++ == view_id) + return srv; + } + } + return srv; +} + +const char *BKE_scene_multiview_render_view_name_get(const RenderData *rd, const int view_id) +{ + SceneRenderView *srv = BKE_scene_multiview_render_view_findindex(rd, view_id); + + if (srv) + return srv->name; + else + return ""; +} + +size_t BKE_scene_multiview_view_id_get(const RenderData *rd, const char *viewname) +{ + SceneRenderView *srv; + size_t nr; + + if ((!rd) || ((rd->scemode & R_MULTIVIEW) == 0)) + return 0; + + if ((!viewname) || (!viewname[0])) + return 0; + + nr = 0; + for (srv = rd->views.first, nr = 0; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(rd, srv)) { + if (STREQ(viewname, srv->name)) { + return nr; + } + else { + nr += 1; + } + } + } + + return 0; +} + +void BKE_scene_multiview_filepath_get( + SceneRenderView *srv, const char *filepath, + char *r_filepath) +{ + BLI_strncpy(r_filepath, filepath, FILE_MAX); + BLI_path_suffix(r_filepath, FILE_MAX, srv->suffix, ""); +} + +/** + * When multiview is not used the filepath is as usual (e.g., ``Image.jpg``). + * When multiview is on, even if only one view is enabled the view is incorporated + * into the file name (e.g., ``Image_L.jpg``). That allows for the user to re-render + * individual views. + */ +void BKE_scene_multiview_view_filepath_get( + const RenderData *rd, const char *filepath, const char *viewname, + char *r_filepath) +{ + SceneRenderView *srv; + char suffix[FILE_MAX]; + + srv = BLI_findstring(&rd->views, viewname, offsetof(SceneRenderView, name)); + if (srv) + BLI_strncpy(suffix, srv->suffix, sizeof(suffix)); + else + BLI_strncpy(suffix, viewname, sizeof(suffix)); + + BLI_strncpy(r_filepath, filepath, FILE_MAX); + BLI_path_suffix(r_filepath, FILE_MAX, suffix, ""); +} + +const char *BKE_scene_multiview_view_suffix_get(const RenderData *rd, const char *viewname) +{ + SceneRenderView *srv; + + if ((viewname == NULL) || (viewname[0] == '\0')) + return viewname; + + srv = BLI_findstring(&rd->views, viewname, offsetof(SceneRenderView, name)); + if (srv) + return srv->suffix; + else + return viewname; +} + +const char *BKE_scene_multiview_view_id_suffix_get(const RenderData *rd, const size_t view_id) +{ + if ((rd->scemode & R_MULTIVIEW) == 0) { + return ""; + } + else { + const char *viewname = BKE_scene_multiview_render_view_name_get(rd, view_id); + return BKE_scene_multiview_view_suffix_get(rd, viewname); + } +} + +void BKE_scene_multiview_view_prefix_get(Scene *scene, const char *name, char *rprefix, char **rext) +{ + SceneRenderView *srv; + size_t index_act; + char *suf_act; + const char delims[] = {'.', '\0'}; + + rprefix[0] = '\0'; + + /* begin of extension */ + index_act = BLI_str_rpartition(name, delims, rext, &suf_act); + BLI_assert(index_act > 0); + + for (srv = scene->r.views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(&scene->r, srv)) { + size_t len = strlen(srv->suffix); + if (STREQLEN(*rext - len, srv->suffix, len)) { + BLI_strncpy(rprefix, name, strlen(name) - strlen(*rext) - len + 1); + break; + } + } + } +} + +void BKE_scene_multiview_videos_dimensions_get( + const RenderData *rd, const size_t width, const size_t height, + size_t *r_width, size_t *r_height) +{ + if ((rd->scemode & R_MULTIVIEW) && + rd->im_format.views_format == R_IMF_VIEWS_STEREO_3D) + { + IMB_stereo3d_write_dimensions( + rd->im_format.stereo3d_format.display_mode, + (rd->im_format.stereo3d_format.flag & S3D_SQUEEZED_FRAME) != 0, + width, height, + r_width, r_height); + } + else { + *r_width = width; + *r_height = height; + } +} + +size_t BKE_scene_multiview_num_videos_get(const RenderData *rd) +{ + if (BKE_imtype_is_movie(rd->im_format.imtype) == false) + return 0; + + if ((rd->scemode & R_MULTIVIEW) == 0) + return 1; + + if (rd->im_format.views_format == R_IMF_VIEWS_STEREO_3D) { + return 1; + } + else { + /* R_IMF_VIEWS_INDIVIDUAL */ + return BKE_scene_multiview_num_views_get(rd); + } +} diff --git a/source/blender/blenkernel/intern/seqcache.c b/source/blender/blenkernel/intern/seqcache.c index 41f17665065..abfc858fd03 100644 --- a/source/blender/blenkernel/intern/seqcache.c +++ b/source/blender/blenkernel/intern/seqcache.c @@ -33,6 +33,7 @@ #include "MEM_guardedalloc.h" #include "DNA_sequence_types.h" +#include "DNA_scene_types.h" #include "IMB_moviecache.h" #include "IMB_imbuf.h" @@ -41,6 +42,7 @@ #include "BLI_listbase.h" #include "BKE_sequencer.h" +#include "BKE_scene.h" typedef struct SeqCacheKey { struct Sequence *seq; @@ -77,7 +79,9 @@ static bool seq_cmp_render_data(const SeqRenderData *a, const SeqRenderData *b) (a->bmain != b->bmain) || (a->scene != b->scene) || (a->motion_blur_shutter != b->motion_blur_shutter) || - (a->motion_blur_samples != b->motion_blur_samples)); + (a->motion_blur_samples != b->motion_blur_samples) || + (a->scene->r.views_format != b->scene->r.views_format) || + (a->view_id != b->view_id)); } static unsigned int seq_hash_render_data(const SeqRenderData *a) @@ -89,6 +93,7 @@ static unsigned int seq_hash_render_data(const SeqRenderData *a) rval ^= ((intptr_t) a->scene) << 6; rval ^= (int)(a->motion_blur_shutter * 100.0f) << 10; rval ^= a->motion_blur_samples << 24; + rval ^= ((a->scene->r.views_format * 2) + a->view_id) << 32; return rval; } diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c index 721555097d7..942426febdd 100644 --- a/source/blender/blenkernel/intern/sequencer.c +++ b/source/blender/blenkernel/intern/sequencer.c @@ -53,6 +53,12 @@ #include "BLI_threads.h" #include "BLI_utildefines.h" +#ifdef WIN32 +# include "BLI_winstuff.h" +#else +# include <unistd.h> +#endif + #include "BLF_translation.h" #include "BKE_animsys.h" @@ -89,6 +95,8 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context, ListBase *seq static ImBuf *seq_render_strip(const SeqRenderData *context, Sequence *seq, float cfra); static void seq_free_animdata(Scene *scene, Sequence *seq); static ImBuf *seq_render_mask(const SeqRenderData *context, Mask *mask, float nr, bool make_float); +static size_t seq_num_files(Scene *scene, char views_format, const bool is_multiview); +static void seq_anim_add_suffix(Scene *scene, struct anim *anim, const size_t view_id); /* **** XXX ******** */ #define SELECT 1 @@ -181,10 +189,7 @@ static void BKE_sequence_free_ex(Scene *scene, Sequence *seq, const bool do_cach if (seq->strip) seq_free_strip(seq->strip); - if (seq->anim) { - IMB_free_anim(seq->anim); - seq->anim = NULL; - } + BKE_sequence_free_anim(seq); if (seq->type & SEQ_TYPE_EFFECT) { struct SeqEffectHandle sh = BKE_sequence_get_effect(seq); @@ -196,6 +201,10 @@ static void BKE_sequence_free_ex(Scene *scene, Sequence *seq, const bool do_cach ((ID *)seq->sound)->us--; } + if (seq->stereo3d_format) { + MEM_freeN(seq->stereo3d_format); + } + /* clipboard has no scene and will never have a sound handle or be active * same goes to sequences copy for proxy rebuild job */ @@ -240,6 +249,22 @@ void BKE_sequence_free(Scene *scene, Sequence *seq) BKE_sequence_free_ex(scene, seq, true); } +/* Function to free imbuf and anim data on changes */ +void BKE_sequence_free_anim(Sequence *seq) +{ + while (seq->anims.last) { + StripAnim *sanim = seq->anims.last; + BLI_remlink(&seq->anims, sanim); + + if (sanim->anim) { + IMB_free_anim(sanim->anim); + sanim->anim = NULL; + } + + MEM_freeN(sanim); + } +} + /* cache must be freed before calling this function * since it leaves the seqbase in an invalid state */ static void seq_free_sequence_recurse(Scene *scene, Sequence *seq) @@ -537,6 +562,7 @@ void BKE_sequencer_new_render_data( r_context->motion_blur_shutter = 0; r_context->skip_cache = false; r_context->is_proxy_render = false; + r_context->view_id = 0; } /* ************************* iterator ************************** */ @@ -771,10 +797,17 @@ void BKE_sequence_calc(Scene *scene, Sequence *seq) } } +static void seq_multiview_name(Scene *scene, const size_t view_id, const char *prefix, + const char *ext, char *r_path, size_t r_size) +{ + const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, view_id); + BLI_snprintf(r_path, r_size, "%s%s%s", prefix, suffix, ext); +} + /* note: caller should run BKE_sequence_calc(scene, seq) after */ void BKE_sequence_reload_new_file(Scene *scene, Sequence *seq, const bool lock_range) { - char str[FILE_MAX]; + char path[FILE_MAX]; int prev_startdisp = 0, prev_enddisp = 0; /* note: don't rename the strip, will break animation curves */ @@ -807,22 +840,67 @@ void BKE_sequence_reload_new_file(Scene *scene, Sequence *seq, const bool lock_r break; } case SEQ_TYPE_MOVIE: - BLI_join_dirfile(str, sizeof(str), seq->strip->dir, + { + StripAnim *sanim; + bool is_multiview_loaded = false; + const bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 && + (scene->r.scemode & R_MULTIVIEW) != 0; + + BLI_join_dirfile(path, sizeof(path), seq->strip->dir, seq->strip->stripdata->name); - BLI_path_abs(str, G.main->name); + BLI_path_abs(path, G.main->name); + + BKE_sequence_free_anim(seq); + + if (is_multiview && (seq->views_format == R_IMF_VIEWS_INDIVIDUAL)) { + char prefix[FILE_MAX]; + char *ext = NULL; + size_t totfiles = seq_num_files(scene, seq->views_format, true); + int i = 0; + + BKE_scene_multiview_view_prefix_get(scene, path, prefix, &ext); - if (seq->anim) IMB_free_anim(seq->anim); + if (prefix[0] != '\0') { + for (i = 0; i < totfiles; i++) { + struct anim *anim; + char str[FILE_MAX]; - seq->anim = openanim(str, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), - seq->streamindex, seq->strip->colorspace_settings.name); + seq_multiview_name(scene, i, prefix, ext, str, FILE_MAX); + anim = openanim(str, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + seq_anim_add_suffix(scene, anim, i); - if (!seq->anim) { + if (anim) { + sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim"); + BLI_addtail(&seq->anims, sanim); + sanim->anim = anim; + } + } + is_multiview_loaded = true; + } + } + + if (is_multiview_loaded == false) { + struct anim *anim; + anim = openanim(path, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + if (anim) { + sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim"); + BLI_addtail(&seq->anims, sanim); + sanim->anim = anim; + } + } + + /* use the first video as reference for everything */ + sanim = seq->anims.first; + + if ((!sanim) || (!sanim->anim)) { return; } - seq->len = IMB_anim_get_duration(seq->anim, seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN); - - seq->anim_preseek = IMB_anim_get_preseek(seq->anim); + seq->len = IMB_anim_get_duration(sanim->anim, seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN); + + seq->anim_preseek = IMB_anim_get_preseek(sanim->anim); seq->len -= seq->anim_startofs; seq->len -= seq->anim_endofs; @@ -830,6 +908,7 @@ void BKE_sequence_reload_new_file(Scene *scene, Sequence *seq, const bool lock_r seq->len = 0; } break; + } case SEQ_TYPE_MOVIECLIP: if (seq->clip == NULL) return; @@ -1297,6 +1376,7 @@ typedef struct SeqIndexBuildContext { int size_flags; int quality; bool overwrite; + size_t view_id; Main *bmain; Scene *scene; @@ -1336,44 +1416,59 @@ static double seq_rendersize_to_scale_factor(int size) return 0.25; } -static void seq_open_anim_file(Editing *ed, Sequence *seq, bool openfile) +/* the number of files will vary according to the stereo format */ +static size_t seq_num_files(Scene *scene, char views_format, const bool is_multiview) +{ + if (!is_multiview) { + return 1; + } + else if (views_format == R_IMF_VIEWS_STEREO_3D) { + return 1; + } + /* R_IMF_VIEWS_INDIVIDUAL */ + else { + return BKE_scene_multiview_num_views_get(&scene->r); + } +} + +static void seq_proxy_index_dir_set(struct anim *anim, const char *base_dir) +{ + char dir[FILE_MAX]; + char fname[FILE_MAXFILE]; + + IMB_anim_get_fname(anim, fname, FILE_MAXFILE); + BLI_strncpy(dir, base_dir, sizeof(dir)); + BLI_path_append(dir, sizeof(dir), fname); + IMB_anim_set_index_dir(anim, dir); +} + +static void seq_open_anim_file(Scene *scene, Sequence *seq, bool openfile) { + char dir[FILE_MAX]; char name[FILE_MAX]; StripProxy *proxy; + bool use_proxy; + bool is_multiview_loaded = false; + Editing *ed = scene->ed; + const bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 && (scene->r.scemode & R_MULTIVIEW) != 0; - if (seq->anim != NULL) { + if ((seq->anims.first != NULL) && (((StripAnim *)seq->anims.first)->anim != NULL)) { return; } + /* reset all the previously created anims */ + BKE_sequence_free_anim(seq); + BLI_join_dirfile(name, sizeof(name), seq->strip->dir, seq->strip->stripdata->name); BLI_path_abs(name, G.main->name); - - if (openfile) { - seq->anim = openanim(name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), - seq->streamindex, seq->strip->colorspace_settings.name); - } - else { - seq->anim = openanim_noload(name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), - seq->streamindex, seq->strip->colorspace_settings.name); - } - - if (seq->anim == NULL) { - return; - } proxy = seq->strip->proxy; - if (proxy == NULL) { - return; - } - - if ((proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR) || - (ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE)) - { - char dir[FILE_MAX]; - char fname[FILE_MAXFILE]; + use_proxy = proxy && ((proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR) != 0 || + (ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE)); + if (use_proxy) { if (ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE) { if (ed->proxy_dir[0] == 0) BLI_strncpy(dir, "//BL_proxy", sizeof(dir)); @@ -1383,21 +1478,93 @@ static void seq_open_anim_file(Editing *ed, Sequence *seq, bool openfile) else { BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir)); } + BLI_path_abs(dir, G.main->name); + } - IMB_anim_get_fname(seq->anim, fname, FILE_MAXFILE); - BLI_path_append(dir, sizeof(dir), fname); + if (is_multiview && seq->views_format == R_IMF_VIEWS_INDIVIDUAL) { + size_t totfiles = seq_num_files(scene, seq->views_format, true); + char prefix[FILE_MAX]; + char *ext = NULL; + int i; - BLI_path_abs(dir, G.main->name); + BKE_scene_multiview_view_prefix_get(scene, name, prefix, &ext); + + if (prefix[0] != '\0') { + for (i = 0; i < totfiles; i++) { + const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, i); + char str[FILE_MAX]; + StripAnim *sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim"); - IMB_anim_set_index_dir(seq->anim, dir); + BLI_addtail(&seq->anims, sanim); + + BLI_snprintf(str, sizeof(str), "%s%s%s", prefix, suffix, ext); + + if (openfile) { + sanim->anim = openanim( + str, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + } + else { + sanim->anim = openanim_noload( + str, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + } + + seq_anim_add_suffix(scene, sanim->anim, i); + + if (sanim->anim == NULL) { + if (openfile) { + sanim->anim = openanim( + name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + } + else { + sanim->anim = openanim_noload( + name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + } + + /* no individual view files - monoscopic, stereo 3d or exr multiview */ + totfiles = 1; + } + + if (sanim->anim && use_proxy) { + seq_proxy_index_dir_set(sanim->anim, dir); + } + } + is_multiview_loaded = true; + } } -} + if (is_multiview_loaded == false) { + StripAnim *sanim; + + sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim"); + BLI_addtail(&seq->anims, sanim); -static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render_size, char *name) + if (openfile) { + sanim->anim = openanim( + name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + } + else { + sanim->anim = openanim_noload( + name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0), + seq->streamindex, seq->strip->colorspace_settings.name); + } + + if (sanim->anim && use_proxy) { + seq_proxy_index_dir_set(sanim->anim, dir); + } + } +} + +static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render_size, char *name, const size_t view_id) { int frameno; char dir[PROXY_MAXFILE]; + StripAnim *sanim; + char suffix[24] = {'\0'}; StripProxy *proxy = seq->strip->proxy; if (!proxy) { @@ -1412,23 +1579,25 @@ static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render * have both, a directory full of jpeg files and proxy avis, so * sorry folks, please rebuild your proxies... */ - if (seq->anim && ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE) { + sanim = BLI_findlink(&seq->anims, view_id); + + if (sanim && sanim->anim && ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE) { char fname[FILE_MAXFILE]; if (ed->proxy_dir[0] == 0) BLI_strncpy(dir, "//BL_proxy", sizeof(dir)); else BLI_strncpy(dir, ed->proxy_dir, sizeof(dir)); - IMB_anim_get_fname(seq->anim, fname, FILE_MAXFILE); + IMB_anim_get_fname(sanim->anim, fname, FILE_MAXFILE); BLI_path_append(dir, sizeof(dir), fname); BLI_path_abs(name, G.main->name); } else if ((proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR) && (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE)) { BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir)); } - else if (seq->anim && (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR)) { + else if (sanim && sanim->anim && (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR)) { char fname[FILE_MAXFILE]; BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir)); - IMB_anim_get_fname(seq->anim, fname, FILE_MAXFILE); + IMB_anim_get_fname(sanim->anim, fname, FILE_MAXFILE); BLI_path_append(dir, sizeof(dir), fname); } else if (seq->type == SEQ_TYPE_IMAGE) { @@ -1438,12 +1607,16 @@ static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render return false; } - if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE && seq->anim && + if (view_id > 0) + BLI_snprintf(suffix, sizeof(suffix), "_%zu", view_id); + + if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE && sanim && sanim->anim && ed->proxy_storage != SEQ_EDIT_PROXY_DIR_STORAGE) { BLI_join_dirfile(name, PROXY_MAXFILE, dir, proxy->file); BLI_path_abs(name, G.main->name); + BLI_snprintf(name, PROXY_MAXFILE, "%s_%s", name, suffix); return true; } @@ -1451,13 +1624,13 @@ static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render /* generate a separate proxy directory for each preview size */ if (seq->type == SEQ_TYPE_IMAGE) { - BLI_snprintf(name, PROXY_MAXFILE, "%s/images/%d/%s_proxy", dir, render_size, - BKE_sequencer_give_stripelem(seq, cfra)->name); + BLI_snprintf(name, PROXY_MAXFILE, "%s/images/%d/%s_proxy%s", dir, render_size, + BKE_sequencer_give_stripelem(seq, cfra)->name, suffix); frameno = 1; } else { frameno = (int)give_stripelem_index(seq, cfra) + seq->anim_startofs; - BLI_snprintf(name, PROXY_MAXFILE, "%s/proxy_misc/%d/####", dir, render_size); + BLI_snprintf(name, PROXY_MAXFILE, "%s/proxy_misc/%d/####%s", dir, render_size, suffix); } BLI_path_abs(name, G.main->name); @@ -1476,6 +1649,7 @@ static ImBuf *seq_proxy_fetch(const SeqRenderData *context, Sequence *seq, int c int render_size = context->preview_render_size; StripProxy *proxy = seq->strip->proxy; Editing *ed = context->scene->ed; + StripAnim *sanim; if (!(seq->flag & SEQ_USE_PROXY)) { return NULL; @@ -1496,7 +1670,7 @@ static ImBuf *seq_proxy_fetch(const SeqRenderData *context, Sequence *seq, int c if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE) { int frameno = (int)give_stripelem_index(seq, cfra) + seq->anim_startofs; if (proxy->anim == NULL) { - if (seq_proxy_get_fname(ed, seq, cfra, render_size, name) == 0) { + if (seq_proxy_get_fname(ed, seq, cfra, render_size, name, context->view_id) == 0) { return NULL; } @@ -1506,14 +1680,15 @@ static ImBuf *seq_proxy_fetch(const SeqRenderData *context, Sequence *seq, int c return NULL; } - seq_open_anim_file(context->scene->ed, seq, true); + seq_open_anim_file(context->scene, seq, true); + sanim = seq->anims.first; - frameno = IMB_anim_index_get_frame_index(seq->anim, proxy->tc, frameno); + frameno = IMB_anim_index_get_frame_index(sanim ? sanim->anim : NULL, seq->strip->proxy->tc, frameno); return IMB_anim_absolute(proxy->anim, frameno, IMB_TC_NONE, IMB_PROXY_NONE); } - if (seq_proxy_get_fname(ed, seq, cfra, render_size, name) == 0) { + if (seq_proxy_get_fname(ed, seq, cfra, render_size, name, context->view_id) == 0) { return NULL; } @@ -1540,7 +1715,7 @@ static void seq_proxy_build_frame(const SeqRenderData *context, Sequence *seq, i ImBuf *ibuf_tmp, *ibuf; Editing *ed = context->scene->ed; - if (!seq_proxy_get_fname(ed, seq, cfra, proxy_render_size, name)) { + if (!seq_proxy_get_fname(ed, seq, cfra, proxy_render_size, name, context->view_id)) { return; } @@ -1581,44 +1756,130 @@ static void seq_proxy_build_frame(const SeqRenderData *context, Sequence *seq, i IMB_freeImBuf(ibuf); } -SeqIndexBuildContext *BKE_sequencer_proxy_rebuild_context(Main *bmain, Scene *scene, Sequence *seq, struct GSet *file_list) +/* returns whether the file this context would read from even exist, if not, don't create the context +*/ +static bool seq_proxy_multiview_context_invalid(Sequence *seq, Scene *scene, const size_t view_id) +{ + if ((scene->r.scemode & R_MULTIVIEW) == 0) + return false; + + if ((seq->type == SEQ_TYPE_IMAGE) && (seq->views_format == R_IMF_VIEWS_INDIVIDUAL)) { + static char prefix[FILE_MAX]; + static char *ext = NULL; + char str[FILE_MAX]; + + if (view_id == 0) { + char path[FILE_MAX]; + BLI_join_dirfile(path, sizeof(path), seq->strip->dir, + seq->strip->stripdata->name); + BLI_path_abs(path, G.main->name); + BKE_scene_multiview_view_prefix_get(scene, path, prefix, &ext); + } + + if (prefix[0] == '\0') + return view_id != 0; + + seq_multiview_name(scene, view_id, prefix, ext, str, FILE_MAX); + + if (BLI_access(str, R_OK) == 0) + return false; + else + return view_id != 0; + } + return false; +} + +/** This returns the maximum possible number of required contexts +*/ +static size_t seq_proxy_context_count(Sequence *seq, Scene *scene) +{ + size_t num_views = 1; + + if ((scene->r.scemode & R_MULTIVIEW) == 0) + return 1; + + switch (seq->type) { + case SEQ_TYPE_MOVIE: + { + num_views = BLI_listbase_count(&seq->anims); + break; + } + case SEQ_TYPE_IMAGE: + { + switch (seq->views_format) { + case R_IMF_VIEWS_INDIVIDUAL: + num_views = BKE_scene_multiview_num_views_get(&scene->r); + break; + case R_IMF_VIEWS_STEREO_3D: + num_views = 2; + break; + case R_IMF_VIEWS_MULTIVIEW: + /* not supported at the moment */ + /* pass through */ + default: + num_views = 1; + } + break; + } + } + + return num_views; +} + +void BKE_sequencer_proxy_rebuild_context(Main *bmain, Scene *scene, Sequence *seq, struct GSet *file_list, ListBase *queue) { SeqIndexBuildContext *context; Sequence *nseq; + LinkData *link; + size_t i; + size_t num_files; if (!seq->strip || !seq->strip->proxy) { - return NULL; + return; } if (!(seq->flag & SEQ_USE_PROXY)) { - return NULL; + return; } - context = MEM_callocN(sizeof(SeqIndexBuildContext), "seq proxy rebuild context"); + num_files = seq_proxy_context_count(seq, scene); + + for (i = 0; i < num_files; i++) { + if (seq_proxy_multiview_context_invalid(seq, scene, i)) + continue; + + context = MEM_callocN(sizeof(SeqIndexBuildContext), "seq proxy rebuild context"); + + nseq = BKE_sequence_dupli_recursive(scene, scene, seq, 0); - nseq = BKE_sequence_dupli_recursive(scene, scene, seq, 0); + context->tc_flags = nseq->strip->proxy->build_tc_flags; + context->size_flags = nseq->strip->proxy->build_size_flags; + context->quality = nseq->strip->proxy->quality; + context->overwrite = (nseq->strip->proxy->build_flags & SEQ_PROXY_SKIP_EXISTING) == 0; - context->tc_flags = nseq->strip->proxy->build_tc_flags; - context->size_flags = nseq->strip->proxy->build_size_flags; - context->quality = nseq->strip->proxy->quality; - context->overwrite = (nseq->strip->proxy->build_flags & SEQ_PROXY_SKIP_EXISTING) == 0; + context->bmain = bmain; + context->scene = scene; + context->orig_seq = seq; + context->seq = nseq; - context->bmain = bmain; - context->scene = scene; - context->orig_seq = seq; - context->seq = nseq; + context->view_id = i; /* only for images */ - if (nseq->type == SEQ_TYPE_MOVIE) { - seq_open_anim_file(scene->ed, nseq, true); + link = BLI_genericNodeN(context); + BLI_addtail(queue, link); - if (nseq->anim) { - context->index_context = IMB_anim_index_rebuild_context(nseq->anim, - context->tc_flags, context->size_flags, context->quality, - context->overwrite, file_list); + if (nseq->type == SEQ_TYPE_MOVIE) { + StripAnim *sanim; + + seq_open_anim_file(scene, nseq, true); + sanim = BLI_findlink(&nseq->anims, i); + + if (sanim->anim) { + context->index_context = IMB_anim_index_rebuild_context(sanim->anim, + context->tc_flags, context->size_flags, context->quality, + context->overwrite, file_list); + } } } - - return context; } void BKE_sequencer_proxy_rebuild(SeqIndexBuildContext *context, short *stop, short *do_update, float *progress) @@ -1657,6 +1918,7 @@ void BKE_sequencer_proxy_rebuild(SeqIndexBuildContext *context, short *stop, sho render_context.skip_cache = true; render_context.is_proxy_render = true; + render_context.view_id = context->view_id; for (cfra = seq->startdisp + seq->startstill; cfra < seq->enddisp - seq->endstill; cfra++) { if (context->size_flags & IMB_PROXY_25) { @@ -1683,8 +1945,14 @@ void BKE_sequencer_proxy_rebuild(SeqIndexBuildContext *context, short *stop, sho void BKE_sequencer_proxy_rebuild_finish(SeqIndexBuildContext *context, bool stop) { if (context->index_context) { - IMB_close_anim_proxies(context->seq->anim); - IMB_close_anim_proxies(context->orig_seq->anim); + StripAnim *sanim; + + for (sanim = context->seq->anims.first; sanim; sanim = sanim->next) + IMB_close_anim_proxies(sanim->anim); + + for (sanim = context->orig_seq->anims.first; sanim; sanim = sanim->next) + IMB_close_anim_proxies(sanim->anim); + IMB_anim_index_rebuild_finish(context->index_context, stop); } @@ -2429,6 +2697,234 @@ static ImBuf *seq_render_effect_strip_impl(const SeqRenderData *context, Sequenc return out; } +static ImBuf *seq_render_image_strip(const SeqRenderData *context, Sequence *seq, float nr, float cfra) +{ + ImBuf *ibuf = NULL; + char name[FILE_MAX]; + bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 && + (context->scene->r.scemode & R_MULTIVIEW) != 0; + StripElem *s_elem = BKE_sequencer_give_stripelem(seq, cfra); + int flag; + + if (s_elem) { + BLI_join_dirfile(name, sizeof(name), seq->strip->dir, s_elem->name); + BLI_path_abs(name, G.main->name); + } + + flag = IB_rect; + if (seq->alpha_mode == SEQ_ALPHA_PREMUL) + flag |= IB_alphamode_premul; + + if (!s_elem) { + /* don't do anything */ + } + else if (is_multiview) { + size_t totfiles = seq_num_files(context->scene, seq->views_format, true); + size_t totviews; + struct ImBuf **ibufs_arr; + char prefix[FILE_MAX]; + char *ext = NULL; + int i; + + if (totfiles > 1) { + BKE_scene_multiview_view_prefix_get(context->scene, name, prefix, &ext); + if (prefix[0] == '\0') { + goto monoview_image; + } + } + + totviews = BKE_scene_multiview_num_views_get(&context->scene->r); + ibufs_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Sequence Image Views Imbufs"); + + for (i = 0; i < totfiles; i++) { + + if (prefix[0] == '\0') { + ibufs_arr[i] = IMB_loadiffname(name, flag, seq->strip->colorspace_settings.name); + } + else { + char str[FILE_MAX]; + seq_multiview_name(context->scene, i, prefix, ext, str, FILE_MAX); + ibufs_arr[i] = IMB_loadiffname(str, flag, seq->strip->colorspace_settings.name); + } + + if (ibufs_arr[i]) { + /* we don't need both (speed reasons)! */ + if (ibufs_arr[i]->rect_float && ibufs_arr[i]->rect) + imb_freerectImBuf(ibufs_arr[i]); + } + } + + if (seq->views_format == R_IMF_VIEWS_STEREO_3D && ibufs_arr[0]) + IMB_ImBufFromStereo3d(seq->stereo3d_format, ibufs_arr[0], &ibufs_arr[0], &ibufs_arr[1]); + + for (i = 0; i < totviews; i++) { + if (ibufs_arr[i]) { + SeqRenderData localcontext = *context; + localcontext.view_id = i; + + /* all sequencer color is done in SRGB space, linear gives odd crossfades */ + BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibufs_arr[i], false); + + if (i != context->view_id) { + copy_to_ibuf_still(&localcontext, seq, nr, ibufs_arr[i]); + BKE_sequencer_cache_put(&localcontext, seq, cfra, SEQ_STRIPELEM_IBUF, ibufs_arr[i]); + } + } + } + + /* return the original requested ImBuf */ + ibuf = ibufs_arr[context->view_id]; + if (ibuf) { + s_elem->orig_width = ibufs_arr[0]->x; + s_elem->orig_height = ibufs_arr[0]->y; + } + + /* "remove" the others (decrease their refcount) */ + for (i = 0; i < totviews; i++) { + if (ibufs_arr[i] != ibuf) { + IMB_freeImBuf(ibufs_arr[i]); + } + } + + MEM_freeN(ibufs_arr); + } + else { +monoview_image: + if ((ibuf = IMB_loadiffname(name, flag, seq->strip->colorspace_settings.name))) { + /* we don't need both (speed reasons)! */ + if (ibuf->rect_float && ibuf->rect) + imb_freerectImBuf(ibuf); + + /* all sequencer color is done in SRGB space, linear gives odd crossfades */ + BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false); + + s_elem->orig_width = ibuf->x; + s_elem->orig_height = ibuf->y; + } + } + + return ibuf; +} + +static ImBuf *seq_render_movie_strip(const SeqRenderData *context, Sequence *seq, float nr, float cfra) +{ + ImBuf *ibuf = NULL; + StripAnim *sanim; + bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 && + (context->scene->r.scemode & R_MULTIVIEW) != 0; + + /* load all the videos */ + seq_open_anim_file(context->scene, seq, false); + + if (is_multiview) { + ImBuf **ibuf_arr; + size_t totviews; + size_t totfiles = seq_num_files(context->scene, seq->views_format, true); + int i; + + if (totfiles != BLI_listbase_count_ex(&seq->anims, totfiles + 1)) + goto monoview_movie; + + totviews = BKE_scene_multiview_num_views_get(&context->scene->r); + ibuf_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Sequence Image Views Imbufs"); + + for (i = 0, sanim = seq->anims.first; sanim; sanim = sanim->next, i++) { + if (sanim->anim) { + IMB_Proxy_Size proxy_size = seq_rendersize_to_proxysize(context->preview_render_size); + IMB_anim_set_preseek(sanim->anim, seq->anim_preseek); + + ibuf_arr[i] = IMB_anim_absolute(sanim->anim, nr + seq->anim_startofs, + seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, + proxy_size); + + /* fetching for requested proxy size failed, try fetching the original instead */ + if (!ibuf_arr[i] && proxy_size != IMB_PROXY_NONE) { + ibuf_arr[i] = IMB_anim_absolute(sanim->anim, nr + seq->anim_startofs, + seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, + IMB_PROXY_NONE); + } + if (ibuf_arr[i]) { + /* we don't need both (speed reasons)! */ + if (ibuf_arr[i]->rect_float && ibuf_arr[i]->rect) + imb_freerectImBuf(ibuf_arr[i]); + } + } + } + + if (seq->views_format == R_IMF_VIEWS_STEREO_3D) { + if (ibuf_arr[0]) { + IMB_ImBufFromStereo3d(seq->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]); + } + else { + /* probably proxy hasn't been created yet */ + MEM_freeN(ibuf_arr); + return NULL; + } + } + + for (i = 0; i < totviews; i++) { + SeqRenderData localcontext = *context; + localcontext.view_id = i; + + if (ibuf_arr[i]) { + /* all sequencer color is done in SRGB space, linear gives odd crossfades */ + BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf_arr[i], false); + } + if (i != context->view_id) { + copy_to_ibuf_still(&localcontext, seq, nr, ibuf_arr[i]); + BKE_sequencer_cache_put(&localcontext, seq, cfra, SEQ_STRIPELEM_IBUF, ibuf_arr[i]); + } + } + + /* return the original requested ImBuf */ + ibuf = ibuf_arr[context->view_id]; + if (ibuf) { + seq->strip->stripdata->orig_width = ibuf->x; + seq->strip->stripdata->orig_height = ibuf->y; + } + + /* "remove" the others (decrease their refcount) */ + for (i = 0; i < totviews; i++) { + if (ibuf_arr[i] != ibuf) { + IMB_freeImBuf(ibuf_arr[i]); + } + } + + MEM_freeN(ibuf_arr); + } + else { +monoview_movie: + sanim = seq->anims.first; + if (sanim && sanim->anim) { + IMB_Proxy_Size proxy_size = seq_rendersize_to_proxysize(context->preview_render_size); + IMB_anim_set_preseek(sanim->anim, seq->anim_preseek); + + ibuf = IMB_anim_absolute(sanim->anim, nr + seq->anim_startofs, + seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, + proxy_size); + + /* fetching for requested proxy size failed, try fetching the original instead */ + if (!ibuf && proxy_size != IMB_PROXY_NONE) { + ibuf = IMB_anim_absolute(sanim->anim, nr + seq->anim_startofs, + seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, + IMB_PROXY_NONE); + } + if (ibuf) { + BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false); + + /* we don't need both (speed reasons)! */ + if (ibuf->rect_float && ibuf->rect) { + imb_freerectImBuf(ibuf); + } + + seq->strip->stripdata->orig_width = ibuf->x; + seq->strip->stripdata->orig_height = ibuf->y; + } + } + } + return ibuf; +} + static ImBuf *seq_render_movieclip_strip(const SeqRenderData *context, Sequence *seq, float nr) { ImBuf *ibuf = NULL; @@ -2560,7 +3056,7 @@ static ImBuf *seq_render_mask_strip(const SeqRenderData *context, Sequence *seq, return seq_render_mask(context, seq->mask, nr, make_float); } -static ImBuf *seq_render_scene_strip(const SeqRenderData *context, Sequence *seq, float nr) +static ImBuf *seq_render_scene_strip(const SeqRenderData *context, Sequence *seq, float nr, float cfra) { ImBuf *ibuf = NULL; float frame; @@ -2658,6 +3154,7 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, Sequence *seq char err_out[256] = "unknown"; int width = (scene->r.xsch * scene->r.size) / 100; int height = (scene->r.ysch * scene->r.size) / 100; + const char *viewname = BKE_scene_multiview_render_view_name_get(&scene->r, context->view_id); /* for old scened this can be uninitialized, * should probably be added to do_versions at some point if the functionality stays */ @@ -2669,14 +3166,18 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, Sequence *seq ibuf = sequencer_view3d_cb(scene, camera, width, height, IB_rect, context->scene->r.seq_prev_type, (context->scene->r.seq_flag & R_SEQ_SOLID_TEX) != 0, - use_gpencil, true, scene->r.alphamode, err_out); + use_gpencil, true, scene->r.alphamode, viewname, err_out); if (ibuf == NULL) { fprintf(stderr, "seq_render_scene_strip failed to get opengl buffer: %s\n", err_out); } } else { Render *re = RE_GetRender(scene->id.name); - RenderResult rres; + size_t totviews = BKE_scene_multiview_num_views_get(&scene->r); + int i; + ImBuf **ibufs_arr; + + ibufs_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Sequence Image Views Imbufs"); /* XXX: this if can be removed when sequence preview rendering uses the job system * @@ -2696,27 +3197,51 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, Sequence *seq /* restore previous state after it was toggled on & off by RE_BlenderFrame */ G.is_rendering = is_rendering; } - - RE_AcquireResultImage(re, &rres); - - if (rres.rectf) { - ibuf = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rectfloat); - memcpy(ibuf->rect_float, rres.rectf, 4 * sizeof(float) * rres.rectx * rres.recty); - if (rres.rectz) { - addzbuffloatImBuf(ibuf); - memcpy(ibuf->zbuf_float, rres.rectz, sizeof(float) * rres.rectx * rres.recty); + + for (i = 0; i < totviews; i++) { + SeqRenderData localcontext = *context; + RenderResult rres; + + localcontext.view_id = i; + + RE_AcquireResultImage(re, &rres, i); + + if (rres.rectf) { + ibufs_arr[i] = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rectfloat); + memcpy(ibufs_arr[i]->rect_float, rres.rectf, 4 * sizeof(float) * rres.rectx * rres.recty); + + if (rres.rectz) { + addzbuffloatImBuf(ibufs_arr[i]); + memcpy(ibufs_arr[i]->zbuf_float, rres.rectz, sizeof(float) * rres.rectx * rres.recty); + } + + /* float buffers in the sequencer are not linear */ + BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibufs_arr[i], false); + } + else if (rres.rect32) { + ibufs_arr[i] = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rect); + memcpy(ibufs_arr[i]->rect, rres.rect32, 4 * rres.rectx * rres.recty); } - /* float buffers in the sequencer are not linear */ - BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false); + if (i != context->view_id) { + copy_to_ibuf_still(&localcontext, seq, nr, ibufs_arr[i]); + BKE_sequencer_cache_put(&localcontext, seq, cfra, SEQ_STRIPELEM_IBUF, ibufs_arr[i]); + } + + RE_ReleaseResultImage(re); } - else if (rres.rect32) { - ibuf = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rect); - memcpy(ibuf->rect, rres.rect32, 4 * rres.rectx * rres.recty); + + /* return the original requested ImBuf */ + ibuf = ibufs_arr[context->view_id]; + + /* "remove" the others (decrease their refcount) */ + for (i = 0; i < totviews; i++) { + if (ibufs_arr[i] != ibuf) { + IMB_freeImBuf(ibufs_arr[i]); + } } - - RE_ReleaseResultImage(re); - + MEM_freeN(ibufs_arr); + // BIF_end_render_callbacks(); } @@ -2743,7 +3268,6 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context, Sequence *s float nr = give_stripelem_index(seq, cfra); int type = (seq->type & SEQ_TYPE_EFFECT && seq->type != SEQ_TYPE_SPEED) ? SEQ_TYPE_EFFECT : seq->type; bool use_preprocess = BKE_sequencer_input_have_to_preprocess(context, seq, cfra); - char name[FILE_MAX]; switch (type) { case SEQ_TYPE_META: @@ -2802,64 +3326,14 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context, Sequence *s case SEQ_TYPE_IMAGE: { - StripElem *s_elem = BKE_sequencer_give_stripelem(seq, cfra); - int flag; - - if (s_elem) { - BLI_join_dirfile(name, sizeof(name), seq->strip->dir, s_elem->name); - BLI_path_abs(name, G.main->name); - } - - flag = IB_rect; - if (seq->alpha_mode == SEQ_ALPHA_PREMUL) - flag |= IB_alphamode_premul; - - if (s_elem && (ibuf = IMB_loadiffname(name, flag, seq->strip->colorspace_settings.name))) { - /* we don't need both (speed reasons)! */ - if (ibuf->rect_float && ibuf->rect) - imb_freerectImBuf(ibuf); - - /* all sequencer color is done in SRGB space, linear gives odd crossfades */ - BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false); - - copy_to_ibuf_still(context, seq, nr, ibuf); - - s_elem->orig_width = ibuf->x; - s_elem->orig_height = ibuf->y; - } + ibuf = seq_render_image_strip(context, seq, nr, cfra); + copy_to_ibuf_still(context, seq, nr, ibuf); break; } case SEQ_TYPE_MOVIE: { - seq_open_anim_file(context->scene->ed, seq, false); - - if (seq->anim) { - IMB_Proxy_Size proxy_size = seq_rendersize_to_proxysize(context->preview_render_size); - IMB_anim_set_preseek(seq->anim, seq->anim_preseek); - - ibuf = IMB_anim_absolute(seq->anim, nr + seq->anim_startofs, - seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, - proxy_size); - - /* fetching for requested proxy size failed, try fetching the original instead */ - if (!ibuf && proxy_size != IMB_PROXY_NONE) { - ibuf = IMB_anim_absolute(seq->anim, nr + seq->anim_startofs, - seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN, - IMB_PROXY_NONE); - } - if (ibuf) { - BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false); - - /* we don't need both (speed reasons)! */ - if (ibuf->rect_float && ibuf->rect) { - imb_freerectImBuf(ibuf); - } - - seq->strip->stripdata->orig_width = ibuf->x; - seq->strip->stripdata->orig_height = ibuf->y; - } - } + ibuf = seq_render_movie_strip(context, seq, nr, cfra); copy_to_ibuf_still(context, seq, nr, ibuf); break; } @@ -2867,7 +3341,7 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context, Sequence *s case SEQ_TYPE_SCENE: { /* scene can be NULL after deletions */ - ibuf = seq_render_scene_strip(context, seq, nr); + ibuf = seq_render_scene_strip(context, seq, nr, cfra); /* Scene strips update all animation, so we need to restore original state.*/ BKE_animsys_evaluate_all_animation(context->bmain, context->scene, cfra); @@ -3364,16 +3838,6 @@ ImBuf *BKE_sequencer_give_ibuf_threaded(const SeqRenderData *context, float cfra return e ? e->ibuf : NULL; } -/* Functions to free imbuf and anim data on changes */ - -static void free_anim_seq(Sequence *seq) -{ - if (seq->anim) { - IMB_free_anim(seq->anim); - seq->anim = NULL; - } -} - /* check whether sequence cur depends on seq */ bool BKE_sequence_check_depend(Sequence *seq, Sequence *cur) { @@ -3425,15 +3889,11 @@ static void sequence_invalidate_cache(Scene *scene, Sequence *seq, bool invalida /* invalidate cache for current sequence */ if (invalidate_self) { - if (seq->anim) { - /* Animation structure holds some buffers inside, - * so for proper cache invalidation we need to - * re-open the animation. - */ - IMB_free_anim(seq->anim); - seq->anim = NULL; - } - + /* Animation structure holds some buffers inside, + * so for proper cache invalidation we need to + * re-open the animation. + */ + BKE_sequence_free_anim(seq); BKE_sequencer_cache_cleanup_sequence(seq); } @@ -3480,7 +3940,7 @@ void BKE_sequencer_free_imbuf(Scene *scene, ListBase *seqbase, bool for_render) if (seq->strip) { if (seq->type == SEQ_TYPE_MOVIE) { - free_anim_seq(seq); + BKE_sequence_free_anim(seq); } if (seq->type == SEQ_TYPE_SPEED) { BKE_sequence_effect_speed_rebuild_map(scene, seq, true); @@ -3527,7 +3987,7 @@ static bool update_changed_seq_recurs(Scene *scene, Sequence *seq, Sequence *cha if (free_imbuf) { if (ibuf_change) { if (seq->type == SEQ_TYPE_MOVIE) - free_anim_seq(seq); + BKE_sequence_free_anim(seq); if (seq->type == SEQ_TYPE_SPEED) { BKE_sequence_effect_speed_rebuild_map(scene, seq, true); } @@ -4415,6 +4875,8 @@ Sequence *BKE_sequence_alloc(ListBase *lb, int cfra, int machine) seq->pitch = 1.0f; seq->scene_sound = NULL; + seq->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Sequence Stereo Format"); + return seq; } @@ -4472,6 +4934,12 @@ Sequence *BKE_sequencer_add_image_strip(bContext *C, ListBase *seqbasep, SeqLoad strip->stripdata = MEM_callocN(seq->len * sizeof(StripElem), "stripelem"); BLI_strncpy(strip->dir, seq_load->path, sizeof(strip->dir)); + if (seq_load->stereo3d_format) + *seq->stereo3d_format = *seq_load->stereo3d_format; + + seq->views_format = seq_load->views_format; + seq->flag |= seq_load->flag & SEQ_USE_VIEWS; + seq_load_apply(scene, seq, seq_load); return seq; @@ -4551,6 +5019,12 @@ Sequence *BKE_sequencer_add_sound_strip(bContext *C, ListBase *seqbasep, SeqLoad } #endif // WITH_AUDASPACE +static void seq_anim_add_suffix(Scene *scene, struct anim *anim, const size_t view_id) +{ + const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, view_id); + IMB_suffix_anim(anim, suffix); +} + Sequence *BKE_sequencer_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoadInfo *seq_load) { Scene *scene = CTX_data_scene(C); /* only for sound */ @@ -4560,29 +5034,84 @@ Sequence *BKE_sequencer_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoad Strip *strip; StripElem *se; char colorspace[64] = "\0"; /* MAX_COLORSPACE_NAME */ - - struct anim *an; + bool is_multiview_loaded = false; + const bool is_multiview = (seq_load->flag & SEQ_USE_VIEWS) != 0; + size_t totfiles = seq_num_files(scene, seq_load->views_format, is_multiview); + struct anim **anim_arr; + int i; BLI_strncpy(path, seq_load->path, sizeof(path)); BLI_path_abs(path, G.main->name); - an = openanim(path, IB_rect, 0, colorspace); + anim_arr = MEM_callocN(sizeof(struct anim *) * totfiles, "Video files"); - if (an == NULL) - return NULL; + if (is_multiview && (seq_load->views_format == R_IMF_VIEWS_INDIVIDUAL)) { + char prefix[FILE_MAX]; + char *ext = NULL; + size_t j = 0; + + BKE_scene_multiview_view_prefix_get(scene, path, prefix, &ext); + + if (prefix[0] != '\0') { + for (i = 0; i < totfiles; i++) { + char str[FILE_MAX]; + + seq_multiview_name(scene, i, prefix, ext, str, FILE_MAX); + anim_arr[j] = openanim(str, IB_rect, 0, colorspace); + seq_anim_add_suffix(scene, anim_arr[j], i); + + if (anim_arr[j]) { + j++; + } + } + + if (j == 0) { + MEM_freeN(anim_arr); + return NULL; + } + is_multiview_loaded = true; + } + } + + if (is_multiview_loaded == false) { + anim_arr[0] = openanim(path, IB_rect, 0, colorspace); + + if (anim_arr[0] == NULL) { + MEM_freeN(anim_arr); + return NULL; + } + } seq = BKE_sequence_alloc(seqbasep, seq_load->start_frame, seq_load->channel); + + /* multiview settings */ + if (seq_load->stereo3d_format) { + *seq->stereo3d_format = *seq_load->stereo3d_format; + seq->views_format = seq_load->views_format; + } + seq->flag |= seq_load->flag & SEQ_USE_VIEWS; + seq->type = SEQ_TYPE_MOVIE; seq->blend_mode = SEQ_TYPE_CROSS; /* so alpha adjustment fade to the strip below */ - seq->anim = an; - seq->anim_preseek = IMB_anim_get_preseek(an); + for (i = 0; i < totfiles; i++) { + if (anim_arr[i]) { + StripAnim *sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim"); + BLI_addtail(&seq->anims, sanim); + sanim->anim = anim_arr[i]; + } + else { + break; + } + } + + seq->anim_preseek = IMB_anim_get_preseek(anim_arr[0]); BLI_strncpy(seq->name + 2, "Movie", SEQ_NAME_MAXSTR - 2); BKE_sequence_base_unique_name_recursive(&scene->ed->seqbase, seq); /* basic defaults */ seq->strip = strip = MEM_callocN(sizeof(Strip), "strip"); - seq->len = IMB_anim_get_duration(an, IMB_TC_RECORD_RUN); + seq->len = IMB_anim_get_duration(anim_arr[0], IMB_TC_RECORD_RUN); strip->us = 1; BLI_strncpy(seq->strip->colorspace_settings.name, colorspace, sizeof(seq->strip->colorspace_settings.name)); @@ -4610,6 +5139,7 @@ Sequence *BKE_sequencer_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoad /* can be NULL */ seq_load_apply(scene, seq, seq_load); + MEM_freeN(anim_arr); return seq; } @@ -4621,6 +5151,8 @@ static Sequence *seq_dupli(Scene *scene, Scene *scene_to, Sequence *seq, int dup seq->tmp = seqn; seqn->strip = MEM_dupallocN(seq->strip); + seqn->stereo3d_format = MEM_dupallocN(seq->stereo3d_format); + /* XXX: add F-Curve duplication stuff? */ if (seq->strip->crop) { @@ -4667,7 +5199,7 @@ static Sequence *seq_dupli(Scene *scene, Scene *scene_to, Sequence *seq, int dup else if (seq->type == SEQ_TYPE_MOVIE) { seqn->strip->stripdata = MEM_dupallocN(seq->strip->stripdata); - seqn->anim = NULL; + BLI_listbase_clear(&seqn->anims); } else if (seq->type == SEQ_TYPE_SOUND_RAM) { seqn->strip->stripdata = diff --git a/source/blender/blenkernel/intern/writeavi.c b/source/blender/blenkernel/intern/writeavi.c index a86ffacfdbd..cec455e01b9 100644 --- a/source/blender/blenkernel/intern/writeavi.c +++ b/source/blender/blenkernel/intern/writeavi.c @@ -50,26 +50,34 @@ /* ********************** general blender movie support ***************************** */ -static int start_stub(Scene *UNUSED(scene), RenderData *UNUSED(rd), int UNUSED(rectx), int UNUSED(recty), - ReportList *UNUSED(reports), bool UNUSED(preview)) +static int start_stub(void *UNUSED(context_v), Scene *UNUSED(scene), RenderData *UNUSED(rd), int UNUSED(rectx), int UNUSED(recty), + ReportList *UNUSED(reports), bool UNUSED(preview), const char *UNUSED(suffix)) { return 0; } -static void end_stub(void) +static void end_stub(void *UNUSED(context_v)) {} -static int append_stub(RenderData *UNUSED(rd), int UNUSED(start_frame), int UNUSED(frame), int *UNUSED(pixels), - int UNUSED(rectx), int UNUSED(recty), ReportList *UNUSED(reports)) +static int append_stub(void *UNUSED(context_v), RenderData *UNUSED(rd), int UNUSED(start_frame), int UNUSED(frame), int *UNUSED(pixels), + int UNUSED(rectx), int UNUSED(recty), const char *UNUSED(suffix), ReportList *UNUSED(reports)) { return 0; } +static void *context_create_stub(void) +{ return NULL; } + +static void context_free_stub(void *UNUSED(context_v)) +{} + #ifdef WITH_AVI # include "AVI_avi.h" /* callbacks */ -static int start_avi(Scene *scene, RenderData *rd, int rectx, int recty, ReportList *reports, bool preview); -static void end_avi(void); -static int append_avi(RenderData *rd, int start_frame, int frame, int *pixels, - int rectx, int recty, ReportList *reports); -static void filepath_avi(char *string, RenderData *rd, bool preview); +static int start_avi(void *context_v, Scene *scene, RenderData *rd, int rectx, int recty, ReportList *reports, bool preview, const char *suffix); +static void end_avi(void *context_v); +static int append_avi(void *context_v, RenderData *rd, int start_frame, int frame, int *pixels, + int rectx, int recty, const char *suffix, ReportList *reports); +static void filepath_avi(char *string, RenderData *rd, bool preview, const char *suffix); +static void *context_create_avi(void); +static void context_free_avi(void *context_v); #endif /* WITH_AVI */ #ifdef WITH_QUICKTIME @@ -93,13 +101,17 @@ bMovieHandle *BKE_movie_handle_get(const char imtype) mh.end_movie = end_stub; mh.get_next_frame = NULL; mh.get_movie_path = NULL; - + mh.context_create = context_create_stub; + mh.context_free = context_free_stub; + /* set the default handle, as builtin */ #ifdef WITH_AVI mh.start_movie = start_avi; mh.append_movie = append_avi; mh.end_movie = end_avi; mh.get_movie_path = filepath_avi; + mh.context_create = context_create_avi; + mh.context_free = context_free_avi; #endif /* do the platform specific handles */ @@ -109,6 +121,8 @@ bMovieHandle *BKE_movie_handle_get(const char imtype) mh.append_movie = append_qt; mh.end_movie = end_qt; mh.get_movie_path = filepath_qt; + mh.context_create = context_create_qt; + mh.context_free = context_free_qt; } #endif #ifdef WITH_FFMPEG @@ -117,6 +131,8 @@ bMovieHandle *BKE_movie_handle_get(const char imtype) mh.append_movie = BKE_ffmpeg_append; mh.end_movie = BKE_ffmpeg_end; mh.get_movie_path = BKE_ffmpeg_filepath_get; + mh.context_create = BKE_ffmpeg_context_create; + mh.context_free = BKE_ffmpeg_context_free; } #endif #ifdef WITH_FRAMESERVER @@ -125,6 +141,8 @@ bMovieHandle *BKE_movie_handle_get(const char imtype) mh.append_movie = BKE_frameserver_append; mh.end_movie = BKE_frameserver_end; mh.get_next_frame = BKE_frameserver_loop; + mh.context_create = BKE_frameserver_context_create; + mh.context_free = BKE_frameserver_context_free; } #endif @@ -137,9 +155,7 @@ bMovieHandle *BKE_movie_handle_get(const char imtype) #ifdef WITH_AVI -static AviMovie *avi = NULL; - -static void filepath_avi(char *string, RenderData *rd, bool preview) +static void filepath_avi(char *string, RenderData *rd, bool preview, const char *suffix) { int sfra, efra; @@ -170,27 +186,27 @@ static void filepath_avi(char *string, RenderData *rd, bool preview) BLI_path_frame_range(string, sfra, efra, 4); } } + + BLI_path_suffix(string, FILE_MAX, suffix, ""); } -static int start_avi(Scene *scene, RenderData *rd, int rectx, int recty, ReportList *reports, bool preview) +static int start_avi(void *context_v, Scene *UNUSED(scene), RenderData *rd, int rectx, int recty, + ReportList *reports, bool preview, const char *suffix) { int x, y; char name[256]; AviFormat format; int quality; double framerate; - - (void)scene; /* unused */ - - filepath_avi(name, rd, preview); + AviMovie *avi = context_v; + + filepath_avi(name, rd, preview, suffix); x = rectx; y = recty; quality = rd->im_format.quality; framerate = (double) rd->frs_sec / (double) rd->frs_sec_base; - - avi = MEM_mallocN(sizeof(AviMovie), "avimovie"); if (rd->im_format.imtype != R_IMF_IMTYPE_AVIJPEG) format = AVI_FORMAT_AVI_RGB; else format = AVI_FORMAT_MJPEG; @@ -216,12 +232,13 @@ static int start_avi(Scene *scene, RenderData *rd, int rectx, int recty, ReportL return 1; } -static int append_avi(RenderData *UNUSED(rd), int start_frame, int frame, int *pixels, - int rectx, int recty, ReportList *UNUSED(reports)) +static int append_avi(void *context_v, RenderData *UNUSED(rd), int start_frame, int frame, int *pixels, + int rectx, int recty, const char *UNUSED(suffix), ReportList *UNUSED(reports)) { unsigned int *rt1, *rt2, *rectot; int x, y; char *cp, rt; + AviMovie *avi = context_v; if (avi == NULL) return 0; @@ -252,22 +269,37 @@ static int append_avi(RenderData *UNUSED(rd), int start_frame, int frame, int *p return 1; } -static void end_avi(void) +static void end_avi(void *context_v) { + AviMovie *avi = context_v; + if (avi == NULL) return; AVI_close_compress(avi); - MEM_freeN(avi); - avi = NULL; } + +static void *context_create_avi(void) +{ + AviMovie *avi = MEM_mallocN(sizeof(AviMovie), "avimovie"); + return avi; +} + +static void context_free_avi(void *context_v) +{ + AviMovie *avi = context_v; + if (avi) { + MEM_freeN(avi); + } +} + #endif /* WITH_AVI */ /* similar to BKE_image_path_from_imformat() */ -void BKE_movie_filepath_get(char *string, RenderData *rd, bool preview) +void BKE_movie_filepath_get(char *string, RenderData *rd, bool preview, const char *suffix) { bMovieHandle *mh = BKE_movie_handle_get(rd->im_format.imtype); if (mh->get_movie_path) - mh->get_movie_path(string, rd, preview); + mh->get_movie_path(string, rd, preview, suffix); else string[0] = '\0'; } diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c index 0717369cde8..af71f19c226 100644 --- a/source/blender/blenkernel/intern/writeffmpeg.c +++ b/source/blender/blenkernel/intern/writeffmpeg.c @@ -61,36 +61,38 @@ #include "ffmpeg_compat.h" -static int ffmpeg_type = 0; -static int ffmpeg_codec = AV_CODEC_ID_MPEG4; -static int ffmpeg_audio_codec = AV_CODEC_ID_NONE; -static int ffmpeg_video_bitrate = 1150; -static int ffmpeg_audio_bitrate = 128; -static int ffmpeg_gop_size = 12; -static int ffmpeg_autosplit = 0; -static int ffmpeg_autosplit_count = 0; -static bool ffmpeg_preview = false; - -static AVFormatContext *outfile = 0; -static AVStream *video_stream = 0; -static AVStream *audio_stream = 0; -static AVFrame *current_frame = 0; -static struct SwsContext *img_convert_ctx = 0; - -static uint8_t *audio_input_buffer = 0; -static uint8_t *audio_deinterleave_buffer = 0; -static int audio_input_samples = 0; +typedef struct FFMpegContext { + int ffmpeg_type; + int ffmpeg_codec; + int ffmpeg_audio_codec; + int ffmpeg_video_bitrate; + int ffmpeg_audio_bitrate; + int ffmpeg_gop_size; + int ffmpeg_autosplit; + int ffmpeg_autosplit_count; + bool ffmpeg_preview; + + AVFormatContext *outfile; + AVStream *video_stream; + AVStream *audio_stream; + AVFrame *current_frame; + struct SwsContext *img_convert_ctx; + + uint8_t *audio_input_buffer; + uint8_t *audio_deinterleave_buffer; + int audio_input_samples; #ifndef FFMPEG_HAVE_ENCODE_AUDIO2 -static uint8_t *audio_output_buffer = 0; -static int audio_outbuf_size = 0; + uint8_t *audio_output_buffer; + int audio_outbuf_size; #endif -static double audio_time = 0.0f; -static bool audio_deinterleave = false; -static int audio_sample_size = 0; + double audio_time; + bool audio_deinterleave; + int audio_sample_size; #ifdef WITH_AUDASPACE -static AUD_Device *audio_mixdown_device = 0; + AUD_Device *audio_mixdown_device; #endif +} FFMpegContext; #define FFMPEG_AUTOSPLIT_SIZE 2000000000 @@ -99,6 +101,7 @@ static AUD_Device *audio_mixdown_device = 0; static void ffmpeg_dict_set_int(AVDictionary **dict, const char *key, int value); static void ffmpeg_dict_set_float(AVDictionary **dict, const char *key, float value); static void ffmpeg_set_expert_options(RenderData *rd); +static void ffmpeg_filepath_get(FFMpegContext *context, char *string, struct RenderData *rd, bool preview, const char *suffix); /* Delete a picture buffer */ @@ -117,50 +120,50 @@ static int request_float_audio_buffer(int codec_id) } #ifdef WITH_AUDASPACE -static int write_audio_frame(void) +static int write_audio_frame(FFMpegContext *context) { AVCodecContext *c = NULL; AVPacket pkt; AVFrame *frame = NULL; int got_output = 0; - c = audio_stream->codec; + c = context->audio_stream->codec; av_init_packet(&pkt); pkt.size = 0; pkt.data = NULL; - AUD_readDevice(audio_mixdown_device, audio_input_buffer, audio_input_samples); - audio_time += (double) audio_input_samples / (double) c->sample_rate; + AUD_readDevice(context->audio_mixdown_device, context->audio_input_buffer, context->audio_input_samples); + context->audio_time += (double) context->audio_input_samples / (double) c->sample_rate; #ifdef FFMPEG_HAVE_ENCODE_AUDIO2 frame = avcodec_alloc_frame(); avcodec_get_frame_defaults(frame); - frame->pts = audio_time / av_q2d(c->time_base); - frame->nb_samples = audio_input_samples; + frame->pts = context->audio_time / av_q2d(c->time_base); + frame->nb_samples = context->audio_input_samples; frame->format = c->sample_fmt; #ifdef FFMPEG_HAVE_FRAME_CHANNEL_LAYOUT frame->channel_layout = c->channel_layout; #endif - if (audio_deinterleave) { + if (context->audio_deinterleave) { int channel, i; uint8_t *temp; for (channel = 0; channel < c->channels; channel++) { for (i = 0; i < frame->nb_samples; i++) { - memcpy(audio_deinterleave_buffer + (i + channel * frame->nb_samples) * audio_sample_size, - audio_input_buffer + (c->channels * i + channel) * audio_sample_size, audio_sample_size); + memcpy(context->audio_deinterleave_buffer + (i + channel * frame->nb_samples) * context->audio_sample_size, + context->audio_input_buffer + (c->channels * i + channel) * context->audio_sample_size, context->audio_sample_size); } } - temp = audio_deinterleave_buffer; - audio_deinterleave_buffer = audio_input_buffer; - audio_input_buffer = temp; + temp = context->audio_deinterleave_buffer; + context->audio_deinterleave_buffer = context->audio_input_buffer; + context->audio_input_buffer = temp; } - avcodec_fill_audio_frame(frame, c->channels, c->sample_fmt, audio_input_buffer, - audio_input_samples * c->channels * audio_sample_size, 1); + avcodec_fill_audio_frame(frame, c->channels, c->sample_fmt, context->audio_input_buffer, + context->audio_input_samples * c->channels * context->audio_sample_size, 1); if (avcodec_encode_audio2(c, &pkt, frame, &got_output) < 0) { // XXX error("Error writing audio packet"); @@ -172,30 +175,30 @@ static int write_audio_frame(void) return 0; } #else - pkt.size = avcodec_encode_audio(c, audio_output_buffer, audio_outbuf_size, (short *) audio_input_buffer); + pkt.size = avcodec_encode_audio(c, context->audio_output_buffer, context->audio_outbuf_size, (short *) context->audio_input_buffer); if (pkt.size < 0) { // XXX error("Error writing audio packet"); return -1; } - pkt.data = audio_output_buffer; + pkt.data = context->audio_output_buffer; got_output = 1; #endif if (got_output) { if (pkt.pts != AV_NOPTS_VALUE) - pkt.pts = av_rescale_q(pkt.pts, c->time_base, audio_stream->time_base); + pkt.pts = av_rescale_q(pkt.pts, c->time_base, context->audio_stream->time_base); if (pkt.dts != AV_NOPTS_VALUE) - pkt.dts = av_rescale_q(pkt.dts, c->time_base, audio_stream->time_base); + pkt.dts = av_rescale_q(pkt.dts, c->time_base, context->audio_stream->time_base); if (pkt.duration > 0) - pkt.duration = av_rescale_q(pkt.duration, c->time_base, audio_stream->time_base); + pkt.duration = av_rescale_q(pkt.duration, c->time_base, context->audio_stream->time_base); - pkt.stream_index = audio_stream->index; + pkt.stream_index = context->audio_stream->index; pkt.flags |= AV_PKT_FLAG_KEY; - if (av_interleaved_write_frame(outfile, &pkt) != 0) { + if (av_interleaved_write_frame(context->outfile, &pkt) != 0) { fprintf(stderr, "Error writing audio packet!\n"); if (frame) avcodec_free_frame(&frame); @@ -302,11 +305,11 @@ static const char **get_file_extensions(int format) } /* Write a frame to the output file */ -static int write_video_frame(RenderData *rd, int cfra, AVFrame *frame, ReportList *reports) +static int write_video_frame(FFMpegContext *context, RenderData *rd, int cfra, AVFrame *frame, ReportList *reports) { int got_output; int ret, success = 1; - AVCodecContext *c = video_stream->codec; + AVCodecContext *c = context->video_stream->codec; AVPacket packet = { 0 }; av_init_packet(&packet); @@ -321,22 +324,22 @@ static int write_video_frame(RenderData *rd, int cfra, AVFrame *frame, ReportLis if (ret >= 0 && got_output) { if (packet.pts != AV_NOPTS_VALUE) { - packet.pts = av_rescale_q(packet.pts, c->time_base, video_stream->time_base); + packet.pts = av_rescale_q(packet.pts, c->time_base, context->video_stream->time_base); PRINT("Video Frame PTS: %d\n", (int)packet.pts); } else { PRINT("Video Frame PTS: not set\n"); } if (packet.dts != AV_NOPTS_VALUE) { - packet.dts = av_rescale_q(packet.dts, c->time_base, video_stream->time_base); + packet.dts = av_rescale_q(packet.dts, c->time_base, context->video_stream->time_base); PRINT("Video Frame DTS: %d\n", (int)packet.dts); } else { PRINT("Video Frame DTS: not set\n"); } - packet.stream_index = video_stream->index; - ret = av_interleaved_write_frame(outfile, &packet); + packet.stream_index = context->video_stream->index; + ret = av_interleaved_write_frame(context->outfile, &packet); success = (ret == 0); } else if (ret < 0) { @@ -350,11 +353,11 @@ static int write_video_frame(RenderData *rd, int cfra, AVFrame *frame, ReportLis } /* read and encode a frame of audio from the buffer */ -static AVFrame *generate_video_frame(uint8_t *pixels, ReportList *reports) +static AVFrame *generate_video_frame(FFMpegContext *context, uint8_t *pixels, ReportList *reports) { uint8_t *rendered_frame; - AVCodecContext *c = video_stream->codec; + AVCodecContext *c = context->video_stream->codec; int width = c->width; int height = c->height; AVFrame *rgb_frame; @@ -367,7 +370,7 @@ static AVFrame *generate_video_frame(uint8_t *pixels, ReportList *reports) } } else { - rgb_frame = current_frame; + rgb_frame = context->current_frame; } rendered_frame = pixels; @@ -411,17 +414,17 @@ static AVFrame *generate_video_frame(uint8_t *pixels, ReportList *reports) } if (c->pix_fmt != PIX_FMT_BGR32) { - sws_scale(img_convert_ctx, (const uint8_t *const *) rgb_frame->data, + sws_scale(context->img_convert_ctx, (const uint8_t *const *) rgb_frame->data, rgb_frame->linesize, 0, c->height, - current_frame->data, current_frame->linesize); + context->current_frame->data, context->current_frame->linesize); delete_picture(rgb_frame); } - current_frame->format = PIX_FMT_BGR32; - current_frame->width = width; - current_frame->height = height; + context->current_frame->format = PIX_FMT_BGR32; + context->current_frame->width = width; + context->current_frame->height = height; - return current_frame; + return context->current_frame; } static void set_ffmpeg_property_option(AVCodecContext *c, IDProperty *prop, AVDictionary **dictionary) @@ -516,7 +519,7 @@ static void set_ffmpeg_properties(RenderData *rd, AVCodecContext *c, const char /* prepare a video stream for the output file */ -static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContext *of, +static AVStream *alloc_video_stream(FFMpegContext *context, RenderData *rd, int codec_id, AVFormatContext *of, int rectx, int recty, char *error, int error_size) { AVStream *st; @@ -542,7 +545,7 @@ static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContex c->height = recty; /* FIXME: Really bad hack (tm) for NTSC support */ - if (ffmpeg_type == FFMPEG_DV && rd->frs_sec != 25) { + if (context->ffmpeg_type == FFMPEG_DV && rd->frs_sec != 25) { c->time_base.den = 2997; c->time_base.num = 100; } @@ -555,8 +558,8 @@ static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContex c->time_base.num = ((double) rd->frs_sec_base) * 100000; } - c->gop_size = ffmpeg_gop_size; - c->bit_rate = ffmpeg_video_bitrate * 1000; + c->gop_size = context->ffmpeg_gop_size; + c->bit_rate = context->ffmpeg_video_bitrate * 1000; c->rc_max_rate = rd->ffcodecdata.rc_max_rate * 1000; c->rc_min_rate = rd->ffcodecdata.rc_min_rate * 1000; c->rc_buffer_size = rd->ffcodecdata.rc_buffer_size * 1024; @@ -585,7 +588,7 @@ static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContex c->pix_fmt = PIX_FMT_YUV422P; } - if (ffmpeg_type == FFMPEG_XVID) { + if (context->ffmpeg_type == FFMPEG_XVID) { /* arghhhh ... */ c->pix_fmt = PIX_FMT_YUV420P; c->codec_tag = (('D' << 24) + ('I' << 16) + ('V' << 8) + 'X'); @@ -655,14 +658,14 @@ static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContex } av_dict_free(&opts); - current_frame = alloc_picture(c->pix_fmt, c->width, c->height); + context->current_frame = alloc_picture(c->pix_fmt, c->width, c->height); - img_convert_ctx = sws_getContext(c->width, c->height, PIX_FMT_BGR32, c->width, c->height, c->pix_fmt, SWS_BICUBIC, + context->img_convert_ctx = sws_getContext(c->width, c->height, PIX_FMT_BGR32, c->width, c->height, c->pix_fmt, SWS_BICUBIC, NULL, NULL, NULL); return st; } -static AVStream *alloc_audio_stream(RenderData *rd, int codec_id, AVFormatContext *of, char *error, int error_size) +static AVStream *alloc_audio_stream(FFMpegContext *context, RenderData *rd, int codec_id, AVFormatContext *of, char *error, int error_size) { AVStream *st; AVCodecContext *c; @@ -680,7 +683,7 @@ static AVStream *alloc_audio_stream(RenderData *rd, int codec_id, AVFormatContex c->codec_type = AVMEDIA_TYPE_AUDIO; c->sample_rate = rd->ffcodecdata.audio_mixrate; - c->bit_rate = ffmpeg_audio_bitrate * 1000; + c->bit_rate = context->ffmpeg_audio_bitrate * 1000; c->sample_fmt = AV_SAMPLE_FMT_S16; c->channels = rd->ffcodecdata.audio_channels; @@ -747,35 +750,35 @@ static AVStream *alloc_audio_stream(RenderData *rd, int codec_id, AVFormatContex st->codec->time_base.den = st->codec->sample_rate; #ifndef FFMPEG_HAVE_ENCODE_AUDIO2 - audio_outbuf_size = FF_MIN_BUFFER_SIZE; + context->audio_outbuf_size = FF_MIN_BUFFER_SIZE; #endif if (c->frame_size == 0) // used to be if ((c->codec_id >= CODEC_ID_PCM_S16LE) && (c->codec_id <= CODEC_ID_PCM_DVD)) // not sure if that is needed anymore, so let's try out if there are any // complaints regarding some ffmpeg versions users might have - audio_input_samples = FF_MIN_BUFFER_SIZE * 8 / c->bits_per_coded_sample / c->channels; + context->audio_input_samples = FF_MIN_BUFFER_SIZE * 8 / c->bits_per_coded_sample / c->channels; else { - audio_input_samples = c->frame_size; + context->audio_input_samples = c->frame_size; #ifndef FFMPEG_HAVE_ENCODE_AUDIO2 - if (c->frame_size * c->channels * sizeof(int16_t) * 4 > audio_outbuf_size) - audio_outbuf_size = c->frame_size * c->channels * sizeof(int16_t) * 4; + if (c->frame_size * c->channels * sizeof(int16_t) * 4 > context->audio_outbuf_size) + context->audio_outbuf_size = c->frame_size * c->channels * sizeof(int16_t) * 4; #endif } - audio_deinterleave = av_sample_fmt_is_planar(c->sample_fmt); + context->audio_deinterleave = av_sample_fmt_is_planar(c->sample_fmt); - audio_sample_size = av_get_bytes_per_sample(c->sample_fmt); + context->audio_sample_size = av_get_bytes_per_sample(c->sample_fmt); - audio_input_buffer = (uint8_t *) av_malloc(audio_input_samples * c->channels * audio_sample_size); + context->audio_input_buffer = (uint8_t *) av_malloc(context->audio_input_samples * c->channels * context->audio_sample_size); #ifndef FFMPEG_HAVE_ENCODE_AUDIO2 - audio_output_buffer = (uint8_t *) av_malloc(audio_outbuf_size); + context->audio_output_buffer = (uint8_t *) av_malloc(context->audio_outbuf_size); #endif - if (audio_deinterleave) - audio_deinterleave_buffer = (uint8_t *) av_malloc(audio_input_samples * c->channels * audio_sample_size); + if (context->audio_deinterleave) + context->audio_deinterleave_buffer = (uint8_t *) av_malloc(context->audio_input_samples * c->channels * context->audio_sample_size); - audio_time = 0.0f; + context->audio_time = 0.0f; return st; } @@ -799,7 +802,7 @@ static void ffmpeg_dict_set_float(AVDictionary **dict, const char *key, float va av_dict_set(dict, key, buffer, 0); } -static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, ReportList *reports) +static int start_ffmpeg_impl(FFMpegContext *context, struct RenderData *rd, int rectx, int recty, const char *suffix, ReportList *reports) { /* Handle to the output file */ AVFormatContext *of; @@ -808,26 +811,26 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report char name[256], error[1024]; const char **exts; - ffmpeg_type = rd->ffcodecdata.type; - ffmpeg_codec = rd->ffcodecdata.codec; - ffmpeg_audio_codec = rd->ffcodecdata.audio_codec; - ffmpeg_video_bitrate = rd->ffcodecdata.video_bitrate; - ffmpeg_audio_bitrate = rd->ffcodecdata.audio_bitrate; - ffmpeg_gop_size = rd->ffcodecdata.gop_size; - ffmpeg_autosplit = rd->ffcodecdata.flags & FFMPEG_AUTOSPLIT_OUTPUT; - + context->ffmpeg_type = rd->ffcodecdata.type; + context->ffmpeg_codec = rd->ffcodecdata.codec; + context->ffmpeg_audio_codec = rd->ffcodecdata.audio_codec; + context->ffmpeg_video_bitrate = rd->ffcodecdata.video_bitrate; + context->ffmpeg_audio_bitrate = rd->ffcodecdata.audio_bitrate; + context->ffmpeg_gop_size = rd->ffcodecdata.gop_size; + context->ffmpeg_autosplit = rd->ffcodecdata.flags & FFMPEG_AUTOSPLIT_OUTPUT; + /* Determine the correct filename */ - BKE_ffmpeg_filepath_get(name, rd, ffmpeg_preview); + ffmpeg_filepath_get(context, name, rd, context->ffmpeg_preview, suffix); PRINT("Starting output to %s(ffmpeg)...\n" " Using type=%d, codec=%d, audio_codec=%d,\n" " video_bitrate=%d, audio_bitrate=%d,\n" " gop_size=%d, autosplit=%d\n" " render width=%d, render height=%d\n", - name, ffmpeg_type, ffmpeg_codec, ffmpeg_audio_codec, - ffmpeg_video_bitrate, ffmpeg_audio_bitrate, - ffmpeg_gop_size, ffmpeg_autosplit, rectx, recty); + name, context->ffmpeg_type, context->ffmpeg_codec, context->ffmpeg_audio_codec, + context->ffmpeg_video_bitrate, context->ffmpeg_audio_bitrate, + context->ffmpeg_gop_size, context->ffmpeg_autosplit, rectx, recty); - exts = get_file_extensions(ffmpeg_type); + exts = get_file_extensions(context->ffmpeg_type); if (!exts) { BKE_report(reports, RPT_ERROR, "No valid formats found"); return 0; @@ -846,7 +849,7 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report of->oformat = fmt; of->packet_size = rd->ffcodecdata.mux_packet_size; - if (ffmpeg_audio_codec != AV_CODEC_ID_NONE) { + if (context->ffmpeg_audio_codec != AV_CODEC_ID_NONE) { ffmpeg_dict_set_int(&opts, "muxrate", rd->ffcodecdata.mux_rate); } else { @@ -857,15 +860,15 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report of->max_delay = (int)(0.7 * AV_TIME_BASE); - fmt->audio_codec = ffmpeg_audio_codec; + fmt->audio_codec = context->ffmpeg_audio_codec; BLI_strncpy(of->filename, name, sizeof(of->filename)); /* set the codec to the user's selection */ - switch (ffmpeg_type) { + switch (context->ffmpeg_type) { case FFMPEG_AVI: case FFMPEG_MOV: case FFMPEG_MKV: - fmt->video_codec = ffmpeg_codec; + fmt->video_codec = context->ffmpeg_codec; break; case FFMPEG_OGG: fmt->video_codec = AV_CODEC_ID_THEORA; @@ -908,9 +911,9 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report } } - if (ffmpeg_type == FFMPEG_DV) { + if (context->ffmpeg_type == FFMPEG_DV) { fmt->audio_codec = AV_CODEC_ID_PCM_S16LE; - if (ffmpeg_audio_codec != AV_CODEC_ID_NONE && rd->ffcodecdata.audio_mixrate != 48000 && rd->ffcodecdata.audio_channels != 2) { + if (context->ffmpeg_audio_codec != AV_CODEC_ID_NONE && rd->ffcodecdata.audio_mixrate != 48000 && rd->ffcodecdata.audio_channels != 2) { BKE_report(reports, RPT_ERROR, "FFMPEG only supports 48khz / stereo audio for DV!"); av_dict_free(&opts); return 0; @@ -918,9 +921,9 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report } if (fmt->video_codec != AV_CODEC_ID_NONE) { - video_stream = alloc_video_stream(rd, fmt->video_codec, of, rectx, recty, error, sizeof(error)); - PRINT("alloc video stream %p\n", video_stream); - if (!video_stream) { + context->video_stream = alloc_video_stream(context, rd, fmt->video_codec, of, rectx, recty, error, sizeof(error)); + PRINT("alloc video stream %p\n", context->video_stream); + if (!context->video_stream) { if (error[0]) BKE_report(reports, RPT_ERROR, error); else @@ -931,9 +934,9 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report } } - if (ffmpeg_audio_codec != AV_CODEC_ID_NONE) { - audio_stream = alloc_audio_stream(rd, fmt->audio_codec, of, error, sizeof(error)); - if (!audio_stream) { + if (context->ffmpeg_audio_codec != AV_CODEC_ID_NONE) { + context->audio_stream = alloc_audio_stream(context, rd, fmt->audio_codec, of, error, sizeof(error)); + if (!context->audio_stream) { if (error[0]) BKE_report(reports, RPT_ERROR, error); else @@ -956,7 +959,7 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report return 0; } - outfile = of; + context->outfile = of; av_dump_format(of, 0, name, 1); av_dict_free(&opts); @@ -980,11 +983,11 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report * parameter. * </p> */ -static void flush_ffmpeg(void) +static void flush_ffmpeg(FFMpegContext *context) { int ret = 0; - AVCodecContext *c = video_stream->codec; + AVCodecContext *c = context->video_stream->codec; /* get the delayed frames */ while (1) { int got_output; @@ -1000,28 +1003,28 @@ static void flush_ffmpeg(void) break; } if (packet.pts != AV_NOPTS_VALUE) { - packet.pts = av_rescale_q(packet.pts, c->time_base, video_stream->time_base); + packet.pts = av_rescale_q(packet.pts, c->time_base, context->video_stream->time_base); PRINT("Video Frame PTS: %d\n", (int) packet.pts); } else { PRINT("Video Frame PTS: not set\n"); } if (packet.dts != AV_NOPTS_VALUE) { - packet.dts = av_rescale_q(packet.dts, c->time_base, video_stream->time_base); + packet.dts = av_rescale_q(packet.dts, c->time_base, context->video_stream->time_base); PRINT("Video Frame DTS: %d\n", (int) packet.dts); } else { PRINT("Video Frame DTS: not set\n"); } - packet.stream_index = video_stream->index; - ret = av_interleaved_write_frame(outfile, &packet); + packet.stream_index = context->video_stream->index; + ret = av_interleaved_write_frame(context->outfile, &packet); if (ret != 0) { fprintf(stderr, "Error writing delayed frame %d\n", ret); break; } } - avcodec_flush_buffers(video_stream->codec); + avcodec_flush_buffers(context->video_stream->codec); } /* ********************************************************************** @@ -1029,7 +1032,7 @@ static void flush_ffmpeg(void) * ********************************************************************** */ /* Get the output filename-- similar to the other output formats */ -void BKE_ffmpeg_filepath_get(char *string, RenderData *rd, bool preview) +static void ffmpeg_filepath_get(FFMpegContext *context, char *string, RenderData *rd, bool preview, const char *suffix) { char autosplit[20]; @@ -1056,7 +1059,9 @@ void BKE_ffmpeg_filepath_get(char *string, RenderData *rd, bool preview) autosplit[0] = 0; if ((rd->ffcodecdata.flags & FFMPEG_AUTOSPLIT_OUTPUT) != 0) { - sprintf(autosplit, "_%03d", ffmpeg_autosplit_count); + if (context) { + sprintf(autosplit, "_%03d", context->ffmpeg_autosplit_count); + } } if (rd->scemode & R_EXTENSION) { @@ -1086,19 +1091,28 @@ void BKE_ffmpeg_filepath_get(char *string, RenderData *rd, bool preview) strcat(string, autosplit); } + + BLI_path_suffix(string, FILE_MAX, suffix, ""); } -int BKE_ffmpeg_start(struct Scene *scene, RenderData *rd, int rectx, int recty, ReportList *reports, bool preview) +void BKE_ffmpeg_filepath_get(char *string, RenderData *rd, bool preview, const char *suffix) +{ + ffmpeg_filepath_get(NULL, string, rd, preview, suffix); +} + +int BKE_ffmpeg_start(void *context_v, struct Scene *scene, RenderData *rd, int rectx, int recty, + ReportList *reports, bool preview, const char *suffix) { int success; + FFMpegContext *context = context_v; - ffmpeg_autosplit_count = 0; - ffmpeg_preview = preview; + context->ffmpeg_autosplit_count = 0; + context->ffmpeg_preview = preview; - success = start_ffmpeg_impl(rd, rectx, recty, reports); + success = start_ffmpeg_impl(context, rd, rectx, recty, suffix, reports); #ifdef WITH_AUDASPACE - if (audio_stream) { - AVCodecContext *c = audio_stream->codec; + if (context->audio_stream) { + AVCodecContext *c = context->audio_stream->codec; AUD_DeviceSpecs specs; specs.channels = c->channels; @@ -1123,7 +1137,7 @@ int BKE_ffmpeg_start(struct Scene *scene, RenderData *rd, int rectx, int recty, } specs.rate = rd->ffcodecdata.audio_mixrate; - audio_mixdown_device = BKE_sound_mixdown(scene, specs, preview ? rd->psfra : rd->sfra, rd->ffcodecdata.audio_volume); + context->audio_mixdown_device = BKE_sound_mixdown(scene, specs, preview ? rd->psfra : rd->sfra, rd->ffcodecdata.audio_volume); #ifdef FFMPEG_CODEC_TIME_BASE c->time_base.den = specs.rate; c->time_base.num = 1; @@ -1133,16 +1147,16 @@ int BKE_ffmpeg_start(struct Scene *scene, RenderData *rd, int rectx, int recty, return success; } -static void end_ffmpeg_impl(int is_autosplit); +static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit); #ifdef WITH_AUDASPACE -static void write_audio_frames(double to_pts) +static void write_audio_frames(FFMpegContext *context, double to_pts) { int finished = 0; - while (audio_stream && !finished) { - if ((audio_time >= to_pts) || - (write_audio_frame())) + while (context->audio_stream && !finished) { + if ((context->audio_time >= to_pts) || + (write_audio_frame(context))) { finished = 1; } @@ -1150,8 +1164,10 @@ static void write_audio_frames(double to_pts) } #endif -int BKE_ffmpeg_append(RenderData *rd, int start_frame, int frame, int *pixels, int rectx, int recty, ReportList *reports) +int BKE_ffmpeg_append(void *context_v, RenderData *rd, int start_frame, int frame, int *pixels, + int rectx, int recty, const char *suffix, ReportList *reports) { + FFMpegContext *context = context_v; AVFrame *avframe; int success = 1; @@ -1160,111 +1176,112 @@ int BKE_ffmpeg_append(RenderData *rd, int start_frame, int frame, int *pixels, i /* why is this done before writing the video frame and again at end_ffmpeg? */ // write_audio_frames(frame / (((double)rd->frs_sec) / rd->frs_sec_base)); - if (video_stream) { - avframe = generate_video_frame((unsigned char *) pixels, reports); - success = (avframe && write_video_frame(rd, frame - start_frame, avframe, reports)); + if (context->video_stream) { + avframe = generate_video_frame(context, (unsigned char *) pixels, reports); + success = (avframe && write_video_frame(context, rd, frame - start_frame, avframe, reports)); - if (ffmpeg_autosplit) { - if (avio_tell(outfile->pb) > FFMPEG_AUTOSPLIT_SIZE) { - end_ffmpeg_impl(true); - ffmpeg_autosplit_count++; - success &= start_ffmpeg_impl(rd, rectx, recty, reports); + if (context->ffmpeg_autosplit) { + if (avio_tell(context->outfile->pb) > FFMPEG_AUTOSPLIT_SIZE) { + end_ffmpeg_impl(context, true); + context->ffmpeg_autosplit_count++; + success &= start_ffmpeg_impl(context, rd, rectx, recty, suffix, reports); } } } #ifdef WITH_AUDASPACE - write_audio_frames((frame - start_frame) / (((double)rd->frs_sec) / (double)rd->frs_sec_base)); + write_audio_frames(context, (frame - start_frame) / (((double)rd->frs_sec) / (double)rd->frs_sec_base)); #endif return success; } -static void end_ffmpeg_impl(int is_autosplit) +static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit) { unsigned int i; PRINT("Closing ffmpeg...\n"); #if 0 - if (audio_stream) { /* SEE UPPER */ - write_audio_frames(); + if (context->audio_stream) { /* SEE UPPER */ + write_audio_frames(context); } #endif #ifdef WITH_AUDASPACE if (is_autosplit == false) { - if (audio_mixdown_device) { - AUD_closeReadDevice(audio_mixdown_device); - audio_mixdown_device = 0; + if (context->audio_mixdown_device) { + AUD_closeReadDevice(context->audio_mixdown_device); + context->audio_mixdown_device = 0; } } #endif - if (video_stream && video_stream->codec) { + if (context->video_stream && context->video_stream->codec) { PRINT("Flushing delayed frames...\n"); - flush_ffmpeg(); + flush_ffmpeg(context); } - if (outfile) { - av_write_trailer(outfile); + if (context->outfile) { + av_write_trailer(context->outfile); } /* Close the video codec */ - if (video_stream && video_stream->codec) { - avcodec_close(video_stream->codec); - PRINT("zero video stream %p\n", video_stream); - video_stream = 0; + if (context->video_stream && context->video_stream->codec) { + avcodec_close(context->video_stream->codec); + PRINT("zero video stream %p\n", context->video_stream); + context->video_stream = 0; } /* Close the output file */ - if (outfile) { - for (i = 0; i < outfile->nb_streams; i++) { - if (&outfile->streams[i]) { - av_freep(&outfile->streams[i]); + if (context->outfile) { + for (i = 0; i < context->outfile->nb_streams; i++) { + if (&context->outfile->streams[i]) { + av_freep(&context->outfile->streams[i]); } } } /* free the temp buffer */ - if (current_frame) { - delete_picture(current_frame); - current_frame = 0; + if (context->current_frame) { + delete_picture(context->current_frame); + context->current_frame = 0; } - if (outfile && outfile->oformat) { - if (!(outfile->oformat->flags & AVFMT_NOFILE)) { - avio_close(outfile->pb); + if (context->outfile && context->outfile->oformat) { + if (!(context->outfile->oformat->flags & AVFMT_NOFILE)) { + avio_close(context->outfile->pb); } } - if (outfile) { - av_free(outfile); - outfile = 0; + if (context->outfile) { + av_free(context->outfile); + context->outfile = 0; } - if (audio_input_buffer) { - av_free(audio_input_buffer); - audio_input_buffer = 0; + if (context->audio_input_buffer) { + av_free(context->audio_input_buffer); + context->audio_input_buffer = 0; } #ifndef FFMPEG_HAVE_ENCODE_AUDIO2 - if (audio_output_buffer) { - av_free(audio_output_buffer); - audio_output_buffer = 0; + if (context->audio_output_buffer) { + av_free(context->audio_output_buffer); + context->audio_output_buffer = 0; } #endif - if (audio_deinterleave_buffer) { - av_free(audio_deinterleave_buffer); - audio_deinterleave_buffer = 0; + if (context->audio_deinterleave_buffer) { + av_free(context->audio_deinterleave_buffer); + context->audio_deinterleave_buffer = 0; } - if (img_convert_ctx) { - sws_freeContext(img_convert_ctx); - img_convert_ctx = 0; + if (context->img_convert_ctx) { + sws_freeContext(context->img_convert_ctx); + context->img_convert_ctx = 0; } } -void BKE_ffmpeg_end(void) +void BKE_ffmpeg_end(void *context_v) { - end_ffmpeg_impl(false); + FFMpegContext *context = context_v; + end_ffmpeg_impl(context, false); } /* properties */ @@ -1658,4 +1675,31 @@ bool BKE_ffmpeg_alpha_channel_is_supported(RenderData *rd) return false; } -#endif +void *BKE_ffmpeg_context_create(void) +{ + FFMpegContext *context; + + /* new ffmpeg data struct */ + context = MEM_callocN(sizeof(FFMpegContext), "new ffmpeg context"); + + context->ffmpeg_codec = AV_CODEC_ID_MPEG4; + context->ffmpeg_audio_codec = AV_CODEC_ID_NONE; + context->ffmpeg_video_bitrate = 1150; + context->ffmpeg_audio_bitrate = 128; + context->ffmpeg_gop_size = 12; + context->ffmpeg_autosplit = 0; + context->ffmpeg_autosplit_count = 0; + context->ffmpeg_preview = false; + + return context; +} + +void BKE_ffmpeg_context_free(void *context_v) +{ + FFMpegContext *context = context_v; + if (context) { + MEM_freeN(context); + } +} + +#endif /* WITH_FFMPEG */ diff --git a/source/blender/blenkernel/intern/writeframeserver.c b/source/blender/blenkernel/intern/writeframeserver.c index 9cac86d62bd..ba58038bbd8 100644 --- a/source/blender/blenkernel/intern/writeframeserver.c +++ b/source/blender/blenkernel/intern/writeframeserver.c @@ -66,12 +66,15 @@ #include "BKE_report.h" #include "DNA_scene_types.h" +#include "MEM_guardedalloc.h" -static int sock; -static int connsock; -static int write_ppm; -static int render_width; -static int render_height; +typedef struct FrameserverContext { + int sock; + int connsock; + int write_ppm; + int render_width; + int render_height; +} FrameserverContext; #if defined(_WIN32) @@ -110,10 +113,11 @@ static int closesocket(int fd) } #endif -int BKE_frameserver_start(struct Scene *scene, RenderData *UNUSED(rd), int rectx, int recty, ReportList *reports, bool UNUSED(preview)) +int BKE_frameserver_start(void *context_v, struct Scene *scene, RenderData *UNUSED(rd), int rectx, int recty, ReportList *reports, bool UNUSED(preview), const char *UNUSED(suffix)) { struct sockaddr_in addr; int arg = 1; + FrameserverContext *context = context_v; (void)scene; /* unused */ @@ -122,33 +126,33 @@ int BKE_frameserver_start(struct Scene *scene, RenderData *UNUSED(rd), int rectx return 0; } - if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + if ((context->sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { shutdown_socket_system(); BKE_report(reports, RPT_ERROR, "Cannot open socket"); return 0; } - setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &arg, sizeof(arg)); + setsockopt(context->sock, SOL_SOCKET, SO_REUSEADDR, (char *) &arg, sizeof(arg)); addr.sin_family = AF_INET; addr.sin_port = htons(U.frameserverport); addr.sin_addr.s_addr = INADDR_ANY; - if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + if (bind(context->sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { shutdown_socket_system(); BKE_report(reports, RPT_ERROR, "Cannot bind to socket"); return 0; } - if (listen(sock, SOMAXCONN) < 0) { + if (listen(context->sock, SOMAXCONN) < 0) { shutdown_socket_system(); BKE_report(reports, RPT_ERROR, "Cannot establish listen backlog"); return 0; } - connsock = -1; + context->connsock = -1; - render_width = rectx; - render_height = recty; + context->render_width = rectx; + context->render_height = recty; return 1; } @@ -177,7 +181,7 @@ static char good_bye[] = "<body><pre>\n" "Render stopped. Goodbye</pre></body></html>"; -static int safe_write(char *s, int tosend) +static int safe_write(const int connsock, char *s, int tosend) { int total = tosend; do { @@ -192,12 +196,12 @@ static int safe_write(char *s, int tosend) return total; } -static int safe_puts(char *s) +static int safe_puts(const int connsock, char *s) { - return safe_write(s, strlen(s)); + return safe_write(connsock, s, strlen(s)); } -static int handle_request(RenderData *rd, char *req) +static int handle_request(FrameserverContext *context, RenderData *rd, char *req) { char *p; char *path; @@ -214,16 +218,16 @@ static int handle_request(RenderData *rd, char *req) *p = 0; - if (STREQ(path, "/index.html") || STREQ(path, "/")) { - safe_puts(index_page); + if (STREQ(path, "/index.html") || strcmp(path, "/")) { + safe_puts(context->connsock, index_page); return -1; } - write_ppm = 0; + context->write_ppm = 0; pathlen = strlen(path); if (pathlen > 12 && memcmp(path, "/images/ppm/", 12) == 0) { - write_ppm = 1; + context->write_ppm = 1; return atoi(path + 12); } if (STREQ(path, "/info.txt")) { @@ -241,24 +245,24 @@ static int handle_request(RenderData *rd, char *req) "ratescale %d\n", rd->sfra, rd->efra, - render_width, - render_height, + context->render_width, + context->render_height, rd->frs_sec, 1 ); - safe_puts(buf); + safe_puts(context->connsock, buf); return -1; } if (STREQ(path, "/close.txt")) { - safe_puts(good_bye); + safe_puts(context->connsock, good_bye); G.is_break = true; /* Abort render */ return -1; } return -1; } -int BKE_frameserver_loop(RenderData *rd, ReportList *UNUSED(reports)) +int BKE_frameserver_loop(void *context_v, RenderData *rd, ReportList *UNUSED(reports)) { fd_set readfds; struct timeval tv; @@ -271,18 +275,20 @@ int BKE_frameserver_loop(RenderData *rd, ReportList *UNUSED(reports)) #endif char buf[4096]; - if (connsock != -1) { - closesocket(connsock); - connsock = -1; + FrameserverContext *context = context_v; + + if (context->connsock != -1) { + closesocket(context->connsock); + context->connsock = -1; } tv.tv_sec = 1; tv.tv_usec = 0; FD_ZERO(&readfds); - FD_SET(sock, &readfds); + FD_SET(context->sock, &readfds); - rval = select(sock + 1, &readfds, NULL, NULL, &tv); + rval = select(context->sock + 1, &readfds, NULL, NULL, &tv); if (rval < 0) { return -1; } @@ -293,19 +299,19 @@ int BKE_frameserver_loop(RenderData *rd, ReportList *UNUSED(reports)) socklen = sizeof(addr); - if ((connsock = accept(sock, (struct sockaddr *)&addr, &socklen)) < 0) { + if ((context->connsock = accept(context->sock, (struct sockaddr *)&addr, &socklen)) < 0) { return -1; } FD_ZERO(&readfds); - FD_SET(connsock, &readfds); + FD_SET(context->connsock, &readfds); for (;;) { /* give 10 seconds for telnet testing... */ tv.tv_sec = 10; tv.tv_usec = 0; - rval = select(connsock + 1, &readfds, NULL, NULL, &tv); + rval = select(context->connsock + 1, &readfds, NULL, NULL, &tv); if (rval > 0) { break; } @@ -319,7 +325,7 @@ int BKE_frameserver_loop(RenderData *rd, ReportList *UNUSED(reports)) } } - len = recv(connsock, buf, sizeof(buf) - 1, 0); + len = recv(context->connsock, buf, sizeof(buf) - 1, 0); if (len < 0) { return -1; @@ -327,13 +333,13 @@ int BKE_frameserver_loop(RenderData *rd, ReportList *UNUSED(reports)) buf[len] = 0; - return handle_request(rd, buf); + return handle_request(context, rd, buf); } -static void serve_ppm(int *pixels, int rectx, int recty) +static void serve_ppm(FrameserverContext *context, int *pixels, int rectx, int recty) { unsigned char *rendered_frame; - unsigned char *row = (unsigned char *) malloc(render_width * 3); + unsigned char *row = (unsigned char *) malloc(context->render_width * 3); int y; char header[1024]; @@ -348,7 +354,7 @@ static void serve_ppm(int *pixels, int rectx, int recty) "255\n", rectx, recty); - safe_puts(header); + safe_puts(context->connsock, header); rendered_frame = (unsigned char *)pixels; @@ -364,36 +370,54 @@ static void serve_ppm(int *pixels, int rectx, int recty) target += 3; src += 4; } - safe_write((char *)row, 3 * rectx); + safe_write(context->connsock, (char *)row, 3 * rectx); } free(row); - closesocket(connsock); - connsock = -1; + closesocket(context->connsock); + context->connsock = -1; } -int BKE_frameserver_append(RenderData *UNUSED(rd), int UNUSED(start_frame), int frame, int *pixels, - int rectx, int recty, ReportList *UNUSED(reports)) +int BKE_frameserver_append(void *context_v, RenderData *UNUSED(rd), int UNUSED(start_frame), int frame, int *pixels, + int rectx, int recty, const char *UNUSED(suffix), ReportList *UNUSED(reports)) { + FrameserverContext *context = context_v; + fprintf(stderr, "Serving frame: %d\n", frame); - if (write_ppm) { - serve_ppm(pixels, rectx, recty); + if (context->write_ppm) { + serve_ppm(context, pixels, rectx, recty); } - if (connsock != -1) { - closesocket(connsock); - connsock = -1; + if (context->connsock != -1) { + closesocket(context->connsock); + context->connsock = -1; } return 1; } -void BKE_frameserver_end(void) +void BKE_frameserver_end(void *context_v) { - if (connsock != -1) { - closesocket(connsock); - connsock = -1; + FrameserverContext *context = context_v; + + if (context->connsock != -1) { + closesocket(context->connsock); + context->connsock = -1; } - closesocket(sock); + closesocket(context->sock); shutdown_socket_system(); } +void *BKE_frameserver_context_create(void) +{ + FrameserverContext *context = MEM_mallocN(sizeof(FrameserverContext), "Frameserver Context"); + return context; +} + +void BKE_frameserver_context_free(void *context_v) +{ + FrameserverContext *context = context_v; + if (context) { + MEM_freeN(context); + } +} + #endif /* WITH_FRAMESERVER */ diff --git a/source/blender/blenlib/BLI_threads.h b/source/blender/blenlib/BLI_threads.h index 74291ca305e..18b8feaa99c 100644 --- a/source/blender/blenlib/BLI_threads.h +++ b/source/blender/blenlib/BLI_threads.h @@ -89,6 +89,7 @@ int BLI_system_num_threads_override_get(void); #define LOCK_MOVIECLIP 7 #define LOCK_COLORMANAGE 8 #define LOCK_FFTW 9 +#define LOCK_VIEW3D 10 void BLI_lock_thread(int type); void BLI_unlock_thread(int type); diff --git a/source/blender/blenlib/intern/threads.c b/source/blender/blenlib/intern/threads.c index ded2fd7e06d..da06500565c 100644 --- a/source/blender/blenlib/intern/threads.c +++ b/source/blender/blenlib/intern/threads.c @@ -122,6 +122,7 @@ static pthread_mutex_t _nodes_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t _movieclip_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t _colormanage_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_mutex_t _fftw_lock = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t _view3d_lock = PTHREAD_MUTEX_INITIALIZER; static pthread_t mainid; static int thread_levels = 0; /* threads can be invoked inside threads */ static int num_threads_override = 0; @@ -402,6 +403,8 @@ void BLI_lock_thread(int type) pthread_mutex_lock(&_colormanage_lock); else if (type == LOCK_FFTW) pthread_mutex_lock(&_fftw_lock); + else if (type == LOCK_VIEW3D) + pthread_mutex_lock(&_view3d_lock); } void BLI_unlock_thread(int type) @@ -426,6 +429,8 @@ void BLI_unlock_thread(int type) pthread_mutex_unlock(&_colormanage_lock); else if (type == LOCK_FFTW) pthread_mutex_unlock(&_fftw_lock); + else if (type == LOCK_VIEW3D) + pthread_mutex_unlock(&_view3d_lock); } /* Mutex Locks */ diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 8c5959f20bf..4eb7413adc1 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -1533,9 +1533,15 @@ void blo_make_packed_pointer_map(FileData *fd, Main *oldmain) fd->packedmap = oldnewmap_new(); - for (ima = oldmain->image.first; ima; ima = ima->id.next) + for (ima = oldmain->image.first; ima; ima = ima->id.next) { + ImagePackedFile *imapf; if (ima->packedfile) insert_packedmap(fd, ima->packedfile); + + for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) + if (imapf->packedfile) + insert_packedmap(fd, imapf->packedfile); + } for (vfont = oldmain->vfont.first; vfont; vfont = vfont->id.next) if (vfont->packedfile) @@ -1568,8 +1574,13 @@ void blo_end_packed_pointer_map(FileData *fd, Main *oldmain) entry->newp = NULL; } - for (ima = oldmain->image.first; ima; ima = ima->id.next) + for (ima = oldmain->image.first; ima; ima = ima->id.next) { + ImagePackedFile *imapf; ima->packedfile = newpackedadr(fd, ima->packedfile); + + for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) + imapf->packedfile = newpackedadr(fd, imapf->packedfile); + } for (vfont = oldmain->vfont.first; vfont; vfont = vfont->id.next) vfont->packedfile = newpackedadr(fd, vfont->packedfile); @@ -3391,6 +3402,8 @@ static void lib_link_image(FileData *fd, Main *main) static void direct_link_image(FileData *fd, Image *ima) { + ImagePackedFile *imapf; + /* for undo system, pointers could be restored */ if (fd->imamap) ima->cache = newimaadr(fd, ima->cache); @@ -3404,8 +3417,7 @@ static void direct_link_image(FileData *fd, Image *ima) ima->gputexture = NULL; ima->rr = NULL; } - - ima->anim = NULL; + ima->repbind = NULL; /* undo system, try to restore render buffers */ @@ -3419,9 +3431,18 @@ static void direct_link_image(FileData *fd, Image *ima) memset(ima->renders, 0, sizeof(ima->renders)); ima->last_render_slot = ima->render_slot; } - + + link_list(fd, &(ima->views)); + link_list(fd, &(ima->packedfiles)); + + for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) { + imapf->packedfile = direct_link_packedfile(fd, imapf->packedfile); + } + + ima->anims.first = ima->anims.last = NULL; ima->packedfile = direct_link_packedfile(fd, ima->packedfile); ima->preview = direct_link_preview_image(fd, ima->preview); + ima->stereo3d_format = newdataadr(fd, ima->stereo3d_format); ima->ok = 1; } @@ -5378,7 +5399,7 @@ static void lib_link_scene(FileData *fd, Main *main) seq->scene_sound = BKE_sound_add_scene_sound_defaults(sce, seq); } } - seq->anim = NULL; + seq->anims.first = seq->anims.last = NULL; lib_link_sequence_modifiers(fd, sce, &seq->modifiers); } @@ -5610,6 +5631,7 @@ static void direct_link_scene(FileData *fd, Scene *sce) if (seq->seq3 == NULL) seq->seq3 = seq->seq2; seq->effectdata = newdataadr(fd, seq->effectdata); + seq->stereo3d_format = newdataadr(fd, seq->stereo3d_format); if (seq->type & SEQ_TYPE_EFFECT) seq->flag |= SEQ_EFFECT_NOT_LOADED; @@ -5722,6 +5744,7 @@ static void direct_link_scene(FileData *fd, Scene *sce) link_list(fd, &(sce->markers)); link_list(fd, &(sce->transform_spaces)); link_list(fd, &(sce->r.layers)); + link_list(fd, &(sce->r.views)); for (srl = sce->r.layers.first; srl; srl = srl->next) { link_list(fd, &(srl->freestyleConfig.modules)); @@ -5785,8 +5808,8 @@ static void direct_link_windowmanager(FileData *fd, wmWindowManager *wm) BLI_listbase_clear(&win->modalhandlers); BLI_listbase_clear(&win->subwindows); BLI_listbase_clear(&win->gesture); + BLI_listbase_clear(&win->drawdata); - win->drawdata = NULL; win->drawmethod = -1; win->drawfail = 0; win->active = 0; @@ -5794,6 +5817,7 @@ static void direct_link_windowmanager(FileData *fd, wmWindowManager *wm) win->cursor = 0; win->lastcursor = 0; win->modalcursor = 0; + win->stereo3d_format = newdataadr(fd, win->stereo3d_format); } BLI_listbase_clear(&wm->timers); @@ -7765,7 +7789,6 @@ static void do_versions_userdef(FileData *fd, BlendFileData *bfd) user->walk_navigation.jump_height = 0.4f; /* m */ user->walk_navigation.teleport_time = 0.2f; /* s */ } - } static void do_versions(FileData *fd, Library *lib, Main *main) @@ -7888,7 +7911,7 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead) link_list(fd, &user->user_keymaps); link_list(fd, &user->addons); link_list(fd, &user->autoexec_paths); - + for (keymap=user->user_keymaps.first; keymap; keymap=keymap->next) { keymap->modal_items= NULL; keymap->poll = NULL; diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c index 50cd39352a8..371ce495b07 100644 --- a/source/blender/blenloader/intern/versioning_270.c +++ b/source/blender/blenloader/intern/versioning_270.c @@ -27,6 +27,7 @@ #include "BLI_utildefines.h" #include "BLI_compiler_attrs.h" +#include "BLI_string.h" /* for MinGW32 definition of NULL, could use BLI_blenlib.h instead too */ #include <stddef.h> @@ -48,14 +49,18 @@ #include "DNA_particle_types.h" #include "DNA_linestyle_types.h" #include "DNA_actuator_types.h" +#include "DNA_camera_types.h" +#include "DNA_view3d_types.h" #include "DNA_genfile.h" #include "BKE_main.h" #include "BKE_modifier.h" #include "BKE_node.h" +#include "BKE_scene.h" #include "BKE_sequencer.h" #include "BKE_screen.h" +#include "BKE_sequencer.h" #include "BLI_math.h" #include "BLI_listbase.h" @@ -460,10 +465,10 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) br->mtex.random_angle = 2.0 * M_PI; br->mask_mtex.random_angle = 2.0 * M_PI; } + } #undef BRUSH_RAKE #undef BRUSH_RANDOM_ROTATION - } /* Customizable Safe Areas */ if (!MAIN_VERSION_ATLEAST(main, 273, 2)) { @@ -724,4 +729,85 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main) #undef SEQ_USE_PROXY_CUSTOM_DIR #undef SEQ_USE_PROXY_CUSTOM_FILE } + + if (!MAIN_VERSION_ATLEAST(main, 274, 3)) + { + SceneRenderView *srv; + wmWindowManager *wm; + bScreen *screen; + wmWindow *win; + Scene *scene; + Camera *cam; + Image *ima; + + for (scene = main->scene.first; scene; scene = scene->id.next) { + Sequence *seq; + + BKE_scene_add_render_view(scene, STEREO_LEFT_NAME); + srv = scene->r.views.first; + BLI_strncpy(srv->suffix, STEREO_LEFT_SUFFIX, sizeof(srv->suffix)); + + BKE_scene_add_render_view(scene, STEREO_RIGHT_NAME); + srv = scene->r.views.last; + BLI_strncpy(srv->suffix, STEREO_RIGHT_SUFFIX, sizeof(srv->suffix)); + + SEQ_BEGIN (scene->ed, seq) + { + seq->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Stereo Display 3d Format"); + } + SEQ_END + } + + for (screen = main->screen.first; screen; screen = screen->id.next) { + ScrArea *sa; + for (sa = screen->areabase.first; sa; sa = sa->next) { + SpaceLink *sl; + + for (sl = sa->spacedata.first; sl; sl = sl->next) { + switch (sl->spacetype) { + case SPACE_VIEW3D: + { + View3D *v3d = (View3D *)sl; + v3d->stereo3d_camera = STEREO_3D_ID; + v3d->stereo3d_flag |= V3D_S3D_DISPPLANE; + v3d->stereo3d_convergence_alpha = 0.15f; + v3d->stereo3d_volume_alpha = 0.05f; + break; + } + case SPACE_IMAGE: + { + SpaceImage *sima = (SpaceImage *) sl; + sima->iuser.flag |= IMA_SHOW_STEREO; + sima->iuser.passtype = SCE_PASS_COMBINED; + break; + } + } + } + } + } + + for (cam = main->camera.first; cam; cam = cam->id.next) { + cam->stereo.interocular_distance = 0.065; + cam->stereo.convergence_distance = 30.f * 0.065; + } + + for (ima = main->image.first; ima; ima = ima->id.next) { + ima->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Image Stereo 3d Format"); + + if (ima->packedfile) { + ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image Packed File"); + BLI_addtail(&ima->packedfiles, imapf); + + imapf->packedfile = ima->packedfile; + BLI_strncpy(imapf->filepath, ima->name, FILE_MAX); + ima->packedfile = NULL; + } + } + + for (wm = main->wm.first; wm; wm = wm->id.next) { + for (win = wm->windows.first; win; win = win->next) { + win->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Stereo Display 3d Format"); + } + } + } } diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c index c616436d584..2580ba64622 100644 --- a/source/blender/blenloader/intern/writefile.c +++ b/source/blender/blenloader/intern/writefile.c @@ -2124,7 +2124,8 @@ static void write_images(WriteData *wd, ListBase *idbase) { Image *ima; PackedFile * pf; - + ImageView *iv; + ImagePackedFile *imapf; ima= idbase->first; while (ima) { @@ -2133,13 +2134,20 @@ static void write_images(WriteData *wd, ListBase *idbase) writestruct(wd, ID_IM, "Image", 1, ima); if (ima->id.properties) IDP_WriteProperty(ima->id.properties, wd); - if (ima->packedfile) { - pf = ima->packedfile; - writestruct(wd, DATA, "PackedFile", 1, pf); - writedata(wd, DATA, pf->size, pf->data); + for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) { + writestruct(wd, DATA, "ImagePackedFile", 1, imapf); + if (imapf->packedfile) { + pf = imapf->packedfile; + writestruct(wd, DATA, "PackedFile", 1, pf); + writedata(wd, DATA, pf->size, pf->data); + } } write_previews(wd, ima->preview); + + for (iv = ima->views.first; iv; iv = iv->next) + writestruct(wd, DATA, "ImageView", 1, iv); + writestruct(wd, DATA, "Stereo3dFormat", 1, ima->stereo3d_format); } ima= ima->id.next; } @@ -2340,6 +2348,7 @@ static void write_scenes(WriteData *wd, ListBase *scebase) TimeMarker *marker; TransformOrientation *ts; SceneRenderLayer *srl; + SceneRenderView *srv; ToolSettings *tos; FreestyleModuleConfig *fmc; FreestyleLineSet *fls; @@ -2421,7 +2430,9 @@ static void write_scenes(WriteData *wd, ListBase *scebase) break; } } - + + writestruct(wd, DATA, "Stereo3dFormat", 1, seq->stereo3d_format); + strip= seq->strip; writestruct(wd, DATA, "Strip", 1, strip); if (seq->flag & SEQ_USE_CROP && strip->crop) { @@ -2486,6 +2497,10 @@ static void write_scenes(WriteData *wd, ListBase *scebase) writestruct(wd, DATA, "FreestyleLineSet", 1, fls); } } + + /* writing MultiView to the blend file */ + for (srv = sce->r.views.first; srv; srv = srv->next) + writestruct(wd, DATA, "SceneRenderView", 1, srv); if (sce->nodetree) { writestruct(wd, DATA, "bNodeTree", 1, sce->nodetree); @@ -2548,8 +2563,10 @@ static void write_windowmanagers(WriteData *wd, ListBase *lb) for (wm= lb->first; wm; wm= wm->id.next) { writestruct(wd, ID_WM, "wmWindowManager", 1, wm); - for (win= wm->windows.first; win; win= win->next) + for (win= wm->windows.first; win; win= win->next) { writestruct(wd, DATA, "wmWindow", 1, win); + writestruct(wd, DATA, "Stereo3dFormat", 1, win->stereo3d_format); + } } } diff --git a/source/blender/collada/ImageExporter.cpp b/source/blender/collada/ImageExporter.cpp index a5c1493208b..aac41e2e93c 100644 --- a/source/blender/collada/ImageExporter.cpp +++ b/source/blender/collada/ImageExporter.cpp @@ -74,7 +74,7 @@ void ImagesExporter::export_UV_Image(Image *image, bool use_copies) short image_source = image->source; bool is_generated = image_source == IMA_SRC_GENERATED; - bool is_packed = image->packedfile != NULL; + bool is_packed = BKE_image_has_packedfile(image); char export_path[FILE_MAX]; char source_path[FILE_MAX]; diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt index 048c974423f..c23aa4ec734 100644 --- a/source/blender/compositor/CMakeLists.txt +++ b/source/blender/compositor/CMakeLists.txt @@ -119,6 +119,8 @@ set(SRC nodes/COM_TimeNode.h nodes/COM_SwitchNode.cpp nodes/COM_SwitchNode.h + nodes/COM_SwitchViewNode.cpp + nodes/COM_SwitchViewNode.h nodes/COM_MovieClipNode.cpp nodes/COM_MovieClipNode.h nodes/COM_OutputFileNode.cpp @@ -370,6 +372,8 @@ set(SRC operations/COM_CompositorOperation.cpp operations/COM_OutputFileOperation.h operations/COM_OutputFileOperation.cpp + operations/COM_OutputFileMultiViewOperation.h + operations/COM_OutputFileMultiViewOperation.cpp operations/COM_ViewerOperation.h operations/COM_ViewerOperation.cpp operations/COM_PreviewOperation.h diff --git a/source/blender/compositor/COM_compositor.h b/source/blender/compositor/COM_compositor.h index 9b22444cf7f..8b0a617f889 100644 --- a/source/blender/compositor/COM_compositor.h +++ b/source/blender/compositor/COM_compositor.h @@ -316,7 +316,8 @@ extern "C" { * generation in display space */ void COM_execute(RenderData *rd, Scene *scene, bNodeTree *editingtree, int rendering, - const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings); + const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings, + const char *viewName); /** * @brief Deinitialize the compositor caches and allocated memory. diff --git a/source/blender/compositor/intern/COM_CompositorContext.h b/source/blender/compositor/intern/COM_CompositorContext.h index 3fbafa0a029..d7ec653f480 100644 --- a/source/blender/compositor/intern/COM_CompositorContext.h +++ b/source/blender/compositor/intern/COM_CompositorContext.h @@ -87,6 +87,11 @@ private: const ColorManagedViewSettings *m_viewSettings; const ColorManagedDisplaySettings *m_displaySettings; + /** + * @brief active rendering view name + */ + const char *m_viewName; + public: /** * @brief constructor initializes the context with default values. @@ -180,7 +185,17 @@ public: * @brief set has this system active openclDevices? */ void setHasActiveOpenCLDevices(bool hasAvtiveOpenCLDevices) { this->m_hasActiveOpenCLDevices = hasAvtiveOpenCLDevices; } - + + /** + * @brief get the active rendering view + */ + const char *getViewName() const { return this->m_viewName; } + + /** + * @brief set the active rendering view + */ + void setViewName(const char *viewName) { this->m_viewName = viewName; } + int getChunksize() const { return this->getbNodeTree()->chunksize; } void setFastCalculation(bool fastCalculation) {this->m_fastCalculation = fastCalculation;} diff --git a/source/blender/compositor/intern/COM_Converter.cpp b/source/blender/compositor/intern/COM_Converter.cpp index 99f66bcb5b4..9de22612bdc 100644 --- a/source/blender/compositor/intern/COM_Converter.cpp +++ b/source/blender/compositor/intern/COM_Converter.cpp @@ -101,6 +101,7 @@ extern "C" { #include "COM_Stabilize2dNode.h" #include "COM_SunBeamsNode.h" #include "COM_SwitchNode.h" +#include "COM_SwitchViewNode.h" #include "COM_TextureNode.h" #include "COM_TimeNode.h" #include "COM_TonemapNode.h" @@ -328,6 +329,9 @@ Node *Converter::convert(bNode *b_node) case CMP_NODE_SWITCH: node = new SwitchNode(b_node); break; + case CMP_NODE_SWITCH_VIEW: + node = new SwitchViewNode(b_node); + break; case CMP_NODE_GLARE: node = new GlareNode(b_node); break; diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.cpp b/source/blender/compositor/intern/COM_ExecutionSystem.cpp index 2ed9e6187ba..caeaa07d9f9 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.cpp +++ b/source/blender/compositor/intern/COM_ExecutionSystem.cpp @@ -43,8 +43,10 @@ extern "C" { #endif ExecutionSystem::ExecutionSystem(RenderData *rd, Scene *scene, bNodeTree *editingtree, bool rendering, bool fastcalculation, - const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings) + const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings, + const char *viewName) { + this->m_context.setViewName(viewName); this->m_context.setScene(scene); this->m_context.setbNodeTree(editingtree); this->m_context.setPreviewHash(editingtree->previews); diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.h b/source/blender/compositor/intern/COM_ExecutionSystem.h index 555964f7b0c..41d63fb3a72 100644 --- a/source/blender/compositor/intern/COM_ExecutionSystem.h +++ b/source/blender/compositor/intern/COM_ExecutionSystem.h @@ -151,7 +151,8 @@ public: * @param rendering [true false] */ ExecutionSystem(RenderData *rd, Scene *scene, bNodeTree *editingtree, bool rendering, bool fastcalculation, - const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings); + const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings, + const char *viewName); /** * Destructor diff --git a/source/blender/compositor/intern/COM_compositor.cpp b/source/blender/compositor/intern/COM_compositor.cpp index ff601e6f58e..7f7fc141aca 100644 --- a/source/blender/compositor/intern/COM_compositor.cpp +++ b/source/blender/compositor/intern/COM_compositor.cpp @@ -45,7 +45,8 @@ static void intern_freeCompositorCaches() void COM_execute(RenderData *rd, Scene *scene, bNodeTree *editingtree, int rendering, const ColorManagedViewSettings *viewSettings, - const ColorManagedDisplaySettings *displaySettings) + const ColorManagedDisplaySettings *displaySettings, + const char *viewName) { /* initialize mutex, TODO this mutex init is actually not thread safe and * should be done somewhere as part of blender startup, all the other @@ -82,7 +83,7 @@ void COM_execute(RenderData *rd, Scene *scene, bNodeTree *editingtree, int rende bool twopass = (editingtree->flag & NTREE_TWO_PASS) > 0 && !rendering; /* initialize execution system */ if (twopass) { - ExecutionSystem *system = new ExecutionSystem(rd, scene, editingtree, rendering, twopass, viewSettings, displaySettings); + ExecutionSystem *system = new ExecutionSystem(rd, scene, editingtree, rendering, twopass, viewSettings, displaySettings, viewName); system->execute(); delete system; @@ -95,7 +96,7 @@ void COM_execute(RenderData *rd, Scene *scene, bNodeTree *editingtree, int rende } ExecutionSystem *system = new ExecutionSystem(rd, scene, editingtree, rendering, false, - viewSettings, displaySettings); + viewSettings, displaySettings, viewName); system->execute(); delete system; diff --git a/source/blender/compositor/nodes/COM_CompositorNode.cpp b/source/blender/compositor/nodes/COM_CompositorNode.cpp index 933b8d0282b..9e8b40d8af4 100644 --- a/source/blender/compositor/nodes/COM_CompositorNode.cpp +++ b/source/blender/compositor/nodes/COM_CompositorNode.cpp @@ -43,6 +43,7 @@ void CompositorNode::convertToOperations(NodeConverter &converter, const Composi CompositorOperation *compositorOperation = new CompositorOperation(); compositorOperation->setSceneName(context.getScene()->id.name); compositorOperation->setRenderData(context.getRenderData()); + compositorOperation->setViewName(context.getViewName()); compositorOperation->setbNodeTree(context.getbNodeTree()); /* alpha socket gives either 1 or a custom alpha value if "use alpha" is enabled */ compositorOperation->setUseAlphaInput(ignore_alpha || alphaSocket->isLinked()); diff --git a/source/blender/compositor/nodes/COM_ImageNode.cpp b/source/blender/compositor/nodes/COM_ImageNode.cpp index cb7ccfaedf9..93ab6dd765a 100644 --- a/source/blender/compositor/nodes/COM_ImageNode.cpp +++ b/source/blender/compositor/nodes/COM_ImageNode.cpp @@ -40,19 +40,19 @@ ImageNode::ImageNode(bNode *editorNode) : Node(editorNode) } NodeOperation *ImageNode::doMultilayerCheck(NodeConverter &converter, RenderLayer *rl, Image *image, ImageUser *user, - int framenumber, int outputsocketIndex, int passindex, DataType datatype) const + int framenumber, int outputsocketIndex, int passtype, int view, DataType datatype) const { NodeOutput *outputSocket = this->getOutputSocket(outputsocketIndex); MultilayerBaseOperation *operation = NULL; switch (datatype) { case COM_DT_VALUE: - operation = new MultilayerValueOperation(passindex); + operation = new MultilayerValueOperation(passtype, view); break; case COM_DT_VECTOR: - operation = new MultilayerVectorOperation(passindex); + operation = new MultilayerVectorOperation(passtype, view); break; case COM_DT_COLOR: - operation = new MultilayerColorOperation(passindex); + operation = new MultilayerColorOperation(passtype, view); break; default: break; @@ -96,6 +96,10 @@ void ImageNode::convertToOperations(NodeConverter &converter, const CompositorCo NodeOperation *operation = NULL; socket = this->getOutputSocket(index); bNodeSocket *bnodeSocket = socket->getbNodeSocket(); + RenderPass *rpass = (RenderPass *)BLI_findstring(&rl->passes, bnodeSocket->identifier, offsetof(RenderPass, internal_name)); + + int view = (rpass ? rpass->view_id : 0); + /* Passes in the file can differ from passes stored in sockets (#36755). * Look up the correct file pass using the socket identifier instead. */ @@ -104,8 +108,22 @@ void ImageNode::convertToOperations(NodeConverter &converter, const CompositorCo int passindex = storage->pass_index;*/ RenderPass *rpass = (RenderPass *)BLI_findlink(&rl->passes, passindex); #endif - int passindex; - RenderPass *rpass; + + /* returns the image view to use for the current active view */ + if (BLI_listbase_count_ex(&image->rr->views, 2) > 1) { + const int view_image = imageuser->view; + const bool is_allview = (view_image == 0); /* if view selected == All (0) */ + + if (is_allview) { + /* heuristic to match image name with scene names + * check if the view name exists in the image */ + view = BLI_findstringindex(&image->rr->views, context.getViewName(), offsetof(RenderView, name)); + } + else { + view = view_image - 1; + } + } + if (STREQ(bnodeSocket->identifier, "Alpha")) { BLI_assert(combined_operation != NULL); NodeOutput *outputSocket = this->getOutputSocket(index); @@ -118,22 +136,21 @@ void ImageNode::convertToOperations(NodeConverter &converter, const CompositorCo operation = separate_operation; } else { - for (rpass = (RenderPass *)rl->passes.first, passindex = 0; rpass; rpass = rpass->next, ++passindex) - if (STREQ(rpass->name, bnodeSocket->identifier)) - break; if (rpass) { - imageuser->pass = passindex; switch (rpass->channels) { case 1: - operation = doMultilayerCheck(converter, rl, image, imageuser, framenumber, index, passindex, COM_DT_VALUE); + operation = doMultilayerCheck(converter, rl, image, imageuser, framenumber, index, + rpass->passtype, view, COM_DT_VALUE); break; /* using image operations for both 3 and 4 channels (RGB and RGBA respectively) */ /* XXX any way to detect actual vector images? */ case 3: - operation = doMultilayerCheck(converter, rl, image, imageuser, framenumber, index, passindex, COM_DT_VECTOR); + operation = doMultilayerCheck(converter, rl, image, imageuser, framenumber, index, + rpass->passtype, view, COM_DT_VECTOR); break; case 4: - operation = doMultilayerCheck(converter, rl, image, imageuser, framenumber, index, passindex, COM_DT_COLOR); + operation = doMultilayerCheck(converter, rl, image, imageuser, framenumber, index, + rpass->passtype, view, COM_DT_COLOR); break; default: /* dummy operation is added below */ @@ -168,6 +185,8 @@ void ImageNode::convertToOperations(NodeConverter &converter, const CompositorCo operation->setImage(image); operation->setImageUser(imageuser); operation->setFramenumber(framenumber); + operation->setRenderData(context.getRenderData()); + operation->setViewName(context.getViewName()); converter.addOperation(operation); if (outputStraightAlpha) { @@ -190,6 +209,8 @@ void ImageNode::convertToOperations(NodeConverter &converter, const CompositorCo alphaOperation->setImage(image); alphaOperation->setImageUser(imageuser); alphaOperation->setFramenumber(framenumber); + alphaOperation->setRenderData(context.getRenderData()); + alphaOperation->setViewName(context.getViewName()); converter.addOperation(alphaOperation); converter.mapOutputSocket(alphaImage, alphaOperation->getOutputSocket()); @@ -200,6 +221,8 @@ void ImageNode::convertToOperations(NodeConverter &converter, const CompositorCo depthOperation->setImage(image); depthOperation->setImageUser(imageuser); depthOperation->setFramenumber(framenumber); + depthOperation->setRenderData(context.getRenderData()); + depthOperation->setViewName(context.getViewName()); converter.addOperation(depthOperation); converter.mapOutputSocket(depthImage, depthOperation->getOutputSocket()); @@ -239,6 +262,7 @@ void ImageNode::convertToOperations(NodeConverter &converter, const CompositorCo } if (operation) { + /* not supporting multiview for this generic case */ converter.addOperation(operation); converter.mapOutputSocket(output, operation->getOutputSocket()); } diff --git a/source/blender/compositor/nodes/COM_ImageNode.h b/source/blender/compositor/nodes/COM_ImageNode.h index 1daa39a2a1f..267794510e1 100644 --- a/source/blender/compositor/nodes/COM_ImageNode.h +++ b/source/blender/compositor/nodes/COM_ImageNode.h @@ -36,7 +36,7 @@ extern "C" { class ImageNode : public Node { private: NodeOperation *doMultilayerCheck(NodeConverter &converter, RenderLayer *rl, Image *image, ImageUser *user, - int framenumber, int outputsocketIndex, int passindex, DataType datatype) const; + int framenumber, int outputsocketIndex, int passtype, int view, DataType datatype) const; public: ImageNode(bNode *editorNode); void convertToOperations(NodeConverter &converter, const CompositorContext &context) const; diff --git a/source/blender/compositor/nodes/COM_OutputFileNode.cpp b/source/blender/compositor/nodes/COM_OutputFileNode.cpp index 92fa74b9a2e..acd2602e216 100644 --- a/source/blender/compositor/nodes/COM_OutputFileNode.cpp +++ b/source/blender/compositor/nodes/COM_OutputFileNode.cpp @@ -23,8 +23,11 @@ #include "COM_OutputFileNode.h" #include "COM_OutputFileOperation.h" +#include "COM_OutputFileMultiViewOperation.h" #include "COM_ExecutionSystem.h" +#include "BKE_scene.h" + #include "BLI_path_util.h" OutputFileNode::OutputFileNode(bNode *editorNode) : Node(editorNode) @@ -35,6 +38,7 @@ OutputFileNode::OutputFileNode(bNode *editorNode) : Node(editorNode) void OutputFileNode::convertToOperations(NodeConverter &converter, const CompositorContext &context) const { NodeImageMultiFile *storage = (NodeImageMultiFile *)this->getbNode()->storage; + const bool is_multiview = (context.getRenderData()->scemode & R_MULTIVIEW) != 0; if (!context.isRendering()) { /* only output files when rendering a sequence - @@ -46,10 +50,18 @@ void OutputFileNode::convertToOperations(NodeConverter &converter, const Composi if (storage->format.imtype == R_IMF_IMTYPE_MULTILAYER) { /* single output operation for the multilayer file */ - OutputOpenExrMultiLayerOperation *outputOperation = new OutputOpenExrMultiLayerOperation( - context.getRenderData(), context.getbNodeTree(), storage->base_path, storage->format.exr_codec); + OutputOpenExrMultiLayerOperation *outputOperation; + + if (is_multiview && storage->format.views_format == R_IMF_VIEWS_MULTIVIEW) { + outputOperation = new OutputOpenExrMultiLayerMultiViewOperation( + context.getRenderData(), context.getbNodeTree(), storage->base_path, storage->format.exr_codec, context.getViewName()); + } + else { + outputOperation = new OutputOpenExrMultiLayerOperation( + context.getRenderData(), context.getbNodeTree(), storage->base_path, storage->format.exr_codec, context.getViewName()); + } converter.addOperation(outputOperation); - + int num_inputs = getNumberOfInputSockets(); bool previewAdded = false; for (int i = 0; i < num_inputs; ++i) { @@ -76,17 +88,31 @@ void OutputFileNode::convertToOperations(NodeConverter &converter, const Composi NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)input->getbNodeSocket()->storage; ImageFormatData *format = (sockdata->use_node_format ? &storage->format : &sockdata->format); char path[FILE_MAX]; - + /* combine file path for the input */ BLI_join_dirfile(path, FILE_MAX, storage->base_path, sockdata->path); - - OutputSingleLayerOperation *outputOperation = new OutputSingleLayerOperation( - context.getRenderData(), context.getbNodeTree(), input->getDataType(), format, path, - context.getViewSettings(), context.getDisplaySettings()); + + NodeOperation *outputOperation = NULL; + + if (is_multiview && format->views_format == R_IMF_VIEWS_MULTIVIEW) { + outputOperation = new OutputOpenExrSingleLayerMultiViewOperation( + context.getRenderData(), context.getbNodeTree(), input->getDataType(), format, path, + context.getViewSettings(), context.getDisplaySettings(), context.getViewName()); + } + else if ((!is_multiview) || (format->views_format == R_IMF_VIEWS_INDIVIDUAL)) { + outputOperation = new OutputSingleLayerOperation( + context.getRenderData(), context.getbNodeTree(), input->getDataType(), format, path, + context.getViewSettings(), context.getDisplaySettings(), context.getViewName()); + } + else { /* R_IMF_VIEWS_STEREO_3D */ + outputOperation = new OutputStereoOperation( + context.getRenderData(), context.getbNodeTree(), input->getDataType(), format, path, + sockdata->layer, context.getViewSettings(), context.getDisplaySettings(), context.getViewName()); + } + converter.addOperation(outputOperation); - converter.mapInputSocket(input, outputOperation->getInputSocket(0)); - + if (!previewAdded) { converter.addNodeInputPreview(input); previewAdded = true; diff --git a/source/blender/compositor/nodes/COM_RenderLayersNode.cpp b/source/blender/compositor/nodes/COM_RenderLayersNode.cpp index cc66c688379..02bf1ec8cfb 100644 --- a/source/blender/compositor/nodes/COM_RenderLayersNode.cpp +++ b/source/blender/compositor/nodes/COM_RenderLayersNode.cpp @@ -42,7 +42,8 @@ void RenderLayersNode::testSocketLink(NodeConverter &converter, const Compositor operation->setScene(scene); operation->setLayerId(layerId); operation->setRenderData(context.getRenderData()); - + operation->setViewName(context.getViewName()); + converter.mapOutputSocket(outputSocket, operation->getOutputSocket()); converter.addOperation(operation); diff --git a/source/blender/compositor/nodes/COM_SplitViewerNode.cpp b/source/blender/compositor/nodes/COM_SplitViewerNode.cpp index 15eca0a97e5..0f917d317f9 100644 --- a/source/blender/compositor/nodes/COM_SplitViewerNode.cpp +++ b/source/blender/compositor/nodes/COM_SplitViewerNode.cpp @@ -22,6 +22,8 @@ #include "COM_SplitViewerNode.h" #include "BKE_global.h" +#include "BKE_image.h" +#include "BKE_scene.h" #include "COM_SplitOperation.h" #include "COM_ViewerOperation.h" @@ -55,6 +57,8 @@ void SplitViewerNode::convertToOperations(NodeConverter &converter, const Compos viewerOperation->setImageUser(imageUser); viewerOperation->setViewSettings(context.getViewSettings()); viewerOperation->setDisplaySettings(context.getDisplaySettings()); + viewerOperation->setRenderData(context.getRenderData()); + viewerOperation->setViewName(context.getViewName()); /* defaults - the viewer node has these options but not exposed for split view * we could use the split to define an area of interest on one axis at least */ diff --git a/source/blender/compositor/nodes/COM_SwitchViewNode.cpp b/source/blender/compositor/nodes/COM_SwitchViewNode.cpp new file mode 100644 index 00000000000..5a23b8b4d9e --- /dev/null +++ b/source/blender/compositor/nodes/COM_SwitchViewNode.cpp @@ -0,0 +1,42 @@ +/* + * Copyright 2015, Blender Foundation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor: + * Dalai Felinto + */ + +#include "COM_SwitchViewNode.h" +#include "BLI_listbase.h" + +SwitchViewNode::SwitchViewNode(bNode *editorNode) : Node(editorNode) +{ + /* pass */ +} + +void SwitchViewNode::convertToOperations(NodeConverter &converter, const CompositorContext &context) const +{ + NodeOperationOutput *result; + const char *viewName = context.getViewName(); + bNode *bnode = this->getbNode(); + + /* get the internal index of the socket with a matching name */ + int nr = BLI_findstringindex(&bnode->inputs, viewName, offsetof(bNodeSocket, name)); + nr = max(nr, 0); + + result = converter.addInputProxy(getInputSocket(nr), false); + converter.mapOutputSocket(getOutputSocket(0), result); +} diff --git a/source/blender/compositor/nodes/COM_SwitchViewNode.h b/source/blender/compositor/nodes/COM_SwitchViewNode.h new file mode 100644 index 00000000000..6ab5145bed5 --- /dev/null +++ b/source/blender/compositor/nodes/COM_SwitchViewNode.h @@ -0,0 +1,37 @@ +/* + * Copyright 2015, Blender Foundation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor: + * Dalai Felinto + */ + +#ifndef _COM_SwitchViewNode_h_ +#define _COM_SwitchViewNode_h_ + +#include "COM_Node.h" +#include "COM_NodeOperation.h" +#include "DNA_node_types.h" +/** + * @brief SwitchViewNode + * @ingroup Node + */ +class SwitchViewNode : public Node { +public: + SwitchViewNode(bNode *editorNode); + void convertToOperations(NodeConverter &converter, const CompositorContext &context) const; +}; +#endif diff --git a/source/blender/compositor/nodes/COM_ViewerNode.cpp b/source/blender/compositor/nodes/COM_ViewerNode.cpp index ff0b8fb1706..ab819ce6e30 100644 --- a/source/blender/compositor/nodes/COM_ViewerNode.cpp +++ b/source/blender/compositor/nodes/COM_ViewerNode.cpp @@ -22,6 +22,9 @@ #include "COM_ViewerNode.h" #include "BKE_global.h" +#include "BKE_image.h" +#include "BLI_listbase.h" +#include "BKE_scene.h" #include "COM_ViewerOperation.h" #include "COM_ExecutionSystem.h" @@ -51,6 +54,8 @@ void ViewerNode::convertToOperations(NodeConverter &converter, const CompositorC viewerOperation->setCenterY(editorNode->custom4); /* alpha socket gives either 1 or a custom alpha value if "use alpha" is enabled */ viewerOperation->setUseAlphaInput(ignore_alpha || alphaSocket->isLinked()); + viewerOperation->setRenderData(context.getRenderData()); + viewerOperation->setViewName(context.getViewName()); viewerOperation->setViewSettings(context.getViewSettings()); viewerOperation->setDisplaySettings(context.getDisplaySettings()); diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cpp b/source/blender/compositor/operations/COM_CompositorOperation.cpp index ffef81d0323..eacbdf91be2 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.cpp +++ b/source/blender/compositor/operations/COM_CompositorOperation.cpp @@ -52,6 +52,7 @@ CompositorOperation::CompositorOperation() : NodeOperation() this->m_active = false; this->m_sceneName[0] = '\0'; + this->m_viewName = NULL; } void CompositorOperation::initExecution() @@ -81,14 +82,16 @@ void CompositorOperation::deinitExecution() RenderResult *rr = RE_AcquireResultWrite(re); if (rr) { - if (rr->rectf != NULL) { - MEM_freeN(rr->rectf); + RenderView *rv = (RenderView *)BLI_findstring(&rr->views, this->m_viewName, offsetof(RenderView, name)); + + if (rv->rectf != NULL) { + MEM_freeN(rv->rectf); } - rr->rectf = this->m_outputBuffer; - if (rr->rectz != NULL) { - MEM_freeN(rr->rectz); + rv->rectf = this->m_outputBuffer; + if (rv->rectz != NULL) { + MEM_freeN(rv->rectz); } - rr->rectz = this->m_depthBuffer; + rv->rectz = this->m_depthBuffer; } else { if (this->m_outputBuffer) { diff --git a/source/blender/compositor/operations/COM_CompositorOperation.h b/source/blender/compositor/operations/COM_CompositorOperation.h index 447d74131b3..e81ba520695 100644 --- a/source/blender/compositor/operations/COM_CompositorOperation.h +++ b/source/blender/compositor/operations/COM_CompositorOperation.h @@ -75,11 +75,17 @@ private: * @brief operation is active for calculating final compo result */ bool m_active; + + /** + * @brief View name, used for multiview + */ + const char *m_viewName; public: CompositorOperation(); const bool isActiveCompositorOutput() const { return this->m_active; } void executeRegion(rcti *rect, unsigned int tileNumber); void setSceneName(const char *sceneName) { BLI_strncpy(this->m_sceneName, sceneName, sizeof(this->m_sceneName)); } + void setViewName(const char *viewName) { this->m_viewName = viewName; } void setRenderData(const RenderData *rd) { this->m_rd = rd; } bool isOutputOperation(bool /*rendering*/) const { return this->isActiveCompositorOutput(); } void initExecution(); diff --git a/source/blender/compositor/operations/COM_ImageOperation.cpp b/source/blender/compositor/operations/COM_ImageOperation.cpp index 624fdf8b626..c140b7afbd7 100644 --- a/source/blender/compositor/operations/COM_ImageOperation.cpp +++ b/source/blender/compositor/operations/COM_ImageOperation.cpp @@ -25,6 +25,7 @@ #include "BLI_listbase.h" #include "DNA_image_types.h" #include "BKE_image.h" +#include "BKE_scene.h" #include "BLI_math.h" extern "C" { @@ -48,6 +49,8 @@ BaseImageOperation::BaseImageOperation() : NodeOperation() this->m_framenumber = 0; this->m_depthBuffer = NULL; this->m_numberOfChannels = 0; + this->m_rd = NULL; + this->m_viewName = NULL; } ImageOperation::ImageOperation() : BaseImageOperation() { @@ -65,8 +68,12 @@ ImageDepthOperation::ImageDepthOperation() : BaseImageOperation() ImBuf *BaseImageOperation::getImBuf() { ImBuf *ibuf; - - ibuf = BKE_image_acquire_ibuf(this->m_image, this->m_imageUser, NULL); + ImageUser iuser = *this->m_imageUser; + + /* local changes to the original ImageUser */ + iuser.multi_index = BKE_scene_multiview_view_id_get(this->m_rd, this->m_viewName); + + ibuf = BKE_image_acquire_ibuf(this->m_image, &iuser, NULL); if (ibuf == NULL || (ibuf->rect == NULL && ibuf->rect_float == NULL)) { BKE_image_release_ibuf(this->m_image, ibuf, NULL); return NULL; diff --git a/source/blender/compositor/operations/COM_ImageOperation.h b/source/blender/compositor/operations/COM_ImageOperation.h index 206f1509f60..75222559810 100644 --- a/source/blender/compositor/operations/COM_ImageOperation.h +++ b/source/blender/compositor/operations/COM_ImageOperation.h @@ -49,7 +49,9 @@ protected: int m_imagewidth; int m_framenumber; int m_numberOfChannels; - + const RenderData *m_rd; + const char *m_viewName; + BaseImageOperation(); /** * Determine the output resolution. The resolution is retrieved from the Renderer @@ -64,7 +66,8 @@ public: void deinitExecution(); void setImage(Image *image) { this->m_image = image; } void setImageUser(ImageUser *imageuser) { this->m_imageUser = imageuser; } - + void setRenderData(const RenderData *rd) { this->m_rd = rd; } + void setViewName(const char *viewName) { this->m_viewName = viewName; } void setFramenumber(int framenumber) { this->m_framenumber = framenumber; } }; class ImageOperation : public BaseImageOperation { diff --git a/source/blender/compositor/operations/COM_MultilayerImageOperation.cpp b/source/blender/compositor/operations/COM_MultilayerImageOperation.cpp index 513140e2f62..00be3b1cdde 100644 --- a/source/blender/compositor/operations/COM_MultilayerImageOperation.cpp +++ b/source/blender/compositor/operations/COM_MultilayerImageOperation.cpp @@ -27,18 +27,27 @@ extern "C" { # include "IMB_imbuf_types.h" } -MultilayerBaseOperation::MultilayerBaseOperation(int passindex) : BaseImageOperation() +MultilayerBaseOperation::MultilayerBaseOperation(int passtype, int view) : BaseImageOperation() { - this->m_passId = passindex; + this->m_passtype = passtype; + this->m_view = view; } + ImBuf *MultilayerBaseOperation::getImBuf() { - RenderPass *rpass = (RenderPass *)BLI_findlink(&this->m_renderlayer->passes, this->m_passId); - if (rpass) { - this->m_imageUser->pass = m_passId; - BKE_image_multilayer_index(this->m_image->rr, this->m_imageUser); - return BaseImageOperation::getImBuf(); + /* temporarily changes the view to get the right ImBuf */ + int view = this->m_imageUser->view; + + this->m_imageUser->view = this->m_view; + this->m_imageUser->passtype = this->m_passtype; + + if (BKE_image_multilayer_index(this->m_image->rr, this->m_imageUser)) { + ImBuf *ibuf = BaseImageOperation::getImBuf(); + this->m_imageUser->view = view; + return ibuf; } + + this->m_imageUser->view = view; return NULL; } diff --git a/source/blender/compositor/operations/COM_MultilayerImageOperation.h b/source/blender/compositor/operations/COM_MultilayerImageOperation.h index 37bee1b6a8c..2e140577d74 100644 --- a/source/blender/compositor/operations/COM_MultilayerImageOperation.h +++ b/source/blender/compositor/operations/COM_MultilayerImageOperation.h @@ -29,7 +29,8 @@ class MultilayerBaseOperation : public BaseImageOperation { private: - int m_passId; + int m_passtype; + int m_view; RenderLayer *m_renderlayer; protected: ImBuf *getImBuf(); @@ -37,13 +38,13 @@ public: /** * Constructor */ - MultilayerBaseOperation(int passindex); + MultilayerBaseOperation(int passtype, int view); void setRenderLayer(RenderLayer *renderlayer) { this->m_renderlayer = renderlayer; } }; class MultilayerColorOperation : public MultilayerBaseOperation { public: - MultilayerColorOperation(int passindex) : MultilayerBaseOperation(passindex) { + MultilayerColorOperation(int passtype, int view) : MultilayerBaseOperation(passtype, view) { this->addOutputSocket(COM_DT_COLOR); } void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); @@ -51,7 +52,7 @@ public: class MultilayerValueOperation : public MultilayerBaseOperation { public: - MultilayerValueOperation(int passindex) : MultilayerBaseOperation(passindex) { + MultilayerValueOperation(int passtype, int view) : MultilayerBaseOperation(passtype, view) { this->addOutputSocket(COM_DT_VALUE); } void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); @@ -59,7 +60,7 @@ public: class MultilayerVectorOperation : public MultilayerBaseOperation { public: - MultilayerVectorOperation(int passindex) : MultilayerBaseOperation(passindex) { + MultilayerVectorOperation(int passtype, int view) : MultilayerBaseOperation(passtype, view) { this->addOutputSocket(COM_DT_VECTOR); } void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cpp b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cpp new file mode 100644 index 00000000000..c927dfa89a1 --- /dev/null +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cpp @@ -0,0 +1,317 @@ +/* + * Copyright 2015, Blender Foundation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor: + * Jeroen Bakker + * Monique Dewanchand + * Lukas Tönne + * Dalai Felinto + */ + +#include "COM_OutputFileOperation.h" +#include "COM_OutputFileMultiViewOperation.h" + +#include <string.h> +#include "BLI_listbase.h" +#include "BLI_path_util.h" +#include "BLI_string.h" +#include "BKE_image.h" +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_scene.h" + +#include "DNA_color_types.h" +#include "MEM_guardedalloc.h" + +extern "C" { +#include "IMB_imbuf.h" +#include "IMB_colormanagement.h" +#include "IMB_imbuf_types.h" +} + +/************************************ OpenEXR Singlelayer Multiview *****************************************/ + +OutputOpenExrSingleLayerMultiViewOperation::OutputOpenExrSingleLayerMultiViewOperation( + const RenderData *rd, const bNodeTree *tree, DataType datatype, ImageFormatData *format, const char *path, + const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings, + const char *viewName) + : OutputSingleLayerOperation(rd, tree, datatype, format, path, viewSettings, displaySettings, viewName) +{ +} + +void *OutputOpenExrSingleLayerMultiViewOperation::get_handle(const char *filename) +{ + size_t width = this->getWidth(); + size_t height = this->getHeight(); + SceneRenderView *srv; + + if (width != 0 && height != 0) { + void *exrhandle; + + exrhandle = IMB_exr_get_handle_name(filename); + + if (!BKE_scene_multiview_is_render_view_first(this->m_rd, this->m_viewName)) + return exrhandle; + + IMB_exr_clear_channels(exrhandle); + + for (srv = (SceneRenderView *)this->m_rd->views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(this->m_rd, srv) == false) + continue; + + IMB_exr_add_view(exrhandle, srv->name); + add_exr_channels(exrhandle, NULL, this->m_datatype, srv->name, width, NULL); + } + + BLI_make_existing_file(filename); + + /* prepare the file with all the channels */ + + if (IMB_exr_begin_write(exrhandle, filename, width, height, this->m_format->exr_codec) == 0) { + printf("Error Writing Singlelayer Multiview Openexr\n"); + IMB_exr_close(exrhandle); + } + else { + IMB_exr_clear_channels(exrhandle); + return exrhandle; + } + } + return NULL; +} + +void OutputOpenExrSingleLayerMultiViewOperation::deinitExecution() +{ + unsigned int width = this->getWidth(); + unsigned int height = this->getHeight(); + + if (width != 0 && height != 0) { + void *exrhandle; + Main *bmain = G.main; /* TODO, have this passed along */ + char filename[FILE_MAX]; + + BKE_image_path_from_imtype( + filename, this->m_path, bmain->name, this->m_rd->cfra, R_IMF_IMTYPE_OPENEXR, + (this->m_rd->scemode & R_EXTENSION) != 0, true, NULL); + + exrhandle = this->get_handle(filename); + add_exr_channels(exrhandle, NULL, this->m_datatype, this->m_viewName, width, this->m_outputBuffer); + + /* memory can only be freed after we write all views to the file */ + this->m_outputBuffer = NULL; + this->m_imageInput = NULL; + + /* ready to close the file */ + if (BKE_scene_multiview_is_render_view_last(this->m_rd, this->m_viewName)) { + IMB_exr_write_channels(exrhandle); + + /* free buffer memory for all the views */ + free_exr_channels(exrhandle, this->m_rd, NULL, this->m_datatype); + + /* remove exr handle and data */ + IMB_exr_close(exrhandle); + } + } +} + +/************************************ OpenEXR Multilayer Multiview *****************************************/ + +OutputOpenExrMultiLayerMultiViewOperation::OutputOpenExrMultiLayerMultiViewOperation( + const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec, const char *viewName) + : OutputOpenExrMultiLayerOperation(rd, tree, path, exr_codec, viewName) +{ +} + +void *OutputOpenExrMultiLayerMultiViewOperation::get_handle(const char *filename) +{ + unsigned int width = this->getWidth(); + unsigned int height = this->getHeight(); + + if (width != 0 && height != 0) { + + void *exrhandle; + SceneRenderView *srv; + + /* get a new global handle */ + exrhandle = IMB_exr_get_handle_name(filename); + + if (!BKE_scene_multiview_is_render_view_first(this->m_rd, this->m_viewName)) + return exrhandle; + + IMB_exr_clear_channels(exrhandle); + + /* check renderdata for amount of views */ + for (srv = (SceneRenderView *) this->m_rd->views.first; srv; srv = srv->next) { + + if (BKE_scene_multiview_is_render_view_active(this->m_rd, srv) == false) + continue; + + IMB_exr_add_view(exrhandle, srv->name); + + for (unsigned int i = 0; i < this->m_layers.size(); ++i) + add_exr_channels(exrhandle, this->m_layers[i].name, this->m_layers[i].datatype, srv->name, width, NULL); + } + + BLI_make_existing_file(filename); + + /* prepare the file with all the channels for the header */ + if (IMB_exr_begin_write(exrhandle, filename, width, height, this->m_exr_codec) == 0) { + printf("Error Writing Multilayer Multiview Openexr\n"); + IMB_exr_close(exrhandle); + } + else { + IMB_exr_clear_channels(exrhandle); + return exrhandle; + } + } + return NULL; +} + +void OutputOpenExrMultiLayerMultiViewOperation::deinitExecution() +{ + unsigned int width = this->getWidth(); + unsigned int height = this->getHeight(); + + if (width != 0 && height != 0) { + void *exrhandle; + Main *bmain = G.main; /* TODO, have this passed along */ + char filename[FILE_MAX]; + + BKE_image_path_from_imtype( + filename, this->m_path, bmain->name, this->m_rd->cfra, R_IMF_IMTYPE_MULTILAYER, + (this->m_rd->scemode & R_EXTENSION) != 0, true, NULL); + + exrhandle = this->get_handle(filename); + + for (unsigned int i = 0; i < this->m_layers.size(); ++i) + add_exr_channels(exrhandle, this->m_layers[i].name, this->m_layers[i].datatype, this->m_viewName, width, this->m_layers[i].outputBuffer); + + for (unsigned int i = 0; i < this->m_layers.size(); ++i) { + /* memory can only be freed after we write all views to the file */ + this->m_layers[i].outputBuffer = NULL; + this->m_layers[i].imageInput = NULL; + } + + /* ready to close the file */ + if (BKE_scene_multiview_is_render_view_last(this->m_rd, this->m_viewName)) { + IMB_exr_write_channels(exrhandle); + + /* free buffer memory for all the views */ + for (unsigned int i = 0; i < this->m_layers.size(); ++i) { + free_exr_channels(exrhandle, this->m_rd, this->m_layers[i].name, this->m_layers[i].datatype); + } + + IMB_exr_close(exrhandle); + } + } +} + + +/******************************** Stereo3D ******************************/ + +OutputStereoOperation::OutputStereoOperation( + const RenderData *rd, const bNodeTree *tree, DataType datatype, ImageFormatData *format, const char *path, + const char *name, const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings, + const char *viewName) + : OutputSingleLayerOperation(rd, tree, datatype, format, path, viewSettings, displaySettings, viewName) +{ + BLI_strncpy(this->m_name, name, sizeof(this->m_name)); + this->m_channels = get_datatype_size(datatype); +} + +void *OutputStereoOperation::get_handle(const char *filename) +{ + size_t width = this->getWidth(); + size_t height = this->getHeight(); + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + size_t i; + + if (width != 0 && height != 0) { + void *exrhandle; + + exrhandle = IMB_exr_get_handle_name(filename); + + if (!BKE_scene_multiview_is_render_view_first(this->m_rd, this->m_viewName)) + return exrhandle; + + IMB_exr_clear_channels(exrhandle); + + for (i = 0; i < 2; i++) + IMB_exr_add_view(exrhandle, names[i]); + + return exrhandle; + } + return NULL; +} + +void OutputStereoOperation::deinitExecution() +{ + unsigned int width = this->getWidth(); + unsigned int height = this->getHeight(); + + if (width != 0 && height != 0) { + void *exrhandle; + + exrhandle = this->get_handle(this->m_path); + float *buf = this->m_outputBuffer; + + /* populate single EXR channel with view data */ + IMB_exr_add_channel(exrhandle, NULL, this->m_name, this->m_viewName, 1, this->m_channels * width * height, buf); + + this->m_imageInput = NULL; + this->m_outputBuffer = NULL; + + /* create stereo ibuf */ + if (BKE_scene_multiview_is_render_view_last(this->m_rd, this->m_viewName)) { + ImBuf *ibuf[3] = {NULL}; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + Main *bmain = G.main; /* TODO, have this passed along */ + char filename[FILE_MAX]; + int i; + + /* get rectf from EXR */ + for (i = 0; i < 2; i++) { + float *rectf = IMB_exr_channel_rect(exrhandle, NULL, this->m_name, names[i]); + ibuf[i] = IMB_allocImBuf(width, height, this->m_format->planes, 0); + + ibuf[i]->channels = this->m_channels; + ibuf[i]->rect_float = rectf; + ibuf[i]->mall |= IB_rectfloat; + ibuf[i]->dither = this->m_rd->dither_intensity; + + /* do colormanagement in the individual views, so it doesn't need to do in the stereo */ + IMB_colormanagement_imbuf_for_write(ibuf[i], true, false, this->m_viewSettings, + this->m_displaySettings, this->m_format); + IMB_prepare_write_ImBuf(IMB_isfloat(ibuf[i]), ibuf[i]); + } + + /* create stereo buffer */ + ibuf[2] = IMB_stereo3d_ImBuf(this->m_format, ibuf[0], ibuf[1]); + + BKE_image_path_from_imformat( + filename, this->m_path, bmain->name, this->m_rd->cfra, this->m_format, + (this->m_rd->scemode & R_EXTENSION) != 0, true, NULL); + + BKE_imbuf_write(ibuf[2], filename, this->m_format); + + /* imbuf knows which rects are not part of ibuf */ + for (i = 0; i < 3; i++) + IMB_freeImBuf(ibuf[i]); + + IMB_exr_close(exrhandle); + } + } +} diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h new file mode 100644 index 00000000000..25716fdb9e1 --- /dev/null +++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h @@ -0,0 +1,74 @@ +/* + * Copyright 2015, Blender Foundation. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor: + * Jeroen Bakker + * Monique Dewanchand + * Lukas Tönne + * Dalai Felinto + */ + +#ifndef _COM_OutputFileMultiViewOperation_h +#define _COM_OutputFileMultiViewOperation_h +#include "COM_NodeOperation.h" + +#include "BLI_rect.h" +#include "BLI_path_util.h" + +#include "DNA_color_types.h" +#include "DNA_userdef_types.h" + +#include "intern/openexr/openexr_multi.h" + +class OutputOpenExrSingleLayerMultiViewOperation : public OutputSingleLayerOperation { +private: +public: + OutputOpenExrSingleLayerMultiViewOperation(const RenderData *rd, const bNodeTree *tree, DataType datatype, + ImageFormatData *format, const char *path, + const ColorManagedViewSettings *viewSettings, + const ColorManagedDisplaySettings *displaySettings, + const char *viewName); + + void *get_handle(const char *filename); + void deinitExecution(); +}; + +/* Writes inputs into OpenEXR multilayer channels. */ +class OutputOpenExrMultiLayerMultiViewOperation : public OutputOpenExrMultiLayerOperation { +private: +public: + OutputOpenExrMultiLayerMultiViewOperation(const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec, const char *viewName); + + void *get_handle(const char *filename); + void deinitExecution(); +}; + +/**/ +class OutputStereoOperation : public OutputSingleLayerOperation { +private: + char m_name[FILE_MAX]; + size_t m_channels; +public: + OutputStereoOperation(const RenderData *rd, const bNodeTree *tree, DataType datatype, + struct ImageFormatData *format, const char *path, const char *name, + const ColorManagedViewSettings *viewSettings, + const ColorManagedDisplaySettings *displaySettings, const char *viewName); + void *get_handle(const char *filename); + void deinitExecution(); +}; + +#endif diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cpp b/source/blender/compositor/operations/COM_OutputFileOperation.cpp index b1c90a6355f..1a7a356f5b4 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.cpp +++ b/source/blender/compositor/operations/COM_OutputFileOperation.cpp @@ -29,6 +29,7 @@ #include "BKE_image.h" #include "BKE_global.h" #include "BKE_main.h" +#include "BKE_scene.h" #include "DNA_color_types.h" @@ -39,7 +40,60 @@ extern "C" { # include "IMB_imbuf_types.h" } -static int get_datatype_size(DataType datatype) +void add_exr_channels(void *exrhandle, const char *layerName, const DataType datatype, const char *viewName, const size_t width, float *buf) +{ + /* create channels */ + switch (datatype) { + case COM_DT_VALUE: + IMB_exr_add_channel(exrhandle, layerName, "V", viewName, 1, width, buf ? buf : NULL); + break; + case COM_DT_VECTOR: + IMB_exr_add_channel(exrhandle, layerName, "X", viewName, 3, 3 * width, buf ? buf : NULL); + IMB_exr_add_channel(exrhandle, layerName, "Y", viewName, 3, 3 * width, buf ? buf + 1 : NULL); + IMB_exr_add_channel(exrhandle, layerName, "Z", viewName, 3, 3 * width, buf ? buf + 2 : NULL); + break; + case COM_DT_COLOR: + IMB_exr_add_channel(exrhandle, layerName, "R", viewName, 4, 4 * width, buf ? buf : NULL); + IMB_exr_add_channel(exrhandle, layerName, "G", viewName, 4, 4 * width, buf ? buf + 1 : NULL); + IMB_exr_add_channel(exrhandle, layerName, "B", viewName, 4, 4 * width, buf ? buf + 2 : NULL); + IMB_exr_add_channel(exrhandle, layerName, "A", viewName, 4, 4 * width, buf ? buf + 3 : NULL); + break; + default: + break; + } +} + +void free_exr_channels(void *exrhandle, const RenderData *rd, const char *layerName, const DataType datatype) +{ + SceneRenderView *srv; + + /* check renderdata for amount of views */ + for (srv = (SceneRenderView *) rd->views.first; srv; srv = srv->next) { + float *rect = NULL; + + if (BKE_scene_multiview_is_render_view_active(rd, srv) == false) + continue; + + /* the pointer is stored in the first channel of each datatype */ + switch (datatype) { + case COM_DT_VALUE: + rect = IMB_exr_channel_rect(exrhandle, layerName, "V", srv->name); + break; + case COM_DT_VECTOR: + rect = IMB_exr_channel_rect(exrhandle, layerName, "X", srv->name); + break; + case COM_DT_COLOR: + rect = IMB_exr_channel_rect(exrhandle, layerName, "R", srv->name); + break; + default: + break; + } + if (rect) + MEM_freeN(rect); + } +} + +int get_datatype_size(DataType datatype) { switch (datatype) { case COM_DT_VALUE: return 1; @@ -94,7 +148,7 @@ static void write_buffer_rect(rcti *rect, const bNodeTree *tree, OutputSingleLayerOperation::OutputSingleLayerOperation( const RenderData *rd, const bNodeTree *tree, DataType datatype, ImageFormatData *format, const char *path, - const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings) + const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings, const char *viewName) { this->m_rd = rd; this->m_tree = tree; @@ -110,6 +164,7 @@ OutputSingleLayerOperation::OutputSingleLayerOperation( this->m_viewSettings = viewSettings; this->m_displaySettings = displaySettings; + this->m_viewName = viewName; } void OutputSingleLayerOperation::initExecution() @@ -131,6 +186,7 @@ void OutputSingleLayerOperation::deinitExecution() ImBuf *ibuf = IMB_allocImBuf(this->getWidth(), this->getHeight(), this->m_format->planes, 0); Main *bmain = G.main; /* TODO, have this passed along */ char filename[FILE_MAX]; + const char *suffix; ibuf->channels = size; ibuf->rect_float = this->m_outputBuffer; @@ -140,10 +196,12 @@ void OutputSingleLayerOperation::deinitExecution() IMB_colormanagement_imbuf_for_write(ibuf, true, false, m_viewSettings, m_displaySettings, this->m_format); + suffix = BKE_scene_multiview_view_suffix_get(this->m_rd, this->m_viewName); + BKE_image_path_from_imformat( - filename, this->m_path, bmain->name, this->m_rd->cfra, - this->m_format, (this->m_rd->scemode & R_EXTENSION) != 0, true); - + filename, this->m_path, bmain->name, this->m_rd->cfra, this->m_format, + (this->m_rd->scemode & R_EXTENSION) != 0, true, suffix); + if (0 == BKE_imbuf_write(ibuf, filename, this->m_format)) printf("Cannot save Node File Output to %s\n", filename); else @@ -155,6 +213,7 @@ void OutputSingleLayerOperation::deinitExecution() this->m_imageInput = NULL; } +/******************************* MultiLayer *******************************/ OutputOpenExrLayer::OutputOpenExrLayer(const char *name_, DataType datatype_, bool use_layer_) { @@ -168,13 +227,14 @@ OutputOpenExrLayer::OutputOpenExrLayer(const char *name_, DataType datatype_, bo } OutputOpenExrMultiLayerOperation::OutputOpenExrMultiLayerOperation( - const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec) + const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec, const char *viewName) { this->m_rd = rd; this->m_tree = tree; BLI_strncpy(this->m_path, path, sizeof(this->m_path)); this->m_exr_codec = exr_codec; + this->m_viewName = viewName; } void OutputOpenExrMultiLayerOperation::add_layer(const char *name, DataType datatype, bool use_layer) @@ -210,52 +270,21 @@ void OutputOpenExrMultiLayerOperation::deinitExecution() if (width != 0 && height != 0) { Main *bmain = G.main; /* TODO, have this passed along */ char filename[FILE_MAX]; + const char *suffix; void *exrhandle = IMB_exr_get_handle(); - + + suffix = BKE_scene_multiview_view_suffix_get(this->m_rd, this->m_viewName); BKE_image_path_from_imtype( filename, this->m_path, bmain->name, this->m_rd->cfra, R_IMF_IMTYPE_MULTILAYER, - (this->m_rd->scemode & R_EXTENSION) != 0, true); + (this->m_rd->scemode & R_EXTENSION) != 0, true, suffix); BLI_make_existing_file(filename); - + for (unsigned int i = 0; i < this->m_layers.size(); ++i) { OutputOpenExrLayer &layer = this->m_layers[i]; if (!layer.imageInput) continue; /* skip unconnected sockets */ - char channelname[EXR_TOT_MAXNAME]; - BLI_strncpy(channelname, this->m_layers[i].name, sizeof(channelname) - 2); - char *channelname_ext = channelname + strlen(channelname); - - float *buf = this->m_layers[i].outputBuffer; - - /* create channels */ - switch (this->m_layers[i].datatype) { - case COM_DT_VALUE: - strcpy(channelname_ext, ".V"); - IMB_exr_add_channel(exrhandle, 0, channelname, 1, width, buf); - break; - case COM_DT_VECTOR: - strcpy(channelname_ext, ".X"); - IMB_exr_add_channel(exrhandle, 0, channelname, 3, 3 * width, buf); - strcpy(channelname_ext, ".Y"); - IMB_exr_add_channel(exrhandle, 0, channelname, 3, 3 * width, buf + 1); - strcpy(channelname_ext, ".Z"); - IMB_exr_add_channel(exrhandle, 0, channelname, 3, 3 * width, buf + 2); - break; - case COM_DT_COLOR: - strcpy(channelname_ext, ".R"); - IMB_exr_add_channel(exrhandle, 0, channelname, 4, 4 * width, buf); - strcpy(channelname_ext, ".G"); - IMB_exr_add_channel(exrhandle, 0, channelname, 4, 4 * width, buf + 1); - strcpy(channelname_ext, ".B"); - IMB_exr_add_channel(exrhandle, 0, channelname, 4, 4 * width, buf + 2); - strcpy(channelname_ext, ".A"); - IMB_exr_add_channel(exrhandle, 0, channelname, 4, 4 * width, buf + 3); - break; - default: - break; - } - + add_exr_channels(exrhandle, this->m_layers[i].name, this->m_layers[i].datatype, "", width, this->m_layers[i].outputBuffer); } /* when the filename has no permissions, this can fail */ @@ -279,3 +308,4 @@ void OutputOpenExrMultiLayerOperation::deinitExecution() } } } + diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.h b/source/blender/compositor/operations/COM_OutputFileOperation.h index af16f186d9d..58ebf055583 100644 --- a/source/blender/compositor/operations/COM_OutputFileOperation.h +++ b/source/blender/compositor/operations/COM_OutputFileOperation.h @@ -34,7 +34,7 @@ /* Writes the image to a single-layer file. */ class OutputSingleLayerOperation : public NodeOperation { -private: +protected: const RenderData *m_rd; const bNodeTree *m_tree; @@ -47,9 +47,11 @@ private: const ColorManagedViewSettings *m_viewSettings; const ColorManagedDisplaySettings *m_displaySettings; + + const char *m_viewName; public: OutputSingleLayerOperation(const RenderData *rd, const bNodeTree *tree, DataType datatype, ImageFormatData *format, const char *path, - const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings); + const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings, const char *viewName); void executeRegion(rcti *rect, unsigned int tileNumber); bool isOutputOperation(bool /*rendering*/) const { return true; } @@ -75,7 +77,7 @@ struct OutputOpenExrLayer { /* Writes inputs into OpenEXR multilayer channels. */ class OutputOpenExrMultiLayerOperation : public NodeOperation { -private: +protected: typedef std::vector<OutputOpenExrLayer> LayerList; const RenderData *m_rd; @@ -84,9 +86,10 @@ private: char m_path[FILE_MAX]; char m_exr_codec; LayerList m_layers; + const char *m_viewName; public: - OutputOpenExrMultiLayerOperation(const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec); + OutputOpenExrMultiLayerOperation(const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec, const char *viewName); void add_layer(const char *name, DataType datatype, bool use_layer); @@ -99,4 +102,8 @@ public: bool isFileOutputOperation() const { return true; } }; +void add_exr_channels(void *exrhandle, const char *layerName, const DataType datatype, const char *viewName, const size_t width, float *buf); +void free_exr_channels(void *exrhandle, const RenderData *rd, const char *layerName, const DataType datatype); +int get_datatype_size(DataType datatype); + #endif diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.cpp b/source/blender/compositor/operations/COM_RenderLayersProg.cpp index defbc2ab57f..af176a766ff 100644 --- a/source/blender/compositor/operations/COM_RenderLayersProg.cpp +++ b/source/blender/compositor/operations/COM_RenderLayersProg.cpp @@ -23,6 +23,7 @@ #include "COM_RenderLayersProg.h" #include "BLI_listbase.h" +#include "BKE_scene.h" #include "DNA_scene_types.h" extern "C" { @@ -57,11 +58,10 @@ void RenderLayersBaseProg::initExecution() if (srl) { RenderLayer *rl = RE_GetRenderLayer(rr, srl->name); - if (rl && rl->rectf) { - this->m_inputBuffer = RE_RenderLayerGetPass(rl, this->m_renderpass); - + if (rl) { + this->m_inputBuffer = RE_RenderLayerGetPass(rl, this->m_renderpass, this->m_viewName); if (this->m_inputBuffer == NULL && this->m_renderpass == SCE_PASS_COMBINED) { - this->m_inputBuffer = rl->rectf; + this->m_inputBuffer = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, this->m_viewName); } } } @@ -195,7 +195,7 @@ void RenderLayersBaseProg::determineResolution(unsigned int resolution[2], unsig SceneRenderLayer *srl = (SceneRenderLayer *)BLI_findlink(&sce->r.layers, getLayerId()); if (srl) { RenderLayer *rl = RE_GetRenderLayer(rr, srl->name); - if (rl && rl->rectf) { + if (rl) { resolution[0] = rl->rectx; resolution[1] = rl->recty; } diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.h b/source/blender/compositor/operations/COM_RenderLayersProg.h index f73d9de3e27..2ddbc968c01 100644 --- a/source/blender/compositor/operations/COM_RenderLayersProg.h +++ b/source/blender/compositor/operations/COM_RenderLayersProg.h @@ -51,7 +51,12 @@ private: * layerId of the layer where this operation needs to get its data from */ short m_layerId; - + + /** + * viewName of the view to use (unless another view is specified by the node + */ + const char *m_viewName; + /** * cached instance to the float buffer inside the layer */ @@ -97,6 +102,8 @@ public: void setRenderData(const RenderData *rd) { this->m_rd = rd; } void setLayerId(short layerId) { this->m_layerId = layerId; } short getLayerId() { return this->m_layerId; } + void setViewName(const char *viewName) { this->m_viewName = viewName; } + const char *getViewName() { return this->m_viewName; } void initExecution(); void deinitExecution(); void executePixelSampled(float output[4], float x, float y, PixelSampler sampler); diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cpp b/source/blender/compositor/operations/COM_ViewerOperation.cpp index da2706a2244..96ee5ecbb03 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.cpp +++ b/source/blender/compositor/operations/COM_ViewerOperation.cpp @@ -23,6 +23,7 @@ #include "COM_ViewerOperation.h" #include "BLI_listbase.h" #include "BKE_image.h" +#include "BKE_scene.h" #include "WM_api.h" #include "WM_types.h" #include "PIL_time.h" @@ -57,6 +58,8 @@ ViewerOperation::ViewerOperation() : NodeOperation() this->m_imageInput = NULL; this->m_alphaInput = NULL; this->m_depthInput = NULL; + this->m_rd = NULL; + this->m_viewName = NULL; } void ViewerOperation::initExecution() @@ -123,8 +126,18 @@ void ViewerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/) void ViewerOperation::initImage() { Image *ima = this->m_image; + ImageUser iuser = *this->m_imageUser; void *lock; - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, this->m_imageUser, &lock); + ImBuf *ibuf; + + /* make sure the image has the correct number of views */ + if (ima && BKE_scene_multiview_is_render_view_first(this->m_rd, this->m_viewName)) { + BKE_image_verify_viewer_views(this->m_rd, ima, this->m_imageUser); + } + + /* local changes to the original ImageUser */ + iuser.multi_index = BKE_scene_multiview_view_id_get(this->m_rd, this->m_viewName); + ibuf = BKE_image_acquire_ibuf(ima, &iuser, &lock); if (!ibuf) return; BLI_lock_thread(LOCK_DRAW_IMAGE); diff --git a/source/blender/compositor/operations/COM_ViewerOperation.h b/source/blender/compositor/operations/COM_ViewerOperation.h index a17375c2de9..107aee3a82f 100644 --- a/source/blender/compositor/operations/COM_ViewerOperation.h +++ b/source/blender/compositor/operations/COM_ViewerOperation.h @@ -40,7 +40,9 @@ private: bool m_doDepthBuffer; ImBuf *m_ibuf; bool m_useAlphaInput; - + const RenderData *m_rd; + const char *m_viewName; + const ColorManagedViewSettings *m_viewSettings; const ColorManagedDisplaySettings *m_displaySettings; @@ -67,6 +69,8 @@ public: const CompositorPriority getRenderPriority() const; bool isViewerOperation() const { return true; } void setUseAlphaInput(bool value) { this->m_useAlphaInput = value; } + void setRenderData(const RenderData *rd) { this->m_rd = rd; } + void setViewName(const char *viewName) { this->m_viewName = viewName; } void setViewSettings(const ColorManagedViewSettings *viewSettings) { this->m_viewSettings = viewSettings; } void setDisplaySettings(const ColorManagedDisplaySettings *displaySettings) { this->m_displaySettings = displaySettings; } 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 diff --git a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp index d1285cf2c18..6b102410aa6 100644 --- a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp +++ b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp @@ -494,13 +494,14 @@ void FRS_composite_result(Render *re, SceneRenderLayer *srl, Render *freestyle_r return; rl = render_get_active_layer( freestyle_render, freestyle_render->result ); - if (!rl || rl->rectf == NULL) { + if (!rl) { if (G.debug & G_DEBUG_FREESTYLE) { cout << "No Freestyle result image to composite" << endl; } return; } - src = rl->rectf; + + src = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, re->viewname); #if 0 if (G.debug & G_DEBUG_FREESTYLE) { cout << "src: " << rl->rectx << " x " << rl->recty << endl; @@ -508,13 +509,13 @@ void FRS_composite_result(Render *re, SceneRenderLayer *srl, Render *freestyle_r #endif rl = RE_GetRenderLayer(re->result, srl->name); - if (!rl || rl->rectf == NULL) { + if (!rl || src == NULL) { if (G.debug & G_DEBUG_FREESTYLE) { cout << "No layer to composite to" << endl; } return; } - dest = rl->rectf; + dest = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, re->viewname); #if 0 if (G.debug & G_DEBUG_FREESTYLE) { cout << "dest: " << rl->rectx << " x " << rl->recty << endl; diff --git a/source/blender/gpu/GPU_draw.h b/source/blender/gpu/GPU_draw.h index cc1a63d6256..20aa4919416 100644 --- a/source/blender/gpu/GPU_draw.h +++ b/source/blender/gpu/GPU_draw.h @@ -121,7 +121,7 @@ void GPU_set_gpu_mipmapping(int gpu_mipmap); /* Image updates and free * - these deal with images bound as opengl textures */ -void GPU_paint_update_image(struct Image *ima, int x, int y, int w, int h); +void GPU_paint_update_image(struct Image *ima, ImageUser *iuser, int x, int y, int w, int h); void GPU_update_images_framechange(void); int GPU_update_image_time(struct Image *ima, double time); int GPU_verify_image(struct Image *ima, struct ImageUser *iuser, int tftile, bool compare, bool mipmap, bool is_data); diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c index 7f3a9981a5c..daf6b90f604 100644 --- a/source/blender/gpu/intern/gpu_draw.c +++ b/source/blender/gpu/intern/gpu_draw.c @@ -1040,11 +1040,11 @@ static bool GPU_check_scaled_image(ImBuf *ibuf, Image *ima, float *frect, int x, return false; } -void GPU_paint_update_image(Image *ima, int x, int y, int w, int h) +void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, int h) { ImBuf *ibuf; - ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); + ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL); if (ima->repbind || (GPU_get_mipmap() && !GTS.gpu_mipmap) || !ima->bindcode || !ibuf || (w == 0) || (h == 0)) diff --git a/source/blender/imbuf/CMakeLists.txt b/source/blender/imbuf/CMakeLists.txt index e8977913948..eadedbd5e00 100644 --- a/source/blender/imbuf/CMakeLists.txt +++ b/source/blender/imbuf/CMakeLists.txt @@ -62,6 +62,7 @@ set(SRC intern/rectop.c intern/rotate.c intern/scaling.c + intern/stereoimbuf.c intern/targa.c intern/thumbs.c intern/thumbs_blend.c diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h index 18682302b3a..a39832dd1cb 100644 --- a/source/blender/imbuf/IMB_imbuf.h +++ b/source/blender/imbuf/IMB_imbuf.h @@ -89,6 +89,13 @@ struct ColorManagedDisplay; struct GSet; /** * + * \attention defined in DNA_scene_types.h + */ +struct ImageFormatData; +struct Stereo3dFormat; + +/** + * * \attention Defined in allocimbuf.c */ void IMB_init(void); @@ -279,10 +286,10 @@ bool IMB_anim_get_fps(struct anim *anim, * \attention Defined in anim_movie.c */ struct anim *IMB_open_anim(const char *name, int ib_flags, int streamindex, char colorspace[IM_MAX_SPACE]); +void IMB_suffix_anim(struct anim *anim, const char *suffix); void IMB_close_anim(struct anim *anim); void IMB_close_anim_proxies(struct anim *anim); - /** * * \attention Defined in anim_movie.c @@ -377,6 +384,7 @@ void IMB_scaleImBuf_threaded(struct ImBuf *ibuf, unsigned int newx, unsigned int * \attention Defined in writeimage.c */ short IMB_saveiff(struct ImBuf *ibuf, const char *filepath, int flags); +struct ImBuf *IMB_prepare_write_ImBuf(const bool isfloat, struct ImBuf *ibuf); /** * @@ -399,6 +407,12 @@ int imb_get_anim_type(const char *name); /** * + * \attention Defined in util.c + */ +bool IMB_isfloat(struct ImBuf *ibuf); + +/** + * * \attention Defined in divers.c */ void IMB_de_interlace(struct ImBuf *ibuf); @@ -557,5 +571,27 @@ void IMB_processor_apply_threaded(int buffer_lines, int handle_size, void *init_ void IMB_ffmpeg_init(void); const char *IMB_ffmpeg_last_error(void); -#endif +/** + * + * \attention defined in stereoimbuf.c + */ +void IMB_stereo3d_write_dimensions( + const char mode, const bool is_squeezed, const size_t width, const size_t height, + size_t *r_width, size_t *r_height); +void IMB_stereo3d_read_dimensions( + const char mode, const bool is_squeezed, const size_t width, const size_t height, + size_t *r_width, size_t *r_height); +int *IMB_stereo3d_from_rect( + struct ImageFormatData *im_format, const size_t x, const size_t y, const size_t channels, + int *rect_left, int *rect_right); +float *IMB_stereo3d_from_rectf( + struct ImageFormatData *im_format, const size_t x, const size_t y, const size_t channels, + float *rectf_left, float *rectf_right); +struct ImBuf *IMB_stereo3d_ImBuf( + struct ImageFormatData *im_format, + struct ImBuf *ibuf_left, struct ImBuf *ibuf_right); +void IMB_ImBufFromStereo3d( + struct Stereo3dFormat *s3d, struct ImBuf *ibuf_stereo, + struct ImBuf **r_ibuf_left, struct ImBuf **r_ibuf_right); +#endif diff --git a/source/blender/imbuf/IMB_imbuf_types.h b/source/blender/imbuf/IMB_imbuf_types.h index 9e7201f53b4..862c587f75b 100644 --- a/source/blender/imbuf/IMB_imbuf_types.h +++ b/source/blender/imbuf/IMB_imbuf_types.h @@ -110,7 +110,7 @@ typedef struct ImBuf { int index; /* reference index for ImBuf lists */ int userflags; /* used to set imbuf to dirty and other stuff */ struct IDProperty *metadata; /* image metadata */ - void *userdata; /* temporary storage, only used by baking at the moment */ + void *userdata; /* temporary storage */ /* file information */ int ftype; /* file type we are going to save as */ @@ -172,6 +172,8 @@ typedef struct ImBuf { #define IB_alphamode_premul (1 << 12) /* indicates whether image on disk have premul alpha */ #define IB_alphamode_detect (1 << 13) /* if this flag is set, alpha mode would be guessed from file */ #define IB_ignore_alpha (1 << 14) /* ignore alpha on load and substitude it with 1.0f */ +#define IB_thumbnail (1 << 15) +#define IB_multiview (1 << 16) /* * The bit flag is stored in the ImBuf.ftype variable. diff --git a/source/blender/imbuf/intern/IMB_anim.h b/source/blender/imbuf/intern/IMB_anim.h index ed349e8f7eb..1fc43e22f68 100644 --- a/source/blender/imbuf/intern/IMB_anim.h +++ b/source/blender/imbuf/intern/IMB_anim.h @@ -194,6 +194,7 @@ struct anim { struct anim_index *curr_idx[IMB_TC_MAX_SLOT]; char colorspace[64]; + char suffix[64]; /* MAX_NAME - multiview */ }; #endif diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c index ffdecb793aa..0bb9f0fce51 100644 --- a/source/blender/imbuf/intern/anim_movie.c +++ b/source/blender/imbuf/intern/anim_movie.c @@ -288,6 +288,11 @@ struct anim *IMB_open_anim(const char *name, int ib_flags, int streamindex, char return(anim); } +void IMB_suffix_anim(struct anim *anim, const char *suffix) +{ + BLI_strncpy(anim->suffix, suffix, sizeof(anim->suffix)); +} + #ifdef WITH_AVI static int startavi(struct anim *anim) { diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c index 183159f7d6c..150ea0995ac 100644 --- a/source/blender/imbuf/intern/indexer.c +++ b/source/blender/imbuf/intern/indexer.c @@ -398,7 +398,7 @@ static void get_proxy_filename(struct anim *anim, IMB_Proxy_Size preview_size, } BLI_snprintf(proxy_name, sizeof(proxy_name), name, - (int) (proxy_fac[i] * 100), stream_suffix); + (int) (proxy_fac[i] * 100), stream_suffix, anim->suffix); get_index_dir(anim, index_dir, sizeof(index_dir)); @@ -411,10 +411,10 @@ static void get_tc_filename(struct anim *anim, IMB_Timecode_Type tc, char index_dir[FILE_MAXDIR]; int i = IMB_timecode_to_array_index(tc); const char *index_names[] = { - "record_run%s.blen_tc", - "free_run%s.blen_tc", - "interp_free_run%s.blen_tc", - "record_run_no_gaps%s.blen_tc" + "record_run%s%s.blen_tc", + "free_run%s%s.blen_tc", + "interp_free_run%s%s.blen_tc", + "record_run_no_gaps%s%s.blen_tc" }; char stream_suffix[20]; @@ -426,7 +426,7 @@ static void get_tc_filename(struct anim *anim, IMB_Timecode_Type tc, BLI_snprintf(stream_suffix, 20, "_st%d", anim->streamindex); } - BLI_snprintf(index_name, 256, index_names[i], stream_suffix); + BLI_snprintf(index_name, 256, index_names[i], stream_suffix, anim->suffix); get_index_dir(anim, index_dir, sizeof(index_dir)); diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp index 45eae89ad9d..1262f0af95d 100644 --- a/source/blender/imbuf/intern/openexr/openexr_api.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp @@ -92,9 +92,32 @@ _CRTIMP void __cdecl _invalid_parameter_noinfo(void) #include <ImfStringAttribute.h> #include <ImfStandardAttributes.h> +/* multiview/multipart */ +#include <ImfMultiView.h> +#include <ImfMultiPartInputFile.h> +#include <ImfInputPart.h> +#include <ImfOutputPart.h> +#include <ImfMultiPartOutputFile.h> +#include <ImfTiledOutputPart.h> +#include <ImfPartType.h> +#include <ImfPartHelper.h> + using namespace Imf; using namespace Imath; +extern "C" +{ +/* prototype */ +static struct ExrPass *imb_exr_get_pass(ListBase *lb, char *passname); +static bool exr_has_multiview(MultiPartInputFile& file); +static bool exr_has_multipart_file(MultiPartInputFile& file); +static int exr_has_alpha(MultiPartInputFile& file); +static int exr_has_zbuffer(MultiPartInputFile& file); +static void exr_printf(const char *__restrict format, ...); +static void imb_exr_type_by_channels(ChannelList& channels, StringVector& views, + bool *r_singlelayer, bool *r_multilayer, bool *r_multiview); +} + /* Memory Input Stream */ class Mem_IStream : public Imf::IStream @@ -340,13 +363,21 @@ static void openexr_header_metadata(Header *header, struct ImBuf *ibuf) addXDensity(*header, ibuf->ppm[0] / 39.3700787); /* 1 meter = 39.3700787 inches */ } -static int imb_save_openexr_half(struct ImBuf *ibuf, const char *name, int flags) +static bool imb_save_openexr_half(ImBuf *ibuf, const char *name, const int flags, const size_t totviews, + const char * (*getview)(void *base, size_t view_id), + ImBuf * (*getbuffer)(void *base, const size_t view_id)) { const int channels = ibuf->channels; const int is_alpha = (channels >= 4) && (ibuf->planes == 32); const int is_zbuf = (flags & IB_zbuffloat) && ibuf->zbuf_float != NULL; /* summarize */ const int width = ibuf->x; const int height = ibuf->y; + const bool is_multiview = (flags & IB_multiview) && ibuf->userdata; + + BLI_assert((!is_multiview) || (getview && getbuffer)); + + std::vector <string> views; + size_t view_id; try { @@ -355,13 +386,22 @@ static int imb_save_openexr_half(struct ImBuf *ibuf, const char *name, int flags openexr_header_compression(&header, ibuf->ftype & OPENEXR_COMPRESS); openexr_header_metadata(&header, ibuf); - header.channels().insert("R", Channel(HALF)); - header.channels().insert("G", Channel(HALF)); - header.channels().insert("B", Channel(HALF)); - if (is_alpha) - header.channels().insert("A", Channel(HALF)); - if (is_zbuf) // z we do as float always - header.channels().insert("Z", Channel(Imf::FLOAT)); + /* create views when possible */ + for (view_id = 0; view_id < totviews; view_id ++) + views.push_back(is_multiview ? getview(ibuf->userdata, view_id) : ""); + + if (is_multiview) + addMultiView(header, views); + + for (view_id = 0; view_id < totviews; view_id ++) { + header.channels().insert(insertViewName("R", views, view_id), Channel(HALF)); + header.channels().insert(insertViewName("G", views, view_id), Channel(HALF)); + header.channels().insert(insertViewName("B", views, view_id), Channel(HALF)); + if (is_alpha) + header.channels().insert(insertViewName("A", views, view_id), Channel(HALF)); + if (is_zbuf) // z we do as float always + header.channels().insert(insertViewName("Z", views, view_id), Channel(Imf::FLOAT)); + } FrameBuffer frameBuffer; @@ -370,75 +410,91 @@ static int imb_save_openexr_half(struct ImBuf *ibuf, const char *name, int flags OutputFile file(file_stream, header); /* we store first everything in half array */ - RGBAZ *pixels = new RGBAZ[height * width]; - RGBAZ *to = pixels; + RGBAZ *pixels = new RGBAZ[height * width * totviews]; int xstride = sizeof(RGBAZ); int ystride = xstride * width; - /* indicate used buffers */ - frameBuffer.insert("R", Slice(HALF, (char *) &pixels[0].r, xstride, ystride)); - frameBuffer.insert("G", Slice(HALF, (char *) &pixels[0].g, xstride, ystride)); - frameBuffer.insert("B", Slice(HALF, (char *) &pixels[0].b, xstride, ystride)); - if (is_alpha) - frameBuffer.insert("A", Slice(HALF, (char *) &pixels[0].a, xstride, ystride)); - if (is_zbuf) - frameBuffer.insert("Z", Slice(Imf::FLOAT, (char *)(ibuf->zbuf_float + (height - 1) * width), - sizeof(float), sizeof(float) * -width)); - if (ibuf->rect_float) { - float *from; - - for (int i = ibuf->y - 1; i >= 0; i--) { - from = ibuf->rect_float + channels * i * width; - - for (int j = ibuf->x; j > 0; j--) { - to->r = from[0]; - to->g = (channels >= 2) ? from[1] : from[0]; - to->b = (channels >= 3) ? from[2] : from[0]; - to->a = (channels >= 4) ? from[3] : 1.0f; - to++; from += channels; + for (view_id = 0; view_id < totviews; view_id ++) { + ImBuf *view_ibuf = is_multiview ? getbuffer(ibuf->userdata, view_id) : ibuf; + const size_t offset = view_id * width * height; + RGBAZ *to = pixels + offset; + + /* indicate used buffers */ + frameBuffer.insert(insertViewName("R", views, view_id), Slice(HALF, (char *) &pixels[offset].r, xstride, ystride)); + frameBuffer.insert(insertViewName("G", views, view_id), Slice(HALF, (char *) &pixels[offset].g, xstride, ystride)); + frameBuffer.insert(insertViewName("B", views, view_id), Slice(HALF, (char *) &pixels[offset].b, xstride, ystride)); + if (is_alpha) + frameBuffer.insert(insertViewName("A", views, view_id), Slice(HALF, (char *) &pixels[offset].a, xstride, ystride)); + if (is_zbuf) + frameBuffer.insert(insertViewName("Z", views, view_id), Slice(Imf::FLOAT, (char *)(view_ibuf->zbuf_float + (height - 1) * width), + sizeof(float), sizeof(float) * -width)); + if (view_ibuf->rect_float) { + float *from; + + for (int i = view_ibuf->y - 1; i >= 0; i--) { + from = view_ibuf->rect_float + channels * i * width; + + for (int j = view_ibuf->x; j > 0; j--) { + to->r = from[0]; + to->g = (channels >= 2) ? from[1] : from[0]; + to->b = (channels >= 3) ? from[2] : from[0]; + to->a = (channels >= 4) ? from[3] : 1.0f; + to++; from += channels; + } } } - } - else { - unsigned char *from; - - for (int i = ibuf->y - 1; i >= 0; i--) { - from = (unsigned char *)ibuf->rect + 4 * i * width; - - for (int j = ibuf->x; j > 0; j--) { - to->r = srgb_to_linearrgb((float)from[0] / 255.0f); - to->g = srgb_to_linearrgb((float)from[1] / 255.0f); - to->b = srgb_to_linearrgb((float)from[2] / 255.0f); - to->a = channels >= 4 ? (float)from[3] / 255.0f : 1.0f; - to++; from += 4; + else { + unsigned char *from; + + for (int i = view_ibuf->y - 1; i >= 0; i--) { + from = (unsigned char *)view_ibuf->rect + 4 * i * width; + + for (int j = view_ibuf->x; j > 0; j--) { + to->r = srgb_to_linearrgb((float)from[0] / 255.0f); + to->g = srgb_to_linearrgb((float)from[1] / 255.0f); + to->b = srgb_to_linearrgb((float)from[2] / 255.0f); + to->a = channels >= 4 ? (float)from[3] / 255.0f : 1.0f; + to++; from += 4; + } } } + + if (is_multiview) + IMB_freeImBuf(view_ibuf); } -// printf("OpenEXR-save: Writing OpenEXR file of height %d.\n", height); + exr_printf("OpenEXR-save: Writing OpenEXR file of height %d.\n", height); file.setFrameBuffer(frameBuffer); file.writePixels(height); delete[] pixels; } - catch (const std::exception &exc) + catch (const std::exception& exc) { printf("OpenEXR-save: ERROR: %s\n", exc.what()); - return (0); + return false; } - return (1); + return true; } -static int imb_save_openexr_float(struct ImBuf *ibuf, const char *name, int flags) +static bool imb_save_openexr_float(ImBuf *ibuf, const char *name, const int flags, const size_t totviews, + const char * (*getview)(void *base, const size_t view_id), + ImBuf * (*getbuffer)(void *base, const size_t view_id)) { const int channels = ibuf->channels; const int is_alpha = (channels >= 4) && (ibuf->planes == 32); const int is_zbuf = (flags & IB_zbuffloat) && ibuf->zbuf_float != NULL; /* summarize */ const int width = ibuf->x; const int height = ibuf->y; + const bool is_multiview = (flags & IB_multiview) && ibuf->userdata; + + BLI_assert((!is_multiview) || (getview && getbuffer)); + + std::vector <string> views; + size_t view_id; try { @@ -447,13 +503,22 @@ static int imb_save_openexr_float(struct ImBuf *ibuf, const char *name, int flag openexr_header_compression(&header, ibuf->ftype & OPENEXR_COMPRESS); openexr_header_metadata(&header, ibuf); - header.channels().insert("R", Channel(Imf::FLOAT)); - header.channels().insert("G", Channel(Imf::FLOAT)); - header.channels().insert("B", Channel(Imf::FLOAT)); - if (is_alpha) - header.channels().insert("A", Channel(Imf::FLOAT)); - if (is_zbuf) - header.channels().insert("Z", Channel(Imf::FLOAT)); + /* create views when possible */ + for (view_id = 0; view_id < totviews; view_id ++) + views.push_back(is_multiview ? getview(ibuf->userdata, view_id) : ""); + + if (is_multiview) + addMultiView(header, views); + + for (view_id = 0; view_id < totviews; view_id ++) { + header.channels().insert(insertViewName("R", views, view_id), Channel(Imf::FLOAT)); + header.channels().insert(insertViewName("G", views, view_id), Channel(Imf::FLOAT)); + header.channels().insert(insertViewName("B", views, view_id), Channel(Imf::FLOAT)); + if (is_alpha) + header.channels().insert(insertViewName("A", views, view_id), Channel(Imf::FLOAT)); + if (is_zbuf) + header.channels().insert(insertViewName("Z", views, view_id), Channel(Imf::FLOAT)); + } FrameBuffer frameBuffer; @@ -463,37 +528,41 @@ static int imb_save_openexr_float(struct ImBuf *ibuf, const char *name, int flag int xstride = sizeof(float) * channels; int ystride = -xstride * width; - float *rect[4] = {NULL, NULL, NULL, NULL}; - /* last scanline, stride negative */ - rect[0] = ibuf->rect_float + channels * (height - 1) * width; - rect[1] = (channels >= 2) ? rect[0] + 1 : rect[0]; - rect[2] = (channels >= 3) ? rect[0] + 2 : rect[0]; - rect[3] = (channels >= 4) ? rect[0] + 3 : rect[0]; /* red as alpha, is this needed since alpha isn't written? */ - - frameBuffer.insert("R", Slice(Imf::FLOAT, (char *)rect[0], xstride, ystride)); - frameBuffer.insert("G", Slice(Imf::FLOAT, (char *)rect[1], xstride, ystride)); - frameBuffer.insert("B", Slice(Imf::FLOAT, (char *)rect[2], xstride, ystride)); - if (is_alpha) - frameBuffer.insert("A", Slice(Imf::FLOAT, (char *)rect[3], xstride, ystride)); - if (is_zbuf) - frameBuffer.insert("Z", Slice(Imf::FLOAT, (char *) (ibuf->zbuf_float + (height - 1) * width), - sizeof(float), sizeof(float) * -width)); + for (view_id = 0; view_id < totviews; view_id ++) { + float *rect[4] = {NULL, NULL, NULL, NULL}; + ImBuf *view_ibuf = is_multiview ? getbuffer(ibuf->userdata, view_id) : ibuf; + + /* last scanline, stride negative */ + rect[0] = view_ibuf->rect_float + channels * (height - 1) * width; + rect[1] = (channels >= 2) ? rect[0] + 1 : rect[0]; + rect[2] = (channels >= 3) ? rect[0] + 2 : rect[0]; + rect[3] = (channels >= 4) ? rect[0] + 3 : rect[0]; /* red as alpha, is this needed since alpha isn't written? */ + + frameBuffer.insert(insertViewName("R", views, view_id), Slice(Imf::FLOAT, (char *)rect[0], xstride, ystride)); + frameBuffer.insert(insertViewName("G", views, view_id), Slice(Imf::FLOAT, (char *)rect[1], xstride, ystride)); + frameBuffer.insert(insertViewName("B", views, view_id), Slice(Imf::FLOAT, (char *)rect[2], xstride, ystride)); + if (is_alpha) + frameBuffer.insert(insertViewName("A", views, view_id), Slice(Imf::FLOAT, (char *)rect[3], xstride, ystride)); + if (is_zbuf) + frameBuffer.insert(insertViewName("Z", views, view_id), Slice(Imf::FLOAT, (char *) (view_ibuf->zbuf_float + (height - 1) * width), + sizeof(float), sizeof(float) * -width)); + + if (is_multiview) + IMB_freeImBuf(view_ibuf); + } file.setFrameBuffer(frameBuffer); file.writePixels(height); } - catch (const std::exception &exc) + catch (const std::exception& exc) { printf("OpenEXR-save: ERROR: %s\n", exc.what()); - - return (0); + return false; } - return (1); - // printf("OpenEXR-save: Done.\n"); + return true; } - int imb_save_openexr(struct ImBuf *ibuf, const char *name, int flags) { if (flags & IB_mem) { @@ -504,16 +573,48 @@ int imb_save_openexr(struct ImBuf *ibuf, const char *name, int flags) } if (ibuf->ftype & OPENEXR_HALF) - return imb_save_openexr_half(ibuf, name, flags); + return (int) imb_save_openexr_half(ibuf, name, flags, 1, NULL, NULL); + else { + /* when no float rect, we save as half (16 bits is sufficient) */ + if (ibuf->rect_float == NULL) + return (int) imb_save_openexr_half(ibuf, name, flags, 1, NULL, NULL); + else + return (int) imb_save_openexr_float(ibuf, name, flags, 1, NULL, NULL); + } +} + +static bool imb_save_openexr_multiview(ImBuf *ibuf, const char *name, const int flags, const size_t totviews, + const char * (*getview)(void *base, const size_t view_id), + ImBuf * (*getbuffer)(void *base, const size_t view_id)) +{ + if (flags & IB_mem) { + printf("OpenEXR-save: Create multiview EXR in memory CURRENTLY NOT SUPPORTED !\n"); + imb_addencodedbufferImBuf(ibuf); + ibuf->encodedsize = 0; + return false; + } + + if (ibuf->ftype & OPENEXR_HALF) + return imb_save_openexr_half(ibuf, name, flags, totviews, getview, getbuffer); else { /* when no float rect, we save as half (16 bits is sufficient) */ if (ibuf->rect_float == NULL) - return imb_save_openexr_half(ibuf, name, flags); + return imb_save_openexr_half(ibuf, name, flags, totviews, getview, getbuffer); else - return imb_save_openexr_float(ibuf, name, flags); + return imb_save_openexr_float(ibuf, name, flags, totviews, getview, getbuffer); } } +/* Save single-layer multiview OpenEXR + * If we have more multiview formats in the future, the function below could be incorporated + * in our ImBuf write functions, meanwhile this is an OpenEXR special case only */ +bool IMB_exr_multiview_save(ImBuf *ibuf, const char *name, const int flags, const size_t totviews, + const char * (*getview)(void *base, size_t view_id), + ImBuf * (*getbuffer)(void *base, const size_t view_id)) +{ + return imb_save_openexr_multiview(ibuf, name, flags, totviews, getview, getbuffer); +} + /* ********************* Nicer API, MultiLayer and with Tile file support ************************************ */ /* naming rules: @@ -527,18 +628,22 @@ static ListBase exrhandles = {NULL, NULL}; typedef struct ExrHandle { struct ExrHandle *next, *prev; + char name[FILE_MAX]; IFileStream *ifile_stream; - InputFile *ifile; + MultiPartInputFile *ifile; OFileStream *ofile_stream; - TiledOutputFile *tofile; + MultiPartOutputFile *mpofile; OutputFile *ofile; int tilex, tiley; int width, height; int mipmap; + StringVector *multiView; /* it needs to be a pointer due to Windows release builds of EXR2.0 segfault when opening EXR bug */ + int parts; + ListBase channels; /* flattened out, ExrChannel */ ListBase layers; /* hierarchical, pointing in end to ExrChannel */ } ExrHandle; @@ -547,10 +652,12 @@ typedef struct ExrHandle { typedef struct ExrChannel { struct ExrChannel *next, *prev; - char name[EXR_TOT_MAXNAME + 1]; /* full name of layer+pass */ + char name[EXR_TOT_MAXNAME + 1]; /* full name with everything */ + struct MultiViewChannelName *m; /* struct to store all multipart channel info */ int xstride, ystride; /* step to next pixel, to next scanline */ float *rect; /* first pointer to write in */ char chan_id; /* quick lookup of channel char */ + int view_id; /* quick lookup of channel view */ } ExrChannel; @@ -562,6 +669,10 @@ typedef struct ExrPass { float *rect; struct ExrChannel *chan[EXR_PASS_MAXCHAN]; char chan_id[EXR_PASS_MAXCHAN]; + + char internal_name[EXR_PASS_MAXNAME]; /* name with no view */ + char view[EXR_VIEW_MAXNAME]; + int view_id; } ExrPass; typedef struct ExrLayer { @@ -575,39 +686,141 @@ typedef struct ExrLayer { void *IMB_exr_get_handle(void) { ExrHandle *data = (ExrHandle *)MEM_callocN(sizeof(ExrHandle), "exr handle"); + data->multiView = new StringVector(); + BLI_addtail(&exrhandles, data); return data; } +void *IMB_exr_get_handle_name(const char *name) +{ + ExrHandle *data = (ExrHandle *) BLI_rfindstring(&exrhandles, name, offsetof(ExrHandle, name)); + + if (data == NULL) { + data = (ExrHandle *)IMB_exr_get_handle(); + BLI_strncpy(data->name, name, strlen(name) + 1); + } + return data; +} + +/* multiview functions */ +} // extern "C" + +extern "C" +{ + +void IMB_exr_add_view(void *handle, const char *name) +{ + ExrHandle *data = (ExrHandle *)handle; + data->multiView->push_back(name); +} + +static int imb_exr_get_multiView_id(StringVector& views, const std::string& name) +{ + int count = 0; + for (StringVector::const_iterator i = views.begin(); count < views.size(); ++i) { + if (name == *i) + return count; + else + count ++; + } + + /* no views or wrong name */ + return -1; +} + +static void imb_exr_get_views(MultiPartInputFile& file, StringVector& views) +{ + if (exr_has_multipart_file(file) == false) { + if (exr_has_multiview(file)) { + StringVector sv = multiView(file.header(0)); + for (StringVector::const_iterator i = sv.begin(); i != sv.end(); ++i) + views.push_back(*i); + } + } + + else { + for (int p = 0; p < file.parts(); p++) { + std::string view = ""; + if (file.header(p).hasView()) + view = file.header(p).view(); + + if (imb_exr_get_multiView_id(views, view) == -1) + views.push_back(view); + } + } +} + +/* Multilayer Blender files have the view name in all the passes (even the default view one) */ +static const char *imb_exr_insert_view_name(const char *passname, const char *viewname) +{ + if (viewname == NULL || viewname[0] == '\0') + return passname; + + static char retstr[EXR_PASS_MAXNAME]; + const char *end = passname + strlen(passname); + const char *token; + + int len = IMB_exr_split_token(passname, end, &token); + + if (len == 0) + BLI_snprintf(retstr, sizeof(retstr), "%s.%s", passname, viewname); + else + BLI_snprintf(retstr, sizeof(retstr), "%.*s%s.%s", + (int)(end - passname) - len, passname, viewname, token); + + return retstr; +} + /* adds flattened ExrChannels */ /* xstride, ystride and rect can be done in set_channel too, for tile writing */ -void IMB_exr_add_channel(void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect) +/* passname does not include view */ +void IMB_exr_add_channel(void *handle, const char *layname, const char *passname, const char *viewname, int xstride, int ystride, float *rect) { ExrHandle *data = (ExrHandle *)handle; ExrChannel *echan; - echan = (ExrChannel *)MEM_callocN(sizeof(ExrChannel), "exr tile channel"); + echan = (ExrChannel *)MEM_callocN(sizeof(ExrChannel), "exr channel"); + echan->m = new MultiViewChannelName (); - if (layname) { - char lay[EXR_LAY_MAXNAME + 1], pass[EXR_PASS_MAXNAME + 1]; - BLI_strncpy(lay, layname, EXR_LAY_MAXNAME); - BLI_strncpy(pass, passname, EXR_PASS_MAXNAME); + if (layname && layname[0] != '\0') { + echan->m->name = layname; + echan->m->name.append("."); + echan->m->name.append(passname); + } + else { + echan->m->name.assign(passname); + } - BLI_snprintf(echan->name, sizeof(echan->name), "%s.%s", lay, pass); + echan->m->internal_name = echan->m->name; + + echan->m->view.assign(viewname ? viewname : ""); + + /* quick look up */ + echan->view_id = std::max(0, imb_exr_get_multiView_id(*data->multiView, echan->m->view)); + + /* name has to be unique, thus it's a combination of layer, pass, view, and channel */ + if (layname && layname[0] != '\0') { + std::string raw_name = imb_exr_insert_view_name(echan->m->name.c_str(), echan->m->view.c_str()); + BLI_strncpy(echan->name, raw_name.c_str(), sizeof(echan->name)); + } + else if (data->multiView->size() > 1) { + std::string raw_name = insertViewName(echan->m->name, *data->multiView, echan->view_id); + BLI_strncpy(echan->name, raw_name.c_str(), sizeof(echan->name)); } else { - BLI_strncpy(echan->name, passname, EXR_TOT_MAXNAME - 1); + BLI_strncpy(echan->name, echan->m->name.c_str(), sizeof(echan->name)); } echan->xstride = xstride; echan->ystride = ystride; echan->rect = rect; - // printf("added channel %s\n", echan->name); + exr_printf("added channel %s\n", echan->name); BLI_addtail(&data->channels, echan); } -/* only used for writing temp. render results (not image files) */ +/* used for output files (from RenderResult) (single and multilayer, single and multiview) */ int IMB_exr_begin_write(void *handle, const char *filename, int width, int height, int compress) { ExrHandle *data = (ExrHandle *)handle; @@ -617,6 +830,8 @@ int IMB_exr_begin_write(void *handle, const char *filename, int width, int heigh data->width = width; data->height = height; + bool is_singlelayer, is_multilayer, is_multiview; + for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) header.channels().insert(echan->name, Channel(Imf::FLOAT)); @@ -624,7 +839,13 @@ int IMB_exr_begin_write(void *handle, const char *filename, int width, int heigh // openexr_header_metadata(&header, ibuf); // no imbuf. cant write /* header.lineOrder() = DECREASING_Y; this crashes in windows for file read! */ - header.insert("BlenderMultiChannel", StringAttribute("Blender V2.55.1 and newer")); + imb_exr_type_by_channels(header.channels(), *data->multiView, &is_singlelayer, &is_multilayer, &is_multiview); + + if (is_multilayer) + header.insert("BlenderMultiChannel", StringAttribute("Blender V2.55.1 and newer")); + + if (is_multiview) + addMultiView(header, *data->multiView); /* avoid crash/abort when we don't have permission to write here */ /* manually create ofstream, so we can handle utf-8 filepaths on windows */ @@ -632,7 +853,7 @@ int IMB_exr_begin_write(void *handle, const char *filename, int width, int heigh data->ofile_stream = new OFileStream(filename); data->ofile = new OutputFile(*(data->ofile_stream), header); } - catch (const std::exception &exc) { + catch (const std::exception& exc) { std::cerr << "IMB_exr_begin_write: ERROR: " << exc.what() << std::endl; delete data->ofile; @@ -645,10 +866,13 @@ int IMB_exr_begin_write(void *handle, const char *filename, int width, int heigh return (data->ofile != NULL); } +/* only used for writing temp. render results (not image files) + * (FSA and Save Buffers) */ void IMB_exrtile_begin_write(void *handle, const char *filename, int mipmap, int width, int height, int tilex, int tiley) { ExrHandle *data = (ExrHandle *)handle; Header header(width, height); + std::vector<Header> headers; ExrChannel *echan; data->tilex = tilex; @@ -657,26 +881,47 @@ void IMB_exrtile_begin_write(void *handle, const char *filename, int mipmap, int data->height = height; data->mipmap = mipmap; - for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) - header.channels().insert(echan->name, Channel(Imf::FLOAT)); - header.setTileDescription(TileDescription(tilex, tiley, (mipmap) ? MIPMAP_LEVELS : ONE_LEVEL)); - header.lineOrder() = RANDOM_Y; header.compression() = RLE_COMPRESSION; + header.setType(TILEDIMAGE); header.insert("BlenderMultiChannel", StringAttribute("Blender V2.43")); + int numparts = data->multiView->size(); + + /* copy header from all parts of input to our header array + * those temporary files have one part per view */ + for (int i = 0; i < numparts; i++) { + headers.push_back (header); + headers[headers.size() - 1].setView((*(data->multiView))[i]); + headers[headers.size() - 1].setName((*(data->multiView))[i]); + } + + exr_printf("\nIMB_exrtile_begin_write\n"); + exr_printf("%s %-6s %-22s \"%s\"\n", "p", "view", "name", "internal_name"); + exr_printf("---------------------------------------------------------------\n"); + + /* assign channels */ + for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { + + echan->m->internal_name = echan->m->name; + echan->m->part_number = echan->view_id; + + headers[echan->view_id].channels().insert(echan->m->internal_name, Channel(Imf::FLOAT)); + exr_printf("%d %-6s %-22s \"%s\"\n", echan->m->part_number, echan->m->view.c_str(), echan->m->name.c_str(), echan->m->internal_name.c_str()); + } + /* avoid crash/abort when we don't have permission to write here */ /* manually create ofstream, so we can handle utf-8 filepaths on windows */ try { data->ofile_stream = new OFileStream(filename); - data->tofile = new TiledOutputFile(*(data->ofile_stream), header); + data->mpofile = new MultiPartOutputFile(*(data->ofile_stream), &headers[0], headers.size()); } catch (const std::exception &) { - delete data->tofile; + delete data->mpofile; delete data->ofile_stream; - data->tofile = NULL; + data->mpofile = NULL; data->ofile_stream = NULL; } } @@ -685,12 +930,13 @@ void IMB_exrtile_begin_write(void *handle, const char *filename, int mipmap, int int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *height) { ExrHandle *data = (ExrHandle *)handle; + ExrChannel *echan; if (BLI_exists(filename) && BLI_file_size(filename) > 32) { /* 32 is arbitrary, but zero length files crashes exr */ /* avoid crash/abort when we don't have permission to write here */ try { data->ifile_stream = new IFileStream(filename); - data->ifile = new InputFile(*(data->ifile_stream)); + data->ifile = new MultiPartInputFile(*(data->ifile_stream)); } catch (const std::exception &) { delete data->ifile; @@ -701,14 +947,24 @@ int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *heig } if (data->ifile) { - Box2i dw = data->ifile->header().dataWindow(); + Box2i dw = data->ifile->header(0).dataWindow(); data->width = *width = dw.max.x - dw.min.x + 1; data->height = *height = dw.max.y - dw.min.y + 1; - const ChannelList &channels = data->ifile->header().channels(); + imb_exr_get_views(*data->ifile, *data->multiView); + + std::vector<MultiViewChannelName> channels; + GetChannelsInMultiPartFile(*data->ifile, channels); - for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) - IMB_exr_add_channel(data, NULL, i.name(), 0, 0, NULL); + for (size_t i = 0; i < channels.size(); i++) { + IMB_exr_add_channel(data, NULL, channels[i].name.c_str(), channels[i].view.c_str(), 0, 0, NULL); + + echan = (ExrChannel *)data->channels.last; + echan->m->name = channels[i].name; + echan->m->view = channels[i].view; + echan->m->part_number = channels[i].part_number; + echan->m->internal_name = channels[i].internal_name; + } return 1; } @@ -717,6 +973,7 @@ int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *heig } /* still clumsy name handling, layers/channels can be ordered as list in list later */ +/* passname here is the raw channel name without the layer */ void IMB_exr_set_channel(void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect) { ExrHandle *data = (ExrHandle *)handle; @@ -741,37 +998,53 @@ void IMB_exr_set_channel(void *handle, const char *layname, const char *passname echan->rect = rect; } else - printf("IMB_exrtile_set_channel error %s\n", name); -} - -void IMB_exrtile_clear_channels(void *handle) -{ - ExrHandle *data = (ExrHandle *)handle; - BLI_freelistN(&data->channels); + printf("IMB_exr_set_channel error %s\n", name); } -void IMB_exrtile_write_channels(void *handle, int partx, int party, int level) +float *IMB_exr_channel_rect(void *handle, const char *layname, const char *passname, const char *viewname) { ExrHandle *data = (ExrHandle *)handle; - FrameBuffer frameBuffer; ExrChannel *echan; + char name[EXR_TOT_MAXNAME + 1]; - for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { - float *rect = echan->rect - echan->xstride * partx - echan->ystride * party; + if (layname) { + char lay[EXR_LAY_MAXNAME + 1], pass[EXR_PASS_MAXNAME + 1]; + BLI_strncpy(lay, layname, EXR_LAY_MAXNAME); + BLI_strncpy(pass, passname, EXR_PASS_MAXNAME); - frameBuffer.insert(echan->name, Slice(Imf::FLOAT, (char *)rect, - echan->xstride * sizeof(float), echan->ystride * sizeof(float))); + BLI_snprintf(name, sizeof(name), "%s.%s", lay, pass); } + else + BLI_strncpy(name, passname, EXR_TOT_MAXNAME - 1); - data->tofile->setFrameBuffer(frameBuffer); - - try { - // printf("write tile %d %d\n", partx/data->tilex, party/data->tiley); - data->tofile->writeTile(partx / data->tilex, party / data->tiley, level); + /* name has to be unique, thus it's a combination of layer, pass, view, and channel */ + if (layname && layname[0] != '\0') { + std::string raw_name = imb_exr_insert_view_name(name, viewname); + BLI_strncpy(name, raw_name.c_str(), sizeof(name)); } - catch (const std::exception &exc) { - std::cerr << "OpenEXR-writeTile: ERROR: " << exc.what() << std::endl; + else if (data->multiView->size() > 1) { + size_t view_id = std::max(0, imb_exr_get_multiView_id(*data->multiView, viewname)); + std::string raw_name = insertViewName(name, *data->multiView, view_id); + BLI_strncpy(name, raw_name.c_str(), sizeof(name)); } + + echan = (ExrChannel *)BLI_findstring(&data->channels, name, offsetof(ExrChannel, name)); + + if (echan) + return echan->rect; + + return NULL; +} + +void IMB_exr_clear_channels(void *handle) +{ + ExrHandle *data = (ExrHandle *)handle; + ExrChannel *chan; + + for (chan = (ExrChannel *)data->channels.first; chan; chan = chan->next) + delete chan->m; + + BLI_freelistN(&data->channels); } void IMB_exr_write_channels(void *handle) @@ -793,7 +1066,7 @@ void IMB_exr_write_channels(void *handle) try { data->ofile->writePixels(data->height); } - catch (const std::exception &exc) { + catch (const std::exception& exc) { std::cerr << "OpenEXR-writePixels: ERROR: " << exc.what() << std::endl; } } @@ -802,49 +1075,171 @@ void IMB_exr_write_channels(void *handle) } } -void IMB_exr_read_channels(void *handle) +/* temporary function, used for FSA and Save Buffers */ +/* called once per tile * view */ +void IMB_exrtile_write_channels(void *handle, int partx, int party, int level, const char *viewname) { ExrHandle *data = (ExrHandle *)handle; FrameBuffer frameBuffer; ExrChannel *echan; + std::string view(viewname); + const size_t view_id = imb_exr_get_multiView_id(*data->multiView, view); + + exr_printf("\nIMB_exrtile_write_channels(view: %s)\n", viewname); + exr_printf("%s %-6s %-22s \"%s\"\n", "p", "view", "name", "internal_name"); + exr_printf("---------------------------------------------------------------------\n"); + + for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { + + /* eventually we can make the parts' channels to include + only the current view TODO */ + if (strcmp(viewname, echan->m->view.c_str()) != 0) + continue; + + exr_printf("%d %-6s %-22s \"%s\"\n", + echan->m->part_number, + echan->m->view.c_str(), + echan->m->name.c_str(), + echan->m->internal_name.c_str() + ); + + float *rect = echan->rect - echan->xstride * partx - echan->ystride * party; + frameBuffer.insert(echan->m->internal_name, + Slice(Imf::FLOAT, + (char *)rect, + echan->xstride * sizeof(float), + echan->ystride * sizeof(float) + ) + ); + } + + TiledOutputPart out (*data->mpofile, view_id); + out.setFrameBuffer(frameBuffer); + + try { + // printf("write tile %d %d\n", partx/data->tilex, party/data->tiley); + out.writeTile(partx / data->tilex, party / data->tiley, level); + } + catch (const std::exception& exc) { + std::cerr << "OpenEXR-writeTile: ERROR: " << exc.what() << std::endl; + } +} + +/* called only when handle has all views */ +void IMB_exrmultiview_write_channels(void *handle, const char *viewname) +{ + ExrHandle *data = (ExrHandle *)handle; + const size_t view_id = viewname ? imb_exr_get_multiView_id(*data->multiView, viewname) : -1; + int numparts = (view_id == -1 ? data->parts : view_id + 1); + std::vector <FrameBuffer> frameBuffers(numparts); + std::vector <OutputPart> outputParts; + ExrChannel *echan; + int i, part; + + if (data->channels.first == NULL) + return; + + exr_printf("\nIMB_exrmultiview_write_channels()\n"); + + for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { + if (view_id != -1 && echan->view_id != view_id) + continue; + + part = (view_id == -1 ? echan->m->part_number : echan->view_id); + + /* last scanline, stride negative */ + float *rect = echan->rect + echan->xstride * (data->height - 1) * data->width; + frameBuffers[part].insert(echan->m->internal_name, + Slice(Imf::FLOAT, + (char *)rect, + echan->xstride * sizeof(float), + -echan->ystride * sizeof(float)) + ); + } + + for (i = 0; i < numparts; i++) { + OutputPart out(*data->mpofile, i); + out.setFrameBuffer(frameBuffers[i]); + outputParts.push_back(out); + } + + try { + for (i = 0; i < numparts; i++) { + if (view_id != -1 && i != view_id) + continue; + + outputParts[i].writePixels(data->height); + } + } + catch (const std::exception& exc) { + std::cerr << "OpenEXR-write Multi Part: ERROR: " << exc.what() << std::endl; + } +} + +void IMB_exr_read_channels(void *handle) +{ + ExrHandle *data = (ExrHandle *)handle; + ExrChannel *echan; + int numparts = data->ifile->parts(); + std::vector<FrameBuffer> frameBuffers(numparts); + std::vector<InputPart> inputParts; /* check if exr was saved with previous versions of blender which flipped images */ - const StringAttribute *ta = data->ifile->header().findTypedAttribute <StringAttribute> ("BlenderMultiChannel"); + const StringAttribute *ta = data->ifile->header(0).findTypedAttribute <StringAttribute> ("BlenderMultiChannel"); short flip = (ta && STREQLEN(ta->value().c_str(), "Blender V2.43", 13)); /* 'previous multilayer attribute, flipped */ + exr_printf("\nIMB_exr_read_channels\n%s %-6s %-22s \"%s\"\n---------------------------------------------------------------------\n", "p", "view", "name", "internal_name"); + for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { + exr_printf("%d %-6s %-22s \"%s\"\n", echan->m->part_number, echan->m->view.c_str(), echan->m->name.c_str(), echan->m->internal_name.c_str()); if (echan->rect) { if (flip) - frameBuffer.insert(echan->name, Slice(Imf::FLOAT, (char *)echan->rect, + frameBuffers[echan->m->part_number].insert(echan->m->internal_name, Slice(Imf::FLOAT, (char *)echan->rect, echan->xstride * sizeof(float), echan->ystride * sizeof(float))); else - frameBuffer.insert(echan->name, Slice(Imf::FLOAT, (char *)(echan->rect + echan->xstride * (data->height - 1) * data->width), + frameBuffers[echan->m->part_number].insert(echan->m->internal_name, Slice(Imf::FLOAT, (char *)(echan->rect + echan->xstride * (data->height - 1) * data->width), echan->xstride * sizeof(float), -echan->ystride * sizeof(float))); } else - printf("warning, channel with no rect set %s\n", echan->name); + printf("warning, channel with no rect set %s\n", echan->m->internal_name.c_str()); } - data->ifile->setFrameBuffer(frameBuffer); + for (int i = 0; i < numparts; i++) { + InputPart in (*data->ifile, i); + in.setFrameBuffer(frameBuffers[i]); + inputParts.push_back(in); + } try { - data->ifile->readPixels(0, data->height - 1); + for (int i = 0; i < numparts; i++) { + Header header = inputParts[i].header(); + exr_printf("readPixels:readPixels[%d]: min.y: %d, max.y: %d\n", i, header.dataWindow().min.y, header.dataWindow().max.y); + inputParts[i].readPixels(header.dataWindow().min.y, header.dataWindow().max.y); + inputParts[i].readPixels(0, data->height - 1); + } } - catch (const std::exception &exc) { + catch (const std::exception& exc) { std::cerr << "OpenEXR-readPixels: ERROR: " << exc.what() << std::endl; } } void IMB_exr_multilayer_convert(void *handle, void *base, + void * (*addview)(void *base, const char *str), void * (*addlayer)(void *base, const char *str), void (*addpass)(void *base, void *lay, const char *str, - float *rect, int totchan, const char *chan_id)) + float *rect, int totchan, const char *chan_id, + const char *view)) { ExrHandle *data = (ExrHandle *)handle; ExrLayer *lay; ExrPass *pass; + /* add views to RenderResult */ + for (StringVector::const_iterator i = data->multiView->begin(); i != data->multiView->end(); ++i) { + addview(base, (*i).c_str()); + } + if (BLI_listbase_is_empty(&data->layers)) { printf("cannot convert multilayer, no layers in handle\n"); return; @@ -854,32 +1249,98 @@ void IMB_exr_multilayer_convert(void *handle, void *base, void *laybase = addlayer(base, lay->name); if (laybase) { for (pass = (ExrPass *)lay->passes.first; pass; pass = pass->next) { - addpass(base, laybase, pass->name, pass->rect, pass->totchan, pass->chan_id); + addpass(base, laybase, pass->internal_name, pass->rect, pass->totchan, pass->chan_id, pass->view); pass->rect = NULL; } } } } +void IMB_exr_multiview_convert(void *handle, void *base, + void (*addview)(void *base, const char *str), + void (*addbuffer)(void *base, const char *str, ImBuf *ibuf, const int frame), + const int frame) +{ + ExrHandle *data = (ExrHandle *)handle; + MultiPartInputFile *file = data->ifile; + ExrLayer *lay; + ExrPass *pass; + ImBuf *ibuf = NULL; + const int is_alpha = exr_has_alpha(*file); + Box2i dw = file->header(0).dataWindow(); + const size_t width = dw.max.x - dw.min.x + 1; + const size_t height = dw.max.y - dw.min.y + 1; + const bool is_depth = exr_has_zbuffer(*file); + + /* add views to RenderResult */ + for (StringVector::const_iterator i = data->multiView->begin(); i != data->multiView->end(); ++i) { + addview(base, (*i).c_str()); + } + + if (BLI_listbase_is_empty(&data->layers)) { + printf("cannot convert multiviews, no views in handle\n"); + return; + } + + /* there is one float/pass per layer (layer here is a view) */ + BLI_assert(BLI_listbase_count_ex(&data->layers, 2) == 1); + lay = (ExrLayer *)data->layers.first; + for (pass = (ExrPass *)lay->passes.first; pass; pass = pass->next) { + if (STREQ(pass->chan_id, "RGB") || STREQ(pass->chan_id, "RGBA")) { + ibuf = IMB_allocImBuf(width, height, is_alpha ? 32 : 24, IB_rectfloat); + + if (!ibuf) { + printf("error creating multiview buffer\n"); + return; + } + + IMB_buffer_float_from_float( + ibuf->rect_float, pass->rect, pass->totchan, + IB_PROFILE_LINEAR_RGB, IB_PROFILE_LINEAR_RGB, false, + ibuf->x, ibuf->y, ibuf->x, ibuf->x); + + if (hasXDensity(file->header(0))) { + ibuf->ppm[0] = xDensity(file->header(0)) * 39.3700787f; + ibuf->ppm[1] = ibuf->ppm[0] * (double)file->header(0).pixelAspectRatio(); + } + + if (is_depth) { + ExrPass *zpass; + for (zpass = (ExrPass *)lay->passes.first; zpass; zpass = zpass->next) { + if (STREQ(zpass->chan_id, "Z") && STREQ(zpass->view, pass->view)) { + addzbuffloatImBuf(ibuf); + memcpy(ibuf->zbuf_float, zpass->rect, sizeof(float) * ibuf->x * ibuf->y); + } + } + } + + addbuffer(base, pass->view, ibuf, frame); + } + } +} void IMB_exr_close(void *handle) { ExrHandle *data = (ExrHandle *)handle; ExrLayer *lay; ExrPass *pass; + ExrChannel *chan; delete data->ifile; delete data->ifile_stream; delete data->ofile; - delete data->tofile; + delete data->mpofile; delete data->ofile_stream; data->ifile = NULL; data->ifile_stream = NULL; data->ofile = NULL; - data->tofile = NULL; + data->mpofile = NULL; data->ofile_stream = NULL; + for (chan = (ExrChannel *)data->channels.first; chan; chan = chan->next) { + delete chan->m; + } BLI_freelistN(&data->channels); for (lay = (ExrLayer *)data->layers.first; lay; lay = lay->next) { @@ -897,7 +1358,7 @@ void IMB_exr_close(void *handle) /* ********* */ /* get a substring from the end of the name, separated by '.' */ -static int imb_exr_split_token(const char *str, const char *end, const char **token) +int IMB_exr_split_token(const char *str, const char *end, const char **token) { ptrdiff_t maxlen = end - str; int len = 0; @@ -911,7 +1372,7 @@ static int imb_exr_split_token(const char *str, const char *end, const char **to static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *passname) { - const char *name = echan->name; + const char *name = echan->m->name.c_str(); const char *end = name + strlen(name); const char *token; char tokenbuf[EXR_TOT_MAXNAME]; @@ -933,7 +1394,7 @@ static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *pa } /* last token is single character channel identifier */ - len = imb_exr_split_token(name, end, &token); + len = IMB_exr_split_token(name, end, &token); if (len == 0) { printf("multilayer read: bad channel name: %s\n", name); return 0; @@ -971,7 +1432,7 @@ static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *pa end -= len + 1; /* +1 to skip '.' separator */ /* second token is pass name */ - len = imb_exr_split_token(name, end, &token); + len = IMB_exr_split_token(name, end, &token); if (len == 0) { printf("multilayer read: bad channel name: %s\n", name); return 0; @@ -1020,7 +1481,7 @@ static ExrPass *imb_exr_get_pass(ListBase *lb, char *passname) } /* creates channels, makes a hierarchy and assigns memory to channels */ -static ExrHandle *imb_exr_begin_read_mem(InputFile *file, int width, int height) +static ExrHandle *imb_exr_begin_read_mem(MultiPartInputFile& file, int width, int height) { ExrLayer *lay; ExrPass *pass; @@ -1029,30 +1490,57 @@ static ExrHandle *imb_exr_begin_read_mem(InputFile *file, int width, int height) int a; char layname[EXR_TOT_MAXNAME], passname[EXR_TOT_MAXNAME]; - data->ifile = file; + data->ifile = &file; data->width = width; data->height = height; - const ChannelList &channels = data->ifile->header().channels(); + std::vector<MultiViewChannelName> channels; + GetChannelsInMultiPartFile(*data->ifile, channels); - for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) - IMB_exr_add_channel(data, NULL, i.name(), 0, 0, NULL); + data->multiView = new StringVector(); + imb_exr_get_views(*data->ifile, *data->multiView); + + for (size_t i = 0; i < channels.size(); i++) { + IMB_exr_add_channel(data, NULL, channels[i].name.c_str(), channels[i].view.c_str(), 0, 0, NULL); + + echan = (ExrChannel *)data->channels.last; + echan->m->name = channels[i].name; + echan->m->view = channels[i].view; + echan->m->part_number = channels[i].part_number; + echan->m->internal_name = channels[i].internal_name; + } /* now try to sort out how to assign memory to the channels */ /* first build hierarchical layer list */ for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) { - if (imb_exr_split_channel_name(echan, layname, passname) ) { + if (imb_exr_split_channel_name(echan, layname, passname)) { + + const char *view = echan->m->view.c_str(); + char internal_name[EXR_PASS_MAXNAME]; + + BLI_strncpy(internal_name, passname, EXR_PASS_MAXNAME); + + if (view[0] != '\0') { + char tmp_pass[EXR_PASS_MAXNAME]; + BLI_snprintf(tmp_pass, sizeof(tmp_pass), "%s.%s", passname, view); + BLI_strncpy(passname, tmp_pass, sizeof(passname)); + } + ExrLayer *lay = imb_exr_get_layer(&data->layers, layname); ExrPass *pass = imb_exr_get_pass(&lay->passes, passname); pass->chan[pass->totchan] = echan; pass->totchan++; + pass->view_id = echan->view_id; + BLI_strncpy(pass->view, view, sizeof(pass->view)); + BLI_strncpy(pass->internal_name, internal_name, EXR_PASS_MAXNAME); + if (pass->totchan >= EXR_PASS_MAXCHAN) break; } } if (echan) { - printf("error, too many channels in one pass: %s\n", echan->name); + printf("error, too many channels in one pass: %s\n", echan->m->name.c_str()); IMB_exr_close(data); return NULL; } @@ -1122,20 +1610,52 @@ static ExrHandle *imb_exr_begin_read_mem(InputFile *file, int width, int height) /* ********************************************************* */ /* debug only */ -static void exr_print_filecontents(InputFile *file) +static void exr_printf(const char *fmt, ...) { - const ChannelList &channels = file->header().channels(); +#if 0 + char output[1024]; + va_list args; + va_start(args, fmt); + std::vsprintf(output, fmt, args); + va_end(args); + printf("%s", output); +#else + (void)fmt; +#endif +} - for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { - const Channel &channel = i.channel(); - printf("OpenEXR-load: Found channel %s of type %d\n", i.name(), channel.type); +static void exr_print_filecontents(MultiPartInputFile& file) +{ + int numparts = file.parts(); + if (numparts == 1 && hasMultiView(file.header(0))) { + const StringVector views = multiView(file.header(0)); + printf("OpenEXR-load: MultiView file\n"); + printf("OpenEXR-load: Default view: %s\n", defaultViewName(views).c_str()); + for (StringVector::const_iterator i = views.begin(); i != views.end(); ++i) { + printf("OpenEXR-load: Found view %s\n", (*i).c_str()); + } + } + else if (numparts > 1) { + printf("OpenEXR-load: MultiPart file\n"); + for (int i = 0; i < numparts; i++) { + if (file.header(i).hasView()) + printf("OpenEXR-load: Part %d: view = \"%s\"\n", i, file.header(i).view().c_str()); + } + } + + for (int j = 0; j < numparts; j++) { + const ChannelList& channels = file.header(j).channels(); + for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { + const Channel& channel = i.channel(); + printf("OpenEXR-load: Found channel %s of type %d\n", i.name(), channel.type); + } } } /* for non-multilayer, map R G B A channel names to something that's in this file */ -static const char *exr_rgba_channelname(InputFile *file, const char *chan) +static const char *exr_rgba_channelname(MultiPartInputFile& file, const char *chan) { - const ChannelList &channels = file->header().channels(); + const ChannelList& channels = file.header(0).channels(); for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) { /* const Channel &channel = i.channel(); */ /* Not used yet */ @@ -1150,48 +1670,48 @@ static const char *exr_rgba_channelname(InputFile *file, const char *chan) return chan; } -static bool exr_has_rgb(InputFile *file) +static bool exr_has_rgb(MultiPartInputFile& file) { - return file->header().channels().findChannel("R") != NULL && - file->header().channels().findChannel("G") != NULL && - file->header().channels().findChannel("B") != NULL; + return file.header(0).channels().findChannel("R") != NULL && + file.header(0).channels().findChannel("G") != NULL && + file.header(0).channels().findChannel("B") != NULL; } -static bool exr_has_luma(InputFile *file) +static bool exr_has_luma(MultiPartInputFile& file) { /* Y channel is the luma and should always present fir luma space images, * optionally it could be also channels for chromas called BY and RY. */ - return file->header().channels().findChannel("Y") != NULL; + return file.header(0).channels().findChannel("Y") != NULL; } -static bool exr_has_chroma(InputFile *file) +static bool exr_has_chroma(MultiPartInputFile& file) { - return file->header().channels().findChannel("BY") != NULL && - file->header().channels().findChannel("RY") != NULL; + return file.header(0).channels().findChannel("BY") != NULL && + file.header(0).channels().findChannel("RY") != NULL; } -static int exr_has_zbuffer(InputFile *file) +static int exr_has_zbuffer(MultiPartInputFile& file) { - return !(file->header().channels().findChannel("Z") == NULL); + return !(file.header(0).channels().findChannel("Z") == NULL); } -static int exr_has_alpha(InputFile *file) +static int exr_has_alpha(MultiPartInputFile& file) { - return !(file->header().channels().findChannel("A") == NULL); + return !(file.header(0).channels().findChannel("A") == NULL); } -static bool exr_is_multilayer(InputFile *file) +static bool imb_exr_is_multilayer_file(MultiPartInputFile& file) { - const StringAttribute *comments = file->header().findTypedAttribute<StringAttribute>("BlenderMultiChannel"); - const ChannelList &channels = file->header().channels(); + const StringAttribute *comments = file.header(0).findTypedAttribute<StringAttribute>("BlenderMultiChannel"); + const ChannelList& channels = file.header(0).channels(); std::set <std::string> layerNames; /* will not include empty layer names */ channels.layers(layerNames); if (comments || layerNames.size() > 1) - return 1; + return true; if (layerNames.size()) { /* if layerNames is not empty, it means at least one layer is non-empty, @@ -1206,17 +1726,122 @@ static bool exr_is_multilayer(InputFile *file) size_t pos = layerName.rfind ('.'); if (pos == std::string::npos) - return 1; + return true; } } - return 0; + return false; +} + +static void imb_exr_type_by_channels(ChannelList& channels, StringVector& views, + bool *r_singlelayer, bool *r_multilayer, bool *r_multiview) +{ + std::set <std::string> layerNames; + + *r_singlelayer = true; + *r_multilayer = *r_multiview = false; + + /* will not include empty layer names */ + channels.layers(layerNames); + + if (views.size() && views[0] != "") + *r_multiview = true; + + if (layerNames.size()) { + /* if layerNames is not empty, it means at least one layer is non-empty, + * but it also could be layers without names in the file and such case + * shall be considered a multilayer exr + * + * that's what we do here: test whether there're empty layer names together + * with non-empty ones in the file + */ + for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); i++) + for (std::set<string>::iterator i = layerNames.begin(); i != layerNames.end(); i++) + /* see if any layername differs from a viewname */ + if (imb_exr_get_multiView_id(views, *i) == -1) { + std::string layerName = *i; + size_t pos = layerName.rfind ('.'); + + if (pos != std::string::npos) { + *r_multilayer = true; + *r_singlelayer = false; + return; + } + } + } + else { + *r_singlelayer = true; + *r_multilayer = false; + *r_multiview = false; + } + + BLI_assert(r_singlelayer != r_multilayer); +} + +bool IMB_exr_has_singlelayer_multiview(void *handle) +{ + ExrHandle *data = (ExrHandle *)handle; + MultiPartInputFile *file = data->ifile; + std::set <std::string> layerNames; + const ChannelList& channels = file->header(0).channels(); + const StringAttribute *comments; + + if (exr_has_multiview(*file) == false) + return false; + + comments = file->header(0).findTypedAttribute<StringAttribute>("BlenderMultiChannel"); + + if (comments) + return false; + + /* will not include empty layer names */ + channels.layers(layerNames); + + /* returns false if any layer differs from views list */ + if (layerNames.size()) + for (std::set<string>::iterator i = layerNames.begin(); i != layerNames.end(); i++) + if (imb_exr_get_multiView_id(*data->multiView, *i) == -1) + return false; + + return true; +} + +bool IMB_exr_has_multilayer(void *handle) +{ + ExrHandle *data = (ExrHandle *)handle; + return imb_exr_is_multilayer_file(*data->ifile); +} + +static bool exr_has_multiview(MultiPartInputFile& file) +{ + return hasMultiView(file.header(0)); +} + +static bool exr_has_multipart_file(MultiPartInputFile& file) +{ + return file.parts() > 1; +} + +/* it returns true if the file is multilayer or multiview */ +static bool imb_exr_is_multi(MultiPartInputFile& file) +{ + /* multipart files are treated as multilayer in blender - even if they are single layer openexr with multiview */ + if (exr_has_multipart_file(file)) + return true; + + if (exr_has_multiview(file)) + return true; + + if (imb_exr_is_multilayer_file(file)) + return true; + + return false; } struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { struct ImBuf *ibuf = NULL; - InputFile *file = NULL; + MultiPartInputFile *file = NULL; if (imb_is_a_openexr(mem) == 0) return(NULL); @@ -1226,9 +1851,9 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char { Mem_IStream *membuf = new Mem_IStream(mem, size); bool is_multi; - file = new InputFile(*membuf); + file = new MultiPartInputFile(*membuf); - Box2i dw = file->header().dataWindow(); + Box2i dw = file->header(0).dataWindow(); const int width = dw.max.x - dw.min.x + 1; const int height = dw.max.y - dw.min.y + 1; @@ -1236,38 +1861,38 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char // dw.min.x, dw.min.y, dw.max.x, dw.max.y); if (0) // debug - exr_print_filecontents(file); + exr_print_filecontents(*file); - is_multi = exr_is_multilayer(file); + is_multi = imb_exr_is_multi(*file); /* do not make an ibuf when */ if (is_multi && !(flags & IB_test) && !(flags & IB_multilayer)) { printf("Error: can't process EXR multilayer file\n"); } else { - const int is_alpha = exr_has_alpha(file); + const int is_alpha = exr_has_alpha(*file); ibuf = IMB_allocImBuf(width, height, is_alpha ? 32 : 24, 0); - if (hasXDensity(file->header())) { - ibuf->ppm[0] = xDensity(file->header()) * 39.3700787f; - ibuf->ppm[1] = ibuf->ppm[0] * (double)file->header().pixelAspectRatio(); + if (hasXDensity(file->header(0))) { + ibuf->ppm[0] = xDensity(file->header(0)) * 39.3700787f; + ibuf->ppm[1] = ibuf->ppm[0] * (double)file->header(0).pixelAspectRatio(); } ibuf->ftype = OPENEXR; if (!(flags & IB_test)) { - if (is_multi) { /* only enters with IB_multilayer flag set */ + if (is_multi && ((flags & IB_thumbnail) == 0)) { /* only enters with IB_multilayer flag set */ /* constructs channels for reading, allocates memory in channels */ - ExrHandle *handle = imb_exr_begin_read_mem(file, width, height); + ExrHandle *handle = imb_exr_begin_read_mem(*file, width, height); if (handle) { IMB_exr_read_channels(handle); ibuf->userdata = handle; /* potential danger, the caller has to check for this! */ } } else { - const bool has_rgb = exr_has_rgb(file); - const bool has_luma = exr_has_luma(file); + const bool has_rgb = exr_has_rgb(*file); + const bool has_luma = exr_has_luma(*file); FrameBuffer frameBuffer; float *first; int xstride = sizeof(float) * 4; @@ -1281,27 +1906,27 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char first += 4 * (height - 1) * width; if (has_rgb) { - frameBuffer.insert(exr_rgba_channelname(file, "R"), + frameBuffer.insert(exr_rgba_channelname(*file, "R"), Slice(Imf::FLOAT, (char *) first, xstride, ystride)); - frameBuffer.insert(exr_rgba_channelname(file, "G"), + frameBuffer.insert(exr_rgba_channelname(*file, "G"), Slice(Imf::FLOAT, (char *) (first + 1), xstride, ystride)); - frameBuffer.insert(exr_rgba_channelname(file, "B"), + frameBuffer.insert(exr_rgba_channelname(*file, "B"), Slice(Imf::FLOAT, (char *) (first + 2), xstride, ystride)); } else if (has_luma) { - frameBuffer.insert(exr_rgba_channelname(file, "Y"), + frameBuffer.insert(exr_rgba_channelname(*file, "Y"), Slice(Imf::FLOAT, (char *) first, xstride, ystride)); - frameBuffer.insert(exr_rgba_channelname(file, "BY"), + frameBuffer.insert(exr_rgba_channelname(*file, "BY"), Slice(Imf::FLOAT, (char *) (first + 1), xstride, ystride, 1, 1, 0.5f)); - frameBuffer.insert(exr_rgba_channelname(file, "RY"), + frameBuffer.insert(exr_rgba_channelname(*file, "RY"), Slice(Imf::FLOAT, (char *) (first + 2), xstride, ystride, 1, 1, 0.5f)); } /* 1.0 is fill value, this still needs to be assigned even when (is_alpha == 0) */ - frameBuffer.insert(exr_rgba_channelname(file, "A"), + frameBuffer.insert(exr_rgba_channelname(*file, "A"), Slice(Imf::FLOAT, (char *) (first + 3), xstride, ystride, 1, 1, 1.0f)); - if (exr_has_zbuffer(file)) { + if (exr_has_zbuffer(*file)) { float *firstz; addzbuffloatImBuf(ibuf); @@ -1310,8 +1935,9 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char frameBuffer.insert("Z", Slice(Imf::FLOAT, (char *)firstz, sizeof(float), -width * sizeof(float))); } - file->setFrameBuffer(frameBuffer); - file->readPixels(dw.min.y, dw.max.y); + InputPart in (*file, 0); + in.setFrameBuffer(frameBuffer); + in.readPixels(dw.min.y, dw.max.y); // XXX, ImBuf has no nice way to deal with this. // ideally IM_rect would be used when the caller wants a rect BUT @@ -1325,7 +1951,7 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char if (!has_rgb && has_luma) { size_t a; - if (exr_has_chroma(file)) { + if (exr_has_chroma(*file)) { for (a = 0; a < (size_t) ibuf->x * ibuf->y; ++a) { float *color = ibuf->rect_float + a * 4; ycc_to_rgb(color[0] * 255.0f, color[1] * 255.0f, color[2] * 255.0f, @@ -1351,7 +1977,7 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char } return(ibuf); } - catch (const std::exception &exc) + catch (const std::exception& exc) { std::cerr << exc.what() << std::endl; if (ibuf) IMB_freeImBuf(ibuf); diff --git a/source/blender/imbuf/intern/openexr/openexr_multi.h b/source/blender/imbuf/intern/openexr/openexr_multi.h index 376d2401b1c..45ab0ed37c7 100644 --- a/source/blender/imbuf/intern/openexr/openexr_multi.h +++ b/source/blender/imbuf/intern/openexr/openexr_multi.h @@ -39,8 +39,9 @@ /* This api also supports max 8 channels per pass now. easy to fix! */ #define EXR_LAY_MAXNAME 64 #define EXR_PASS_MAXNAME 64 +#define EXR_VIEW_MAXNAME 64 #define EXR_TOT_MAXNAME 64 -#define EXR_PASS_MAXCHAN 8 +#define EXR_PASS_MAXCHAN 24 #ifdef __cplusplus @@ -48,25 +49,49 @@ extern "C" { #endif void *IMB_exr_get_handle(void); -void IMB_exr_add_channel(void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect); +void *IMB_exr_get_handle_name(const char *name); +void IMB_exr_add_channel(void *handle, const char *layname, const char *passname, const char *view, int xstride, int ystride, float *rect); int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *height); int IMB_exr_begin_write(void *handle, const char *filename, int width, int height, int compress); void IMB_exrtile_begin_write(void *handle, const char *filename, int mipmap, int width, int height, int tilex, int tiley); void IMB_exr_set_channel(void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect); +float *IMB_exr_channel_rect(void *handle, const char *layname, const char *passname, const char *view); void IMB_exr_read_channels(void *handle); void IMB_exr_write_channels(void *handle); -void IMB_exrtile_write_channels(void *handle, int partx, int party, int level); -void IMB_exrtile_clear_channels(void *handle); +void IMB_exrtile_write_channels(void *handle, int partx, int party, int level, const char *viewname); +void IMB_exrmultiview_write_channels(void *handle, const char *viewname); +void IMB_exr_clear_channels(void *handle); -void IMB_exr_multilayer_convert(void *handle, void *base, - void * (*addlayer)(void *base, const char *str), - void (*addpass)(void *base, void *lay, const char *str, float *rect, int totchan, const char *chan_id)); +void IMB_exr_multilayer_convert( + void *handle, void *base, + void * (*addview)(void *base, const char *str), + void * (*addlayer)(void *base, const char *str), + void (*addpass)(void *base, void *lay, const char *str, float *rect, int totchan, + const char *chan_id, const char *view)); + +void IMB_exr_multiview_convert( + void *handle, void *base, + void (*addview)(void *base, const char *str), + void (*addbuffer)(void *base, const char *str, struct ImBuf *ibuf, const int frame), + const int frame); + +bool IMB_exr_multiview_save( + struct ImBuf *ibuf, const char *name, const int flags, const size_t totviews, + const char * (*getview)(void *base, size_t view_id), + struct ImBuf * (*getbuffer)(void *base, const size_t view_id)); void IMB_exr_close(void *handle); +void IMB_exr_add_view(void *handle, const char *name); + +int IMB_exr_split_token(const char *str, const char *end, const char **token); + +bool IMB_exr_has_multilayer(void *handle); +bool IMB_exr_has_singlelayer_multiview(void *handle); + #ifdef __cplusplus } // extern "C" #endif diff --git a/source/blender/imbuf/intern/openexr/openexr_stub.cpp b/source/blender/imbuf/intern/openexr/openexr_stub.cpp index 21fa878c08a..c326efd2427 100644 --- a/source/blender/imbuf/intern/openexr/openexr_stub.cpp +++ b/source/blender/imbuf/intern/openexr/openexr_stub.cpp @@ -31,27 +31,55 @@ #include "openexr_api.h" #include "openexr_multi.h" - +#include "BLI_utildefines.h" /* UNUSED_VARS */ void *IMB_exr_get_handle (void) {return NULL;} -void IMB_exr_add_channel (void *handle, const char *layname, const char *channame, int xstride, int ystride, float *rect) { (void)handle; (void)layname; (void)channame; (void)xstride; (void)ystride; (void)rect; } +void *IMB_exr_get_handle_name (const char *name) {(void)name; return NULL;} +void IMB_exr_add_channel (void *handle, const char *layname, const char *passname, const char *view, int xstride, int ystride, float *rect) { (void)handle; (void)layname; (void)passname; (void)xstride; (void)ystride; (void)rect; } int IMB_exr_begin_read (void *handle, const char *filename, int *width, int *height) { (void)handle; (void)filename; (void)width; (void)height; return 0;} int IMB_exr_begin_write (void *handle, const char *filename, int width, int height, int compress) { (void)handle; (void)filename; (void)width; (void)height; (void)compress; return 0;} void IMB_exrtile_begin_write (void *handle, const char *filename, int mipmap, int width, int height, int tilex, int tiley) { (void)handle; (void)filename; (void)mipmap; (void)width; (void)height; (void)tilex; (void)tiley; } -void IMB_exr_set_channel (void *handle, const char *layname, const char *channame, int xstride, int ystride, float *rect) { (void)handle; (void)layname; (void)channame; (void)xstride; (void)ystride; (void)rect; } +void IMB_exr_set_channel (void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect) { (void)handle; (void)layname; (void)passname; (void)xstride; (void)ystride; (void)rect; } +float *IMB_exr_channel_rect (void *handle, const char *layname, const char *passname, const char *view) { (void)handle; (void)layname; (void)passname; (void)view; return NULL; } void IMB_exr_read_channels (void *handle) { (void)handle; } void IMB_exr_write_channels (void *handle) { (void)handle; } -void IMB_exrtile_write_channels (void *handle, int partx, int party, int level) { (void)handle; (void)partx; (void)party; (void)level; } -void IMB_exrtile_clear_channels (void *handle) { (void)handle; } +void IMB_exrtile_write_channels (void *handle, int partx, int party, int level, const char *viewname) { UNUSED_VARS(handle, partx, party, level, viewname); } +void IMB_exrmultiview_write_channels(void *handle, const char *viewname) { UNUSED_VARS(handle, viewname); } +void IMB_exr_clear_channels (void *handle) { (void)handle; } + +void IMB_exr_multilayer_convert( + void *handle, void *base, + void * (*addview)(void *base, const char *str), + void * (*addlayer)(void *base, const char *str), + void (*addpass)(void *base, void *lay, const char *str, float *rect, int totchan, + const char *chan_id, const char *view)) +{ + UNUSED_VARS(handle, base, addview, addlayer, addpass); +} -void IMB_exr_multilayer_convert (void *handle, void *base, - void * (*addlayer)(void *base, const char *str), - void (*addpass)(void *base, void *lay, const char *str, float *rect, int totchan, const char *chan_id)) +void IMB_exr_multiview_convert( + void *handle, void *base, + void (*addview)(void *base, const char *str), + void (*addbuffer)(void *base, const char *str, struct ImBuf *ibuf, const int frame), const int frame) { - (void)handle; (void)base; (void)addlayer; (void)addpass; + UNUSED_VARS(handle, base, addview, addbuffer, frame); +} + +bool IMB_exr_multiview_save( + struct ImBuf *ibuf, const char *name, const int flags, const size_t totviews, + const char * (*getview)(void *base, size_t view_id), + struct ImBuf * (*getbuffer)(void *base, const size_t view_id)) +{ + UNUSED_VARS(ibuf, name, flags, totviews, getview, getbuffer); + return false; } void IMB_exr_close (void *handle) { (void)handle; } + +void IMB_exr_add_view(void *handle, const char *name) { UNUSED_VARS(handle, name); } +int IMB_exr_split_token(const char *str, const char *end, const char **token) { UNUSED_VARS(str, end, token); return 1; } +bool IMB_exr_has_multilayer(void *handle) { UNUSED_VARS(handle); return false; } +bool IMB_exr_has_singlelayer_multiview(void *handle) { UNUSED_VARS(handle); return false; } diff --git a/source/blender/imbuf/intern/stereoimbuf.c b/source/blender/imbuf/intern/stereoimbuf.c new file mode 100644 index 00000000000..cf9c6b388ff --- /dev/null +++ b/source/blender/imbuf/intern/stereoimbuf.c @@ -0,0 +1,1292 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 by Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Dalai Felinto + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/imbuf/intern/stereoimbuf.c + * \ingroup imbuf + */ + +#include <stddef.h> + +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "IMB_allocimbuf.h" +#include "IMB_filetype.h" +#include "IMB_metadata.h" +#include "IMB_colormanagement_intern.h" + +#include "imbuf.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_threads.h" +#include "BLI_utildefines.h" + +#include "BLI_math.h" + +#include "DNA_userdef_types.h" +#include "DNA_scene_types.h" + +/* prototypes */ +struct Stereo3DData; +static void imb_stereo3d_write_doit(struct Stereo3DData *s3d_data, struct Stereo3dFormat *s3d); +static void imb_stereo3d_read_doit(struct Stereo3DData *s3d_data, struct Stereo3dFormat *s3d); + +typedef struct Stereo3DData { + struct { float *left, *right, *stereo; } rectf; + struct { uchar *left, *right, *stereo; } rect; + size_t x, y, channels; + bool is_float; +} Stereo3DData; + +static void imb_stereo3d_write_anaglyph(Stereo3DData *s3d, enum eStereo3dAnaglyphType mode) +{ + int x, y; + size_t width = s3d->x; + size_t height = s3d->y; + const size_t channels = s3d->channels; + + const int stride_from = width; + const int stride_to = width; + + int anaglyph_encoding[3][3] = { + {0, 1, 1}, + {1, 0, 1}, + {0, 0, 1}, + }; + + int r, g, b; + + r = anaglyph_encoding[mode][0]; + g = anaglyph_encoding[mode][1]; + b = anaglyph_encoding[mode][2]; + + if (s3d->is_float) { + float *rect_left = s3d->rectf.left; + float *rect_right = s3d->rectf.right; + float *rect_to = s3d->rectf.stereo; + + if (channels == 3) { + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * 3; + float *from[2] = { + rect_left + stride_from * y * 3, + rect_right + stride_from * y * 3, + }; + + for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) { + to[0] = from[r][0]; + to[1] = from[g][1]; + to[2] = from[b][2]; + } + } + } + else if (channels == 4) { + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * 4; + float *from[2] = { + rect_left + stride_from * y * 4, + rect_right + stride_from * y * 4, + }; + + for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) { + to[0] = from[r][0]; + to[1] = from[g][1]; + to[2] = from[b][2]; + to[3] = MAX2(from[0][2], from[0][2]); + } + } + } + } + else { + uchar *rect_left = s3d->rect.left; + uchar *rect_right = s3d->rect.right; + uchar *rect_to = s3d->rect.stereo; + + if (channels == 3) { + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * 3; + uchar *from[2] = { + rect_left + stride_from * y * 3, + rect_right + stride_from * y * 3, + }; + + for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) { + to[0] = from[r][0]; + to[1] = from[g][1]; + to[2] = from[b][2]; + } + } + } + else if (channels == 4) { + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * 4; + uchar *from[2] = { + rect_left + stride_from * y * 4, + rect_right + stride_from * y * 4, + }; + + for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) { + to[0] = from[r][0]; + to[1] = from[g][1]; + to[2] = from[b][2]; + to[3] = MAX2(from[0][2], from[0][2]); + } + } + } + } +} + +static void imb_stereo3d_write_interlace(Stereo3DData *s3d, enum eStereo3dInterlaceType mode, const bool swap) +{ + int x, y; + size_t width = s3d->x; + size_t height = s3d->y; + const size_t channels = s3d->channels; + + const int stride_from = width; + const int stride_to = width; + + if (s3d->is_float) { + const float *rect_left = s3d->rectf.left; + const float *rect_right = s3d->rectf.right; + float *rect_to = s3d->rectf.stereo; + + switch (mode) { + case S3D_INTERLACE_ROW: + { + char i = (char) swap; + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * channels; + const float *from[2] = { + rect_left + stride_from * y * channels, + rect_right + stride_from * y * channels, + }; + memcpy(to, from[i], sizeof(float) * channels * stride_from); + i = !i; + } + break; + } + case S3D_INTERLACE_COLUMN: + { + if (channels == 1) { + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y; + const float *from[2] = { + rect_left + stride_from * y, + rect_right + stride_from * y, + }; + + char i = (char) swap; + for (x = 0; x < width; x++, from[0] += 1, from[1] += 1, to += 1) { + to[0] = from[i][0]; + i = !i; + } + } + } + else if (channels == 3) { + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * 3; + const float *from[2] = { + rect_left + stride_from * y * 3, + rect_right + stride_from * y * 3, + }; + + char i = (char) swap; + for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) { + copy_v3_v3(to, from[i]); + i = !i; + } + } + } + else if (channels == 4) { + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * channels; + const float *from[2] = { + rect_left + stride_from * y * channels, + rect_right + stride_from * y * channels, + }; + + char i = (char) swap; + for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) { + copy_v4_v4(to, from[i]); + i = !i; + } + } + } + break; + } + case S3D_INTERLACE_CHECKERBOARD: + { + if (channels == 1) { + char i = (char) swap; + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y; + const float *from[2] = { + rect_left + stride_from * y, + rect_right + stride_from * y, + }; + char j = i; + for (x = 0; x < width; x++, from[0] += 1, from[1] += 1, to += 1) { + to[0] = from[j][0]; + j = !j; + } + i = !i; + } + } + else if (channels == 3) { + char i = (char) swap; + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * 3; + const float *from[2] = { + rect_left + stride_from * y * 3, + rect_right + stride_from * y * 3, + }; + char j = i; + for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) { + copy_v3_v3(to, from[j]); + j = !j; + } + i = !i; + } + } + else if (channels == 4) { + char i = (char) swap; + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * 4; + const float *from[2] = { + rect_left + stride_from * y * 4, + rect_right + stride_from * y * 4, + }; + char j = i; + for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) { + copy_v4_v4(to, from[j]); + j = !j; + } + i = !i; + } + } + break; + } + default: + { + break; + } + } + } + else { + const uchar *rect_left = s3d->rect.left; + const uchar *rect_right = s3d->rect.right; + uchar *rect_to = s3d->rect.stereo; + + switch (mode) { + case S3D_INTERLACE_ROW: + { + char i = (char) swap; + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * channels; + const uchar *from[2] = { + rect_left + stride_from * y * channels, + rect_right + stride_from * y * channels, + }; + memcpy(to, from[i], sizeof(uchar) * channels * stride_from); + i = !i; + } + break; + } + case S3D_INTERLACE_COLUMN: + { + if (channels == 1) { + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y; + const uchar *from[2] = { + rect_left + stride_from * y, + rect_right + stride_from * y, + }; + char i = (char) swap; + for (x = 0; x < width; x++, from[0] += 1, from[1] += 1, to += 1) { + to[0] = from[i][0]; + i = !i; + } + } + } + else if (channels == 3) { + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * 3; + const uchar *from[2] = { + rect_left + stride_from * y * 3, + rect_right + stride_from * y * 3, + }; + char i = (char) swap; + for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) { + copy_v3_v3_char((char *)to, (char *)from[i]); + i = !i; + } + } + } + else if (channels == 4) { + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * 4; + const uchar *from[2] = { + rect_left + stride_from * y * 4, + rect_right + stride_from * y * 4, + }; + char i = (char) swap; + for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) { + copy_v4_v4_char((char *)to, (char *)from[i]); + i = !i; + } + } + } + break; + } + case S3D_INTERLACE_CHECKERBOARD: + { + if (channels == 1) { + char i = (char) swap; + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y; + const uchar *from[2] = { + rect_left + stride_from * y, + rect_right + stride_from * y, + }; + char j = i; + for (x = 0; x < width; x++, from[0] += 1, from[1] += 1, to += 1) { + to[0] = from[j][0]; + j = !j; + } + i = !i; + } + } + else if (channels == 3) { + char i = (char) swap; + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * 3; + const uchar *from[2] = { + rect_left + stride_from * y * 3, + rect_right + stride_from * y * 3, + }; + char j = i; + for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) { + copy_v3_v3_char((char *)to, (char *)from[j]); + j = !j; + } + i = !i; + } + } + else if (channels == 4) { + char i = (char) swap; + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * 4; + const uchar *from[2] = { + rect_left + stride_from * y * 4, + rect_right + stride_from * y * 4, + }; + char j = i; + for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) { + copy_v4_v4_char((char *)to, (char *)from[j]); + j = !j; + } + i = !i; + } + } + break; + } + default: + { + break; + } + } + } +} + +/* stereo3d output (s3d->rectf.stereo) is always unsqueezed */ +static void imb_stereo3d_write_sidebyside(Stereo3DData *s3d, const bool crosseyed) +{ + int y; + size_t width = s3d->x; + size_t height = s3d->y; + const size_t channels = s3d->channels; + + const int stride_from = width; + const int stride_to = width * 2; + + const int l = (int) crosseyed; + const int r = !l; + + if (s3d->is_float) { + const float *rect_left = s3d->rectf.left; + const float *rect_right = s3d->rectf.right; + float *rect_to = s3d->rectf.stereo; + + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * channels; + const float *from[2] = { + rect_left + stride_from * y * channels, + rect_right + stride_from * y * channels, + }; + + memcpy(to, from[l], sizeof(float) * channels * stride_from); + memcpy(to + channels * stride_from, from[r], sizeof(float) * channels * stride_from); + } + } + else { + const uchar *rect_left = s3d->rect.left; + const uchar *rect_right = s3d->rect.right; + uchar *rect_to = s3d->rect.stereo; + + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * channels; + const uchar *from[2] = { + rect_left + stride_from * y * channels, + rect_right + stride_from * y * channels, + }; + + memcpy(to, from[l], sizeof(uchar) * channels * stride_from); + memcpy(to + channels * stride_from, from[r], sizeof(uchar) * channels * stride_from); + } + } +} + +/* stereo3d output (s3d->rectf.stereo) is always unsqueezed */ +static void imb_stereo3d_write_topbottom(Stereo3DData *s3d) +{ + int y; + size_t width = s3d->x; + size_t height = s3d->y; + const size_t channels = s3d->channels; + + const int stride_from = width; + const int stride_to = width; + + if (s3d->is_float) { + const float *rect_left = s3d->rectf.left; + const float *rect_right = s3d->rectf.right; + float *rect_to = s3d->rectf.stereo; + + for (y = 0; y < height; y++) { + float *to = rect_to + stride_to * y * channels; + const float *from[2] = { + rect_left + stride_from * y * channels, + rect_right + stride_from * y * channels, + }; + + memcpy(to, from[1], sizeof(float) * channels * stride_from); + memcpy(to + channels * height * stride_from, from[0], sizeof(float) * channels * stride_from); + } + } + else { + const uchar *rect_left = s3d->rect.left; + const uchar *rect_right = s3d->rect.right; + uchar *rect_to = s3d->rect.stereo; + + for (y = 0; y < height; y++) { + uchar *to = rect_to + stride_to * y * channels; + const uchar *from[2] = { + rect_left + stride_from * y * channels, + rect_right + stride_from * y * channels, + }; + + memcpy(to, from[1], sizeof(uchar) * channels * stride_from); + memcpy(to + channels * height * stride_from, from[0], sizeof(uchar) * channels * stride_from); + } + } +} + +/**************************** dimension utils ****************************************/ + +void IMB_stereo3d_write_dimensions( + const char mode, const bool is_squeezed, const size_t width, const size_t height, + size_t *r_width, size_t *r_height) +{ + switch (mode) { + case S3D_DISPLAY_SIDEBYSIDE: + { + *r_width = is_squeezed ? width : width * 2; + *r_height = height; + break; + } + case S3D_DISPLAY_TOPBOTTOM: + { + *r_width = width; + *r_height = is_squeezed ? height : height * 2; + break; + } + case S3D_DISPLAY_ANAGLYPH: + case S3D_DISPLAY_INTERLACE: + default: + { + *r_width = width; + *r_height = height; + break; + } + } +} + +void IMB_stereo3d_read_dimensions( + const char mode, const bool is_squeezed, const size_t width, const size_t height, + size_t *r_width, size_t *r_height) +{ + switch (mode) { + case S3D_DISPLAY_SIDEBYSIDE: + { + *r_width = is_squeezed ? width / 2 : width; + *r_height = height; + break; + } + case S3D_DISPLAY_TOPBOTTOM: + { + *r_width = width; + *r_height = is_squeezed ? height / 2 : height; + break; + } + case S3D_DISPLAY_ANAGLYPH: + case S3D_DISPLAY_INTERLACE: + default: + { + *r_width = width; + *r_height = height; + break; + } + } +} + +/**************************** un/squeeze frame ****************************************/ + +static void imb_stereo3d_squeeze_ImBuf(ImBuf *ibuf, Stereo3dFormat *s3d, const size_t x, const size_t y) +{ + if (ELEM(s3d->display_mode, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM) == false) + return; + + if ((s3d->flag & S3D_SQUEEZED_FRAME) == 0) + return; + + IMB_scaleImBuf_threaded(ibuf, x, y); +} + +static void imb_stereo3d_unsqueeze_ImBuf(ImBuf *ibuf, Stereo3dFormat *s3d, const size_t x, const size_t y) +{ + if (ELEM(s3d->display_mode, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM) == false) + return; + + if ((s3d->flag & S3D_SQUEEZED_FRAME) == 0) + return; + + IMB_scaleImBuf_threaded(ibuf, x, y); +} + +static void imb_stereo3d_squeeze_rectf(float *rectf, Stereo3dFormat *s3d, const size_t x, const size_t y, const size_t channels) +{ + ImBuf *ibuf; + size_t width, height; + + if (ELEM(s3d->display_mode, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM) == false) + return; + + if ((s3d->flag & S3D_SQUEEZED_FRAME) == 0) + return; + + /* creates temporary imbuf to store the rectf */ + IMB_stereo3d_write_dimensions(s3d->display_mode, false, x, y, &width, &height); + ibuf = IMB_allocImBuf(width, height, channels, IB_rectfloat); + + IMB_buffer_float_from_float( + ibuf->rect_float, rectf, channels, + IB_PROFILE_LINEAR_RGB, IB_PROFILE_LINEAR_RGB, false, + width, height, width, width); + + IMB_scaleImBuf_threaded(ibuf, x, y); + rectf = MEM_dupallocN(ibuf->rect_float); + IMB_freeImBuf(ibuf); +} + +static void imb_stereo3d_squeeze_rect(int *rect, Stereo3dFormat *s3d, const size_t x, const size_t y, const size_t channels) +{ + ImBuf *ibuf; + size_t width, height; + + if (ELEM(s3d->display_mode, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM) == false) + return; + + if ((s3d->flag & S3D_SQUEEZED_FRAME) == 0) + return; + + /* creates temporary imbuf to store the rectf */ + IMB_stereo3d_write_dimensions(s3d->display_mode, false, x, y, &width, &height); + ibuf = IMB_allocImBuf(width, height, channels, IB_rect); + + IMB_buffer_byte_from_byte( + (unsigned char *)ibuf->rect, (unsigned char *)rect, + IB_PROFILE_SRGB, IB_PROFILE_SRGB, false, + width, height, width, width); + + IMB_scaleImBuf_threaded(ibuf, x, y); + rect = MEM_dupallocN(ibuf->rect); + IMB_freeImBuf(ibuf); +} + + +/*************************** preparing to call the write functions **************************/ + +static void imb_stereo3d_data_initialize( + Stereo3DData *s3d_data, const bool is_float, + const size_t x, const size_t y, const size_t channels, + int *rect_left, int *rect_right, int *rect_stereo, + float *rectf_left, float *rectf_right, float *rectf_stereo) +{ + s3d_data->is_float = is_float; + s3d_data->x = x; + s3d_data->y = y; + s3d_data->channels = channels; + s3d_data->rect.left = (uchar *)rect_left; + s3d_data->rect.right = (uchar *)rect_right; + s3d_data->rect.stereo = (uchar *)rect_stereo; + s3d_data->rectf.left = rectf_left; + s3d_data->rectf.right = rectf_right; + s3d_data->rectf.stereo = rectf_stereo; +} + +int *IMB_stereo3d_from_rect( + ImageFormatData *im_format, const size_t x, const size_t y, const size_t channels, + int *rect_left, int *rect_right) +{ + int *r_rect; + Stereo3DData s3d_data = {{NULL}}; + size_t width, height; + const bool is_float = im_format->depth > 8; + + IMB_stereo3d_write_dimensions(im_format->stereo3d_format.display_mode, false, x, y, &width, &height); + r_rect = MEM_mallocN(channels * sizeof(int) * width * height, __func__); + + imb_stereo3d_data_initialize(&s3d_data, is_float, x, y, channels, rect_left, rect_right, r_rect, NULL, NULL, NULL); + imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format); + imb_stereo3d_squeeze_rect(r_rect, &im_format->stereo3d_format, x, y, channels); + + return r_rect; +} + +float *IMB_stereo3d_from_rectf( + ImageFormatData *im_format, const size_t x, const size_t y, const size_t channels, + float *rectf_left, float *rectf_right) +{ + float *r_rectf; + Stereo3DData s3d_data = {{NULL}}; + size_t width, height; + const bool is_float = im_format->depth > 8; + + IMB_stereo3d_write_dimensions(im_format->stereo3d_format.display_mode, false, x, y, &width, &height); + r_rectf = MEM_mallocN(channels * sizeof(float) * width * height, __func__); + + imb_stereo3d_data_initialize(&s3d_data, is_float, x, y, channels, NULL, NULL, NULL, rectf_left, rectf_right, r_rectf); + imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format); + imb_stereo3d_squeeze_rectf(r_rectf, &im_format->stereo3d_format, x, y, channels); + + return r_rectf; +} + +/* left/right are always float */ +ImBuf *IMB_stereo3d_ImBuf(ImageFormatData *im_format, ImBuf *ibuf_left, ImBuf *ibuf_right) +{ + ImBuf *ibuf_stereo = NULL; + Stereo3DData s3d_data = {{NULL}}; + size_t width, height; + const bool is_float = im_format->depth > 8; + + IMB_stereo3d_write_dimensions(im_format->stereo3d_format.display_mode, false, ibuf_left->x, ibuf_left->y, &width, &height); + ibuf_stereo = IMB_allocImBuf(width, height, ibuf_left->planes, (is_float ? IB_rectfloat : IB_rect)); + + /* copy flags for IB_fields and other settings */ + ibuf_stereo->flags = ibuf_left->flags; + + imb_stereo3d_data_initialize( + &s3d_data, is_float, ibuf_left->x, ibuf_left->y, 4, + (int *)ibuf_left->rect, (int *)ibuf_right->rect, (int *)ibuf_stereo->rect, + ibuf_left->rect_float, ibuf_right->rect_float, ibuf_stereo->rect_float); + + imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format); + imb_stereo3d_squeeze_ImBuf(ibuf_stereo, &im_format->stereo3d_format, ibuf_left->x, ibuf_left->y); + + return ibuf_stereo; +} + +static void imb_stereo3d_write_doit(Stereo3DData *s3d_data, Stereo3dFormat *s3d) +{ + switch (s3d->display_mode) { + case S3D_DISPLAY_ANAGLYPH: + imb_stereo3d_write_anaglyph(s3d_data, s3d->anaglyph_type); + break; + case S3D_DISPLAY_INTERLACE: + imb_stereo3d_write_interlace(s3d_data, s3d->interlace_type, (s3d->flag & S3D_INTERLACE_SWAP) != 0); + break; + case S3D_DISPLAY_SIDEBYSIDE: + imb_stereo3d_write_sidebyside(s3d_data, (s3d->flag & S3D_SIDEBYSIDE_CROSSEYED) != 0); + break; + case S3D_DISPLAY_TOPBOTTOM: + imb_stereo3d_write_topbottom(s3d_data); + break; + default: + break; + } +} + +/******************************** reading stereo imbufs **********************/ + +static void imb_stereo3d_read_anaglyph(Stereo3DData *s3d, enum eStereo3dAnaglyphType mode) +{ + int x, y; + size_t width = s3d->x; + size_t height = s3d->y; + const size_t channels = s3d->channels; + + const int stride_from = width; + const int stride_to = width; + + int anaglyph_encoding[3][3] = { + {0, 1, 1}, + {1, 0, 1}, + {0, 0, 1}, + }; + + int r, g, b; + + r = anaglyph_encoding[mode][0]; + g = anaglyph_encoding[mode][1]; + b = anaglyph_encoding[mode][2]; + + if (s3d->is_float) { + float *rect_left = s3d->rectf.left; + float *rect_right = s3d->rectf.right; + float *rect_from = s3d->rectf.stereo; + + if (channels == 3) { + for (y = 0; y < height; y++) { + float *from = rect_from + stride_from * y * 3; + float *to[2] = { + rect_left + stride_to * y * 3, + rect_right + stride_to * y * 3, + }; + + for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) { + to[r][0] = from[0]; + to[g][1] = from[1]; + to[b][2] = from[2]; + } + } + } + else if (channels == 4) { + for (y = 0; y < height; y++) { + float *from = rect_from + stride_from * y * 4; + float *to[2] = { + rect_left + stride_to * y * 4, + rect_right + stride_to * y * 4, + }; + + for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) { + to[r][0] = from[0]; + to[g][1] = from[1]; + to[b][2] = from[2]; + to[0][3] = to[1][3] = from[3]; + } + } + } + } + else { + uchar *rect_left = s3d->rect.left; + uchar *rect_right = s3d->rect.right; + uchar *rect_from = s3d->rect.stereo; + + if (channels == 3) { + for (y = 0; y < height; y++) { + uchar *from = rect_from + stride_from * y * 3; + uchar *to[2] = { + rect_left + stride_to * y * 3, + rect_right + stride_to * y * 3, + }; + + for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) { + to[r][0] = from[0]; + to[g][1] = from[1]; + to[b][2] = from[2]; + } + } + } + else if (channels == 4) { + for (y = 0; y < height; y++) { + uchar *from = rect_from + stride_from * y * 4; + uchar *to[2] = { + rect_left + stride_to * y * 4, + rect_right + stride_to * y * 4, + }; + + for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) { + to[r][0] = from[0]; + to[g][1] = from[1]; + to[b][2] = from[2]; + to[0][3] = to[1][3] = from[3]; + } + } + } + } +} + +static void imb_stereo3d_read_interlace(Stereo3DData *s3d, enum eStereo3dInterlaceType mode, const bool swap) +{ + int x, y; + size_t width = s3d->x; + size_t height = s3d->y; + const size_t channels = s3d->channels; + + const int stride_from = width; + const int stride_to = width; + + if (s3d->is_float) { + float *rect_left = s3d->rectf.left; + float *rect_right = s3d->rectf.right; + const float *rect_from = s3d->rectf.stereo; + + switch (mode) { + case S3D_INTERLACE_ROW: + { + char i = (char) swap; + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y * channels; + float *to[2] = { + rect_left + stride_to * y * channels, + rect_right + stride_to * y * channels, + }; + memcpy(to[i], from, sizeof(float) * channels * stride_to); + i = !i; + } + break; + } + case S3D_INTERLACE_COLUMN: + { + if (channels == 1) { + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y; + float *to[2] = { + rect_left + stride_to * y, + rect_right + stride_to * y, + }; + + char i = (char) swap; + for (x = 0; x < width; x++, from += 1, to[0] += 1, to[1] += 1) { + to[i][0] = from[0]; + i = !i; + } + } + } + else if (channels == 3) { + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y * 3; + float *to[2] = { + rect_left + stride_to * y * 3, + rect_right + stride_to * y * 3, + }; + + char i = (char) swap; + for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) { + copy_v3_v3(to[i], from); + i = !i; + } + } + } + else if (channels == 4) { + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y * channels; + float *to[2] = { + rect_left + stride_to * y * channels, + rect_right + stride_to * y * channels, + }; + + char i = (char) swap; + for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) { + copy_v4_v4(to[i], from); + i = !i; + } + } + } + break; + } + case S3D_INTERLACE_CHECKERBOARD: + { + if (channels == 1) { + char i = (char) swap; + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y; + float *to[2] = { + rect_left + stride_to * y, + rect_right + stride_to * y, + }; + char j = i; + for (x = 0; x < width; x++, from += 1, to[0] += 1, to[1] += 1) { + to[j][0] = from[0]; + j = !j; + } + i = !i; + } + } + else if (channels == 3) { + char i = (char) swap; + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y * 3; + float *to[2] = { + rect_left + stride_to * y * 3, + rect_right + stride_to * y * 3, + }; + char j = i; + for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) { + copy_v3_v3(to[j], from); + j = !j; + } + i = !i; + } + } + else if (channels == 4) { + char i = (char) swap; + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y * 4; + float *to[2] = { + rect_left + stride_to * y * 4, + rect_right + stride_to * y * 4, + }; + char j = i; + for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) { + copy_v4_v4(to[j], from); + j = !j; + } + i = !i; + } + } + break; + } + default: + { + break; + } + } + } + else { + uchar *rect_left = s3d->rect.right; + uchar *rect_right = s3d->rect.left; + const uchar *rect_from = s3d->rect.stereo; + + switch (mode) { + case S3D_INTERLACE_ROW: + { + char i = (char) swap; + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y * channels; + uchar *to[2] = { + rect_left + stride_to * y * channels, + rect_right + stride_to * y * channels, + }; + memcpy(to[i], from, sizeof(uchar) * channels * stride_to); + i = !i; + } + break; + } + case S3D_INTERLACE_COLUMN: + { + if (channels == 1) { + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y; + uchar *to[2] = { + rect_left + stride_to * y, + rect_right + stride_to * y, + }; + char i = (char) swap; + for (x = 0; x < width; x++, from += 1, to[0] += 1, to[1] += 1) { + to[i][0] = from[0]; + i = !i; + } + } + } + else if (channels == 3) { + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y * 3; + uchar *to[2] = { + rect_left + stride_to * y * 3, + rect_right + stride_to * y * 3, + }; + char i = (char) swap; + for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) { + copy_v3_v3_char((char *)to[i], (char *)from); + i = !i; + } + } + } + else if (channels == 4) { + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y * 4; + uchar *to[2] = { + rect_left + stride_to * y * 4, + rect_right + stride_to * y * 4, + }; + char i = (char) swap; + for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) { + copy_v4_v4_char((char *)to[i], (char *)from); + i = !i; + } + } + } + break; + } + case S3D_INTERLACE_CHECKERBOARD: + { + if (channels == 1) { + char i = (char) swap; + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y; + uchar *to[2] = { + rect_left + stride_to * y, + rect_right + stride_to * y, + }; + char j = i; + for (x = 0; x < width; x++, from += 1, to[0] += 1, to[1] += 1) { + to[j][0] = from[0]; + j = !j; + } + i = !i; + } + } + else if (channels == 3) { + char i = (char) swap; + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y * 3; + uchar *to[2] = { + rect_left + stride_to * y * 3, + rect_right + stride_to * y * 3, + }; + char j = i; + for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) { + copy_v3_v3_char((char *)to[j], (char *)from); + j = !j; + } + i = !i; + } + } + else if (channels == 4) { + char i = (char) swap; + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y * 4; + uchar *to[2] = { + rect_left + stride_to * y * 4, + rect_right + stride_to * y * 4, + }; + char j = i; + for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) { + copy_v4_v4_char((char *)to[j], (char *)from); + j = !j; + } + i = !i; + } + } + break; + } + default: + { + break; + } + } + } +} + +/* stereo input (s3d->rectf.stereo) is always unsqueezed */ +static void imb_stereo3d_read_sidebyside(Stereo3DData *s3d, const bool crosseyed) +{ + int y; + size_t width = s3d->x; + size_t height = s3d->y; + const size_t channels = s3d->channels; + + const int stride_from = width * 2; + const int stride_to = width; + + const int l = (int) crosseyed; + const int r = !l; + + if (s3d->is_float) { + float *rect_left = s3d->rectf.left; + float *rect_right = s3d->rectf.right; + const float *rect_from = s3d->rectf.stereo; + + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y * channels; + float *to[2] = { + rect_left + stride_to * y * channels, + rect_right + stride_to * y * channels, + }; + + memcpy(to[l], from, sizeof(float) * channels * stride_to); + memcpy(to[r], from + channels * stride_to, sizeof(float) * channels * stride_to); + } + } + else { + uchar *rect_left = s3d->rect.left; + uchar *rect_right = s3d->rect.right; + const uchar *rect_from = s3d->rect.stereo; + + /* always RGBA input/output */ + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y * channels; + uchar *to[2] = { + rect_left + stride_to * y * channels, + rect_right + stride_to * y * channels, + }; + + memcpy(to[l], from, sizeof(uchar) * channels * stride_to); + memcpy(to[r], from + channels * stride_to, sizeof(uchar) * channels * stride_to); + } + } +} + +/* stereo input (s3d->rectf.stereo) is always unsqueezed */ +static void imb_stereo3d_read_topbottom(Stereo3DData *s3d) +{ + int y; + size_t width = s3d->x; + size_t height = s3d->y; + const size_t channels = s3d->channels; + + const int stride_from = width; + const int stride_to = width; + + if (s3d->is_float) { + float *rect_left = s3d->rectf.left; + float *rect_right = s3d->rectf.right; + const float *rect_from = s3d->rectf.stereo; + + for (y = 0; y < height; y++) { + const float *from = rect_from + stride_from * y * channels; + float *to[2] = { + rect_left + stride_to * y * channels, + rect_right + stride_to * y * channels, + }; + + memcpy(to[1], from, sizeof(float) * channels * stride_to); + memcpy(to[0], from + channels * height * stride_to, sizeof(float) * channels * stride_to); + } + } + else { + uchar *rect_left = s3d->rect.left; + uchar *rect_right = s3d->rect.right; + const uchar *rect_from = s3d->rect.stereo; + + for (y = 0; y < height; y++) { + const uchar *from = rect_from + stride_from * y * channels; + uchar *to[2] = { + rect_left + stride_to * y * channels, + rect_right + stride_to * y * channels, + }; + + memcpy(to[1], from, sizeof(uchar) * channels * stride_to); + memcpy(to[0], from + channels * height * stride_to, sizeof(uchar) * channels * stride_to); + } + } +} + + +/*************************** preparing to call the read functions **************************/ + +/* reading a stereo encoded ibuf (*left) and generating two ibufs from it (*left and *right) */ +void IMB_ImBufFromStereo3d( + Stereo3dFormat *s3d, ImBuf *ibuf_stereo3d, + ImBuf **r_ibuf_left, ImBuf **r_ibuf_right) +{ + Stereo3DData s3d_data = {{NULL}}; + ImBuf *ibuf_left, *ibuf_right; + size_t width, height; + const bool is_float = (ibuf_stereo3d->rect_float != NULL); + + IMB_stereo3d_read_dimensions( + s3d->display_mode, ((s3d->flag & S3D_SQUEEZED_FRAME) == 0), ibuf_stereo3d->x, ibuf_stereo3d->y, + &width, &height); + + ibuf_left = IMB_allocImBuf(width, height, ibuf_stereo3d->planes, (is_float ? IB_rectfloat : IB_rect)); + ibuf_right = IMB_allocImBuf(width, height, ibuf_stereo3d->planes, (is_float ? IB_rectfloat : IB_rect)); + + /* copy flags for IB_fields and other settings */ + ibuf_left->flags = ibuf_stereo3d->flags; + ibuf_right->flags = ibuf_stereo3d->flags; + + /* we always work with unsqueezed formats */ + IMB_stereo3d_write_dimensions( + s3d->display_mode, ((s3d->flag & S3D_SQUEEZED_FRAME) == 0), ibuf_stereo3d->x, ibuf_stereo3d->y, + &width, &height); + imb_stereo3d_unsqueeze_ImBuf(ibuf_stereo3d, s3d, width, height); + + imb_stereo3d_data_initialize( + &s3d_data, is_float, ibuf_left->x, ibuf_left->y, 4, + (int *)ibuf_left->rect, (int *)ibuf_right->rect, (int *)ibuf_stereo3d->rect, + ibuf_left->rect_float, ibuf_right->rect_float, ibuf_stereo3d->rect_float); + + imb_stereo3d_read_doit(&s3d_data, s3d); + + if (ibuf_stereo3d->flags & (IB_zbuf | IB_zbuffloat)) { + if (is_float) { + addzbuffloatImBuf(ibuf_left); + addzbuffloatImBuf(ibuf_right); + } + else { + addzbufImBuf(ibuf_left); + addzbufImBuf(ibuf_right); + } + + imb_stereo3d_data_initialize( + &s3d_data, is_float, ibuf_left->x, ibuf_left->y, 1, + (int *)ibuf_left->zbuf, (int *)ibuf_right->zbuf, (int *)ibuf_stereo3d->zbuf, + ibuf_left->zbuf_float, ibuf_right->zbuf_float, ibuf_stereo3d->zbuf_float); + + imb_stereo3d_read_doit(&s3d_data, s3d); + } + + IMB_freeImBuf(ibuf_stereo3d); + + *r_ibuf_left = ibuf_left; + *r_ibuf_right = ibuf_right; +} + +static void imb_stereo3d_read_doit(Stereo3DData *s3d_data, Stereo3dFormat *s3d) +{ + switch (s3d->display_mode) { + case S3D_DISPLAY_ANAGLYPH: + imb_stereo3d_read_anaglyph(s3d_data, s3d->anaglyph_type); + break; + case S3D_DISPLAY_INTERLACE: + imb_stereo3d_read_interlace(s3d_data, s3d->interlace_type, (s3d->flag & S3D_INTERLACE_SWAP) != 0); + break; + case S3D_DISPLAY_SIDEBYSIDE: + imb_stereo3d_read_sidebyside(s3d_data, (s3d->flag & S3D_SIDEBYSIDE_CROSSEYED) != 0); + break; + case S3D_DISPLAY_TOPBOTTOM: + imb_stereo3d_read_topbottom(s3d_data); + break; + default: + break; + } +} diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c index 118f0405303..21f440a8232 100644 --- a/source/blender/imbuf/intern/thumbs.c +++ b/source/blender/imbuf/intern/thumbs.c @@ -338,7 +338,7 @@ ImBuf *IMB_thumb_create(const char *path, ThumbSize size, ThumbSource source, Im img = IMB_loadblend_thumb(path); } else { - img = IMB_loadiffname(path, IB_rect | IB_metadata, NULL); + img = IMB_loadiffname(path, IB_rect | IB_metadata | IB_thumbnail, NULL); } } diff --git a/source/blender/imbuf/intern/util.c b/source/blender/imbuf/intern/util.c index 32100aa2288..3fee6688014 100644 --- a/source/blender/imbuf/intern/util.c +++ b/source/blender/imbuf/intern/util.c @@ -439,3 +439,15 @@ bool IMB_isanim(const char *filename) return (type && type != ANIM_SEQUENCE); } + +bool IMB_isfloat(ImBuf *ibuf) +{ + ImFileType *type; + + for (type = IMB_FILE_TYPES; type < IMB_FILE_TYPES_LAST; type++) { + if (type->ftype(type, ibuf)) { + return (type->flag & IM_FTYPE_FLOAT) != 0; + } + } + return false; +} diff --git a/source/blender/imbuf/intern/writeimage.c b/source/blender/imbuf/intern/writeimage.c index 087330d10d2..5e9ccf4437e 100644 --- a/source/blender/imbuf/intern/writeimage.c +++ b/source/blender/imbuf/intern/writeimage.c @@ -43,19 +43,7 @@ static ImBuf *prepare_write_imbuf(ImFileType *type, ImBuf *ibuf) { - ImBuf *write_ibuf = ibuf; - - if (type->flag & IM_FTYPE_FLOAT) { - /* pass */ - } - else { - if (ibuf->rect == NULL && ibuf->rect_float) { - ibuf->rect_colorspace = colormanage_colorspace_get_roled(COLOR_ROLE_DEFAULT_BYTE); - IMB_rect_from_float(ibuf); - } - } - - return write_ibuf; + return IMB_prepare_write_ImBuf((type->flag & IM_FTYPE_FLOAT), ibuf); } short IMB_saveiff(struct ImBuf *ibuf, const char *name, int flags) @@ -86,3 +74,19 @@ short IMB_saveiff(struct ImBuf *ibuf, const char *name, int flags) return false; } +ImBuf *IMB_prepare_write_ImBuf(const bool isfloat, ImBuf *ibuf) +{ + ImBuf *write_ibuf = ibuf; + + if (isfloat) { + /* pass */ + } + else { + if (ibuf->rect == NULL && ibuf->rect_float) { + ibuf->rect_colorspace = colormanage_colorspace_get_roled(COLOR_ROLE_DEFAULT_BYTE); + IMB_rect_from_float(ibuf); + } + } + + return write_ibuf; +} diff --git a/source/blender/makesdna/DNA_camera_types.h b/source/blender/makesdna/DNA_camera_types.h index c67a356a708..6a9942aea66 100644 --- a/source/blender/makesdna/DNA_camera_types.h +++ b/source/blender/makesdna/DNA_camera_types.h @@ -44,6 +44,16 @@ struct Object; struct AnimData; struct Ipo; +/* ------------------------------------------- */ +/* Stereo Settings */ +typedef struct CameraStereoSettings { + float interocular_distance; + float convergence_distance; + short convergence_mode; + short pivot; + short pad, pad2; +} CameraStereoSettings; + typedef struct Camera { ID id; struct AnimData *adt; /* animation data (must be immediately after id for utilities to use it) */ @@ -69,6 +79,9 @@ typedef struct Camera { char sensor_fit; char pad[7]; + + /* Stereo settings */ + struct CameraStereoSettings stereo; } Camera; /* **************** CAMERA ********************* */ @@ -123,6 +136,20 @@ enum { #define DEFAULT_SENSOR_WIDTH 32.0f #define DEFAULT_SENSOR_HEIGHT 18.0f +/* stereo->convergence_mode */ +enum { + CAM_S3D_OFFAXIS = 0, + CAM_S3D_PARALLEL = 1, + CAM_S3D_TOE = 2, +}; + +/* stereo->pivot */ +enum { + CAM_S3D_PIVOT_LEFT = 0, + CAM_S3D_PIVOT_RIGHT = 1, + CAM_S3D_PIVOT_CENTER = 2, +}; + #ifdef __cplusplus } #endif diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 7444abb6d11..79980c6d2d5 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -43,7 +43,6 @@ struct MovieCache; struct RenderResult; struct GPUTexture; - /* ImageUser is in Texture, in Nodes, Background Image, Image Window, .... */ /* should be used in conjunction with an ID * to Image. */ typedef struct ImageUser { @@ -53,16 +52,34 @@ typedef struct ImageUser { int frames; /* total amount of frames to use */ int offset, sfra; /* offset within movie, start frame in global time */ char fie_ima, cycl; /* fields/image in movie, cyclic flag */ - char ok, pad; + char ok; + + char multiview_eye; /* multiview current eye - for internal use of drawing routines */ - short multi_index, layer, pass; /* listbase indices, for menu browsing or retrieve buffer */ + short multi_index, view, layer, pass; /* listbase indices, for menu browsing or retrieve buffer */ short flag; - - int pad2; + short passtype; } ImageUser; +typedef struct ImageAnim { + struct ImageAnim *next, *prev; + struct anim *anim; +} ImageAnim; + +typedef struct ImageView { + struct ImageView *next, *prev; + char name[64]; /* MAX_NAME */ + char filepath[1024]; /* 1024 = FILE_MAX */ +} ImageView; + +typedef struct ImagePackedFile { + struct ImagePackedFile *next, *prev; + struct PackedFile *packedfile; + char filepath[1024]; /* 1024 = FILE_MAX */ +} ImagePackedFile; + typedef struct RenderSlot { char name[64]; /* 64 = MAX_NAME */ } RenderSlot; @@ -72,6 +89,7 @@ typedef struct RenderSlot { #define IMA_ANIM_REFRESHED 2 /* #define IMA_DO_PREMUL 4 */ #define IMA_NEED_FRAME_RECALC 8 +#define IMA_SHOW_STEREO 16 typedef struct Image { ID id; @@ -82,13 +100,13 @@ typedef struct Image { struct GPUTexture *gputexture; /* not written in file */ /* sources from: */ - struct anim *anim; + ListBase anims; struct RenderResult *rr; struct RenderResult *renders[8]; /* IMA_MAX_RENDER_SLOT */ short render_slot, last_render_slot; - - short ok, flag; + + int flag; short source, type; int lastframe; @@ -99,14 +117,16 @@ typedef struct Image { unsigned int bindcode; /* only for current image... */ unsigned int *repbind; /* for repeat of parts of images */ - struct PackedFile *packedfile; + struct PackedFile *packedfile; /* deprecated */ + struct ListBase packedfiles; struct PreviewImage *preview; /* game engine tile animation */ float lastupdate; int lastused; short animspeed; - short pad2; + + short ok; /* for generated images */ int gen_x, gen_y; @@ -121,7 +141,14 @@ typedef struct Image { ColorManagedColorspaceSettings colorspace_settings; char alpha_mode; - char pad[7]; + char pad[5]; + + /* Multiview */ + char eye; /* for viewer node stereoscopy */ + char views_format; + ListBase views; + struct Stereo3dFormat *stereo3d_format; + RenderSlot render_slots[8]; /* 8 = IMA_MAX_RENDER_SLOT */ } Image; @@ -143,6 +170,9 @@ enum { IMA_VIEW_AS_RENDER = (1 << 11), IMA_IGNORE_ALPHA = (1 << 12), IMA_DEINTERLACE = (1 << 13), + IMA_USE_VIEWS = (1 << 14), + IMA_IS_STEREO = (1 << 15), + IMA_IS_MULTIVIEW = (1 << 16), /* similar to stereo, but a more general case */ }; #if (DNA_DEPRECATED_GCC_POISON == 1) diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h index 9ab6527daee..2a1a35d158a 100644 --- a/source/blender/makesdna/DNA_node_types.h +++ b/source/blender/makesdna/DNA_node_types.h @@ -263,6 +263,7 @@ typedef struct bNode { */ #define NODE_UPDATE 0xFFFF /* generic update flag (includes all others) */ #define NODE_UPDATE_ID 1 /* associated id data block has changed */ +#define NODE_UPDATE_OPERATOR 2 /* node update triggered from update operator */ /* Unique hash key for identifying node instances * Defined as a struct because DNA does not support other typedefs. diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index ac0803d494c..250bfe85ec2 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -47,6 +47,7 @@ extern "C" { #include "DNA_ID.h" #include "DNA_freestyle_types.h" #include "DNA_gpu_types.h" +#include "DNA_userdef_types.h" struct CurveMapping; struct Object; @@ -249,6 +250,73 @@ typedef enum ScenePassType { /* note, srl->passflag is treestore element 'nr' in outliner, short still... */ +/* View - MultiView */ +typedef struct SceneRenderView { + struct SceneRenderView *next, *prev; + + char name[64]; /* MAX_NAME */ + char suffix[64]; /* MAX_NAME */ + + int viewflag; + int pad[2]; + char pad2[4]; + +} SceneRenderView; + +/* srv->viewflag */ +#define SCE_VIEW_DISABLE (1<<0) + +/* scene.render.views_format */ +enum { + SCE_VIEWS_FORMAT_STEREO_3D = 0, + SCE_VIEWS_FORMAT_MULTIVIEW = 1, +}; + +/* ImageFormatData.views_output */ +enum { + R_IMF_VIEWS_INDIVIDUAL = 0, + R_IMF_VIEWS_STEREO_3D = 1, + R_IMF_VIEWS_MULTIVIEW = 2, +}; + +typedef struct Stereo3dFormat { + short flag; + char display_mode; /* encoding mode */ + char anaglyph_type; /* anaglyph scheme for the user display */ + char interlace_type; /* interlace type for the user display */ + char pad[3]; +} Stereo3dFormat; + +/* Stereo3dFormat.display_mode */ +typedef enum eStereoDisplayMode { + S3D_DISPLAY_ANAGLYPH = 0, + S3D_DISPLAY_INTERLACE = 1, + S3D_DISPLAY_PAGEFLIP = 2, + S3D_DISPLAY_SIDEBYSIDE = 3, + S3D_DISPLAY_TOPBOTTOM = 4, +} eStereoDisplayMode; + +/* Stereo3dFormat.flag */ +typedef enum eStereo3dFlag { + S3D_INTERLACE_SWAP = (1 << 0), + S3D_SIDEBYSIDE_CROSSEYED = (1 << 1), + S3D_SQUEEZED_FRAME = (1 << 2), +} eStereo3dFlag; + +/* Stereo3dFormat.anaglyph_type */ +typedef enum eStereo3dAnaglyphType { + S3D_ANAGLYPH_REDCYAN = 0, + S3D_ANAGLYPH_GREENMAGENTA = 1, + S3D_ANAGLYPH_YELLOWBLUE = 2, +} eStereo3dAnaglyphType; + +/* Stereo3dFormat.interlace_type */ +typedef enum eStereo3dInterlaceType { + S3D_INTERLACE_ROW = 0, + S3D_INTERLACE_COLUMN = 1, + S3D_INTERLACE_CHECKERBOARD = 2, +} eStereo3dInterlaceType; + /* *************************************************************** */ /* Generic image format settings, @@ -286,7 +354,11 @@ typedef struct ImageFormatData { char jp2_flag; char jp2_codec; - char pad[6]; + char pad[5]; + + /* Multiview */ + char views_format; + Stereo3dFormat stereo3d_format; /* color management */ ColorManagedViewSettings view_settings; @@ -616,6 +688,12 @@ typedef struct RenderData { int preview_start_resolution; int pad; + + /* MultiView */ + ListBase views; + short actview; + short views_format; + short pad8[2]; } RenderData; /* *************************************************************** */ @@ -810,6 +888,19 @@ enum { #define UV_SCULPT_TOOL_RELAX_LAPLACIAN 1 #define UV_SCULPT_TOOL_RELAX_HC 2 +/* Stereo Flags */ +#define STEREO_RIGHT_NAME "right" +#define STEREO_LEFT_NAME "left" +#define STEREO_RIGHT_SUFFIX "_R" +#define STEREO_LEFT_SUFFIX "_L" + +typedef enum StereoViews { + STEREO_LEFT_ID = 0, + STEREO_RIGHT_ID = 1, + STEREO_3D_ID = 2, + STEREO_MONO_ID = 3, +} StereoViews; + /* Markers */ typedef struct TimeMarker { @@ -1440,6 +1531,7 @@ typedef struct Scene { #define R_TEXNODE_PREVIEW 0x40000 #define R_VIEWPORT_PREVIEW 0x80000 #define R_EXR_CACHE_FILE 0x100000 +#define R_MULTIVIEW 0x200000 /* r->stamp */ #define R_STAMP_TIME 0x0001 diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h index 46a91cf6ceb..76c27fd3547 100644 --- a/source/blender/makesdna/DNA_sequence_types.h +++ b/source/blender/makesdna/DNA_sequence_types.h @@ -53,6 +53,11 @@ struct MovieClip; /* strlens; 256= FILE_MAXFILE, 768= FILE_MAXDIR */ +typedef struct StripAnim { + struct StripAnim *next, *prev; + struct anim *anim; +} StripAnim; + typedef struct StripElem { char name[256]; int orig_width, orig_height; @@ -156,8 +161,7 @@ typedef struct Sequence { struct Object *scene_camera; /* override scene camera */ struct MovieClip *clip; /* for MOVIECLIP strips */ struct Mask *mask; /* for MASK strips */ - - struct anim *anim; /* for MOVIE strips */ + ListBase anims; /* for MOVIE strips */ float effect_fader; float speed_fader; @@ -187,7 +191,11 @@ typedef struct Sequence { int sfra; /* starting frame according to the timeline of the scene. */ char alpha_mode; - char pad[3]; + char pad[2]; + + /* Multiview */ + char views_format; + struct Stereo3dFormat *stereo3d_format; struct IDProperty *prop; @@ -376,6 +384,7 @@ enum { /* don't include Grease Pencil in OpenGL previews of Scene strips */ SEQ_SCENE_NO_GPENCIL = (1 << 28), + SEQ_USE_VIEWS = (1 << 29), SEQ_INVALID_EFFECT = (1 << 31), }; diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h index dff6d3cacb5..05127f176b4 100644 --- a/source/blender/makesdna/DNA_space_types.h +++ b/source/blender/makesdna/DNA_space_types.h @@ -500,6 +500,9 @@ typedef struct SpaceSeq { struct bGPdata *gpd; /* grease-pencil data */ struct SequencerScopes scopes; /* different scoped displayed in space */ + + char multiview_eye; /* multiview current eye - for internal use */ + char pad2[7]; } SpaceSeq; diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h index 93e61487000..66ee1fe4242 100644 --- a/source/blender/makesdna/DNA_view3d_types.h +++ b/source/blender/makesdna/DNA_view3d_types.h @@ -215,8 +215,10 @@ typedef struct View3D { /* drawflags, denoting state */ char zbuf, transp, xray; + char multiview_eye; /* multiview current eye - for internal use */ + /* built-in shader effects (eGPUFXFlags) */ - char pad3[5]; + char pad3[4]; /* note, 'fx_settings.dof' is currently _not_ allocated, * instead set (temporarily) from camera */ @@ -228,9 +230,21 @@ typedef struct View3D { /* XXX deprecated? */ struct bGPdata *gpd DNA_DEPRECATED; /* Grease-Pencil Data (annotation layers) */ + /* multiview - stereo 3d */ + short stereo3d_flag; + char stereo3d_camera; + char pad4; + float stereo3d_convergence_factor; + float stereo3d_volume_alpha; + float stereo3d_convergence_alpha; } View3D; +/* View3D->stereo_flag (short) */ +#define V3D_S3D_DISPCAMERAS (1 << 0) +#define V3D_S3D_DISPPLANE (1 << 1) +#define V3D_S3D_DISPVOLUME (1 << 2) + /* View3D->flag (short) */ /*#define V3D_DISPIMAGE 1*/ /*UNUSED*/ #define V3D_DISPBGPICS 2 diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h index ebbafb19aa5..dca2faf0d54 100644 --- a/source/blender/makesdna/DNA_windowmanager_types.h +++ b/source/blender/makesdna/DNA_windowmanager_types.h @@ -33,6 +33,7 @@ #include "DNA_listBase.h" #include "DNA_vec_types.h" +#include "DNA_userdef_types.h" #include "DNA_ID.h" @@ -56,6 +57,7 @@ struct PointerRNA; struct ReportList; struct Report; struct uiLayout; +struct Stereo3dFormat; #define OP_MAX_TYPENAME 64 #define KMAP_MAX_NAME 64 @@ -206,7 +208,7 @@ typedef struct wmWindow { struct wmIMEData *ime_data; int drawmethod, drawfail; /* internal for wm_draw.c only */ - void *drawdata; /* internal for wm_draw.c only */ + ListBase drawdata; /* internal for wm_draw.c only */ ListBase queue; /* all events (ghost level events were handled) */ ListBase handlers; /* window+screen handlers, handled last */ @@ -214,6 +216,8 @@ typedef struct wmWindow { ListBase subwindows; /* opengl stuff for sub windows, see notes in wm_subwindow.c */ ListBase gesture; /* gesture stuff */ + + struct Stereo3dFormat *stereo3d_format; /* properties for stereoscopic displays */ } wmWindow; #ifdef ime_data diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index 8aa1bc59b43..cff9e424f31 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -172,6 +172,7 @@ extern StructRNA RNA_CompositorNodeSepYCCA; extern StructRNA RNA_CompositorNodeSepYUVA; extern StructRNA RNA_CompositorNodeSetAlpha; extern StructRNA RNA_CompositorNodeSplitViewer; +extern StructRNA RNA_CompositorNodeSwitchView; extern StructRNA RNA_CompositorNodeTexture; extern StructRNA RNA_CompositorNodeTime; extern StructRNA RNA_CompositorNodeTonemap; @@ -566,6 +567,7 @@ extern StructRNA RNA_SpeedControlSequence; extern StructRNA RNA_Spline; extern StructRNA RNA_SplineIKConstraint; extern StructRNA RNA_SpotLamp; +extern StructRNA RNA_Stereo3dDisplay; extern StructRNA RNA_StretchToConstraint; extern StructRNA RNA_StringProperty; extern StructRNA RNA_Struct; diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h index 4d836601ac4..d3d3578e8b0 100644 --- a/source/blender/makesrna/RNA_enum_types.h +++ b/source/blender/makesrna/RNA_enum_types.h @@ -72,6 +72,13 @@ extern EnumPropertyItem normal_space_items[]; extern EnumPropertyItem normal_swizzle_items[]; extern EnumPropertyItem bake_save_mode_items[]; +extern EnumPropertyItem views_format_items[]; +extern EnumPropertyItem views_format_multilayer_items[]; +extern EnumPropertyItem views_format_multiview_items[]; +extern EnumPropertyItem stereo3d_display_items[]; +extern EnumPropertyItem stereo3d_anaglyph_type_items[]; +extern EnumPropertyItem stereo3d_interlace_type_items[]; + extern EnumPropertyItem exr_codec_items[]; extern EnumPropertyItem color_sets_items[]; diff --git a/source/blender/makesrna/intern/rna_camera.c b/source/blender/makesrna/intern/rna_camera.c index 31e991dd2b6..b63e2f97da8 100644 --- a/source/blender/makesrna/intern/rna_camera.c +++ b/source/blender/makesrna/intern/rna_camera.c @@ -89,6 +89,60 @@ static void rna_Camera_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Pointer #else +static void rna_def_camera_stereo_data(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static EnumPropertyItem convergence_mode_items[] = { + {CAM_S3D_OFFAXIS, "OFFAXIS", 0, "Off-Axis", "Off-axis frustrums converging in a plane"}, + {CAM_S3D_PARALLEL, "PARALLEL", 0, "Parallel", "Parallel cameras with no convergence"}, + {CAM_S3D_TOE, "TOE", 0, "Toe-in", "Rotated cameras, looking at the convergence distance"}, + {0, NULL, 0, NULL, NULL} + }; + + static EnumPropertyItem pivot_items[] = { + {CAM_S3D_PIVOT_LEFT, "LEFT", 0, "Left", ""}, + {CAM_S3D_PIVOT_RIGHT, "RIGHT", 0, "Right", ""}, + {CAM_S3D_PIVOT_CENTER, "CENTER", 0, "Center", ""}, + {0, NULL, 0, NULL, NULL} + }; + + srna = RNA_def_struct(brna, "CameraStereoData", NULL); + RNA_def_struct_sdna(srna, "CameraStereoSettings"); + RNA_def_struct_nested(brna, srna, "Camera"); + RNA_def_struct_ui_text(srna, "Stereo", "Stereoscopy settings for a Camera datablock"); + + prop = RNA_def_property(srna, "convergence_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, convergence_mode_items); + RNA_def_property_ui_text(prop, "Mode", ""); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + + prop = RNA_def_property(srna, "pivot", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, pivot_items); + RNA_def_property_ui_text(prop, "Pivot", ""); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + + prop = RNA_def_property(srna, "interocular_distance", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_range(prop, 0.0f, 100.0f); + RNA_def_property_ui_range(prop, 0.0f, 1.f, 1, 2); + RNA_def_property_ui_text(prop, "Interocular Distance", "Set the distance between the eyes - the stereo plane distance / 30 should be fine"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + + prop = RNA_def_property(srna, "convergence_distance", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_range(prop, 0.00001f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 15.f, 1, 2); + RNA_def_property_ui_text(prop, "Convergence Plane Distance", "The converge point for the stereo cameras (often the distance between a projector and the projection screen)"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + + prop = RNA_def_property(srna, "viewport_convergence", PROP_FLOAT, PROP_DISTANCE); + RNA_def_property_float_sdna(prop, NULL, "convergence_distance"); + RNA_def_property_range(prop, 0.00001f, FLT_MAX); + RNA_def_property_ui_range(prop, 0.0f, 15.f, 1, 2); + RNA_def_property_ui_text(prop, "Viewport Convergence", "Preview convergence distance for the stereo effect in the viewport. It doesn't affect the render!"); + RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); +} + void RNA_def_camera(BlenderRNA *brna) { StructRNA *srna; @@ -241,6 +295,13 @@ void RNA_def_camera(BlenderRNA *brna) RNA_def_property_ui_text(prop, "DOF Distance", "Distance to the focus point for depth of field"); RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL); + /* Stereo Settings */ + prop = RNA_def_property(srna, "stereo", PROP_POINTER, PROP_NONE); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_pointer_sdna(prop, NULL, "stereo"); + RNA_def_property_struct_type(prop, "CameraStereoData"); + RNA_def_property_ui_text(prop, "Stereo", ""); + /* flag */ prop = RNA_def_property(srna, "show_limits", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", CAM_SHOWLIMITS); @@ -298,6 +359,12 @@ void RNA_def_camera(BlenderRNA *brna) rna_def_animdata_common(srna); + /* Nested Data */ + RNA_define_animate_sdna(true); + + /* *** Animated *** */ + rna_def_camera_stereo_data(brna); + /* Camera API */ RNA_api_camera(srna); } diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c index e6193b31260..0c871317d04 100644 --- a/source/blender/makesrna/intern/rna_color.c +++ b/source/blender/makesrna/intern/rna_color.c @@ -620,10 +620,8 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *UNUSED(bmain) } if (seq_found) { - if (seq->anim) { - IMB_free_anim(seq->anim); - seq->anim = NULL; - } + BKE_sequence_free_anim(seq); + if (seq->strip->proxy && seq->strip->proxy->anim) { IMB_free_anim(seq->strip->proxy->anim); seq->strip->proxy->anim = NULL; @@ -635,10 +633,7 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *UNUSED(bmain) else { SEQ_BEGIN(scene->ed, seq); { - if (seq->anim) { - IMB_free_anim(seq->anim); - seq->anim = NULL; - } + BKE_sequence_free_anim(seq); } SEQ_END; diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index cf2973c49a7..bac5c4aadba 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -35,6 +35,7 @@ #include "BKE_depsgraph.h" #include "BKE_image.h" +#include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" @@ -137,6 +138,23 @@ static void rna_Image_colormanage_update(Main *UNUSED(bmain), Scene *UNUSED(scen WM_main_add_notifier(NC_IMAGE | NA_EDITED, &ima->id); } +static void rna_Image_views_format_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr) +{ + Image *ima = ptr->id.data; + ImBuf *ibuf; + void *lock; + + ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); + + if (ibuf) { + ImageUser iuser = {NULL}; + iuser.scene = scene; + BKE_image_signal(ima, &iuser, IMA_SIGNAL_FREE); + } + + BKE_image_release_ibuf(ima, ibuf, lock); +} + static void rna_ImageUser_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr) { ImageUser *iuser = ptr->data; @@ -295,17 +313,16 @@ static int rna_Image_frame_duration_get(PointerRNA *ptr) Image *ima = ptr->id.data; int duration = 1; - if (!ima->anim) { + if (BKE_image_has_anim(ima)) { + duration = IMB_anim_get_duration(((ImageAnim *)ima->anims.first)->anim, IMB_TC_RECORD_RUN); + } + else { /* acquire ensures ima->anim is set, if possible! */ void *lock; ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); BKE_image_release_ibuf(ima, ibuf, lock); } - if (ima->anim) { - duration = IMB_anim_get_duration(ima->anim, IMB_TC_RECORD_RUN); - } - return duration; } @@ -409,6 +426,19 @@ static int rna_Image_is_float_get(PointerRNA *ptr) return is_float; } +static PointerRNA rna_Image_packed_file_get(PointerRNA *ptr) +{ + Image *ima = (Image *)ptr->id.data; + + if (BKE_image_has_packedfile(ima)) { + ImagePackedFile *imapf = ima->packedfiles.first; + return rna_pointer_inherit_refine(ptr, &RNA_PackedFile, imapf->packedfile); + } + else { + return PointerRNA_NULL; + } +} + static void rna_Image_render_slots_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { Image *image = (Image *)ptr->id.data; @@ -512,6 +542,32 @@ static void rna_def_imageuser(BlenderRNA *brna) RNA_def_property_int_sdna(prop, NULL, "pass"); RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* image_multi_cb */ RNA_def_property_ui_text(prop, "Pass", "Pass in multilayer image"); + + prop = RNA_def_property(srna, "multilayer_view", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, NULL, "view"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* image_multi_cb */ + RNA_def_property_ui_text(prop, "View", "View in multilayer image"); +} + +/* image.packed_files */ +static void rna_def_image_packed_files(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "ImagePackedFile", NULL); + RNA_def_struct_sdna(srna, "ImagePackedFile"); + + prop = RNA_def_property(srna, "packed_file", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "packedfile"); + RNA_def_property_ui_text(prop, "Packed File", ""); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + + prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH); + RNA_def_property_string_sdna(prop, NULL, "filepath"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_struct_name_property(srna, prop); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); } static void rna_def_render_slot(BlenderRNA *brna) @@ -613,15 +669,23 @@ static void rna_def_image(BlenderRNA *brna) RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, NULL); prop = RNA_def_property(srna, "packed_file", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "PackedFile"); RNA_def_property_pointer_sdna(prop, NULL, "packedfile"); - RNA_def_property_ui_text(prop, "Packed File", ""); - + RNA_def_property_pointer_funcs(prop, "rna_Image_packed_file_get", NULL, NULL, NULL); + RNA_def_property_ui_text(prop, "Packed File", "First packed file of the image"); + + prop = RNA_def_property(srna, "packed_files", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "packedfiles", NULL); + RNA_def_property_struct_type(prop, "ImagePackedFile"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Packed Files", "Collection of packed images"); + prop = RNA_def_property(srna, "field_order", PROP_ENUM, PROP_NONE); RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag"); RNA_def_property_enum_items(prop, prop_field_order_items); RNA_def_property_ui_text(prop, "Field Order", "Order of video fields (select which lines are displayed first)"); RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, NULL); - + /* booleans */ prop = RNA_def_property(srna, "use_fields", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, NULL, "flag", IMA_FIELDS); @@ -645,6 +709,21 @@ static void rna_def_image(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Deinterlace", "Deinterlace movie file on load"); RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_reload_update"); + prop = RNA_def_property(srna, "use_multiview", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", IMA_USE_VIEWS); + RNA_def_property_ui_text(prop, "Use Multi-View", "Use Multiple Views (when available)"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_views_format_update"); + + prop = RNA_def_property(srna, "is_stereo_3d", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", IMA_IS_STEREO); + RNA_def_property_ui_text(prop, "Stereo 3D", "Image has left and right views"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + + prop = RNA_def_property(srna, "is_multiview", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", IMA_IS_MULTIVIEW); + RNA_def_property_ui_text(prop, "Multiple Views", "Image has more than one view"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + prop = RNA_def_property(srna, "is_dirty", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_funcs(prop, "rna_Image_dirty_get", NULL); RNA_def_property_clear_flag(prop, PROP_EDITABLE); @@ -825,6 +904,19 @@ static void rna_def_image(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Alpha Mode", "Representation of alpha information in the RGBA pixels"); RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_colormanage_update"); + /* multiview */ + prop = RNA_def_property(srna, "views_format", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "views_format"); + RNA_def_property_enum_items(prop, views_format_items); + RNA_def_property_ui_text(prop, "Views Format", "Mode to load image views"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_views_format_update"); + + prop = RNA_def_property(srna, "stereo_3d_format", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "stereo3d_format"); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "Stereo3dFormat"); + RNA_def_property_ui_text(prop, "Stereo 3D Format", "Settings for stereo 3d"); + RNA_api_image(srna); } @@ -834,6 +926,7 @@ void RNA_def_image(BlenderRNA *brna) rna_def_render_slots(brna); rna_def_image(brna); rna_def_imageuser(brna); + rna_def_image_packed_files(brna); } #endif diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c index 7fb5885f709..47be7bec432 100644 --- a/source/blender/makesrna/intern/rna_image_api.c +++ b/source/blender/makesrna/intern/rna_image_api.c @@ -167,7 +167,7 @@ static void rna_Image_pack( image->packedfile = newPackedFileMemory(data_dup, data_len); } else { - image->packedfile = newPackedFile(reports, image->name, ID_BLEND_PATH(bmain, &image->id)); + BKE_image_packfiles(reports, image, ID_BLEND_PATH(bmain, &image->id)); } } diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c index 1d232720fc5..59c3befad9e 100644 --- a/source/blender/makesrna/intern/rna_main_api.c +++ b/source/blender/makesrna/intern/rna_main_api.c @@ -341,10 +341,10 @@ static void rna_Main_lamps_remove(Main *bmain, ReportList *reports, PointerRNA * } } -static Image *rna_Main_images_new(Main *bmain, const char *name, int width, int height, int alpha, int float_buffer) +static Image *rna_Main_images_new(Main *bmain, const char *name, int width, int height, int alpha, int float_buffer, int stereo3d) { float color[4] = {0.0, 0.0, 0.0, 1.0}; - Image *image = BKE_image_add_generated(bmain, width, height, name, alpha ? 32 : 24, float_buffer, 0, color); + Image *image = BKE_image_add_generated(bmain, width, height, name, alpha ? 32 : 24, float_buffer, 0, color, stereo3d); id_us_min(&image->id); return image; } @@ -1174,6 +1174,7 @@ void RNA_def_main_images(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_property_flag(parm, PROP_REQUIRED); RNA_def_boolean(func, "alpha", 0, "Alpha", "Use alpha channel"); RNA_def_boolean(func, "float_buffer", 0, "Float Buffer", "Create an image with floating point color"); + RNA_def_boolean(func, "stereo3d", 0, "Stereo 3D", "Create left and right views"); /* return type */ parm = RNA_def_pointer(func, "image", "Image", "", "New image datablock"); RNA_def_function_return(func, parm); diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c index e577f7635f1..075b139c6c4 100644 --- a/source/blender/makesrna/intern/rna_nodetree.c +++ b/source/blender/makesrna/intern/rna_nodetree.c @@ -2616,6 +2616,70 @@ static EnumPropertyItem *rna_Node_image_layer_itemf(bContext *UNUSED(C), Pointer return item; } +static int rna_Node_image_has_layers_get(PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + Image *ima = (Image *)node->id; + + if (!ima || !(ima->rr)) return 0; + + return RE_layers_have_name(ima->rr); +} + +static int rna_Node_image_has_views_get(PointerRNA *ptr) +{ + bNode *node = (bNode *)ptr->data; + Image *ima = (Image *)node->id; + + if (!ima || !(ima->rr)) return 0; + + return BLI_listbase_count_ex(&ima->rr->views, 2) > 1; +} + +static EnumPropertyItem *renderresult_views_add_enum(RenderView *rv) +{ + EnumPropertyItem *item = NULL; + EnumPropertyItem tmp = {0, "ALL", 0, "All", ""}; + int i = 1, totitem = 0; + + /* option to use all views */ + RNA_enum_item_add(&item, &totitem, &tmp); + + while (rv) { + tmp.identifier = rv->name; + /* little trick: using space char instead empty string makes the item selectable in the dropdown */ + if (rv->name[0] == '\0') + tmp.name = " "; + else + tmp.name = rv->name; + tmp.value = i++; + RNA_enum_item_add(&item, &totitem, &tmp); + rv = rv->next; + } + + RNA_enum_item_end(&item, &totitem); + + return item; +} + +static EnumPropertyItem *rna_Node_image_view_itemf(bContext *UNUSED(C), PointerRNA *ptr, + PropertyRNA *UNUSED(prop), bool *free) +{ + bNode *node = (bNode *)ptr->data; + Image *ima = (Image *)node->id; + EnumPropertyItem *item = NULL; + RenderView *rv; + + if (!ima || !(ima->rr)) return NULL; + + rv = ima->rr->views.first; + item = renderresult_views_add_enum(rv); + + *free = true; + + return item; +} + static EnumPropertyItem *rna_Node_scene_layer_itemf(bContext *UNUSED(C), PointerRNA *ptr, PropertyRNA *UNUSED(prop), bool *r_free) { @@ -2907,6 +2971,11 @@ static EnumPropertyItem prop_image_layer_items[] = { {0, NULL, 0, NULL, NULL} }; +static EnumPropertyItem prop_image_view_items[] = { + { 0, "ALL", 0, "All", ""}, + {0, NULL, 0, NULL, NULL} +}; + static EnumPropertyItem prop_scene_layer_items[] = { { 0, "PLACEHOLDER", 0, "Placeholder", ""}, {0, NULL, 0, NULL, NULL} @@ -4263,6 +4332,24 @@ static void def_node_image_user(StructRNA *srna) RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); RNA_def_property_ui_text(prop, "Layer", ""); RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_image_layer_update"); + + prop = RNA_def_property(srna, "has_layers", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, "rna_Node_image_has_layers_get", NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Has Layers", "True if this image has any named layer"); + + prop = RNA_def_property(srna, "view", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "view"); + RNA_def_property_enum_items(prop, prop_image_view_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_Node_image_view_itemf"); + RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE); + RNA_def_property_ui_text(prop, "View", ""); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); + + prop = RNA_def_property(srna, "has_views", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, "rna_Node_image_has_views_get", NULL); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_ui_text(prop, "Has View", "True if this image has multiple views"); } static void def_cmp_image(StructRNA *srna) @@ -5811,6 +5898,16 @@ static void def_cmp_switch(StructRNA *srna) RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); } +static void def_cmp_switch_view(StructRNA *srna) +{ + PropertyRNA *prop; + + prop = RNA_def_property(srna, "check", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "custom1", 0); + RNA_def_property_ui_text(prop, "Switch", "Off: first socket, On: second socket"); + RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update"); +} + static void def_cmp_colorcorrection(StructRNA *srna) { PropertyRNA *prop; diff --git a/source/blender/makesrna/intern/rna_render.c b/source/blender/makesrna/intern/rna_render.c index 124a83a5f30..94fabb857e3 100644 --- a/source/blender/makesrna/intern/rna_render.c +++ b/source/blender/makesrna/intern/rna_render.c @@ -27,6 +27,7 @@ #include <stdlib.h> #include "DNA_scene_types.h" +#include "DNA_object_types.h" #include "BLI_utildefines.h" #include "BLI_path_util.h" @@ -322,38 +323,35 @@ static PointerRNA rna_RenderEngine_render_get(PointerRNA *ptr) } } -static void rna_RenderResult_layers_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +static PointerRNA rna_RenderEngine_camera_override_get(PointerRNA *ptr) { - RenderResult *rr = (RenderResult *)ptr->data; - rna_iterator_listbase_begin(iter, &rr->layers, NULL); -} + RenderEngine *engine = (RenderEngine *)ptr->data; -static void rna_RenderLayer_passes_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) -{ - RenderLayer *rl = (RenderLayer *)ptr->data; - rna_iterator_listbase_begin(iter, &rl->passes, NULL); + if (engine->re) { + Object *cam = RE_GetCamera(engine->re); + return rna_pointer_inherit_refine(ptr, &RNA_Object, cam); + } + else { + return rna_pointer_inherit_refine(ptr, &RNA_Object, engine->camera_override); + } } -static int rna_RenderLayer_rect_get_length(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) +static void rna_RenderResult_views_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { - RenderLayer *rl = (RenderLayer *)ptr->data; - - length[0] = rl->rectx * rl->recty; - length[1] = 4; - - return length[0] * length[1]; + RenderResult *rr = (RenderResult *)ptr->data; + rna_iterator_listbase_begin(iter, &rr->views, NULL); } -static void rna_RenderLayer_rect_get(PointerRNA *ptr, float *values) +static void rna_RenderResult_layers_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { - RenderLayer *rl = (RenderLayer *)ptr->data; - memcpy(values, rl->rectf, sizeof(float) * rl->rectx * rl->recty * 4); + RenderResult *rr = (RenderResult *)ptr->data; + rna_iterator_listbase_begin(iter, &rr->layers, NULL); } -void rna_RenderLayer_rect_set(PointerRNA *ptr, const float *values) +static void rna_RenderLayer_passes_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) { RenderLayer *rl = (RenderLayer *)ptr->data; - memcpy(rl->rectf, values, sizeof(float) * rl->rectx * rl->recty * 4); + rna_iterator_listbase_begin(iter, &rl->passes, NULL); } static int rna_RenderPass_rect_get_length(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION]) @@ -384,6 +382,18 @@ static PointerRNA rna_BakePixel_next_get(PointerRNA *ptr) return rna_pointer_inherit_refine(ptr, &RNA_BakePixel, bp + 1); } +static RenderPass *rna_RenderPass_find_by_type(RenderLayer *rl, int passtype, const char *view) +{ + RenderPass *rp; + for (rp = rl->passes.first; rp; rp = rp->next) { + if (rp->passtype == passtype) { + if (STREQ(rp->view, view)) + return rp; + } + } + return NULL; +} + #else /* RNA_RUNTIME */ static void rna_def_render_engine(BlenderRNA *brna) @@ -467,6 +477,7 @@ static void rna_def_render_engine(BlenderRNA *brna) prop = RNA_def_int(func, "h", 0, 0, INT_MAX, "Height", "", 0, INT_MAX); RNA_def_property_flag(prop, PROP_REQUIRED); RNA_def_string(func, "layer", NULL, 0, "Layer", "Single layer to get render result for"); /* NULL ok here */ + RNA_def_string(func, "view", NULL, 0, "View", "Single view to get render result for"); /* NULL ok here */ prop = RNA_def_pointer(func, "result", "RenderResult", "Result", ""); RNA_def_function_return(func, prop); @@ -487,6 +498,22 @@ static void rna_def_render_engine(BlenderRNA *brna) prop = RNA_def_boolean(func, "do_break", 0, "Break", ""); RNA_def_function_return(func, prop); + func = RNA_def_function(srna, "active_view_set", "RE_engine_active_view_set"); + RNA_def_string(func, "view", NULL, 0, "View", "Single view to set as active"); /* NULL ok here */ + RNA_def_property_flag(prop, PROP_REQUIRED); + + func = RNA_def_function(srna, "camera_shift_x", "RE_engine_get_camera_shift_x"); + prop = RNA_def_pointer(func, "camera", "Object", "", ""); + RNA_def_property_flag(prop, PROP_REQUIRED); + prop = RNA_def_float(func, "shift_x", 0.0f, 0.0f, FLT_MAX, "Shift X", "", 0.0f, FLT_MAX); + RNA_def_function_return(func, prop); + + func = RNA_def_function(srna, "camera_model_matrix", "RE_engine_get_camera_model_matrix"); + prop = RNA_def_pointer(func, "camera", "Object", "", ""); + RNA_def_property_flag(prop, PROP_REQUIRED); + prop = RNA_def_float_matrix(func, "r_model_matrix", 4, 4, NULL, 0.0f, 0.0f, "Model Matrix", "Normalized camera model matrix", 0.0f, 0.0f); + RNA_def_property_flag(prop, PROP_REQUIRED); + func = RNA_def_function(srna, "update_stats", "RE_engine_update_stats"); RNA_def_function_ui_description(func, "Update and signal to redraw render status text"); prop = RNA_def_string(func, "stats", NULL, 0, "Stats", ""); @@ -548,7 +575,7 @@ static void rna_def_render_engine(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "flag", RE_ENGINE_PREVIEW); prop = RNA_def_property(srna, "camera_override", PROP_POINTER, PROP_NONE); - RNA_def_property_pointer_sdna(prop, NULL, "camera_override"); + RNA_def_property_pointer_funcs(prop, "rna_RenderEngine_camera_override_get", NULL, NULL, NULL); RNA_def_property_struct_type(prop, "Object"); prop = RNA_def_property(srna, "layer_override", PROP_BOOLEAN, PROP_LAYER_MEMBER); @@ -647,9 +674,56 @@ static void rna_def_render_result(BlenderRNA *brna) "rna_iterator_listbase_end", "rna_iterator_listbase_get", NULL, NULL, NULL, NULL); + parm = RNA_def_property(srna, "views", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(parm, "RenderView"); + RNA_def_property_collection_funcs(parm, "rna_RenderResult_views_begin", "rna_iterator_listbase_next", + "rna_iterator_listbase_end", "rna_iterator_listbase_get", + NULL, NULL, NULL, NULL); + + RNA_define_verify_sdna(1); +} + +static void rna_def_render_view(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "RenderView", NULL); + RNA_def_struct_ui_text(srna, "Render View", ""); + + RNA_define_verify_sdna(0); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "name"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_struct_name_property(srna, prop); + RNA_define_verify_sdna(1); } +static void rna_def_render_passes(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "RenderPasses"); + srna = RNA_def_struct(brna, "RenderPasses", NULL); + RNA_def_struct_sdna(srna, "RenderLayer"); + RNA_def_struct_ui_text(srna, "Render Passes", "Collection of render passes"); + + func = RNA_def_function(srna, "find_by_type", "rna_RenderPass_find_by_type"); + RNA_def_function_ui_description(func, "Get the render pass for a given type and view"); + parm = RNA_def_enum(func, "pass_type", render_pass_type_items, SCE_PASS_COMBINED, "Pass", ""); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm = RNA_def_string(func, "view", NULL, 0, "View", "Render view to get pass from"); /* NULL ok here */ + RNA_def_property_flag(parm, PROP_REQUIRED); + parm = RNA_def_pointer(func, "render_pass", "RenderPass", "", "The matching render pass"); + RNA_def_function_return(func, parm); + +} + static void rna_def_render_layer(BlenderRNA *brna) { StructRNA *srna; @@ -679,12 +753,7 @@ static void rna_def_render_layer(BlenderRNA *brna) RNA_def_property_collection_funcs(prop, "rna_RenderLayer_passes_begin", "rna_iterator_listbase_next", "rna_iterator_listbase_end", "rna_iterator_listbase_get", NULL, NULL, NULL, NULL); - - prop = RNA_def_property(srna, "rect", PROP_FLOAT, PROP_NONE); - RNA_def_property_flag(prop, PROP_DYNAMIC); - RNA_def_property_multi_array(prop, 2, NULL); - RNA_def_property_dynamic_array_funcs(prop, "rna_RenderLayer_rect_get_length"); - RNA_def_property_float_funcs(prop, "rna_RenderLayer_rect_get", "rna_RenderLayer_rect_set", NULL); + rna_def_render_passes(brna, prop); RNA_define_verify_sdna(1); } @@ -728,6 +797,10 @@ static void rna_def_render_pass(BlenderRNA *brna) RNA_def_property_dynamic_array_funcs(prop, "rna_RenderPass_rect_get_length"); RNA_def_property_float_funcs(prop, "rna_RenderPass_rect_get", "rna_RenderPass_rect_set", NULL); + prop = RNA_def_property(srna, "view_id", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, NULL, "view_id"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + prop = RNA_def_property(srna, "debug_type", PROP_ENUM, PROP_NONE); RNA_def_property_enum_sdna(prop, NULL, "debug_type"); RNA_def_property_enum_items(prop, render_pass_debug_type_items); @@ -783,6 +856,7 @@ void RNA_def_render(BlenderRNA *brna) { rna_def_render_engine(brna); rna_def_render_result(brna); + rna_def_render_view(brna); rna_def_render_layer(brna); rna_def_render_pass(brna); rna_def_render_bake_pixel(brna); diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index c9d1a4b6429..a59724f4a16 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -36,6 +36,8 @@ #include "DNA_userdef_types.h" #include "DNA_world_types.h" +#include "IMB_imbuf_types.h" + #include "BLI_math.h" #include "BLF_translation.h" @@ -217,13 +219,13 @@ EnumPropertyItem snap_uv_element_items[] = { #endif #ifdef WITH_OPENEXR -# define R_IMF_ENUM_EXR_MULTI {R_IMF_IMTYPE_MULTILAYER, "OPEN_EXR_MULTILAYER", ICON_FILE_IMAGE, \ +# define R_IMF_ENUM_EXR_MULTILAYER {R_IMF_IMTYPE_MULTILAYER, "OPEN_EXR_MULTILAYER", ICON_FILE_IMAGE, \ "OpenEXR MultiLayer", \ "Output image in multilayer OpenEXR format"}, # define R_IMF_ENUM_EXR {R_IMF_IMTYPE_OPENEXR, "OPEN_EXR", ICON_FILE_IMAGE, "OpenEXR", \ "Output image in OpenEXR format"}, #else -# define R_IMF_ENUM_EXR_MULTI +# define R_IMF_ENUM_EXR_MULTILAYER # define R_IMF_ENUM_EXR #endif @@ -252,7 +254,7 @@ EnumPropertyItem snap_uv_element_items[] = { {0, "", 0, " ", NULL}, \ R_IMF_ENUM_CINEON \ R_IMF_ENUM_DPX \ - R_IMF_ENUM_EXR_MULTI \ + R_IMF_ENUM_EXR_MULTILAYER \ R_IMF_ENUM_EXR \ R_IMF_ENUM_HDR \ R_IMF_ENUM_TIFF \ @@ -336,6 +338,58 @@ EnumPropertyItem bake_save_mode_items[] = { {0, NULL, 0, NULL, NULL} }; +#define R_IMF_VIEWS_ENUM_IND {R_IMF_VIEWS_INDIVIDUAL, "INDIVIDUAL", 0, "Individual", "Individual files for each view with the prefix as defined by the scene views"}, +#define R_IMF_VIEWS_ENUM_S3D {R_IMF_VIEWS_STEREO_3D, "STEREO_3D", 0, "Stereo 3D", "Single file with an encoded stereo pair"}, +#define R_IMF_VIEWS_ENUM_MV {R_IMF_VIEWS_MULTIVIEW, "MULTIVIEW", 0, "Multi-View", "Single file with all the views"}, + +EnumPropertyItem views_format_items[] = { + R_IMF_VIEWS_ENUM_IND + R_IMF_VIEWS_ENUM_S3D + {0, NULL, 0, NULL, NULL} +}; + +EnumPropertyItem views_format_multilayer_items[] = { + R_IMF_VIEWS_ENUM_IND + R_IMF_VIEWS_ENUM_MV + {0, NULL, 0, NULL, NULL} +}; + +EnumPropertyItem views_format_multiview_items[] = { + R_IMF_VIEWS_ENUM_IND + R_IMF_VIEWS_ENUM_S3D + R_IMF_VIEWS_ENUM_MV + {0, NULL, 0, NULL, NULL} +}; + +#undef R_IMF_VIEWS_ENUM_IND +#undef R_IMF_VIEWS_ENUM_S3D +#undef R_IMF_VIEWS_ENUM_MV + +EnumPropertyItem stereo3d_display_items[] = { + {S3D_DISPLAY_ANAGLYPH, "ANAGLYPH", 0, "Anaglyph", "Render two differently filtered colored images for each eye. Anaglyph glasses are required"}, + {S3D_DISPLAY_INTERLACE, "INTERLACE", 0, "Interlace", "Render two images for each eye into one interlaced image. 3D-ready monitor is requiered"}, +#ifdef DEBUG /* MULTIVIEW_TODO: quadbuffer mode not fully working */ + {S3D_DISPLAY_PAGEFLIP, "TIMESEQUENTIAL", 0, "Time Sequential", "Renders alternate eyes (also known as pageflip). It requires Quadbuffer support in the graphic card"}, +#endif + {S3D_DISPLAY_SIDEBYSIDE, "SIDEBYSIDE", 0, "Side-by-Side", "Render images for left and right eye side-by-side"}, + {S3D_DISPLAY_TOPBOTTOM, "TOPBOTTOM", 0, "Top-Bottom", "Render images for left and right eye one above another"}, + {0, NULL, 0, NULL, NULL} +}; + +EnumPropertyItem stereo3d_anaglyph_type_items[] = { + {S3D_ANAGLYPH_REDCYAN, "RED_CYAN", 0, "Red-Cyan", ""}, + {S3D_ANAGLYPH_GREENMAGENTA, "GREEN_MAGENTA", 0, "Green-Magenta", ""}, + {S3D_ANAGLYPH_YELLOWBLUE, "YELLOW_BLUE", 0, "Yellow-Blue", ""}, + {0, NULL, 0, NULL, NULL} +}; + +EnumPropertyItem stereo3d_interlace_type_items[] = { + {S3D_INTERLACE_ROW, "ROW_INTERLEAVED", 0, "Row Interleaved", ""}, + {S3D_INTERLACE_COLUMN, "COLUMN_INTERLEAVED", 0, "Column Interleaved", ""}, + {S3D_INTERLACE_CHECKERBOARD, "CHECKERBOARD_INTERLEAVED", 0, "Checkerboard Interleaved", ""}, + {0, NULL, 0, NULL, NULL} +}; + #ifdef RNA_RUNTIME #include "DNA_anim_types.h" @@ -723,6 +777,26 @@ static void rna_Scene_all_keyingsets_next(CollectionPropertyIterator *iter) iter->valid = (internal->link != NULL); } +static int rna_RenderSettings_stereoViews_skip(CollectionPropertyIterator *iter, void *UNUSED(data)) +{ + ListBaseIterator *internal = &iter->internal.listbase; + SceneRenderView *srv = (SceneRenderView *)internal->link; + + if ((STREQ(srv->name, STEREO_LEFT_NAME)) || + (STREQ(srv->name, STEREO_RIGHT_NAME))) + { + return 0; + } + + return 1; +}; + +static void rna_RenderSettings_stereoViews_begin(CollectionPropertyIterator *iter, PointerRNA *ptr) +{ + RenderData *rd = (RenderData *)ptr->data; + rna_iterator_listbase_begin(iter, &rd->views, rna_RenderSettings_stereoViews_skip); +} + static char *rna_RenderSettings_path(PointerRNA *UNUSED(ptr)) { return BLI_sprintfN("render"); @@ -945,6 +1019,25 @@ static EnumPropertyItem *rna_ImageFormatSettings_color_depth_itemf(bContext *UNU } } +static EnumPropertyItem *rna_ImageFormatSettings_views_format_itemf(bContext *UNUSED(C), PointerRNA *ptr, +PropertyRNA *UNUSED(prop), bool *UNUSED(r_free)) +{ + ImageFormatData *imf = (ImageFormatData *)ptr->data; + + if (imf == NULL) { + return views_format_items; + } + else if (imf->imtype == R_IMF_IMTYPE_OPENEXR) { + return views_format_multiview_items; + } + else if (imf->imtype == R_IMF_IMTYPE_MULTILAYER) { + return views_format_multilayer_items; + } + else { + return views_format_items; + } +} + #ifdef WITH_OPENEXR /* OpenEXR */ @@ -1158,6 +1251,69 @@ static void rna_RenderLayer_remove(ID *id, RenderData *UNUSED(rd), Main *bmain, WM_main_add_notifier(NC_SCENE | ND_RENDER_OPTIONS, NULL); } +static int rna_RenderSettings_active_view_index_get(PointerRNA *ptr) +{ + RenderData *rd = (RenderData *)ptr->data; + return rd->actview; +} + +static void rna_RenderSettings_active_view_index_set(PointerRNA *ptr, int value) +{ + RenderData *rd = (RenderData *)ptr->data; + rd->actview = value; +} + +static void rna_RenderSettings_active_view_index_range(PointerRNA *ptr, int *min, int *max, int *UNUSED(softmin), int *UNUSED(softmax)) +{ + RenderData *rd = (RenderData *)ptr->data; + + *min = 0; + *max = max_ii(0, BLI_listbase_count(&rd->views) - 1); +} + +static PointerRNA rna_RenderSettings_active_view_get(PointerRNA *ptr) +{ + RenderData *rd = (RenderData *)ptr->data; + SceneRenderView *srv = BLI_findlink(&rd->views, rd->actview); + + return rna_pointer_inherit_refine(ptr, &RNA_SceneRenderView, srv); +} + +static void rna_RenderSettings_active_view_set(PointerRNA *ptr, PointerRNA value) +{ + RenderData *rd = (RenderData *)ptr->data; + SceneRenderView *srv = (SceneRenderView *)value.data; + const int index = BLI_findindex(&rd->views, srv); + if (index != -1) rd->actview = index; +} + +static SceneRenderView *rna_RenderView_new(ID *id, RenderData *UNUSED(rd), const char *name) +{ + Scene *scene = (Scene *)id; + SceneRenderView *srv = BKE_scene_add_render_view(scene, name); + + WM_main_add_notifier(NC_SCENE | ND_RENDER_OPTIONS, NULL); + + return srv; +} + +static void rna_RenderView_remove(ID *id, RenderData *UNUSED(rd), Main *UNUSED(bmain), ReportList *reports, + PointerRNA *srv_ptr) +{ + SceneRenderView *srv = srv_ptr->data; + Scene *scene = (Scene *)id; + + if (!BKE_scene_remove_render_view(scene, srv)) { + BKE_reportf(reports, RPT_ERROR, "Render view '%s' could not be removed from scene '%s'", + srv->name, scene->id.name + 2); + return; + } + + RNA_POINTER_INVALIDATE(srv_ptr); + + WM_main_add_notifier(NC_SCENE | ND_RENDER_OPTIONS, NULL); +} + static void rna_RenderSettings_engine_set(PointerRNA *ptr, int value) { RenderData *rd = (RenderData *)ptr->data; @@ -1263,6 +1419,34 @@ static char *rna_SceneRenderLayer_path(PointerRNA *ptr) return BLI_sprintfN("render.layers[\"%s\"]", name_esc); } +static void rna_SceneRenderView_name_set(PointerRNA *ptr, const char *value) +{ + Scene *scene = (Scene *)ptr->id.data; + SceneRenderView *rv = (SceneRenderView *)ptr->data; + BLI_strncpy_utf8(rv->name, value, sizeof(rv->name)); + BLI_uniquename(&scene->r.views, rv, DATA_("RenderView"), '.', offsetof(SceneRenderView, name), sizeof(rv->name)); +} + +static char *rna_SceneRenderView_path(PointerRNA *ptr) +{ + SceneRenderView *srv = (SceneRenderView *)ptr->data; + return BLI_sprintfN("render.views[\"%s\"]", srv->name); +} + +static void rna_RenderSettings_views_format_set(PointerRNA *ptr, int value) +{ + RenderData *rd = (RenderData *)ptr->data; + + if (rd->views_format == SCE_VIEWS_FORMAT_MULTIVIEW && + value == SCE_VIEWS_FORMAT_STEREO_3D) + { + /* make sure the actview is visible */ + if (rd->actview > 1) rd->actview = 1; + } + + rd->views_format = value; +} + static int rna_RenderSettings_multiple_engines_get(PointerRNA *UNUSED(ptr)) { return (BLI_listbase_count(&R_engines) > 1); @@ -1759,6 +1943,26 @@ static void rna_GPUDOFSettings_blades_set(PointerRNA *ptr, const int value) dofsettings->num_blades = value; } +static void rna_Stereo3dFormat_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + ID *id = ptr->id.data; + + if (id && GS(id->name) == ID_IM) { + Image *ima = (Image *)id; + ImBuf *ibuf; + void *lock; + + if ((ima->flag & IMA_IS_STEREO) == 0) + return; + + ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); + + if (ibuf) { + BKE_image_signal(ima, NULL, IMA_SIGNAL_FREE); + } + BKE_image_release_ibuf(ima, ibuf, lock); + } +} static int rna_gpu_is_hq_supported_get(PointerRNA *UNUSED(ptr)) { @@ -4103,6 +4307,134 @@ static void rna_def_render_layers(BlenderRNA *brna, PropertyRNA *cprop) RNA_def_property_clear_flag(parm, PROP_THICK_WRAP); } +/* Render Views - MultiView */ +static void rna_def_scene_render_view(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "SceneRenderView", NULL); + RNA_def_struct_ui_text(srna, "Scene Render View", "Render viewpoint for 3D stereo and multiview rendering"); + RNA_def_struct_ui_icon(srna, ICON_RESTRICT_RENDER_OFF); + RNA_def_struct_path_func(srna, "rna_SceneRenderView_path"); + + prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE); + RNA_def_property_string_funcs(prop, NULL, NULL, "rna_SceneRenderView_name_set"); + RNA_def_property_ui_text(prop, "Name", "Render view name"); + RNA_def_struct_name_property(srna, prop); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + + prop = RNA_def_property(srna, "file_suffix", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "suffix"); + RNA_def_property_ui_text(prop, "File Suffix", "Suffix added to the render images for this view"); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + + prop = RNA_def_property(srna, "camera_suffix", PROP_STRING, PROP_NONE); + RNA_def_property_string_sdna(prop, NULL, "suffix"); + RNA_def_property_ui_text(prop, "Camera Suffix", "Suffix to identify the cameras to use, and added to the render images for this view"); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + + prop = RNA_def_property(srna, "use", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_negative_sdna(prop, NULL, "viewflag", SCE_VIEW_DISABLE); + RNA_def_property_ui_text(prop, "Enabled", "Disable or enable the render view"); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); +} + +static void rna_def_render_views(BlenderRNA *brna, PropertyRNA *cprop) +{ + StructRNA *srna; + PropertyRNA *prop; + + FunctionRNA *func; + PropertyRNA *parm; + + RNA_def_property_srna(cprop, "RenderViews"); + srna = RNA_def_struct(brna, "RenderViews", NULL); + RNA_def_struct_sdna(srna, "RenderData"); + RNA_def_struct_ui_text(srna, "Render Views", "Collection of render views"); + + prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED); + RNA_def_property_int_sdna(prop, NULL, "actview"); + RNA_def_property_int_funcs(prop, "rna_RenderSettings_active_view_index_get", + "rna_RenderSettings_active_view_index_set", + "rna_RenderSettings_active_view_index_range"); + RNA_def_property_ui_text(prop, "Active View Index", "Active index in render view array"); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + + prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE); + RNA_def_property_struct_type(prop, "SceneRenderView"); + RNA_def_property_pointer_funcs(prop, "rna_RenderSettings_active_view_get", + "rna_RenderSettings_active_view_set", NULL, NULL); + RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_NULL); + RNA_def_property_ui_text(prop, "Active Render View", "Active Render View"); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + + func = RNA_def_function(srna, "new", "rna_RenderView_new"); + RNA_def_function_ui_description(func, "Add a render view to scene"); + RNA_def_function_flag(func, FUNC_USE_SELF_ID); + parm = RNA_def_string(func, "name", "RenderView", 0, "", "New name for the marker (not unique)"); + RNA_def_property_flag(parm, PROP_REQUIRED); + parm = RNA_def_pointer(func, "result", "SceneRenderView", "", "Newly created render view"); + RNA_def_function_return(func, parm); + + func = RNA_def_function(srna, "remove", "rna_RenderView_remove"); + RNA_def_function_ui_description(func, "Remove a render view"); + RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS | FUNC_USE_SELF_ID); + parm = RNA_def_pointer(func, "view", "SceneRenderView", "", "Render view to remove"); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR); + RNA_def_property_clear_flag(parm, PROP_THICK_WRAP); +} + +static void rna_def_image_format_stereo3d_format(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + static EnumPropertyItem stereo3d_display_items[] = { + {S3D_DISPLAY_ANAGLYPH, "ANAGLYPH", 0, "Anaglyph", "Render two differently filtered colored images for each eye. Anaglyph glasses are required"}, + {S3D_DISPLAY_INTERLACE, "INTERLACE", 0, "Interlace", "Render two images for each eye into one interlaced image. 3D-ready monitor is requiered"}, + {S3D_DISPLAY_SIDEBYSIDE, "SIDEBYSIDE", 0, "Side-by-Side", "Render images for left and right eye side-by-side"}, + {S3D_DISPLAY_TOPBOTTOM, "TOPBOTTOM", 0, "Top-Bottom", "Render images for left and right eye one above another"}, + {0, NULL, 0, NULL, NULL} + }; + + srna = RNA_def_struct(brna, "Stereo3dFormat", NULL); + RNA_def_struct_sdna(srna, "Stereo3dFormat"); + RNA_def_struct_clear_flag(srna, STRUCT_UNDO); + RNA_def_struct_ui_text(srna, "Stereo Output", "Settings for stereo output"); + + prop = RNA_def_property(srna, "display_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "display_mode"); + RNA_def_property_enum_items(prop, stereo3d_display_items); + RNA_def_property_ui_text(prop, "Stereo Mode", ""); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Stereo3dFormat_update"); + + prop = RNA_def_property(srna, "anaglyph_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, stereo3d_anaglyph_type_items); + RNA_def_property_ui_text(prop, "Anaglyph Type", ""); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Stereo3dFormat_update"); + + prop = RNA_def_property(srna, "interlace_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, stereo3d_interlace_type_items); + RNA_def_property_ui_text(prop, "Interlace Type", ""); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Stereo3dFormat_update"); + + prop = RNA_def_property(srna, "use_interlace_swap", PROP_BOOLEAN, PROP_BOOLEAN); + RNA_def_property_boolean_sdna(prop, NULL, "flag", S3D_INTERLACE_SWAP); + RNA_def_property_ui_text(prop, "Swap Left/Right", "Swap left and right stereo channels"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Stereo3dFormat_update"); + + prop = RNA_def_property(srna, "use_sidebyside_crosseyed", PROP_BOOLEAN, PROP_BOOLEAN); + RNA_def_property_boolean_sdna(prop, NULL, "flag", S3D_SIDEBYSIDE_CROSSEYED); + RNA_def_property_ui_text(prop, "Cross-Eyed", "Right eye should see left image and vice-versa"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Stereo3dFormat_update"); + + prop = RNA_def_property(srna, "use_squeezed_frame", PROP_BOOLEAN, PROP_BOOLEAN); + RNA_def_property_boolean_sdna(prop, NULL, "flag", S3D_SQUEEZED_FRAME); + RNA_def_property_ui_text(prop, "Squeezed Frame", "Combine both views in a squeezed image"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Stereo3dFormat_update"); +} + /* use for render output and image save operator, * note: there are some cases where the members act differently when this is * used from a scene, video formats can only be selected for render output @@ -4122,6 +4454,8 @@ static void rna_def_scene_image_format_data(BlenderRNA *brna) StructRNA *srna; PropertyRNA *prop; + rna_def_image_format_stereo3d_format(brna); + srna = RNA_def_struct(brna, "ImageFormatSettings", NULL); RNA_def_struct_sdna(srna, "ImageFormatData"); RNA_def_struct_nested(brna, srna, "Scene"); @@ -4190,7 +4524,6 @@ static void rna_def_scene_image_format_data(BlenderRNA *brna) RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_ImageFormatSettings_exr_codec_itemf"); RNA_def_property_ui_text(prop, "Codec", "Codec settings for OpenEXR"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); - #endif #ifdef WITH_OPENJPEG @@ -4242,6 +4575,20 @@ static void rna_def_scene_image_format_data(BlenderRNA *brna) RNA_def_property_ui_text(prop, "G", "Log conversion gamma"); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + /* multiview */ + prop = RNA_def_property(srna, "views_format", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "views_format"); + RNA_def_property_enum_items(prop, views_format_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_ImageFormatSettings_views_format_itemf"); + RNA_def_property_ui_text(prop, "Views Format", "Format of multiview media"); + RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + + prop = RNA_def_property(srna, "stereo_3d_format", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "stereo3d_format"); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "Stereo3dFormat"); + RNA_def_property_ui_text(prop, "Stereo 3D Format", "Settings for stereo 3d"); + /* color management */ prop = RNA_def_property(srna, "view_settings", PROP_POINTER, PROP_NONE); RNA_def_property_pointer_sdna(prop, NULL, "view_settings"); @@ -4644,6 +4991,16 @@ static void rna_def_scene_render_data(BlenderRNA *brna) "resolution to 480 pixels"}, {0, NULL, 0, NULL, NULL}}; + static EnumPropertyItem views_format_items[] = { + {SCE_VIEWS_FORMAT_STEREO_3D, "STEREO_3D", 0, "Stereo 3D", + "Single stereo camera system, adjust the stereo settings in the camera panel"}, + {SCE_VIEWS_FORMAT_MULTIVIEW, "MULTIVIEW", 0, "Multi-View", + "Multi camera system, adjust the cameras individually"}, + {0, NULL, 0, NULL, NULL} + }; + + + rna_def_scene_ffmpeg_settings(brna); #ifdef WITH_QUICKTIME rna_def_scene_quicktime_settings(brna); @@ -5326,6 +5683,32 @@ static void rna_def_scene_render_data(BlenderRNA *brna) RNA_def_property_ui_icon(prop, ICON_UNPINNED, 1); RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL); + /* views (stereoscopy et al) */ + prop = RNA_def_property(srna, "views", PROP_COLLECTION, PROP_NONE); + RNA_def_property_struct_type(prop, "SceneRenderView"); + RNA_def_property_ui_text(prop, "Render Views", ""); + rna_def_render_views(brna, prop); + + prop = RNA_def_property(srna, "stereo_views", PROP_COLLECTION, PROP_NONE); + RNA_def_property_collection_sdna(prop, NULL, "views", NULL); + RNA_def_property_collection_funcs(prop, "rna_RenderSettings_stereoViews_begin", "rna_iterator_listbase_next", + "rna_iterator_listbase_end", "rna_iterator_listbase_get", + NULL, NULL, NULL, NULL); + RNA_def_property_struct_type(prop, "SceneRenderView"); + RNA_def_property_ui_text(prop, "Render Views", ""); + + prop = RNA_def_property(srna, "use_multiview", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "scemode", R_MULTIVIEW); + RNA_def_property_ui_text(prop, "Multiple Views", "Use multiple views in the scene"); + RNA_def_property_update(prop, NC_WINDOW, NULL); + + prop = RNA_def_property(srna, "views_format", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, views_format_items); + RNA_def_property_clear_flag(prop, PROP_ANIMATABLE); + RNA_def_property_ui_text(prop, "Setup Stereo Mode", ""); + RNA_def_property_enum_funcs(prop, NULL, "rna_RenderSettings_views_format_set", NULL); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + /* engine */ prop = RNA_def_property(srna, "engine", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, engine_items); @@ -6076,8 +6459,8 @@ void RNA_def_scene(BlenderRNA *brna) /* *** Animated *** */ rna_def_scene_render_data(brna); rna_def_scene_render_layer(brna); - rna_def_gpu_fx(brna); + rna_def_scene_render_view(brna); /* Scene API */ RNA_api_scene(srna); diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c index 0d8d58eae43..0d70b6f3ba8 100644 --- a/source/blender/makesrna/intern/rna_scene_api.c +++ b/source/blender/makesrna/intern/rna_scene_api.c @@ -119,15 +119,15 @@ static void rna_Scene_update_tagged(Scene *scene) #endif } -static void rna_SceneRender_get_frame_path(RenderData *rd, int frame, int preview, char *name) +static void rna_SceneRender_get_frame_path(RenderData *rd, int frame, int preview, const char *view, char *name) { if (BKE_imtype_is_movie(rd->im_format.imtype)) { - BKE_movie_filepath_get(name, rd, preview != 0); + BKE_movie_filepath_get(name, rd, preview != 0, view); } else { BKE_image_path_from_imformat( name, rd->pic, G.main->name, (frame == INT_MIN) ? rd->cfra : frame, - &rd->im_format, (rd->scemode & R_EXTENSION) != 0, true); + &rd->im_format, (rd->scemode & R_EXTENSION) != 0, true, view); } } @@ -288,6 +288,8 @@ void RNA_api_scene_render(StructRNA *srna) RNA_def_int(func, "frame", INT_MIN, INT_MIN, INT_MAX, "", "Frame number to use, if unset the current frame will be used", MINAFRAME, MAXFRAME); parm = RNA_def_boolean(func, "preview", 0, "Preview", "Use preview range"); + parm = RNA_def_string_file_path(func, "view", NULL, FILE_MAX, "View", + "The name of the view to use to replace the \"%\" chars"); parm = RNA_def_string_file_path(func, "filepath", NULL, FILE_MAX, "File Path", "The resulting filepath from the scenes render settings"); diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c index fc9a501dae8..ee4ec1cb659 100644 --- a/source/blender/makesrna/intern/rna_sequencer.c +++ b/source/blender/makesrna/intern/rna_sequencer.c @@ -186,6 +186,11 @@ static void rna_SequenceEditor_elements_begin(CollectionPropertyIterator *iter, rna_SequenceEditor_elements_length(ptr), 0, NULL); } +static void rna_Sequence_views_format_update(Main *bmain, Scene *scene, PointerRNA *ptr) +{ + rna_Sequence_update(bmain, scene, ptr); +} + static void do_sequence_frame_change_update(Scene *scene, Sequence *seq) { Editing *ed = BKE_sequencer_editing_get(scene, false); @@ -1834,6 +1839,24 @@ static void rna_def_image(BlenderRNA *brna) "rna_SequenceEditor_elements_length", NULL, NULL, NULL); RNA_api_sequence_elements(brna, prop); + /* multiview */ + prop = RNA_def_property(srna, "use_multiview", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_USE_VIEWS); + RNA_def_property_ui_text(prop, "Use Multi-View", "Use Multiple Views (when available)"); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_views_format_update"); + + prop = RNA_def_property(srna, "views_format", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "views_format"); + RNA_def_property_enum_items(prop, views_format_items); + RNA_def_property_ui_text(prop, "Views Format", "Mode to load image views"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Sequence_views_format_update"); + + prop = RNA_def_property(srna, "stereo_3d_format", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "stereo3d_format"); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "Stereo3dFormat"); + RNA_def_property_ui_text(prop, "Stereo 3D Format", "Settings for stereo 3d"); + rna_def_filter_video(srna); rna_def_proxy(srna); rna_def_input(srna); @@ -1925,6 +1948,24 @@ static void rna_def_movie(BlenderRNA *brna) "rna_Sequence_filepath_set"); RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_filepath_update"); + /* multiview */ + prop = RNA_def_property(srna, "use_multiview", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_USE_VIEWS); + RNA_def_property_ui_text(prop, "Use Multi-View", "Use Multiple Views (when available)"); + RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_views_format_update"); + + prop = RNA_def_property(srna, "views_format", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "views_format"); + RNA_def_property_enum_items(prop, views_format_items); + RNA_def_property_ui_text(prop, "Views Format", "Mode to load movie views"); + RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Sequence_views_format_update"); + + prop = RNA_def_property(srna, "stereo_3d_format", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "stereo3d_format"); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "Stereo3dFormat"); + RNA_def_property_ui_text(prop, "Stereo 3D Format", "Settings for stereo 3d"); + rna_def_filter_video(srna); rna_def_proxy(srna); rna_def_input(srna); diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c index 2d3e3c56672..881688281f3 100644 --- a/source/blender/makesrna/intern/rna_sequencer_api.c +++ b/source/blender/makesrna/intern/rna_sequencer_api.c @@ -198,6 +198,7 @@ static Sequence *rna_Sequences_new_movie(ID *id, Editing *ed, ReportList *report { Scene *scene = (Scene *)id; Sequence *seq; + StripAnim *sanim; struct anim *an = openanim(file, IB_rect, 0, NULL); @@ -207,7 +208,11 @@ static Sequence *rna_Sequences_new_movie(ID *id, Editing *ed, ReportList *report } seq = alloc_generic_sequence(ed, name, frame_start, channel, SEQ_TYPE_MOVIE, file); - seq->anim = an; + + sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim"); + BLI_addtail(&seq->anims, sanim); + sanim->anim = an; + seq->anim_preseek = IMB_anim_get_preseek(an); seq->len = IMB_anim_get_duration(an, IMB_TC_RECORD_RUN); diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c index 6aaa947e9e4..4a4c2a34fdf 100644 --- a/source/blender/makesrna/intern/rna_space.c +++ b/source/blender/makesrna/intern/rna_space.c @@ -31,6 +31,7 @@ #include "BLF_translation.h" +#include "BKE_image.h" #include "BKE_key.h" #include "BKE_movieclip.h" #include "BKE_node.h" @@ -90,6 +91,34 @@ EnumPropertyItem space_type_items[] = { {0, NULL, 0, NULL, NULL} }; +#define V3D_S3D_CAMERA_LEFT {STEREO_LEFT_ID, "LEFT", ICON_RESTRICT_RENDER_OFF, "Left", ""}, +#define V3D_S3D_CAMERA_RIGHT {STEREO_RIGHT_ID, "RIGHT", ICON_RESTRICT_RENDER_OFF, "Right", ""}, +#define V3D_S3D_CAMERA_S3D {STEREO_3D_ID, "S3D", ICON_CAMERA_STEREO, "3D", ""}, +#ifdef RNA_RUNTIME +#define V3D_S3D_CAMERA_VIEWS {STEREO_MONO_ID, "MONO", ICON_RESTRICT_RENDER_OFF, "Views", ""}, +#endif + +static EnumPropertyItem stereo3d_camera_items[] = { + V3D_S3D_CAMERA_LEFT + V3D_S3D_CAMERA_RIGHT + V3D_S3D_CAMERA_S3D + {0, NULL, 0, NULL, NULL} +}; + +#ifdef RNA_RUNTIME +static EnumPropertyItem multiview_camera_items[] = { + V3D_S3D_CAMERA_VIEWS + V3D_S3D_CAMERA_S3D + {0, NULL, 0, NULL, NULL} +}; +#endif + +#undef V3D_S3D_CAMERA_LEFT +#undef V3D_S3D_CAMERA_RIGHT +#undef V3D_S3D_CAMERA_S3D +#undef V3D_S3D_CAMERA_VIEWS + + static EnumPropertyItem pivot_items_full[] = { {V3D_CENTER, "BOUNDING_BOX_CENTER", ICON_ROTATE, "Bounding Box Center", "Pivot around bounding box center of selected object(s)"}, @@ -659,6 +688,17 @@ static EnumPropertyItem *rna_SpaceView3D_viewport_shade_itemf(bContext *UNUSED(C return item; } +static EnumPropertyItem *rna_SpaceView3D_stereo3d_camera_itemf(bContext *UNUSED(C), PointerRNA *ptr, + PropertyRNA *UNUSED(prop), bool *UNUSED(r_free)) +{ + Scene *scene = ((bScreen *)ptr->id.data)->scene; + + if (scene->r.views_format == SCE_VIEWS_FORMAT_MULTIVIEW) + return multiview_camera_items; + else + return stereo3d_camera_items; +} + /* Space Image Editor */ static PointerRNA rna_SpaceImageEditor_uvedit_get(PointerRNA *ptr) @@ -671,6 +711,38 @@ static void rna_SpaceImageEditor_mode_update(Main *bmain, Scene *scene, PointerR ED_space_image_paint_update(bmain->wm.first, scene->toolsettings); } + +static void rna_SpaceImageEditor_show_stereo_set(PointerRNA *ptr, int value) +{ + SpaceImage *sima = (SpaceImage *)(ptr->data); + + if (value) + sima->iuser.flag |= IMA_SHOW_STEREO; + else + sima->iuser.flag &= ~IMA_SHOW_STEREO; +} + +static int rna_SpaceImageEditor_show_stereo_get(PointerRNA *ptr) +{ + SpaceImage *sima = (SpaceImage *)(ptr->data); + return (sima->iuser.flag & IMA_SHOW_STEREO) != 0; +} + +static void rna_SpaceImageEditor_show_stereo_update(Main *UNUSED(bmain), Scene *UNUSED(unused), PointerRNA *ptr) +{ + SpaceImage *sima = (SpaceImage *)(ptr->data); + Image *ima = sima->image; + + if (ima) { + if (ima->rr) { + BKE_image_multilayer_index(ima->rr, &sima->iuser); + } + else { + BKE_image_multiview_index(ima, &sima->iuser); + } + } +} + static int rna_SpaceImageEditor_show_render_get(PointerRNA *ptr) { SpaceImage *sima = (SpaceImage *)(ptr->data); @@ -798,6 +870,24 @@ static void rna_SpaceImageEditor_cursor_location_set(PointerRNA *ptr, const floa } } +static void rna_SpaceImageEditor_image_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr) +{ + SpaceImage *sima = (SpaceImage *)ptr->data; + Image *ima = sima->image; + + /* make sure all the iuser settings are valid for the sima image */ + if (ima) { + if (ima->rr) { + if (BKE_image_multilayer_index(sima->image->rr, &sima->iuser) == NULL) { + BKE_image_init_imageuser(sima->image, &sima->iuser); + } + } + else { + BKE_image_multiview_index(ima, &sima->iuser); + } + } +} + static void rna_SpaceImageEditor_scopes_update(struct bContext *C, struct PointerRNA *ptr) { SpaceImage *sima = (SpaceImage *)ptr->data; @@ -2472,6 +2562,41 @@ static void rna_def_space_view3d(BlenderRNA *brna) RNA_def_property_ui_text(prop, "FX Options", "Options used for real time compositing"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + /* Stereo Settings */ + prop = RNA_def_property(srna, "stereo_3d_camera", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_sdna(prop, NULL, "stereo3d_camera"); + RNA_def_property_enum_items(prop, stereo3d_camera_items); + RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_SpaceView3D_stereo3d_camera_itemf"); + RNA_def_property_ui_text(prop, "Camera", ""); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "show_stereo_3d_cameras", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "stereo3d_flag", V3D_S3D_DISPCAMERAS); + RNA_def_property_ui_text(prop, "Cameras", "Show the left and right cameras"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "show_stereo_3d_convergence_plane", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "stereo3d_flag", V3D_S3D_DISPPLANE); + RNA_def_property_ui_text(prop, "Plane", "Show the stereo 3d convergence plane"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "stereo_3d_convergence_plane_alpha", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "stereo3d_convergence_alpha"); + RNA_def_property_ui_text(prop, "Plane Alpha", "Opacity (alpha) of the convergence plane"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "show_stereo_3d_volume", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "stereo3d_flag", V3D_S3D_DISPVOLUME); + RNA_def_property_ui_text(prop, "Volume", "Show the stereo 3d frustum volume"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + prop = RNA_def_property(srna, "stereo_3d_volume_alpha", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "stereo3d_volume_alpha"); + RNA_def_property_ui_text(prop, "Volume Alpha", "Opacity (alpha) of the cameras frustum volume"); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL); + + /* *** Animated *** */ + RNA_define_animate_sdna(true); /* region */ srna = RNA_def_struct(brna, "RegionView3D", NULL); @@ -2645,7 +2770,7 @@ static void rna_def_space_image(BlenderRNA *brna) RNA_def_property_pointer_funcs(prop, NULL, "rna_SpaceImageEditor_image_set", NULL, NULL); RNA_def_property_ui_text(prop, "Image", "Image displayed and edited in this space"); RNA_def_property_flag(prop, PROP_EDITABLE); - RNA_def_property_update(prop, NC_GEOM | ND_DATA, NULL); /* is handled in image editor too */ + RNA_def_property_update(prop, NC_GEOM | ND_DATA, "rna_SpaceImageEditor_image_update"); /* is handled in image editor too */ prop = RNA_def_property(srna, "image_user", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_NEVER_NULL); @@ -2697,6 +2822,12 @@ static void rna_def_space_image(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Draw Channels", "Channels of the image to draw"); RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL); + prop = RNA_def_property(srna, "show_stereo_3d", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_funcs(prop, "rna_SpaceImageEditor_show_stereo_get", "rna_SpaceImageEditor_show_stereo_set"); + RNA_def_property_ui_text(prop, "Show Stereo", "Display the image in Stereo 3D"); + RNA_def_property_ui_icon(prop, ICON_CAMERA_STEREO, 0); + RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, "rna_SpaceImageEditor_show_stereo_update"); + /* uv */ prop = RNA_def_property(srna, "uv_editor", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_NEVER_NULL); diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c index fea8b630af6..832a29a2761 100644 --- a/source/blender/makesrna/intern/rna_ui_api.c +++ b/source/blender/makesrna/intern/rna_ui_api.c @@ -779,6 +779,16 @@ void RNA_api_ui_layout(StructRNA *srna) RNA_def_property_flag(parm, PROP_REQUIRED | PROP_RNAPTR | PROP_NEVER_NULL); RNA_def_boolean(func, "color_management", false, "", "Show color management settings"); + func = RNA_def_function(srna, "template_image_stereo_3d", "uiTemplateImageStereo3d"); + RNA_def_function_ui_description(func, "User interface for setting image stereo 3d options"); + parm = RNA_def_pointer(func, "stereo_3d_format", "Stereo3dFormat", "", ""); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_RNAPTR | PROP_NEVER_NULL); + + func = RNA_def_function(srna, "template_image_views", "uiTemplateImageViews"); + RNA_def_function_ui_description(func, "User interface for setting image views output options"); + parm = RNA_def_pointer(func, "image_settings", "ImageFormatSettings", "", ""); + RNA_def_property_flag(parm, PROP_REQUIRED | PROP_RNAPTR | PROP_NEVER_NULL); + func = RNA_def_function(srna, "template_movieclip", "uiTemplateMovieClip"); RNA_def_function_ui_description(func, "Item(s). User interface for selecting movie clips and their source paths"); RNA_def_function_flag(func, FUNC_USE_CONTEXT); diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c index fc6d28e67c2..23412a549fa 100644 --- a/source/blender/makesrna/intern/rna_userdef.c +++ b/source/blender/makesrna/intern/rna_userdef.c @@ -4124,8 +4124,8 @@ static void rna_def_userdef_system(BlenderRNA *brna) RNA_def_property_boolean_sdna(prop, NULL, "uiflag2", USER_REGION_OVERLAP); RNA_def_property_ui_text(prop, "Region Overlap", "Draw tool/property regions over the main region, when using Triple Buffer"); - RNA_def_property_update(prop, 0, "rna_userdef_dpi_update"); - + RNA_def_property_update(prop, 0, "rna_userdef_dpi_update"); + #ifdef WITH_CYCLES prop = RNA_def_property(srna, "compute_device_type", PROP_ENUM, PROP_NONE); RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT); diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c index 64ecf61f774..dcd77e5a0f6 100644 --- a/source/blender/makesrna/intern/rna_wm.c +++ b/source/blender/makesrna/intern/rna_wm.c @@ -1750,6 +1750,37 @@ static void rna_def_piemenu(BlenderRNA *brna) RNA_define_verify_sdna(1); /* not in sdna */ } +static void rna_def_window_stereo3d(BlenderRNA *brna) +{ + StructRNA *srna; + PropertyRNA *prop; + + srna = RNA_def_struct(brna, "Stereo3dDisplay", NULL); + RNA_def_struct_sdna(srna, "Stereo3dFormat"); + RNA_def_struct_clear_flag(srna, STRUCT_UNDO); + RNA_def_struct_ui_text(srna, "Stereo 3d Display", "Settings for stereo 3d display"); + + prop = RNA_def_property(srna, "display_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, stereo3d_display_items); + RNA_def_property_ui_text(prop, "Display Mode", ""); + + prop = RNA_def_property(srna, "anaglyph_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, stereo3d_anaglyph_type_items); + RNA_def_property_ui_text(prop, "Anaglyph Type", ""); + + prop = RNA_def_property(srna, "interlace_type", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, stereo3d_interlace_type_items); + RNA_def_property_ui_text(prop, "Interlace Type", ""); + + prop = RNA_def_property(srna, "use_interlace_swap", PROP_BOOLEAN, PROP_BOOLEAN); + RNA_def_property_boolean_sdna(prop, NULL, "flag", S3D_INTERLACE_SWAP); + RNA_def_property_ui_text(prop, "Swap Left/Right", "Swap left and right stereo channels"); + + prop = RNA_def_property(srna, "use_sidebyside_crosseyed", PROP_BOOLEAN, PROP_BOOLEAN); + RNA_def_property_boolean_sdna(prop, NULL, "flag", S3D_SIDEBYSIDE_CROSSEYED); + RNA_def_property_ui_text(prop, "Cross-Eyed", "Right eye should see left image and vice-versa"); +} + static void rna_def_window(BlenderRNA *brna) { StructRNA *srna; @@ -1759,6 +1790,8 @@ static void rna_def_window(BlenderRNA *brna) RNA_def_struct_ui_text(srna, "Window", "Open window"); RNA_def_struct_sdna(srna, "wmWindow"); + rna_def_window_stereo3d(brna); + prop = RNA_def_property(srna, "screen", PROP_POINTER, PROP_NONE); RNA_def_property_flag(prop, PROP_NEVER_NULL); RNA_def_property_struct_type(prop, "Screen"); @@ -1788,6 +1821,12 @@ static void rna_def_window(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE); RNA_def_property_ui_text(prop, "Height", "Window height"); + prop = RNA_def_property(srna, "stereo_3d_display", PROP_POINTER, PROP_NONE); + RNA_def_property_pointer_sdna(prop, NULL, "stereo3d_format"); + RNA_def_property_flag(prop, PROP_NEVER_NULL); + RNA_def_property_struct_type(prop, "Stereo3dDisplay"); + RNA_def_property_ui_text(prop, "Stereo 3D Display", "Settings for stereo 3d display"); + RNA_api_window(srna); } diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt index 9eb6c1674a3..c95daffe557 100644 --- a/source/blender/nodes/CMakeLists.txt +++ b/source/blender/nodes/CMakeLists.txt @@ -122,6 +122,7 @@ set(SRC composite/nodes/node_composite_boxmask.c composite/nodes/node_composite_ellipsemask.c composite/nodes/node_composite_switch.c + composite/nodes/node_composite_switchview.c composite/nodes/node_composite_colorcorrection.c composite/nodes/node_composite_pixelate.c diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h index 961fdbfc0fb..0215db1dd55 100644 --- a/source/blender/nodes/NOD_composite.h +++ b/source/blender/nodes/NOD_composite.h @@ -133,6 +133,7 @@ void register_node_type_cmp_ellipsemask(void); void register_node_type_cmp_bokehimage(void); void register_node_type_cmp_bokehblur(void); void register_node_type_cmp_switch(void); +void register_node_type_cmp_switch_view(void); void register_node_type_cmp_pixelate(void); void register_node_type_cmp_trackpos(void); void register_node_type_cmp_planetrackdeform(void); diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h index 6b9a97c0b18..b33bec51c2c 100644 --- a/source/blender/nodes/NOD_static_types.h +++ b/source/blender/nodes/NOD_static_types.h @@ -202,6 +202,7 @@ DefNode( CompositorNode, CMP_NODE_MASK_ELLIPSE, def_cmp_ellipsemask, "ELLIP DefNode( CompositorNode, CMP_NODE_BOKEHIMAGE, def_cmp_bokehimage, "BOKEHIMAGE", BokehImage, "Bokeh Image", "" ) DefNode( CompositorNode, CMP_NODE_BOKEHBLUR, def_cmp_bokehblur, "BOKEHBLUR", BokehBlur, "Bokeh Blur", "" ) DefNode( CompositorNode, CMP_NODE_SWITCH, def_cmp_switch, "SWITCH", Switch, "Switch", "" ) +DefNode( CompositorNode, CMP_NODE_SWITCH_VIEW, def_cmp_switch_view, "VIEWSWITCH", SwitchView, "View Switch", "" ) DefNode( CompositorNode, CMP_NODE_COLORCORRECTION,def_cmp_colorcorrection,"COLORCORRECTION",ColorCorrection, "Color Correction", "" ) DefNode( CompositorNode, CMP_NODE_MASK, def_cmp_mask, "MASK", Mask, "Mask", "" ) DefNode( CompositorNode, CMP_NODE_KEYINGSCREEN, def_cmp_keyingscreen, "KEYINGSCREEN", KeyingScreen, "Keying Screen", "" ) diff --git a/source/blender/nodes/composite/node_composite_tree.c b/source/blender/nodes/composite/node_composite_tree.c index e616680647e..8bc61862458 100644 --- a/source/blender/nodes/composite/node_composite_tree.c +++ b/source/blender/nodes/composite/node_composite_tree.c @@ -226,12 +226,13 @@ void *COM_linker_hack = NULL; void ntreeCompositExecTree(Scene *scene, bNodeTree *ntree, RenderData *rd, int rendering, int do_preview, const ColorManagedViewSettings *view_settings, - const ColorManagedDisplaySettings *display_settings) + const ColorManagedDisplaySettings *display_settings, + const char *view_name) { #ifdef WITH_COMPOSITOR - COM_execute(rd, scene, ntree, rendering, view_settings, display_settings); + COM_execute(rd, scene, ntree, rendering, view_settings, display_settings, view_name); #else - UNUSED_VARS(scene, ntree, rd, rendering, view_settings, display_settings); + UNUSED_VARS(scene, ntree, rd, rendering, view_settings, display_settings, view_name); #endif UNUSED_VARS(do_preview); diff --git a/source/blender/nodes/composite/nodes/node_composite_image.c b/source/blender/nodes/composite/nodes/node_composite_image.c index 34f3350c2ff..23d47065f59 100644 --- a/source/blender/nodes/composite/nodes/node_composite_image.c +++ b/source/blender/nodes/composite/nodes/node_composite_image.c @@ -168,14 +168,21 @@ static void cmp_node_image_add_multilayer_outputs(bNodeTree *ntree, bNode *node, NodeImageLayer *sockdata; RenderPass *rpass; int index; + int passflag = 0; for (rpass = rl->passes.first, index = 0; rpass; rpass = rpass->next, ++index) { int type; if (rpass->channels == 1) type = SOCK_FLOAT; else type = SOCK_RGBA; - - sock = nodeAddStaticSocket(ntree, node, SOCK_OUT, type, PROP_NONE, rpass->name, rpass->name); + + /* we only need one socket per type */ + if (passflag & rpass->passtype) + continue; + + passflag |= rpass->passtype; + + sock = nodeAddStaticSocket(ntree, node, SOCK_OUT, type, PROP_NONE, rpass->internal_name, rpass->internal_name); /* extra socket info */ sockdata = MEM_callocN(sizeof(NodeImageLayer), "node image layer"); sock->storage = sockdata; diff --git a/source/blender/nodes/composite/nodes/node_composite_outputFile.c b/source/blender/nodes/composite/nodes/node_composite_outputFile.c index 8e602c6179c..7d1087435c2 100644 --- a/source/blender/nodes/composite/nodes/node_composite_outputFile.c +++ b/source/blender/nodes/composite/nodes/node_composite_outputFile.c @@ -127,7 +127,7 @@ bNodeSocket *ntreeCompositOutputFileAddSocket(bNodeTree *ntree, bNode *node, con BKE_imformat_defaults(&sockdata->format); /* use node data format by default */ sockdata->use_node_format = true; - + nimf->active_input = BLI_findindex(&node->inputs, sock); return sock; @@ -189,7 +189,7 @@ static void init_output_file(const bContext *C, PointerRNA *ptr) } else BKE_imformat_defaults(&nimf->format); - + /* add one socket by default */ ntreeCompositOutputFileAddSocket(ntree, node, "Image", format); } diff --git a/source/blender/nodes/composite/nodes/node_composite_switchview.c b/source/blender/nodes/composite/nodes/node_composite_switchview.c new file mode 100644 index 00000000000..d805cf4d87f --- /dev/null +++ b/source/blender/nodes/composite/nodes/node_composite_switchview.c @@ -0,0 +1,153 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2006 Blender Foundation. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): + * Dalai Felinto + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/nodes/composite/nodes/node_composite_switchview.c + * \ingroup cmpnodes + */ + +#include "BKE_context.h" +#include "../node_composite_util.h" + +/* **************** SWITCH VIEW ******************** */ +static bNodeSocketTemplate cmp_node_switch_view_out[] = { + { SOCK_RGBA, 0, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f}, + { -1, 0, "" } +}; + +static bNodeSocket *ntreeCompositSwitchViewAddSocket(bNodeTree *ntree, bNode *node, const char *name) +{ + bNodeSocket *sock = nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, NULL, name); + return sock; +} + +static void cmp_node_switch_view_sanitycheck(bNodeTree *ntree, bNode *node) +{ + bNodeSocket *sock; + + if (!BLI_listbase_is_empty(&node->inputs)) + return; + + sock = ntreeCompositSwitchViewAddSocket(ntree, node, "No View"); + sock->flag |= SOCK_HIDDEN; +} + +static void cmp_node_switch_view_update(bNodeTree *ntree, bNode *node) +{ + bNodeSocket *sock; + SceneRenderView *srv; + Scene *scene = (Scene *)node->id; + + /* only update when called from the operator button */ + if (node->update != NODE_UPDATE_OPERATOR) + return; + + if (scene == NULL) { + nodeRemoveAllSockets(ntree, node); + /* make sure there is always one socket */ + cmp_node_switch_view_sanitycheck(ntree, node); + return; + } + + /* remove the views that were removed */ + sock = node->inputs.last; + while (sock) { + srv = BLI_findstring(&scene->r.views, sock->name, offsetof(SceneRenderView, name)); + + if (srv == NULL) { + bNodeSocket *sock_del = sock; + sock = sock->prev; + nodeRemoveSocket(ntree, node, sock_del); + } + else { + if (srv->viewflag & SCE_VIEW_DISABLE) + sock->flag |= SOCK_HIDDEN; + else + sock->flag &= ~SOCK_HIDDEN; + + sock = sock->prev; + } + } + + /* add the new views */ + for (srv = scene->r.views.first; srv; srv = srv->next) { + sock = BLI_findstring(&node->inputs, srv->name, offsetof(bNodeSocket, name)); + + if (sock == NULL) + sock = ntreeCompositSwitchViewAddSocket(ntree, node, srv->name); + + if (srv->viewflag & SCE_VIEW_DISABLE) + sock->flag |= SOCK_HIDDEN; + else + sock->flag &= ~SOCK_HIDDEN; + } + + /* make sure there is always one socket */ + cmp_node_switch_view_sanitycheck(ntree, node); +} + +static void init_switch_view(const bContext *C, PointerRNA *ptr) +{ + Scene *scene = CTX_data_scene(C); + bNodeTree *ntree = ptr->id.data; + bNode *node = ptr->data; + SceneRenderView *srv; + bNodeSocket *sock; + int nr; + + /* store scene for updates */ + node->id = (ID *)scene; + + if (scene) { + RenderData *rd = &scene->r; + + for (nr = 0, srv = rd->views.first; srv; srv = srv->next, nr++) { + sock = ntreeCompositSwitchViewAddSocket(ntree, node, srv->name); + + if ((srv->viewflag & SCE_VIEW_DISABLE)) + sock->flag |= SOCK_HIDDEN; + } + } + + /* make sure there is always one socket */ + cmp_node_switch_view_sanitycheck(ntree, node); +} + +/* custom1 = mix type */ +void register_node_type_cmp_switch_view(void) +{ + static bNodeType ntype; + + cmp_node_type_base(&ntype, CMP_NODE_SWITCH_VIEW, "Switch View", NODE_CLASS_CONVERTOR, 0); + node_type_socket_templates(&ntype, NULL, cmp_node_switch_view_out); + + ntype.initfunc_api = init_switch_view; + + node_type_update(&ntype, cmp_node_switch_view_update, NULL); + + nodeRegisterType(&ntype); +} diff --git a/source/blender/quicktime/apple/qtkit_export.m b/source/blender/quicktime/apple/qtkit_export.m index 4214fa9ac32..db2d4e72dc5 100644 --- a/source/blender/quicktime/apple/qtkit_export.m +++ b/source/blender/quicktime/apple/qtkit_export.m @@ -98,8 +98,6 @@ typedef struct QuicktimeExport { } QuicktimeExport; -static struct QuicktimeExport *qtexport; - #define AUDIOOUTPUTBUFFERSIZE 65536 #pragma mark rna helper functions @@ -245,7 +243,7 @@ void makeqtstring(RenderData *rd, char *string, bool preview) } } -void filepath_qt(char *string, RenderData *rd, bool preview) +void filepath_qt(char *string, RenderData *rd, bool preview, const char *suffix) { int sfra, efra; @@ -276,8 +274,23 @@ void filepath_qt(char *string, RenderData *rd, bool preview) BLI_path_frame_range(string, sfra, efra, 4); } } + + BLI_path_suffix(string, FILE_MAX, suffix, ""); } +void *context_create_qt(void) +{ + QuicktimeExport *qtexport = MEM_callocN(sizeof(QuicktimeExport), "QuicktimeExport"); + return qtexport; +} + +void context_free_qt(void *context_v) +{ + QuicktimeExport *qtexport = context_v; + if (qtexport) { + MEM_freeN(qtexport); + } +} #pragma mark audio export functions @@ -302,12 +315,13 @@ static OSStatus write_cookie(AudioConverterRef converter, AudioFileID outfile) } /* AudioConverter input stream callback */ -static OSStatus AudioConverterInputCallback(AudioConverterRef inAudioConverter, +static OSStatus AudioConverterInputCallback(AudioConverterRef inAudioConverter, UInt32* ioNumberDataPackets, AudioBufferList* ioData, AudioStreamPacketDescription** outDataPacketDescription, void* inUserData) -{ +{ + QuicktimeExport *qtexport = inUserData; if (qtexport->audioTotalExportedFrames >= qtexport->audioLastFrame) { /* EOF */ *ioNumberDataPackets = 0; return noErr; @@ -334,7 +348,7 @@ static OSStatus AudioConverterInputCallback(AudioConverterRef inAudioConverter, #pragma mark export functions -int start_qt(struct Scene *scene, struct RenderData *rd, int rectx, int recty, ReportList *reports, bool preview) +int start_qt(void *context_v, struct Scene *scene, struct RenderData *rd, int rectx, int recty, ReportList *reports, bool preview, const char *UNUSED(suffix)) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSError *error; @@ -342,9 +356,8 @@ int start_qt(struct Scene *scene, struct RenderData *rd, int rectx, int recty, R int success = 1; OSStatus err = noErr; int sfra, efra; + QuicktimeExport *qtexport = context_v; - if (qtexport == NULL) qtexport = MEM_callocN(sizeof(QuicktimeExport), "QuicktimeExport"); - if (preview) { sfra = rd->psfra; efra = rd->pefra; @@ -639,7 +652,7 @@ int start_qt(struct Scene *scene, struct RenderData *rd, int rectx, int recty, R return success; } -int append_qt(struct RenderData *rd, int start_frame, int frame, int *pixels, int rectx, int recty, ReportList *reports) +int append_qt(void *context_v, struct RenderData *rd, int start_frame, int frame, int *pixels, int rectx, int recty, const char *UNUSED(suffix), ReportList *reports) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; NSBitmapImageRep *blBitmapFormatImage; @@ -647,6 +660,7 @@ int append_qt(struct RenderData *rd, int start_frame, int frame, int *pixels, in OSStatus err = noErr; unsigned char *from_Ptr,*to_Ptr; int y,from_i,to_i; + QuicktimeExport *qtexport = context_v; /* Create bitmap image rep in blender format (32bit RGBA) */ blBitmapFormatImage = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL @@ -704,7 +718,7 @@ int append_qt(struct RenderData *rd, int start_frame, int frame, int *pixels, in audioPacketsConverted = 1; err = AudioConverterFillComplexBuffer(qtexport->audioConverter, AudioConverterInputCallback, - NULL, &audioPacketsConverted, &qtexport->audioBufferList, qtexport->audioOutputPktDesc); + qtexport, &audioPacketsConverted, &qtexport->audioBufferList, qtexport->audioOutputPktDesc); if (audioPacketsConverted) { AudioFileWritePackets(qtexport->audioFile, false, qtexport->audioBufferList.mBuffers[0].mDataByteSize, qtexport->audioOutputPktDesc, qtexport->audioOutputPktPos, &audioPacketsConverted, qtexport->audioOutputBuffer); @@ -737,9 +751,11 @@ int append_qt(struct RenderData *rd, int start_frame, int frame, int *pixels, in } -void end_qt(void) +void end_qt(void *context_v) { NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init]; + QuicktimeExport *qtexport = context_v; + if (qtexport->movie) { if (qtexport->audioFile) @@ -830,11 +846,6 @@ void end_qt(void) } [QTMovie exitQTKitOnThread]; - - if (qtexport) { - MEM_freeN(qtexport); - qtexport = NULL; - } [pool drain]; } diff --git a/source/blender/quicktime/quicktime_export.h b/source/blender/quicktime/quicktime_export.h index 360c475fdb5..8a10a4a05d6 100644 --- a/source/blender/quicktime/quicktime_export.h +++ b/source/blender/quicktime/quicktime_export.h @@ -55,10 +55,12 @@ struct RenderData; struct ReportList; struct Scene; -int start_qt(struct Scene *scene, struct RenderData *rd, int rectx, int recty, struct ReportList *reports, bool preview); //for movie handle (BKE writeavi.c now) -int append_qt(struct RenderData *rd, int start_frame, int frame, int *pixels, int rectx, int recty, struct ReportList *reports); -void end_qt(void); -void filepath_qt(char *string, struct RenderData *rd, bool preview); +int start_qt(void *context_v, struct Scene *scene, struct RenderData *rd, int rectx, int recty, struct ReportList *reports, bool preview, const char *suffix); //for movie handle (BKE writeavi.c now) +int append_qt(void *context_v, struct RenderData *rd, int start_frame, int frame, int *pixels, int rectx, int recty, const char *suffix, struct ReportList *reports); +void end_qt(void *context_v); +void filepath_qt(char *string, struct RenderData *rd, bool preview, const char *suffix); +void *context_create_qt(void); +void context_free_qt(void *context_v); /*RNA helper functions */ void quicktime_verify_image_type(struct RenderData *rd, struct ImageFormatData *imf); //used by RNA for defaults values init, if needed diff --git a/source/blender/render/extern/include/RE_engine.h b/source/blender/render/extern/include/RE_engine.h index a52244ff87f..4857c409f08 100644 --- a/source/blender/render/extern/include/RE_engine.h +++ b/source/blender/render/extern/include/RE_engine.h @@ -135,10 +135,14 @@ void RE_engine_free(RenderEngine *engine); void RE_layer_load_from_file(struct RenderLayer *layer, struct ReportList *reports, const char *filename, int x, int y); void RE_result_load_from_file(struct RenderResult *result, struct ReportList *reports, const char *filename); -struct RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h, const char *layername); +struct RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h, const char *layername, const char *viewname); void RE_engine_update_result(RenderEngine *engine, struct RenderResult *result); void RE_engine_end_result(RenderEngine *engine, struct RenderResult *result, int cancel, int merge_results); +void RE_engine_active_view_set(RenderEngine *engine, const char *viewname); +float RE_engine_get_camera_shift_x(RenderEngine *engine, struct Object *camera); +void RE_engine_get_camera_model_matrix(RenderEngine *engine, struct Object *camera, float *r_modelmat); + int RE_engine_test_break(RenderEngine *engine); void RE_engine_update_stats(RenderEngine *engine, const char *stats, const char *info); void RE_engine_update_progress(RenderEngine *engine, float progress); diff --git a/source/blender/render/extern/include/RE_pipeline.h b/source/blender/render/extern/include/RE_pipeline.h index d1b6673c792..2a07246eaba 100644 --- a/source/blender/render/extern/include/RE_pipeline.h +++ b/source/blender/render/extern/include/RE_pipeline.h @@ -35,8 +35,10 @@ #include "DNA_listBase.h" #include "DNA_vec_types.h" +struct bMovieHandle; struct bNodeTree; struct Image; +struct ImageFormatData; struct Main; struct NodeBlurData; struct Object; @@ -46,6 +48,7 @@ struct ReportList; struct Scene; struct SceneRenderLayer; struct EnvMap; +struct RenderResult; /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* this include is what is exposed of render to outside world */ @@ -65,6 +68,19 @@ typedef struct Render Render; * and how it's converted */ +typedef struct RenderView { + struct RenderView *next, *prev; + char name[64]; /* EXR_VIEW_MAXNAME */ + + /* if this exists, result of composited layers */ + float *rectf; + /* if this exists, result of composited layers */ + float *rectz; + /* optional, 32 bits version of picture, used for sequencer, ogl render and image curves */ + int *rect32; + +} RenderView; + typedef struct RenderPass { struct RenderPass *next, *prev; int passtype, channels; @@ -72,6 +88,11 @@ typedef struct RenderPass { char chan_id[8]; /* amount defined in openexr_multi.h */ float *rect; int rectx, recty; + + char internal_name[64]; /* EXR_PASS_MAXNAME */ + char view[64]; /* EXR_VIEW_MAXNAME */ + int view_id; /* quick lookup */ + int debug_type; } RenderPass; @@ -81,7 +102,7 @@ enum { /* a renderlayer is a full image, but with all passes and samples */ /* size of the rects is defined in RenderResult */ -/* after render, the Combined pass is in rectf, for renderlayers read from files it is a real pass */ +/* after render, the Combined pass is in combined, for renderlayers read from files it is a real pass */ typedef struct RenderLayer { struct RenderLayer *next, *prev; @@ -92,8 +113,10 @@ typedef struct RenderLayer { struct Material *mat_override; struct Group *light_override; - - float *rectf; /* 4 float, standard rgba buffer (read not above!) */ + + /* MULTIVIEW_TODO: acolrect and scolrect are not supported by multiview at the moment. + * If they are really required they should be in RenderView instead */ + float *acolrect; /* 4 float, optional transparent buffer, needs storage for display updates */ float *scolrect; /* 4 float, optional strand buffer, needs storage for display updates */ int *display_buffer; /* 4 char, optional color managed display buffer which is used when @@ -129,6 +152,9 @@ typedef struct RenderResult { /* the main buffers */ ListBase layers; + /* multiView maps to a StringVector in OpenEXR */ + ListBase views; + /* allowing live updates: */ volatile rcti renrect; volatile RenderLayer *renlay; @@ -183,16 +209,18 @@ void RE_FreeRenderResult(struct RenderResult *rr); struct RenderResult *RE_AcquireResultRead(struct Render *re); struct RenderResult *RE_AcquireResultWrite(struct Render *re); void RE_ReleaseResult(struct Render *re); -void RE_AcquireResultImage(struct Render *re, struct RenderResult *rr); +void RE_AcquireResultImageViews(struct Render *re, struct RenderResult *rr); +void RE_ReleaseResultImageViews(struct Render *re, struct RenderResult *rr); +void RE_AcquireResultImage(struct Render *re, struct RenderResult *rr, const int view_id); void RE_ReleaseResultImage(struct Render *re); void RE_SwapResult(struct Render *re, struct RenderResult **rr); struct RenderStats *RE_GetStats(struct Render *re); void RE_ResultGet32(struct Render *re, unsigned int *rect); -void RE_AcquiredResultGet32(struct Render *re, struct RenderResult *result, unsigned int *rect); +void RE_AcquiredResultGet32(struct Render *re, struct RenderResult *result, unsigned int *rect, const int view_id); struct RenderLayer *RE_GetRenderLayer(struct RenderResult *rr, const char *name); -float *RE_RenderLayerGetPass(struct RenderLayer *rl, int passtype); +float *RE_RenderLayerGetPass(volatile struct RenderLayer *rl, int passtype, const char *viewname); /* obligatory initialize call, disprect is optional */ void RE_InitState(struct Render *re, struct Render *source, struct RenderData *rd, @@ -203,6 +231,7 @@ void RE_ChangeModeFlag(struct Render *re, int flag, bool clear); /* set up the viewplane/perspective matrix, three choices */ struct Object *RE_GetCamera(struct Render *re); /* return camera override if set */ +void RE_SetOverrideCamera(struct Render *re, struct Object *camera); void RE_SetCamera(struct Render *re, struct Object *camera); void RE_SetEnvmapCamera(struct Render *re, struct Object *cam_ob, float viewscale, float clipsta, float clipend); void RE_SetWindow(struct Render *re, rctf *viewplane, float clipsta, float clipend); @@ -232,6 +261,11 @@ void RE_init_threadcount(Render *re); /* the main processor, assumes all was set OK! */ void RE_TileProcessor(struct Render *re); +bool RE_WriteRenderViewsImage(struct ReportList *reports, struct RenderResult *rr, struct Scene *scene, struct Object *camera, const bool stamp, char *name); +bool RE_WriteRenderViewsMovie(struct ReportList *reports, struct RenderResult *rr, struct Scene *scene, struct RenderData *rd, + struct bMovieHandle *mh, const size_t width, const size_t height, void **movie_ctx_arr, + const size_t totvideos); + /* only RE_NewRender() needed, main Blender render calls */ void RE_BlenderFrame(struct Render *re, struct Main *bmain, struct Scene *scene, struct SceneRenderLayer *srl, struct Object *camera_override, @@ -243,6 +277,9 @@ void RE_RenderFreestyleStrokes(struct Render *re, struct Main *bmain, struct Sce void RE_RenderFreestyleExternal(struct Render *re); #endif +void RE_SetActiveRenderView(struct Render *re, const char *viewname); +const char *RE_GetActiveRenderView(struct Render *re); + /* error reporting */ void RE_SetReports(struct Render *re, struct ReportList *reports); @@ -250,7 +287,7 @@ void RE_SetReports(struct Render *re, struct ReportList *reports); void RE_PreviewRender(struct Render *re, struct Main *bmain, struct Scene *scene); bool RE_ReadRenderResult(struct Scene *scene, struct Scene *scenode); -bool RE_WriteRenderResult(struct ReportList *reports, RenderResult *rr, const char *filename, int compress); +bool RE_WriteRenderResult(struct ReportList *reports, RenderResult *rr, const char *filename, struct ImageFormatData *imf, const bool multiview, const char *view); struct RenderResult *RE_MultilayerConvert(void *exrhandle, const char *colorspace, bool predivide, int rectx, int recty); extern const float default_envmap_layout[]; @@ -276,6 +313,8 @@ void RE_zbuf_accumulate_vecblur(struct NodeBlurData *nbd, int xsize, int ysize, int RE_seq_render_active(struct Scene *scene, struct RenderData *rd); +bool RE_layers_have_name(struct RenderResult *result); + /* shaded view or baking options */ #define RE_BAKE_LIGHT 0 /* not listed in rna_scene.c -> can't be enabled! */ #define RE_BAKE_ALL 1 @@ -297,6 +336,7 @@ void RE_Database_Baking(struct Render *re, struct Main *bmain, struct Scene *sce void RE_DataBase_GetView(struct Render *re, float mat[4][4]); void RE_GetCameraWindow(struct Render *re, struct Object *camera, int frame, float mat[4][4]); +void RE_GetCameraModelMatrix(struct Render *re, struct Object *camera, float r_mat[4][4]); struct Scene *RE_GetScene(struct Render *re); bool RE_force_single_renderlayer(struct Scene *scene); @@ -304,5 +344,16 @@ bool RE_is_rendering_allowed(struct Scene *scene, struct Object *camera_override bool RE_allow_render_generic_object(struct Object *ob); +/******* defined in render_result.c *********/ + +bool RE_HasFakeLayer(RenderResult *res); +bool RE_RenderResult_is_stereo(RenderResult *res); + +float *RE_RenderViewGetRectf(struct RenderResult *rr, const int view_id); +float *RE_RenderViewGetRectz(struct RenderResult *rr, const int view_id); +int *RE_RenderViewGetRect32(struct RenderResult *rr, const int view_id); +void RE_RenderViewSetRectf(struct RenderResult *res, const int view_id, float *rect); +void RE_RenderViewSetRectz(struct RenderResult *res, const int view_id, float *rect); + #endif /* __RE_PIPELINE_H__ */ diff --git a/source/blender/render/intern/include/render_result.h b/source/blender/render/intern/include/render_result.h index 90ff69dbfbe..e7a2cbf99cd 100644 --- a/source/blender/render/intern/include/render_result.h +++ b/source/blender/render/intern/include/render_result.h @@ -38,6 +38,7 @@ #define RR_USE_EXR 1 #define RR_ALL_LAYERS NULL +#define RR_ALL_VIEWS NULL struct ImBuf; struct ListBase; @@ -53,12 +54,14 @@ struct ColorManagedViewSettings; /* New */ struct RenderResult *render_result_new(struct Render *re, - struct rcti *partrct, int crop, int savebuffers, const char *layername); + struct rcti *partrct, int crop, int savebuffers, const char *layername, const char *viewname); struct RenderResult *render_result_new_full_sample(struct Render *re, - struct ListBase *lb, struct rcti *partrct, int crop, int savebuffers); + struct ListBase *lb, struct rcti *partrct, int crop, int savebuffers, const char *viewname); struct RenderResult *render_result_new_from_exr(void *exrhandle, const char *colorspace, bool predivide, int rectx, int recty); +void render_result_views_new(struct RenderResult *rr, struct RenderData *rd); + /* Merge */ void render_result_merge(struct RenderResult *rr, struct RenderResult *rrpart); @@ -78,7 +81,7 @@ void render_result_single_layer_end(struct Render *re); void render_result_exr_file_begin(struct Render *re); void render_result_exr_file_end(struct Render *re); -void render_result_exr_file_merge(struct RenderResult *rr, struct RenderResult *rrpart); +void render_result_exr_file_merge(struct RenderResult *rr, struct RenderResult *rrpart, const char *viewname); void render_result_exr_file_path(struct Scene *scene, const char *layname, int sample, char *filepath); int render_result_exr_file_read_sample(struct Render *re, int sample); @@ -91,15 +94,19 @@ bool render_result_exr_file_cache_read(struct Render *re); /* Combined Pixel Rect */ -struct ImBuf *render_result_rect_to_ibuf(struct RenderResult *rr, struct RenderData *rd); +struct ImBuf *render_result_rect_to_ibuf(struct RenderResult *rr, struct RenderData *rd, const int view_id); void render_result_rect_from_ibuf(struct RenderResult *rr, struct RenderData *rd, - struct ImBuf *ibuf); + struct ImBuf *ibuf, const int view_id); -void render_result_rect_fill_zero(struct RenderResult *rr); +void render_result_rect_fill_zero(struct RenderResult *rr, const int view_id); void render_result_rect_get_pixels(struct RenderResult *rr, unsigned int *rect, int rectx, int recty, const struct ColorManagedViewSettings *view_settings, - const struct ColorManagedDisplaySettings *display_settings); + const struct ColorManagedDisplaySettings *display_settings, + const int view_id); + +void render_result_views_shallowcopy(struct RenderResult *dst, struct RenderResult *src); +void render_result_views_shallowdelete(struct RenderResult *rr); #endif /* __RENDER_RESULT_H__ */ diff --git a/source/blender/render/intern/include/render_types.h b/source/blender/render/intern/include/render_types.h index 0d23c81afb0..8fe12015e87 100644 --- a/source/blender/render/intern/include/render_types.h +++ b/source/blender/render/intern/include/render_types.h @@ -275,6 +275,9 @@ struct Render struct ImagePool *pool; struct EvaluationContext *eval_ctx; + + void **movie_ctx_arr; + char viewname[MAX_NAME]; }; /* ------------------------------------------------------------------------- */ diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c index c0ba2f43c36..d36ab46d145 100644 --- a/source/blender/render/intern/source/convertblender.c +++ b/source/blender/render/intern/source/convertblender.c @@ -5171,8 +5171,7 @@ void RE_Database_FromScene(Render *re, Main *bmain, Scene *scene, unsigned int l * above call to BKE_scene_update_for_newframe, fixes bug. [#22702]. * following calls don't depend on 'RE_SetCamera' */ RE_SetCamera(re, camera); - - normalize_m4_m4(mat, camera->obmat); + RE_GetCameraModelMatrix(re, camera, mat); invert_m4(mat); RE_SetView(re, mat); @@ -5342,7 +5341,8 @@ static void database_fromscene_vectors(Render *re, Scene *scene, unsigned int la /* if no camera, viewmat should have been set! */ if (camera) { - normalize_m4_m4(mat, camera->obmat); + RE_GetCameraModelMatrix(re, camera, mat); + normalize_m4(mat); invert_m4(mat); RE_SetView(re, mat); } diff --git a/source/blender/render/intern/source/envmap.c b/source/blender/render/intern/source/envmap.c index 0698f849073..ed3033d8393 100644 --- a/source/blender/render/intern/source/envmap.c +++ b/source/blender/render/intern/source/envmap.c @@ -495,9 +495,11 @@ static void render_envmap(Render *re, EnvMap *env) RenderLayer *rl = envre->result->layers.first; int y; float *alpha; - + float *rect; + + rect = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, re->viewname); ibuf = IMB_allocImBuf(envre->rectx, envre->recty, 24, IB_rect | IB_rectfloat); - memcpy(ibuf->rect_float, rl->rectf, ibuf->channels * ibuf->x * ibuf->y * sizeof(float)); + memcpy(ibuf->rect_float, rect, ibuf->channels * ibuf->x * ibuf->y * sizeof(float)); /* envmap renders without alpha */ alpha = ibuf->rect_float + 3; diff --git a/source/blender/render/intern/source/external_engine.c b/source/blender/render/intern/source/external_engine.c index e56466bdf48..343c4a9387a 100644 --- a/source/blender/render/intern/source/external_engine.c +++ b/source/blender/render/intern/source/external_engine.c @@ -42,6 +42,7 @@ #include "BLI_string.h" #include "BLI_utildefines.h" +#include "BKE_camera.h" #include "BKE_global.h" #include "BKE_report.h" #include "BKE_scene.h" @@ -181,7 +182,7 @@ static RenderPart *get_part_from_result(Render *re, RenderResult *result) return NULL; } -RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h, const char *layername) +RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h, const char *layername, const char *viewname) { Render *re = engine->re; RenderResult *result; @@ -204,7 +205,7 @@ RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, disprect.ymin = y; disprect.ymax = y + h; - result = render_result_new(re, &disprect, 0, RR_USE_MEM, layername); + result = render_result_new(re, &disprect, 0, RR_USE_MEM, layername, viewname); /* todo: make this thread safe */ @@ -269,7 +270,7 @@ void RE_engine_end_result(RenderEngine *engine, RenderResult *result, int cancel if (!cancel || merge_results) { if (re->result->do_exr_tile) { if (!cancel) { - render_result_exr_file_merge(re->result, result); + render_result_exr_file_merge(re->result, result, re->viewname); } } else if (!(re->test_break(re->tbh) && (re->r.scemode & R_BUTS_PREVIEW))) @@ -368,6 +369,24 @@ void RE_engine_set_error_message(RenderEngine *engine, const char *msg) } } +void RE_engine_active_view_set(RenderEngine *engine, const char *viewname) +{ + Render *re = engine->re; + RE_SetActiveRenderView(re, viewname); +} + +float RE_engine_get_camera_shift_x(RenderEngine *engine, Object *camera) +{ + Render *re = engine->re; + return BKE_camera_multiview_shift_x(re ? &re->r : NULL, camera, re->viewname); +} + +void RE_engine_get_camera_model_matrix(RenderEngine *engine, Object *camera, float *r_modelmat) +{ + Render *re = engine->re; + BKE_camera_multiview_model_matrix(re ? &re->r : NULL, camera, re->viewname, (float (*)[4])r_modelmat); +} + rcti* RE_engine_get_current_tiles(Render *re, int *r_total_tiles, bool *r_needs_free) { static rcti tiles_static[BLENDER_MAX_THREADS]; @@ -612,7 +631,7 @@ int RE_engine_render(Render *re, int do_all) if ((type->flag & RE_USE_SAVE_BUFFERS) && (re->r.scemode & R_EXR_TILE_FILE)) savebuffers = RR_USE_EXR; - re->result = render_result_new(re, &re->disprect, 0, savebuffers, RR_ALL_LAYERS); + re->result = render_result_new(re, &re->disprect, 0, savebuffers, RR_ALL_LAYERS, RR_ALL_VIEWS); } BLI_rw_mutex_unlock(&re->resultmutex); diff --git a/source/blender/render/intern/source/initrender.c b/source/blender/render/intern/source/initrender.c index a0fcc7cdcd2..970a3937657 100644 --- a/source/blender/render/intern/source/initrender.c +++ b/source/blender/render/intern/source/initrender.c @@ -426,10 +426,10 @@ void make_sample_tables(Render *re) /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ - struct Object *RE_GetCamera(Render *re) { - return re->camera_override ? re->camera_override : re->scene->camera; + Object *camera = re->camera_override ? re->camera_override : re->scene->camera; + return BKE_camera_multiview_render(re->scene, camera, re->viewname); } static void re_camera_params_get(Render *re, CameraParams *params, Object *cam_ob) @@ -470,6 +470,16 @@ void RE_SetEnvmapCamera(Render *re, Object *cam_ob, float viewscale, float clips re_camera_params_get(re, ¶ms, cam_ob); } +void RE_SetOverrideCamera(Render *re, Object *camera) +{ + re->camera_override = camera; +} + +static void re_camera_params_stereo3d(Render *re, CameraParams *params, Object *cam_ob) +{ + BKE_camera_multiview_params(&re->r, params, cam_ob, re->viewname); +} + /* call this after InitState() */ /* per render, there's one persistent viewplane. Parts will set their own viewplanes */ void RE_SetCamera(Render *re, Object *cam_ob) @@ -479,6 +489,7 @@ void RE_SetCamera(Render *re, Object *cam_ob) /* setup parameters */ BKE_camera_params_init(¶ms); BKE_camera_params_from_object(¶ms, cam_ob); + re_camera_params_stereo3d(re, ¶ms, cam_ob); params.use_fields = (re->r.mode & R_FIELDS); params.field_second = (re->flag & R_SEC_FIELD); @@ -505,6 +516,11 @@ void RE_GetCameraWindow(struct Render *re, struct Object *camera, int frame, flo copy_m4_m4(mat, re->winmat); } +void RE_GetCameraModelMatrix(Render *re, struct Object *camera, float r_mat[4][4]) +{ + BKE_camera_multiview_model_matrix(&re->r, camera, re->viewname, r_mat); +} + /* ~~~~~~~~~~~~~~~~ part (tile) calculus ~~~~~~~~~~~~~~~~~~~~~~ */ diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index 4bde3dddaff..25adb4db4f7 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -70,6 +70,7 @@ #include "BKE_scene.h" #include "BKE_sequencer.h" #include "BKE_writeavi.h" /* <------ should be replaced once with generic movie module */ +#include "BKE_object.h" #include "PIL_time.h" #include "IMB_colormanagement.h" @@ -131,7 +132,7 @@ Render R; /* ********* alloc and free ******** */ -static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovieHandle *mh, const char *name_override); +static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovieHandle *mh, const size_t totvideos, const char *name_override); static volatile int g_break = 0; static int thread_break(void *UNUSED(arg)) @@ -190,14 +191,23 @@ void RE_FreeRenderResult(RenderResult *res) render_result_free(res); } -float *RE_RenderLayerGetPass(RenderLayer *rl, int passtype) +float *RE_RenderLayerGetPass(volatile RenderLayer *rl, int passtype, const char *viewname) { RenderPass *rpass; - - for (rpass = rl->passes.first; rpass; rpass = rpass->next) - if (rpass->passtype == passtype) - return rpass->rect; - return NULL; + float *rect = NULL; + + for (rpass = rl->passes.last; rpass; rpass = rpass->prev) { + if (rpass->passtype == passtype) { + rect = rpass->rect; + + if (viewname == NULL) + break; + else if (strcmp(rpass->view, viewname) == 0) + break; + } + } + + return rect; } RenderLayer *RE_GetRenderLayer(RenderResult *rr, const char *name) @@ -307,8 +317,69 @@ Scene *RE_GetScene(Render *re) return NULL; } +/** + * Same as #RE_AcquireResultImage but creating the necessary views to store the result + * fill provided result struct with a copy of thew views of what is done so far the + * #RenderResult.views #ListBase needs to be freed after with #RE_ReleaseResultImageViews +*/ +void RE_AcquireResultImageViews(Render *re, RenderResult *rr) +{ + memset(rr, 0, sizeof(RenderResult)); + + if (re) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_READ); + + if (re->result) { + RenderLayer *rl; + RenderView *rv, *rview; + + rr->rectx = re->result->rectx; + rr->recty = re->result->recty; + + /* creates a temporary duplication of views */ + render_result_views_shallowcopy(rr, re->result); + + rv = rr->views.first; + + rr->have_combined = (rv->rectf != NULL); + + /* active layer */ + rl = render_get_active_layer(re, re->result); + + if (rl) { + if (rv->rectf == NULL) { + for (rview = (RenderView *)rr->views.first; rview; rview = rview->next) { + rview->rectf = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, rview->name); + } + } + + if (rv->rectz == NULL) { + for (rview = (RenderView *)rr->views.first; rview; rview = rview->next) { + rview->rectz = RE_RenderLayerGetPass(rl, SCE_PASS_Z, rview->name); + } + } + } + + rr->layers = re->result->layers; + rr->xof = re->disprect.xmin; + rr->yof = re->disprect.ymin; + } + } +} + +/* clear temporary renderresult struct */ +void RE_ReleaseResultImageViews(Render *re, RenderResult *rr) +{ + if (re) { + if (rr) { + render_result_views_shallowdelete(rr); + } + BLI_rw_mutex_unlock(&re->resultmutex); + } +} + /* fill provided result struct with what's currently active or done */ -void RE_AcquireResultImage(Render *re, RenderResult *rr) +void RE_AcquireResultImage(Render *re, RenderResult *rr, const int view_id) { memset(rr, 0, sizeof(RenderResult)); @@ -317,27 +388,35 @@ void RE_AcquireResultImage(Render *re, RenderResult *rr) if (re->result) { RenderLayer *rl; + RenderView *rv; rr->rectx = re->result->rectx; rr->recty = re->result->recty; - rr->rectf = re->result->rectf; - rr->rectz = re->result->rectz; - rr->rect32 = re->result->rect32; - + /* actview view */ + rv = BLI_findlink(&re->result->views, view_id); + if (rv == NULL) + rv = (RenderView *)re->result->views.first; + + rr->rectf = rv ? rv->rectf : NULL; + rr->rectz = rv ? rv->rectz : NULL; + rr->rect32 = rv ? rv->rect32 : NULL; + /* active layer */ rl = render_get_active_layer(re, re->result); - if (rl) { - if (rr->rectf == NULL) - rr->rectf = rl->rectf; - if (rr->rectz == NULL) - rr->rectz = RE_RenderLayerGetPass(rl, SCE_PASS_Z); + if (rl && rv) { + if (rv->rectf == NULL) + rr->rectf = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, rv->name); + + if (rv->rectz == NULL) + rr->rectz = RE_RenderLayerGetPass(rl, SCE_PASS_Z, rv->name); } - rr->have_combined = (re->result->rectf != NULL); + rr->have_combined = rv ? (rv->rectf != NULL) : false; rr->layers = re->result->layers; - + rr->views = re->result->views; + rr->xof = re->disprect.xmin; rr->yof = re->disprect.ymin; } @@ -354,17 +433,18 @@ void RE_ReleaseResultImage(Render *re) void RE_ResultGet32(Render *re, unsigned int *rect) { RenderResult rres; - - RE_AcquireResultImage(re, &rres); - render_result_rect_get_pixels(&rres, rect, re->rectx, re->recty, &re->scene->view_settings, &re->scene->display_settings); + const size_t view_id = BKE_scene_multiview_view_id_get(&re->r, re->viewname); + + RE_AcquireResultImage(re, &rres, view_id); + render_result_rect_get_pixels(&rres, rect, re->rectx, re->recty, &re->scene->view_settings, &re->scene->display_settings, 0); RE_ReleaseResultImage(re); } /* caller is responsible for allocating rect in correct size! */ /* Only for acquired results, for lock */ -void RE_AcquiredResultGet32(Render *re, RenderResult *result, unsigned int *rect) +void RE_AcquiredResultGet32(Render *re, RenderResult *result, unsigned int *rect, const int view_id) { - render_result_rect_get_pixels(result, rect, re->rectx, re->recty, &re->scene->view_settings, &re->scene->display_settings); + render_result_rect_get_pixels(result, rect, re->rectx, re->recty, &re->scene->view_settings, &re->scene->display_settings, view_id); } RenderStats *RE_GetStats(Render *re) @@ -683,6 +763,8 @@ void RE_InitState(Render *re, Render *source, RenderData *rd, RE_init_threadcount(re); } +/* This function is only called by view3d rendering, which doesn't support + * multiview at the moment. so handle only one view here */ static void render_result_rescale(Render *re) { RenderResult *result = re->result; @@ -694,7 +776,7 @@ static void render_result_rescale(Render *re) if (src_rectf == NULL) { RenderLayer *rl = render_get_active_layer(re, re->result); if (rl != NULL) { - src_rectf = rl->rectf; + src_rectf = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, ""); } } @@ -704,7 +786,8 @@ static void render_result_rescale(Render *re) &re->disprect, 0, RR_USE_MEM, - RR_ALL_LAYERS); + RR_ALL_LAYERS, + ""); if (re->result != NULL) { dst_rectf = re->result->rectf; @@ -712,7 +795,7 @@ static void render_result_rescale(Render *re) RenderLayer *rl; rl = render_get_active_layer(re, re->result); if (rl != NULL) { - dst_rectf = rl->rectf; + dst_rectf = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, ""); } } @@ -910,9 +993,9 @@ static void *do_part_thread(void *pa_v) if (R.test_break(R.tbh) == 0) { if (!R.sss_points && (R.r.scemode & R_FULL_SAMPLE)) - pa->result = render_result_new_full_sample(&R, &pa->fullresult, &pa->disprect, pa->crop, RR_USE_MEM); + pa->result = render_result_new_full_sample(&R, &pa->fullresult, &pa->disprect, pa->crop, RR_USE_MEM, R.viewname); else - pa->result = render_result_new(&R, &pa->disprect, pa->crop, RR_USE_MEM, RR_ALL_LAYERS); + pa->result = render_result_new(&R, &pa->disprect, pa->crop, RR_USE_MEM, RR_ALL_LAYERS, R.viewname); /* Copy EXR tile settings, so pipeline knows whether this is a result * for Save Buffers enabled rendering. @@ -935,7 +1018,7 @@ static void *do_part_thread(void *pa_v) /* merge too on break! */ if (R.result->do_exr_tile) { - render_result_exr_file_merge(R.result, pa->result); + render_result_exr_file_merge(R.result, pa->result, R.viewname); } else if (render_display_update_enabled(&R)) { /* on break, don't merge in result for preview renders, looks nicer */ @@ -1115,16 +1198,23 @@ static void *do_render_thread(void *thread_v) return NULL; } -static void threaded_tile_processor(Render *re) +static void main_render_result_end(Render *re) +{ + if (re->result->do_exr_tile) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + render_result_exr_file_end(re); + BLI_rw_mutex_unlock(&re->resultmutex); + } + + if (re->r.scemode & R_EXR_CACHE_FILE) { + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + render_result_exr_file_cache_write(re); + BLI_rw_mutex_unlock(&re->resultmutex); + } +} + +static void main_render_result_new(Render *re) { - RenderThread thread[BLENDER_MAX_THREADS]; - ThreadQueue *workqueue, *donequeue; - ListBase threads; - RenderPart *pa; - rctf viewplane = re->viewplane; - double lastdraw, elapsed, redrawtime = 1.0f; - int totpart = 0, minx = 0, slice = 0, a, wait; - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); /* first step; free the entire render result, make new, and/or prepare exr buffer saving */ @@ -1132,25 +1222,35 @@ static void threaded_tile_processor(Render *re) render_result_free(re->result); if (re->sss_points && render_display_update_enabled(re)) - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS); + re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); else if (re->r.scemode & R_FULL_SAMPLE) - re->result = render_result_new_full_sample(re, &re->fullresult, &re->disprect, 0, RR_USE_EXR); + re->result = render_result_new_full_sample(re, &re->fullresult, &re->disprect, 0, RR_USE_EXR, RR_ALL_VIEWS); else re->result = render_result_new(re, &re->disprect, 0, - (re->r.scemode & R_EXR_TILE_FILE) ? RR_USE_EXR : RR_USE_MEM, RR_ALL_LAYERS); + (re->r.scemode & R_EXR_TILE_FILE) ? RR_USE_EXR : RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); } BLI_rw_mutex_unlock(&re->resultmutex); + + if (re->result->do_exr_tile) + render_result_exr_file_begin(re); +} + +static void threaded_tile_processor(Render *re) +{ + RenderThread thread[BLENDER_MAX_THREADS]; + ThreadQueue *workqueue, *donequeue; + ListBase threads; + RenderPart *pa; + rctf viewplane = re->viewplane; + double lastdraw, elapsed, redrawtime = 1.0f; + int totpart = 0, minx = 0, slice = 0, a, wait; if (re->result == NULL) return; /* warning; no return here without closing exr file */ - RE_parts_init(re, true); - - if (re->result->do_exr_tile) - render_result_exr_file_begin(re); /* assuming no new data gets added to dbase... */ R = *re; @@ -1256,18 +1356,6 @@ static void threaded_tile_processor(Render *re) BLI_thread_queue_free(donequeue); BLI_thread_queue_free(workqueue); - if (re->result->do_exr_tile) { - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - render_result_exr_file_end(re); - BLI_rw_mutex_unlock(&re->resultmutex); - } - - if (re->r.scemode & R_EXR_CACHE_FILE) { - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - render_result_exr_file_cache_write(re); - BLI_rw_mutex_unlock(&re->resultmutex); - } - /* unset threadsafety */ g_break = 0; BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE); @@ -1284,6 +1372,7 @@ static void free_all_freestyle_renders(void); /* currently only called by preview renders and envmap */ void RE_TileProcessor(Render *re) { + main_render_result_new(re); threaded_tile_processor(re); re->i.lastframetime = PIL_check_seconds_timer() - re->i.starttime; @@ -1309,6 +1398,7 @@ void RE_TileProcessor(Render *re) static void do_render_3d(Render *re) { + RenderView *rv; int cfra_backup; re->current_scene_update(re->suh, re->scene); @@ -1325,40 +1415,49 @@ static void do_render_3d(Render *re) BKE_scene_frame_set(re->scene, (double)re->scene->r.cfra + (double)re->mblur_offs + (double)re->field_offs); - /* lock drawing in UI during data phase */ - if (re->draw_lock) - re->draw_lock(re->dlh, 1); - - /* make render verts/faces/halos/lamps */ - if (render_scene_needs_vector(re)) { - RE_Database_FromScene_Vectors(re, re->main, re->scene, re->lay); - } - else { - RE_Database_FromScene(re, re->main, re->scene, re->lay, 1); - RE_Database_Preprocess(re); - } + /* init main render result */ + main_render_result_new(re); + + /* we need a new database for each view */ + for (rv = re->result->views.first; rv; rv = rv->next) { + RE_SetActiveRenderView(re, rv->name); + + /* lock drawing in UI during data phase */ + if (re->draw_lock) + re->draw_lock(re->dlh, 1); + + /* make render verts/faces/halos/lamps */ + if (render_scene_needs_vector(re)) + RE_Database_FromScene_Vectors(re, re->main, re->scene, re->lay); + else { + RE_Database_FromScene(re, re->main, re->scene, re->lay, 1); + RE_Database_Preprocess(re); + } - /* clear UI drawing locks */ - if (re->draw_lock) - re->draw_lock(re->dlh, 0); + /* clear UI drawing locks */ + if (re->draw_lock) + re->draw_lock(re->dlh, 0); - threaded_tile_processor(re); + threaded_tile_processor(re); #ifdef WITH_FREESTYLE - /* Freestyle */ - if (re->r.mode & R_EDGE_FRS) - if (!re->test_break(re->tbh)) - add_freestyle(re, 1); + /* Freestyle */ + if (re->r.mode & R_EDGE_FRS) + if (!re->test_break(re->tbh)) + add_freestyle(re, 1); #endif - /* do left-over 3d post effects (flares) */ - if (re->flag & R_HALO) - if (!re->test_break(re->tbh)) - add_halo_flare(re); - - /* free all render verts etc */ - RE_Database_Free(re); - + /* do left-over 3d post effects (flares) */ + if (re->flag & R_HALO) + if (!re->test_break(re->tbh)) + add_halo_flare(re); + + /* free all render verts etc */ + RE_Database_Free(re); + } + + main_render_result_end(re); + re->scene->r.cfra = cfra_backup; re->scene->r.subframe = 0.f; } @@ -1431,19 +1530,13 @@ static void merge_renderresult_blur(RenderResult *rr, RenderResult *brr, float b rl1 = brr->layers.first; for (rl = rr->layers.first; rl && rl1; rl = rl->next, rl1 = rl1->next) { - - /* combined */ - if (rl->rectf && rl1->rectf) { - if (key_alpha) - addblur_rect_key(rr, rl->rectf, rl1->rectf, blurfac); - else - addblur_rect(rr, rl->rectf, rl1->rectf, blurfac, 4); - } - /* passes are allocated in sync */ rpass1 = rl1->passes.first; for (rpass = rl->passes.first; rpass && rpass1; rpass = rpass->next, rpass1 = rpass1->next) { - addblur_rect(rr, rpass->rect, rpass1->rect, blurfac, rpass->channels); + if ((rpass->passtype & SCE_PASS_COMBINED) && key_alpha) + addblur_rect_key(rr, rpass->rect, rpass1->rect, blurfac); + else + addblur_rect(rr, rpass->rect, rpass1->rect, blurfac, rpass->channels); } } } @@ -1456,7 +1549,7 @@ static void do_render_blur_3d(Render *re) int blur = re->r.mblur_samples; /* create accumulation render result */ - rres = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS); + rres = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); /* do the blur steps */ while (blur--) { @@ -1518,10 +1611,6 @@ static void merge_renderresult_fields(RenderResult *rr, RenderResult *rr1, Rende rl2 = rr2->layers.first; for (rl = rr->layers.first; rl && rl1 && rl2; rl = rl->next, rl1 = rl1->next, rl2 = rl2->next) { - /* combined */ - if (rl->rectf && rl1->rectf && rl2->rectf) - interleave_rect(rr, rl->rectf, rl1->rectf, rl2->rectf, 4); - /* passes are allocated in sync */ rpass1 = rl1->passes.first; rpass2 = rl2->passes.first; @@ -1589,7 +1678,7 @@ static void do_render_fields_3d(Render *re) re->disprect.ymax *= 2; BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS); + re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); if (rr2) { if (re->r.mode & R_ODDFIELD) @@ -1661,7 +1750,7 @@ static void do_render_fields_blur_3d(Render *re) /* weak is: it chances disprect from border */ render_result_disprect_to_full_resolution(re); - rres = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS); + rres = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); render_result_merge(rres, re->result); render_result_free(re->result); @@ -2068,11 +2157,14 @@ static void free_all_freestyle_renders(void) } #endif -/* reads all buffers, calls optional composite, merges in first result->rectf */ +/* reads all buffers, calls optional composite, merges in first result->views rectf */ static void do_merge_fullsample(Render *re, bNodeTree *ntree) { + ListBase *rectfs; + RenderView *rv; float *rectf, filt[3][3]; int x, y, sample; + int nr, numviews; /* interaction callbacks */ if (ntree) { @@ -2087,9 +2179,18 @@ static void do_merge_fullsample(Render *re, bNodeTree *ntree) /* filtmask needs it */ R = *re; - /* we accumulate in here */ - rectf = MEM_mapallocN(re->rectx * re->recty * sizeof(float) * 4, "fullsample rgba"); - + /* temporary storage of the acccumulation buffers */ + rectfs = MEM_callocN(sizeof(ListBase), "fullsample accumulation buffers"); + + numviews = BLI_listbase_count(&re->result->views); + for (nr = 0; nr < numviews; nr++) { + rv = MEM_callocN(sizeof(RenderView), "fullsample renderview"); + + /* we accumulate in here */ + rv->rectf = MEM_mapallocN(re->rectx * re->recty * sizeof(float) * 4, "fullsample rgba"); + BLI_addtail(rectfs, rv); + } + for (sample = 0; sample < re->r.osa; sample++) { Scene *sce; Render *re1; @@ -2126,54 +2227,70 @@ static void do_merge_fullsample(Render *re, bNodeTree *ntree) if (ntree) { ntreeCompositTagRender(re->scene); ntreeCompositTagAnimated(ntree); - - ntreeCompositExecTree(re->scene, ntree, &re->r, true, G.background == 0, &re->scene->view_settings, &re->scene->display_settings); + + for (rv = re->result->views.first; rv; rv = rv->next) { + ntreeCompositExecTree(re->scene, ntree, &re->r, true, G.background == 0, &re->scene->view_settings, &re->scene->display_settings, rv->name); + } } - - /* ensure we get either composited result or the active layer */ - RE_AcquireResultImage(re, &rres); - - /* accumulate with filter, and clip */ - mask = (1 << sample); - mask_array(mask, filt); - for (y = 0; y < re->recty; y++) { - float *rf = rectf + 4 * y * re->rectx; - float *col = rres.rectf + 4 * y * re->rectx; - - for (x = 0; x < re->rectx; x++, rf += 4, col += 4) { - /* clamping to 1.0 is needed for correct AA */ - if (col[0] < 0.0f) col[0] = 0.0f; else if (col[0] > 1.0f) col[0] = 1.0f; - if (col[1] < 0.0f) col[1] = 0.0f; else if (col[1] > 1.0f) col[1] = 1.0f; - if (col[2] < 0.0f) col[2] = 0.0f; else if (col[2] > 1.0f) col[2] = 1.0f; + for (nr = 0, rv = re->result->views.first; rv; rv = rv->next, nr++) { + rectf = rv->rectf; + + /* ensure we get either composited result or the active layer */ + RE_AcquireResultImage(re, &rres, nr); + + /* accumulate with filter, and clip */ + mask = (1 << sample); + mask_array(mask, filt); + + for (y = 0; y < re->recty; y++) { + float *rf = rectf + 4 * y * re->rectx; + float *col = rres.rectf + 4 * y * re->rectx; - add_filt_fmask_coord(filt, col, rf, re->rectx, re->recty, x, y); + for (x = 0; x < re->rectx; x++, rf += 4, col += 4) { + /* clamping to 1.0 is needed for correct AA */ + if (col[0] < 0.0f) col[0] = 0.0f; else if (col[0] > 1.0f) col[0] = 1.0f; + if (col[1] < 0.0f) col[1] = 0.0f; else if (col[1] > 1.0f) col[1] = 1.0f; + if (col[2] < 0.0f) col[2] = 0.0f; else if (col[2] > 1.0f) col[2] = 1.0f; + + add_filt_fmask_coord(filt, col, rf, re->rectx, re->recty, x, y); + } } - } - RE_ReleaseResultImage(re); + RE_ReleaseResultImage(re); - /* show stuff */ - if (sample != re->osa - 1) { - /* weak... the display callback wants an active renderlayer pointer... */ - re->result->renlay = render_get_active_layer(re, re->result); - re->display_update(re->duh, re->result, NULL); + /* show stuff */ + if (sample != re->osa - 1) { + /* weak... the display callback wants an active renderlayer pointer... */ + re->result->renlay = render_get_active_layer(re, re->result); + RE_SetActiveRenderView(re, rv->name); + re->display_update(re->duh, re->result, NULL); + } } - - if (re->test_break(re->tbh)) - break; } - /* clamp alpha and RGB to 0..1 and 0..inf, can go outside due to filter */ - for (y = 0; y < re->recty; y++) { - float *rf = rectf + 4 * y * re->rectx; + for (nr = 0; nr < numviews; nr++) { + rectf = ((RenderView *)BLI_findlink(rectfs, nr))->rectf; + + /* clamp alpha and RGB to 0..1 and 0..inf, can go outside due to filter */ + for (y = 0; y < re->recty; y++) { + float *rf = rectf + 4 * y * re->rectx; - for (x = 0; x < re->rectx; x++, rf += 4) { - rf[0] = MAX2(rf[0], 0.0f); - rf[1] = MAX2(rf[1], 0.0f); - rf[2] = MAX2(rf[2], 0.0f); - CLAMP(rf[3], 0.0f, 1.0f); + for (x = 0; x < re->rectx; x++, rf += 4) { + rf[0] = MAX2(rf[0], 0.0f); + rf[1] = MAX2(rf[1], 0.0f); + rf[2] = MAX2(rf[2], 0.0f); + CLAMP(rf[3], 0.0f, 1.0f); + } } + + /* store the final result */ + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + rv = BLI_findlink(&re->result->views, nr); + if (rv->rectf) + MEM_freeN(rv->rectf); + rv->rectf = rectf; + BLI_rw_mutex_unlock(&re->resultmutex); } /* clear interaction callbacks */ @@ -2186,12 +2303,14 @@ static void do_merge_fullsample(Render *re, bNodeTree *ntree) /* disable full sample print */ R.i.curfsa = 0; - - BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); - if (re->result->rectf) - MEM_freeN(re->result->rectf); - re->result->rectf = rectf; - BLI_rw_mutex_unlock(&re->resultmutex); + + /* garbage collection */ + while (rectfs->first) { + RenderView *rv = rectfs->first; + BLI_remlink(rectfs, rv); + MEM_freeN(rv); + } + MEM_freeN(rectfs); } /* called externally, via compositor */ @@ -2267,7 +2386,11 @@ static void do_render_composite_fields_blur_3d(Render *re) if (composite_needs_render(re->scene, 1)) { /* save memory... free all cached images */ ntreeFreeCache(ntree); - + + /* render the frames + * it could be optimized to render only the needed view + * but what if a scene has a different number of views + * than the main scene? */ do_render_fields_blur_3d(re); } else { @@ -2280,7 +2403,7 @@ static void do_render_composite_fields_blur_3d(Render *re) if ((re->r.mode & R_CROP) == 0) { render_result_disprect_to_full_resolution(re); } - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS); + re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); BLI_rw_mutex_unlock(&re->resultmutex); @@ -2325,7 +2448,10 @@ static void do_render_composite_fields_blur_3d(Render *re) if (re->r.scemode & R_FULL_SAMPLE) do_merge_fullsample(re, ntree); else { - ntreeCompositExecTree(re->scene, ntree, &re->r, true, G.background == 0, &re->scene->view_settings, &re->scene->display_settings); + RenderView *rv; + for (rv = re->result->views.first; rv; rv = rv->next) { + ntreeCompositExecTree(re->scene, ntree, &re->r, true, G.background == 0, &re->scene->view_settings, &re->scene->display_settings, rv->name); + } } ntree->stats_draw = NULL; @@ -2350,13 +2476,17 @@ static void do_render_composite_fields_blur_3d(Render *re) static void renderresult_stampinfo(Render *re) { RenderResult rres; + RenderView *rv; + int nr; /* this is the basic trick to get the displayed float or char rect from render result */ - RE_AcquireResultImage(re, &rres); - BKE_image_stamp_buf( - re->scene, RE_GetCamera(re), - (unsigned char *)rres.rect32, rres.rectf, rres.rectx, rres.recty, 4); - RE_ReleaseResultImage(re); + nr = 0; + for (rv = re->result->views.first;rv;rv = rv->next, nr++) { + RE_SetActiveRenderView(re, rv->name); + RE_AcquireResultImage(re, &rres, nr); + BKE_image_stamp_buf(re->scene, RE_GetCamera(re), (unsigned char *)rv->rect32, rv->rectf, rres.rectx, rres.recty, 4); + RE_ReleaseResultImage(re); + } } int RE_seq_render_active(Scene *scene, RenderData *rd) @@ -2380,10 +2510,12 @@ int RE_seq_render_active(Scene *scene, RenderData *rd) static void do_render_seq(Render *re) { static int recurs_depth = 0; - struct ImBuf *ibuf, *out; + struct ImBuf *out; RenderResult *rr; /* don't assign re->result here as it might change during give_ibuf_seq */ int cfra = re->r.cfra; SeqRenderData context; + size_t view_id, tot_views; + struct ImBuf **ibuf_arr; int re_x, re_y; re->i.cfra = cfra; @@ -2406,45 +2538,67 @@ static void do_render_seq(Render *re) re_y = re->result->recty; } + tot_views = BKE_scene_multiview_num_views_get(&re->r); + ibuf_arr = MEM_mallocN(sizeof(ImBuf *) * tot_views, "Sequencer Views ImBufs"); + BKE_sequencer_new_render_data( re->eval_ctx, re->main, re->scene, re_x, re_y, 100, &context); - out = BKE_sequencer_give_ibuf(&context, cfra, 0); + /* the renderresult gets destroyed during the rendering, so we first collect all ibufs + * and then we populate the final renderesult */ - if (out) { - ibuf = IMB_dupImBuf(out); - IMB_freeImBuf(out); - BKE_sequencer_imbuf_from_sequencer_space(re->scene, ibuf); - } - else { - ibuf = NULL; - } + for (view_id = 0; view_id < tot_views; view_id++) { + context.view_id = view_id; + out = BKE_sequencer_give_ibuf(&context, cfra, 0); - recurs_depth--; + if (out) { + ibuf_arr[view_id] = IMB_dupImBuf(out); + IMB_freeImBuf(out); + BKE_sequencer_imbuf_from_sequencer_space(re->scene, ibuf_arr[view_id]); + } + else { + ibuf_arr[view_id] = NULL; + } + } rr = re->result; - + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + render_result_views_new(rr, &re->r); + BLI_rw_mutex_unlock(&re->resultmutex); - if (ibuf) { - /* copy ibuf into combined pixel rect */ - render_result_rect_from_ibuf(rr, &re->r, ibuf); - - if (recurs_depth == 0) { /* with nested scenes, only free on toplevel... */ - Editing *ed = re->scene->ed; - if (ed) - BKE_sequencer_free_imbuf(re->scene, &ed->seqbase, true); + for (view_id = 0; view_id < tot_views; view_id++) { + RenderView *rv = BLI_findlink(&rr->views, view_id); + BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE); + + if (ibuf_arr[view_id]) { + /* copy ibuf into combined pixel rect */ + render_result_rect_from_ibuf(rr, &re->r, ibuf_arr[view_id], view_id); + + if (recurs_depth == 0) { /* with nested scenes, only free on toplevel... */ + Editing *ed = re->scene->ed; + if (ed) + BKE_sequencer_free_imbuf(re->scene, &ed->seqbase, true); + } + IMB_freeImBuf(ibuf_arr[view_id]); } - IMB_freeImBuf(ibuf); - } - else { - /* render result is delivered empty in most cases, nevertheless we handle all cases */ - render_result_rect_fill_zero(rr); + else { + /* render result is delivered empty in most cases, nevertheless we handle all cases */ + render_result_rect_fill_zero(rr, view_id); + } + + BLI_rw_mutex_unlock(&re->resultmutex); + + /* would mark display buffers as invalid */ + RE_SetActiveRenderView(re, rv->name); + re->display_update(re->duh, re->result, NULL); } - BLI_rw_mutex_unlock(&re->resultmutex); + MEM_freeN(ibuf_arr); + + recurs_depth--; /* just in case this flag went missing at some point */ re->r.scemode |= R_DOSEQ; @@ -2454,9 +2608,6 @@ static void do_render_seq(Render *re) re->progress(re->prh, (float)(cfra - re->r.sfra) / (re->r.efra - re->r.sfra)); else re->progress(re->prh, 1.0f); - - /* would mark display buffers as invalid */ - re->display_update(re->duh, re->result, NULL); } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -2543,11 +2694,50 @@ static bool check_valid_compositing_camera(Scene *scene, Object *camera_override } } +static bool check_valid_camera_multiview(Scene *scene, Object *camera, ReportList *reports) +{ + SceneRenderView *srv; + bool active_view = false; + + if ((scene->r.scemode & R_MULTIVIEW) == 0) + return true; + + for (srv = scene->r.views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(&scene->r, srv)) { + active_view = true; + + if (scene->r.views_format == SCE_VIEWS_FORMAT_MULTIVIEW) { + Object *view_camera; + view_camera = BKE_camera_multiview_render(scene, camera, srv->name); + + if (view_camera == camera) { + /* if the suffix is not in the camera, means we are using the fallback camera */ + if (!BLI_str_endswith(view_camera->id.name + 2, srv->suffix)) { + BKE_reportf(reports, RPT_ERROR, "Camera \"%s\" is not a multi-view camera", + camera->id.name + 2); + return false; + } + } + } + } + } + + if (!active_view) { + BKE_reportf(reports, RPT_ERROR, "No active view found in scene \"%s\"", scene->id.name + 2); + return false; + } + + return true; +} + static int check_valid_camera(Scene *scene, Object *camera_override, ReportList *reports) { if (camera_override == NULL && scene->camera == NULL) scene->camera = BKE_scene_camera_find(scene); + if (!check_valid_camera_multiview(scene, scene->camera, reports)) + return false; + if (RE_seq_render_active(scene, &scene->r)) { if (scene->ed) { Sequence *seq = scene->ed->seqbase.first; @@ -2565,6 +2755,8 @@ static int check_valid_camera(Scene *scene, Object *camera_override, ReportList } } } + else if (!check_valid_camera_multiview(seq->scene, seq->scene_camera, reports)) + return false; } seq = seq->next; @@ -2729,6 +2921,17 @@ static void update_physics_cache(Render *re, Scene *scene, int UNUSED(anim_init) BKE_ptcache_bake(&baker); } + +void RE_SetActiveRenderView(Render *re, const char *viewname) +{ + BLI_strncpy(re->viewname, viewname, sizeof(re->viewname)); +} + +const char *RE_GetActiveRenderView(Render *re) +{ + return re->viewname; +} + /* evaluating scene options for general Blender render */ static int render_initialize_from_main(Render *re, RenderData *rd, Main *bmain, Scene *scene, SceneRenderLayer *srl, Object *camera_override, unsigned int lay_override, int anim, int anim_init) @@ -2764,7 +2967,8 @@ static int render_initialize_from_main(Render *re, RenderData *rd, Main *bmain, re->lay = lay_override ? lay_override : scene->lay; re->layer_override = lay_override; re->i.localview = (re->lay & 0xFF000000) != 0; - + re->viewname[0] = '\0'; + /* not too nice, but it survives anim-border render */ if (anim) { render_update_anim_renderdata(re, &scene->r); @@ -2838,10 +3042,10 @@ void RE_BlenderFrame(Render *re, Main *bmain, Scene *scene, SceneRenderLayer *sr char name[FILE_MAX]; BKE_image_path_from_imformat( name, scene->r.pic, bmain->name, scene->r.cfra, - &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, false); + &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, false, NULL); /* reports only used for Movie */ - do_write_image_or_movie(re, bmain, scene, NULL, name); + do_write_image_or_movie(re, bmain, scene, NULL, 0, name); } } @@ -2879,96 +3083,269 @@ void RE_RenderFreestyleExternal(Render *re) } #endif -static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovieHandle *mh, const char *name_override) +bool RE_WriteRenderViewsImage(ReportList *reports, RenderResult *rr, Scene *scene, struct Object *camera, const bool stamp, char *name) { - char name[FILE_MAX]; - RenderResult rres; - Object *camera = RE_GetCamera(re); - double render_time; - int ok = 1; - - RE_AcquireResultImage(re, &rres); + bool is_mono; + bool ok = true; + RenderData *rd = &scene->r; - /* write movie or image */ - if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { - bool do_free = false; - ImBuf *ibuf = render_result_rect_to_ibuf(&rres, &scene->r); + if (!rr) + return false; - /* note; the way it gets 32 bits rects is weak... */ - if (ibuf->rect == NULL) { - ibuf->rect = MEM_mapallocN(sizeof(int) * rres.rectx * rres.recty, "temp 32 bits rect"); - ibuf->mall |= IB_rect; - RE_AcquiredResultGet32(re, &rres, ibuf->rect); - do_free = true; - } + is_mono = BLI_listbase_count_ex(&rr->views, 2) < 2; + if (ELEM(rd->im_format.imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER) && + rd->im_format.views_format == R_IMF_VIEWS_MULTIVIEW) + { + RE_WriteRenderResult(reports, rr, name, &rd->im_format, true, NULL); + printf("Saved: %s\n", name); + } - IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings, - &scene->display_settings, &scene->r.im_format); + /* mono, legacy code */ + else if (is_mono || (rd->im_format.views_format == R_IMF_VIEWS_INDIVIDUAL)) + { + RenderView *rv; + size_t view_id; + char filepath[FILE_MAX]; - ok = mh->append_movie(&re->r, scene->r.sfra, scene->r.cfra, (int *) ibuf->rect, - ibuf->x, ibuf->y, re->reports); - if (do_free) { - MEM_freeN(ibuf->rect); - ibuf->rect = NULL; - ibuf->mall &= ~IB_rect; - } + BLI_strncpy(filepath, name, sizeof(filepath)); - /* imbuf knows which rects are not part of ibuf */ - IMB_freeImBuf(ibuf); + for (view_id = 0, rv = rr->views.first; rv; rv = rv->next, view_id++) { + BKE_scene_multiview_view_filepath_get(&scene->r, filepath, rv->name, name); - printf("Append frame %d", scene->r.cfra); - } - else { - if (name_override) - BLI_strncpy(name, name_override, sizeof(name)); - else - BKE_image_path_from_imformat( - name, scene->r.pic, bmain->name, scene->r.cfra, - &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true); - - if (re->r.im_format.imtype == R_IMF_IMTYPE_MULTILAYER) { - if (re->result) { - RE_WriteRenderResult(re->reports, re->result, name, scene->r.im_format.exr_codec); - printf("Saved: %s", name); + if (rd->im_format.imtype == R_IMF_IMTYPE_MULTILAYER) { + + RE_WriteRenderResult(reports, rr, name, &rd->im_format, false, rv->name); + printf("Saved: %s\n", name); } + else { + ImBuf *ibuf = render_result_rect_to_ibuf(rr, rd, view_id); + + IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings, + &scene->display_settings, &rd->im_format); + + if (stamp) { + /* writes the name of the individual cameras */ + Object *view_camera = BKE_camera_multiview_render(scene, camera, rv->name); + ok = BKE_imbuf_write_stamp(scene, view_camera, ibuf, name, &rd->im_format); + } + else { + ok = BKE_imbuf_write(ibuf, name, &rd->im_format); + } + + if (ok == false) { + printf("Render error: cannot save %s\n", name); + } + else printf("Saved: %s\n", name); + + /* optional preview images for exr */ + if (ok && rd->im_format.imtype == R_IMF_IMTYPE_OPENEXR && (rd->im_format.flag & R_IMF_FLAG_PREVIEW_JPG)) { + ImageFormatData imf = rd->im_format; + imf.imtype = R_IMF_IMTYPE_JPEG90; + + if (BLI_testextensie(name, ".exr")) + name[strlen(name) - 4] = 0; + BKE_image_path_ensure_ext_from_imformat(name, &imf); + ibuf->planes = 24; + + IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings, + &scene->display_settings, &rd->im_format); + + if (stamp) { + /* writes the name of the individual cameras */ + Object *view_camera = BKE_camera_multiview_render(scene, camera, rv->name); + ok = BKE_imbuf_write_stamp(scene, view_camera, ibuf, name, &rd->im_format); + } + else { + ok = BKE_imbuf_write(ibuf, name, &rd->im_format); + } + printf("Saved: %s\n", name); + } + + /* imbuf knows which rects are not part of ibuf */ + IMB_freeImBuf(ibuf); + } + } + } + else { /* R_IMF_VIEWS_STEREO_3D */ + BLI_assert(scene->r.im_format.views_format == R_IMF_VIEWS_STEREO_3D); + + if (rd->im_format.imtype == R_IMF_IMTYPE_MULTILAYER) { + printf("Stereo 3D not support for MultiLayer image: %s\n", name); } else { - ImBuf *ibuf = render_result_rect_to_ibuf(&rres, &scene->r); + ImBuf *ibuf_arr[3] = {NULL}; + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + int i; + + for (i = 0; i < 2; i++) { + int view_id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name)); + ibuf_arr[i] = render_result_rect_to_ibuf(rr, rd, view_id); + IMB_colormanagement_imbuf_for_write(ibuf_arr[i], true, false, &scene->view_settings, + &scene->display_settings, &scene->r.im_format); + IMB_prepare_write_ImBuf(IMB_isfloat(ibuf_arr[i]), ibuf_arr[i]); + } - IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings, - &scene->display_settings, &scene->r.im_format); + ibuf_arr[2] = IMB_stereo3d_ImBuf(&scene->r.im_format, ibuf_arr[0], ibuf_arr[1]); - ok = BKE_imbuf_write_stamp(scene, camera, ibuf, name, &scene->r.im_format); - - if (ok == 0) { + if (stamp) + ok = BKE_imbuf_write_stamp(scene, camera, ibuf_arr[2], name, &rd->im_format); + else + ok = BKE_imbuf_write(ibuf_arr[2], name, &rd->im_format); + + if (ok == false) printf("Render error: cannot save %s\n", name); - } - else printf("Saved: %s", name); - + else + printf("Saved: %s\n", name); + /* optional preview images for exr */ - if (ok && scene->r.im_format.imtype == R_IMF_IMTYPE_OPENEXR && (scene->r.im_format.flag & R_IMF_FLAG_PREVIEW_JPG)) { - ImageFormatData imf = scene->r.im_format; + if (ok && rd->im_format.imtype == R_IMF_IMTYPE_OPENEXR && + (rd->im_format.flag & R_IMF_FLAG_PREVIEW_JPG)) + { + ImageFormatData imf = rd->im_format; imf.imtype = R_IMF_IMTYPE_JPEG90; if (BLI_testextensie(name, ".exr")) name[strlen(name) - 4] = 0; + BKE_image_path_ensure_ext_from_imformat(name, &imf); - ibuf->planes = 24; + ibuf_arr[2]->planes = 24; - IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings, + IMB_colormanagement_imbuf_for_write(ibuf_arr[2], true, false, &scene->view_settings, &scene->display_settings, &imf); - BKE_imbuf_write_stamp(scene, camera, ibuf, name, &imf); - printf("\nSaved: %s", name); + if (stamp) + ok = BKE_imbuf_write_stamp(scene, camera, ibuf_arr[2], name, &rd->im_format); + else + ok = BKE_imbuf_write(ibuf_arr[2], name, &imf); + + printf("Saved: %s\n", name); } - + + /* imbuf knows which rects are not part of ibuf */ + for (i = 0; i < 3; i++) { + IMB_freeImBuf(ibuf_arr[i]); + } + } + } + + return ok; +} + +bool RE_WriteRenderViewsMovie(ReportList *reports, RenderResult *rr, Scene *scene, RenderData *rd, bMovieHandle *mh, + const size_t width, const size_t height, void **movie_ctx_arr, const size_t totvideos) +{ + bool is_mono; + bool ok = true; + + if (!rr) + return false; + + is_mono = BLI_listbase_count_ex(&rr->views, 2) < 2; + + if (is_mono || (scene->r.im_format.views_format == R_IMF_VIEWS_INDIVIDUAL)) { + size_t view_id; + for (view_id = 0; view_id < totvideos; view_id++) { + bool do_free = false; + const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, view_id); + ImBuf *ibuf = render_result_rect_to_ibuf(rr, &scene->r, view_id); + + /* note; the way it gets 32 bits rects is weak... */ + if (ibuf->rect == NULL) { + ibuf->rect = MEM_mapallocN(sizeof(int) * rr->rectx * rr->recty, "temp 32 bits rect"); + ibuf->mall |= IB_rect; + render_result_rect_get_pixels(rr, ibuf->rect, width, height, &scene->view_settings, &scene->display_settings, view_id); + do_free = true; + } + + IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings, + &scene->display_settings, &scene->r.im_format); + + ok &= mh->append_movie(movie_ctx_arr[view_id], rd, scene->r.sfra, scene->r.cfra, + (int *) ibuf->rect, ibuf->x, ibuf->y, suffix, reports); + + if (do_free) { + MEM_freeN(ibuf->rect); + ibuf->rect = NULL; + ibuf->mall &= ~IB_rect; + } + /* imbuf knows which rects are not part of ibuf */ IMB_freeImBuf(ibuf); } + printf("Append frame %d\n", scene->r.cfra); + } + else { /* R_IMF_VIEWS_STEREO_3D */ + const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME}; + ImBuf *ibuf_arr[3] = {NULL}; + bool do_free[2] = {false, false}; + size_t i; + + BLI_assert((totvideos == 1) && (scene->r.im_format.views_format == R_IMF_VIEWS_STEREO_3D)); + + for (i = 0; i < 2; i++) { + int view_id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name)); + ibuf_arr[i] = render_result_rect_to_ibuf(rr, &scene->r, view_id); + + /* note; the way it gets 32 bits rects is weak... */ + if (ibuf_arr[i]->rect == NULL) { + ibuf_arr[i]->rect = MEM_mapallocN(sizeof(int) * width * height, "temp 32 bits rect"); + ibuf_arr[i]->mall |= IB_rect; + render_result_rect_get_pixels(rr, ibuf_arr[i]->rect, width, height, &scene->view_settings, &scene->display_settings, view_id); + do_free[i] = true; + } + + IMB_colormanagement_imbuf_for_write(ibuf_arr[i], true, false, &scene->view_settings, + &scene->display_settings, &scene->r.im_format); + } + + ibuf_arr[2] = IMB_stereo3d_ImBuf(&scene->r.im_format, ibuf_arr[0], ibuf_arr[1]); + + ok = mh->append_movie(movie_ctx_arr[0], rd, scene->r.sfra, scene->r.cfra, (int *) ibuf_arr[2]->rect, + ibuf_arr[2]->x, ibuf_arr[2]->y, "", reports); + + for (i = 0; i < 2; i++) { + if (do_free[i]) { + MEM_freeN(ibuf_arr[i]->rect); + ibuf_arr[i]->rect = NULL; + ibuf_arr[i]->mall &= ~IB_rect; + } + + /* imbuf knows which rects are not part of ibuf */ + IMB_freeImBuf(ibuf_arr[i]); + } + } + + return ok; +} + +static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovieHandle *mh, const size_t totvideos, const char *name_override) +{ + char name[FILE_MAX]; + RenderResult rres; + double render_time; + bool ok = true; + Object *camera = RE_GetCamera(re); + + RE_AcquireResultImageViews(re, &rres); + + /* write movie or image */ + if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { + RE_WriteRenderViewsMovie(re->reports, &rres, scene, &re->r, mh, re->rectx, re->recty, re->movie_ctx_arr, totvideos); + } + else { + if (name_override) + BLI_strncpy(name, name_override, sizeof(name)); + else + BKE_image_path_from_imformat( + name, scene->r.pic, bmain->name, scene->r.cfra, + &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true, NULL); + + /* write images as individual images or stereo */ + ok = RE_WriteRenderViewsImage(re->reports, &rres, scene, camera, true, name); } - RE_ReleaseResultImage(re); + RE_ReleaseResultImageViews(re, &rres); render_time = re->i.lastframetime; re->i.lastframetime = PIL_check_seconds_timer() - re->i.starttime; @@ -2987,20 +3364,51 @@ static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovie return ok; } +static void get_videos_dimensions(Render *re, RenderData *rd, size_t *r_width, size_t *r_height) +{ + size_t width, height; + if (re->r.mode & R_BORDER) { + if ((re->r.mode & R_CROP) == 0) { + width = re->winx; + height = re->winy; + } + else { + width = re->rectx; + height = re->recty; + } + } + else { + width = re->rectx; + height = re->recty; + } + + BKE_scene_multiview_videos_dimensions_get(rd, width, height, r_width, r_height); +} + /* saves images to disk */ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_override, unsigned int lay_override, int sfra, int efra, int tfra) { RenderData rd = scene->r; - bMovieHandle *mh = BKE_movie_handle_get(scene->r.im_format.imtype); + bMovieHandle *mh = NULL; int cfrao = scene->r.cfra; int nfra, totrendered = 0, totskipped = 0; - + const size_t totvideos = BKE_scene_multiview_num_videos_get(&rd); + const bool is_movie = BKE_imtype_is_movie(scene->r.im_format.imtype); + const bool is_multiview_name = ((scene->r.scemode & R_MULTIVIEW) != 0 && + (scene->r.im_format.views_format == R_IMF_VIEWS_INDIVIDUAL)); + BLI_callback_exec(re->main, (ID *)scene, BLI_CB_EVT_RENDER_INIT); /* do not fully call for each frame, it initializes & pops output window */ if (!render_initialize_from_main(re, &rd, bmain, scene, NULL, camera_override, lay_override, 0, 1)) return; + + /* we don't support Frame Server and streaming of individual views */ + if ((rd.im_format.imtype == R_IMF_IMTYPE_FRAMESERVER) && (totvideos > 1)) { + BKE_report(re->reports, RPT_ERROR, "Frame Server only support stereo output for multiview rendering"); + return; + } /* ugly global still... is to prevent renderwin events and signal subsurfs etc to make full resol */ /* is also set by caller renderwin.c */ @@ -3008,31 +3416,33 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri re->flag |= R_ANIMATION; - if (BKE_imtype_is_movie(scene->r.im_format.imtype)) { - int width, height; - if (re->r.mode & R_BORDER) { - if ((re->r.mode & R_CROP) == 0) { - width = re->winx; - height = re->winy; - } - else { - width = re->rectx; - height = re->recty; - } - } - else { - width = re->rectx; - height = re->recty; - } + if (is_movie) { + size_t i, width, height; + + get_videos_dimensions(re, &rd, &width, &height); + + mh = BKE_movie_handle_get(scene->r.im_format.imtype); + re->movie_ctx_arr = MEM_mallocN(sizeof(void *) * totvideos, "Movies' Context"); + + for (i = 0; i < totvideos; i++) { + const char *suffix = BKE_scene_multiview_view_id_suffix_get(&re->r, i); - /* last argument here depends on users really, but no users using preview have been found so far */ - if (!mh->start_movie(scene, &re->r, width, height, re->reports, false)) - G.is_break = true; + re->movie_ctx_arr[i] = mh->context_create(); + + if (!mh->start_movie(re->movie_ctx_arr[i], scene, &re->r, width, height, re->reports, false, suffix)) + G.is_break = true; + } } - if (mh->get_next_frame) { + if (mh && mh->get_next_frame) { + /* MULTIVIEW_TODO: + * in case a new video format is added that implements get_next_frame multiview has to be addressed + * or the error throwing for R_IMF_IMTYPE_FRAMESERVER has to be extended for those cases as well + */ + BLI_assert(totvideos < 2); + while (!(G.is_break == 1)) { - int nf = mh->get_next_frame(&re->r, re->reports); + int nf = mh->get_next_frame(re->movie_ctx_arr[0], &re->r, re->reports); if (nf >= 0 && nf >= scene->r.sfra && nf <= scene->r.efra) { scene->r.cfra = re->r.cfra = nf; @@ -3042,7 +3452,7 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri totrendered++; if (re->test_break(re->tbh) == 0) { - if (!do_write_image_or_movie(re, bmain, scene, mh, NULL)) + if (!do_write_image_or_movie(re, bmain, scene, mh, totvideos, NULL)) G.is_break = true; } @@ -3085,20 +3495,67 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri nfra += tfra; /* Touch/NoOverwrite options are only valid for image's */ - if (BKE_imtype_is_movie(scene->r.im_format.imtype) == 0) { + if (is_movie == false) { if (scene->r.mode & (R_NO_OVERWRITE | R_TOUCH)) BKE_image_path_from_imformat( name, scene->r.pic, bmain->name, scene->r.cfra, - &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true); + &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true, NULL); + + if (scene->r.mode & R_NO_OVERWRITE) { + if (!is_multiview_name) { + if (BLI_exists(name)) { + printf("skipping existing frame \"%s\"\n", name); + totskipped++; + continue; + } + } + else { + SceneRenderView *srv; + bool is_skip = false; + char filepath[FILE_MAX]; - if (scene->r.mode & R_NO_OVERWRITE && BLI_exists(name)) { - printf("skipping existing frame \"%s\"\n", name); - totskipped++; - continue; + for (srv = scene->r.views.first; srv; srv = srv->next) { + if (!BKE_scene_multiview_is_render_view_active(&scene->r, srv)) + continue; + + BKE_scene_multiview_filepath_get(srv, name, filepath); + + if (BLI_exists(filepath)) { + is_skip = true; + printf("skipping existing frame \"%s\" for view \"%s\"\n", filepath, srv->name); + } + } + + if (is_skip) { + totskipped++; + continue; + } + } } - if (scene->r.mode & R_TOUCH && !BLI_exists(name)) { - BLI_make_existing_file(name); /* makes the dir if its not there */ - BLI_file_touch(name); + + if (scene->r.mode & R_TOUCH) { + if (!is_multiview_name) { + if (!BLI_exists(name)) { + BLI_make_existing_file(name); /* makes the dir if its not there */ + BLI_file_touch(name); + } + } + else { + SceneRenderView *srv; + char filepath[FILE_MAX]; + + for (srv = scene->r.views.first; srv; srv = srv->next) { + if (!BKE_scene_multiview_is_render_view_active(&scene->r, srv)) + continue; + + BKE_scene_multiview_filepath_get(srv, name, filepath); + + if (!BLI_exists(filepath)) { + BLI_make_existing_file(filepath); /* makes the dir if its not there */ + BLI_file_touch(filepath); + } + } + } } } @@ -3113,7 +3570,7 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri if (re->test_break(re->tbh) == 0) { if (!G.is_break) - if (!do_write_image_or_movie(re, bmain, scene, mh, NULL)) + if (!do_write_image_or_movie(re, bmain, scene, mh, totvideos, NULL)) G.is_break = true; } else @@ -3121,10 +3578,30 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri if (G.is_break == true) { /* remove touched file */ - if (BKE_imtype_is_movie(scene->r.im_format.imtype) == 0) { - if ((scene->r.mode & R_TOUCH) && (BLI_file_size(name) == 0)) { - /* BLI_exists(name) is implicit */ - BLI_delete(name, false, false); + if (is_movie == false) { + if ((scene->r.mode & R_TOUCH)) { + if (!is_multiview_name) { + if ((BLI_file_size(name) == 0)) { + /* BLI_exists(name) is implicit */ + BLI_delete(name, false, false); + } + } + else { + SceneRenderView *srv; + char filepath[FILE_MAX]; + + for (srv = scene->r.views.first; srv; srv = srv->next) { + if (!BKE_scene_multiview_is_render_view_active(&scene->r, srv)) + continue; + + BKE_scene_multiview_filepath_get(srv, name, filepath); + + if ((BLI_file_size(filepath) == 0)) { + /* BLI_exists(filepath) is implicit */ + BLI_delete(filepath, false, false); + } + } + } } } @@ -3139,8 +3616,17 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri } /* end movie */ - if (BKE_imtype_is_movie(scene->r.im_format.imtype)) - mh->end_movie(); + if (is_movie) { + size_t i; + for (i = 0; i < totvideos; i++) { + mh->end_movie(re->movie_ctx_arr[i]); + mh->context_free(re->movie_ctx_arr[i]); + } + + if (re->movie_ctx_arr) { + MEM_freeN(re->movie_ctx_arr); + } + } if (totskipped && totrendered == 0) BKE_report(re->reports, RPT_INFO, "No frames rendered, skipped to not overwrite"); @@ -3238,13 +3724,22 @@ void RE_layer_load_from_file(RenderLayer *layer, ReportList *reports, const char { /* OCIO_TODO: assume layer was saved in defaule color space */ ImBuf *ibuf = IMB_loadiffname(filename, IB_rect, NULL); + RenderPass *rpass = NULL; + + /* multiview: since the API takes no 'view', we use the first combined pass found */ + for (rpass = layer->passes.first; rpass; rpass = rpass->next) + if (rpass->passtype == SCE_PASS_COMBINED) + break; + + if (rpass == NULL) + BKE_reportf(reports, RPT_ERROR, "RE_layer_load_from_file: no Combined pass found in the render layer '%s'", filename); if (ibuf && (ibuf->rect || ibuf->rect_float)) { if (ibuf->x == layer->rectx && ibuf->y == layer->recty) { if (ibuf->rect_float == NULL) IMB_float_from_rect(ibuf); - memcpy(layer->rectf, ibuf->rect_float, sizeof(float) * 4 * layer->rectx * layer->recty); + memcpy(rpass->rect, ibuf->rect_float, sizeof(float) * 4 * layer->rectx * layer->recty); } else { if ((ibuf->x - x >= layer->rectx) && (ibuf->y - y >= layer->recty)) { @@ -3257,7 +3752,7 @@ void RE_layer_load_from_file(RenderLayer *layer, ReportList *reports, const char if (ibuf_clip) { IMB_rectcpy(ibuf_clip, ibuf, 0, 0, x, y, layer->rectx, layer->recty); - memcpy(layer->rectf, ibuf_clip->rect_float, sizeof(float) * 4 * layer->rectx * layer->recty); + memcpy(rpass->rect, ibuf_clip->rect_float, sizeof(float) * 4 * layer->rectx * layer->recty); IMB_freeImBuf(ibuf_clip); } else { @@ -3345,3 +3840,21 @@ bool RE_WriteEnvmapResult(struct ReportList *reports, Scene *scene, EnvMap *env, } } +/* used in the interface to decide whether to show layers */ +bool RE_layers_have_name(struct RenderResult *rr) +{ + switch (BLI_listbase_count_ex(&rr->layers, 2)) { + case 0: + return false; + break; + case 1: + return (((RenderLayer *)rr->layers.first)->name[0] != '\0'); + break; + default: + return true; + break; + } + return false; +} + + diff --git a/source/blender/render/intern/source/render_result.c b/source/blender/render/intern/source/render_result.c index 6486844bacf..90f6eab0cc1 100644 --- a/source/blender/render/intern/source/render_result.c +++ b/source/blender/render/intern/source/render_result.c @@ -48,9 +48,8 @@ #include "BKE_global.h" #include "BKE_main.h" #include "BKE_report.h" -#ifdef WITH_CYCLES_DEBUG -# include "BKE_scene.h" -#endif +#include "BKE_camera.h" +#include "BKE_scene.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -63,14 +62,32 @@ /********************************** Free *************************************/ +static void render_result_views_free(RenderResult *res) +{ + while (res->views.first) { + RenderView *rv = res->views.first; + BLI_remlink(&res->views, rv); + + if (rv->rect32) + MEM_freeN(rv->rect32); + + if (rv->rectz) + MEM_freeN(rv->rectz); + + if (rv->rectf) + MEM_freeN(rv->rectf); + + MEM_freeN(rv); + } +} + void render_result_free(RenderResult *res) { if (res == NULL) return; while (res->layers.first) { RenderLayer *rl = res->layers.first; - - if (rl->rectf) MEM_freeN(rl->rectf); + /* acolrect and scolrect are optionally allocated in shade_tile, only free here since it can be used for drawing */ if (rl->acolrect) MEM_freeN(rl->acolrect); if (rl->scolrect) MEM_freeN(rl->scolrect); @@ -85,7 +102,9 @@ void render_result_free(RenderResult *res) BLI_remlink(&res->layers, rl); MEM_freeN(rl); } - + + render_result_views_free(res); + if (res->rect32) MEM_freeN(res->rect32); if (res->rectz) @@ -115,13 +134,44 @@ void render_result_free_list(ListBase *lb, RenderResult *rr) } } -/********************************* Names *************************************/ +/********************************* multiview *************************************/ -/* NOTE: OpenEXR only supports 32 chars for layer+pass names - * In blender we now use max 10 chars for pass, max 20 for layer */ -static const char *get_pass_name(int passtype, int channel) +/* create a new views Listbase in rr without duplicating the memory pointers */ +void render_result_views_shallowcopy(RenderResult *dst, RenderResult *src) +{ + RenderView *rview; + + if (dst == NULL || src == NULL) + return; + + for (rview = src->views.first; rview; rview = rview->next) { + RenderView *rv; + + rv = MEM_mallocN(sizeof(RenderView), "new render view"); + BLI_addtail(&dst->views, rv); + + BLI_strncpy(rv->name, rview->name, sizeof(rv->name)); + rv->rectf = rview->rectf; + rv->rectz = rview->rectz; + rv->rect32 = rview->rect32; + } +} + +/* free the views created temporarily */ +void render_result_views_shallowdelete(RenderResult *rr) +{ + if (rr == NULL) + return; + + while (rr->views.first) { + RenderView *rv = rr->views.first; + BLI_remlink(&rr->views, rv); + MEM_freeN(rv); + } +} + +static const char *name_from_passtype(int passtype, int channel) { - if (passtype == SCE_PASS_COMBINED) { if (channel == -1) return "Combined"; if (channel == 0) return "Combined.R"; @@ -308,108 +358,131 @@ static const char *get_pass_name(int passtype, int channel) static int passtype_from_name(const char *str) { - - if (STREQ(str, "Combined")) + if (STRPREFIX(str, "Combined")) return SCE_PASS_COMBINED; - if (STREQ(str, "Depth")) + if (STRPREFIX(str, "Depth")) return SCE_PASS_Z; - if (STREQ(str, "Vector")) + if (STRPREFIX(str, "Vector")) return SCE_PASS_VECTOR; - if (STREQ(str, "Normal")) + if (STRPREFIX(str, "Normal")) return SCE_PASS_NORMAL; - if (STREQ(str, "UV")) + if (STRPREFIX(str, "UV")) return SCE_PASS_UV; - if (STREQ(str, "Color")) + if (STRPREFIX(str, "Color")) return SCE_PASS_RGBA; - if (STREQ(str, "Emit")) + if (STRPREFIX(str, "Emit")) return SCE_PASS_EMIT; - if (STREQ(str, "Diffuse")) + if (STRPREFIX(str, "Diffuse")) return SCE_PASS_DIFFUSE; - if (STREQ(str, "Spec")) + if (STRPREFIX(str, "Spec")) return SCE_PASS_SPEC; - if (STREQ(str, "Shadow")) + if (STRPREFIX(str, "Shadow")) return SCE_PASS_SHADOW; - if (STREQ(str, "AO")) + if (STRPREFIX(str, "AO")) return SCE_PASS_AO; - if (STREQ(str, "Env")) + if (STRPREFIX(str, "Env")) return SCE_PASS_ENVIRONMENT; - if (STREQ(str, "Indirect")) + if (STRPREFIX(str, "Indirect")) return SCE_PASS_INDIRECT; - if (STREQ(str, "Reflect")) + if (STRPREFIX(str, "Reflect")) return SCE_PASS_REFLECT; - if (STREQ(str, "Refract")) + if (STRPREFIX(str, "Refract")) return SCE_PASS_REFRACT; - if (STREQ(str, "IndexOB")) + if (STRPREFIX(str, "IndexOB")) return SCE_PASS_INDEXOB; - if (STREQ(str, "IndexMA")) + if (STRPREFIX(str, "IndexMA")) return SCE_PASS_INDEXMA; - if (STREQ(str, "Mist")) + if (STRPREFIX(str, "Mist")) return SCE_PASS_MIST; - if (STREQ(str, "RayHits")) + if (STRPREFIX(str, "RayHits")) return SCE_PASS_RAYHITS; - if (STREQ(str, "DiffDir")) + if (STRPREFIX(str, "DiffDir")) return SCE_PASS_DIFFUSE_DIRECT; - if (STREQ(str, "DiffInd")) + if (STRPREFIX(str, "DiffInd")) return SCE_PASS_DIFFUSE_INDIRECT; - if (STREQ(str, "DiffCol")) + if (STRPREFIX(str, "DiffCol")) return SCE_PASS_DIFFUSE_COLOR; - if (STREQ(str, "GlossDir")) + if (STRPREFIX(str, "GlossDir")) return SCE_PASS_GLOSSY_DIRECT; - if (STREQ(str, "GlossInd")) + if (STRPREFIX(str, "GlossInd")) return SCE_PASS_GLOSSY_INDIRECT; - if (STREQ(str, "GlossCol")) + if (STRPREFIX(str, "GlossCol")) return SCE_PASS_GLOSSY_COLOR; - if (STREQ(str, "TransDir")) + if (STRPREFIX(str, "TransDir")) return SCE_PASS_TRANSM_DIRECT; - if (STREQ(str, "TransInd")) + if (STRPREFIX(str, "TransInd")) return SCE_PASS_TRANSM_INDIRECT; - if (STREQ(str, "TransCol")) + if (STRPREFIX(str, "TransCol")) return SCE_PASS_TRANSM_COLOR; - if (STREQ(str, "SubsurfaceDir")) + if (STRPREFIX(str, "SubsurfaceDir")) return SCE_PASS_SUBSURFACE_DIRECT; - if (STREQ(str, "SubsurfaceInd")) + if (STRPREFIX(str, "SubsurfaceInd")) return SCE_PASS_SUBSURFACE_INDIRECT; - if (STREQ(str, "SubsurfaceCol")) + if (STRPREFIX(str, "SubsurfaceCol")) return SCE_PASS_SUBSURFACE_COLOR; return 0; } + +static void set_pass_name(char *passname, int passtype, int channel, const char *view) +{ + const char *end; + const char *token; + int len; + + const char *passtype_name = name_from_passtype(passtype, channel); + + if (view == NULL || view[0] == '\0') { + BLI_strncpy(passname, passtype_name, EXR_PASS_MAXNAME); + return; + } + + end = passtype_name + strlen(passtype_name); + len = IMB_exr_split_token(passtype_name, end, &token); + + if (len == strlen(passtype_name)) + sprintf(passname, "%s.%s", passtype_name, view); + else + sprintf(passname, "%.*s%s.%s", (int)(end-passtype_name) - len, passtype_name, view, token); +} + /********************************** New **************************************/ -static RenderPass *render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int channels, int passtype) +static RenderPass *render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int channels, int passtype, const char *viewname) { - const char *typestr = get_pass_name(passtype, 0); + const size_t view_id = BLI_findstringindex(&rr->views, viewname, offsetof(RenderView, name)); + const char *typestr = name_from_passtype(passtype, -1); RenderPass *rpass = MEM_callocN(sizeof(RenderPass), typestr); int rectsize = rr->rectx * rr->recty * channels; @@ -418,12 +491,16 @@ static RenderPass *render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int rpass->channels = channels; rpass->rectx = rl->rectx; rpass->recty = rl->recty; - BLI_strncpy(rpass->name, get_pass_name(rpass->passtype, -1), sizeof(rpass->name)); + rpass->view_id = view_id; + + set_pass_name(rpass->name, rpass->passtype, -1, viewname); + BLI_strncpy(rpass->internal_name, typestr, sizeof(rpass->internal_name)); + BLI_strncpy(rpass->view, viewname, sizeof(rpass->view)); if (rl->exrhandle) { int a; for (a = 0; a < channels; a++) - IMB_exr_add_channel(rl->exrhandle, rl->name, get_pass_name(passtype, a), 0, 0, NULL); + IMB_exr_add_channel(rl->exrhandle, rl->name, name_from_passtype(passtype, a), viewname, 0, 0, NULL); } else { float *rect; @@ -460,9 +537,10 @@ static RenderPass *render_layer_add_debug_pass(RenderResult *rr, RenderLayer *rl, int channels, int pass_type, - int debug_type) + int debug_type, + const char *view) { - RenderPass *rpass = render_layer_add_pass(rr, rl, channels, pass_type); + RenderPass *rpass = render_layer_add_pass(rr, rl, channels, pass_type, view); rpass->debug_type = debug_type; BLI_strncpy(rpass->name, debug_pass_type_name_get(debug_type), @@ -475,12 +553,14 @@ static RenderPass *render_layer_add_debug_pass(RenderResult *rr, /* will read info from Render *re to define layers */ /* called in threads */ /* re->winx,winy is coordinate space of entire image, partrct the part within */ -RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuffers, const char *layername) +RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuffers, const char *layername, const char *viewname) { RenderResult *rr; RenderLayer *rl; + RenderView *rv; SceneRenderLayer *srl; - int rectx, recty, nr; + int rectx, recty; + int nr, i; rectx = BLI_rcti_size_x(partrct); recty = BLI_rcti_size_y(partrct); @@ -505,6 +585,8 @@ RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuf rr->do_exr_tile = true; } + render_result_views_new(rr, &re->r); + /* check renderdata for amount of layers */ for (nr = 0, srl = re->r.layers.first; srl; srl = srl->next, nr++) { @@ -538,84 +620,90 @@ RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuf if (rr->do_exr_tile) { rl->display_buffer = MEM_mapallocN(rectx * recty * sizeof(unsigned int), "Combined display space rgba"); - rl->exrhandle = IMB_exr_get_handle(); - - IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.R", 0, 0, NULL); - IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.G", 0, 0, NULL); - IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.B", 0, 0, NULL); - IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.A", 0, 0, NULL); } - else - rl->rectf = MEM_mapallocN(rectx * recty * sizeof(float) * 4, "Combined rgba"); - - if (srl->passflag & SCE_PASS_Z) - render_layer_add_pass(rr, rl, 1, SCE_PASS_Z); - if (srl->passflag & SCE_PASS_VECTOR) - render_layer_add_pass(rr, rl, 4, SCE_PASS_VECTOR); - if (srl->passflag & SCE_PASS_NORMAL) - render_layer_add_pass(rr, rl, 3, SCE_PASS_NORMAL); - if (srl->passflag & SCE_PASS_UV) - render_layer_add_pass(rr, rl, 3, SCE_PASS_UV); - if (srl->passflag & SCE_PASS_RGBA) - render_layer_add_pass(rr, rl, 4, SCE_PASS_RGBA); - if (srl->passflag & SCE_PASS_EMIT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_EMIT); - if (srl->passflag & SCE_PASS_DIFFUSE) - render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE); - if (srl->passflag & SCE_PASS_SPEC) - render_layer_add_pass(rr, rl, 3, SCE_PASS_SPEC); - if (srl->passflag & SCE_PASS_AO) - render_layer_add_pass(rr, rl, 3, SCE_PASS_AO); - if (srl->passflag & SCE_PASS_ENVIRONMENT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_ENVIRONMENT); - if (srl->passflag & SCE_PASS_INDIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_INDIRECT); - if (srl->passflag & SCE_PASS_SHADOW) - render_layer_add_pass(rr, rl, 3, SCE_PASS_SHADOW); - if (srl->passflag & SCE_PASS_REFLECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_REFLECT); - if (srl->passflag & SCE_PASS_REFRACT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_REFRACT); - if (srl->passflag & SCE_PASS_INDEXOB) - render_layer_add_pass(rr, rl, 1, SCE_PASS_INDEXOB); - if (srl->passflag & SCE_PASS_INDEXMA) - render_layer_add_pass(rr, rl, 1, SCE_PASS_INDEXMA); - if (srl->passflag & SCE_PASS_MIST) - render_layer_add_pass(rr, rl, 1, SCE_PASS_MIST); - if (rl->passflag & SCE_PASS_RAYHITS) - render_layer_add_pass(rr, rl, 4, SCE_PASS_RAYHITS); - if (srl->passflag & SCE_PASS_DIFFUSE_DIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE_DIRECT); - if (srl->passflag & SCE_PASS_DIFFUSE_INDIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE_INDIRECT); - if (srl->passflag & SCE_PASS_DIFFUSE_COLOR) - render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE_COLOR); - if (srl->passflag & SCE_PASS_GLOSSY_DIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_GLOSSY_DIRECT); - if (srl->passflag & SCE_PASS_GLOSSY_INDIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_GLOSSY_INDIRECT); - if (srl->passflag & SCE_PASS_GLOSSY_COLOR) - render_layer_add_pass(rr, rl, 3, SCE_PASS_GLOSSY_COLOR); - if (srl->passflag & SCE_PASS_TRANSM_DIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_TRANSM_DIRECT); - if (srl->passflag & SCE_PASS_TRANSM_INDIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_TRANSM_INDIRECT); - if (srl->passflag & SCE_PASS_TRANSM_COLOR) - render_layer_add_pass(rr, rl, 3, SCE_PASS_TRANSM_COLOR); - if (srl->passflag & SCE_PASS_SUBSURFACE_DIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_DIRECT); - if (srl->passflag & SCE_PASS_SUBSURFACE_INDIRECT) - render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_INDIRECT); - if (srl->passflag & SCE_PASS_SUBSURFACE_COLOR) - render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_COLOR); + + for (rv = rr->views.first; rv; rv = rv->next) { + const char *view = rv->name; + + if (viewname && viewname[0]) + if (!STREQ(view, viewname)) + continue; + + if (rr->do_exr_tile) + IMB_exr_add_view(rl->exrhandle, view); + + /* a renderlayer should always have a Combined pass*/ + render_layer_add_pass(rr, rl, 4, SCE_PASS_COMBINED, view); + + if (srl->passflag & SCE_PASS_Z) + render_layer_add_pass(rr, rl, 1, SCE_PASS_Z, view); + if (srl->passflag & SCE_PASS_VECTOR) + render_layer_add_pass(rr, rl, 4, SCE_PASS_VECTOR, view); + if (srl->passflag & SCE_PASS_NORMAL) + render_layer_add_pass(rr, rl, 3, SCE_PASS_NORMAL, view); + if (srl->passflag & SCE_PASS_UV) + render_layer_add_pass(rr, rl, 3, SCE_PASS_UV, view); + if (srl->passflag & SCE_PASS_RGBA) + render_layer_add_pass(rr, rl, 4, SCE_PASS_RGBA, view); + if (srl->passflag & SCE_PASS_EMIT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_EMIT, view); + if (srl->passflag & SCE_PASS_DIFFUSE) + render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE, view); + if (srl->passflag & SCE_PASS_SPEC) + render_layer_add_pass(rr, rl, 3, SCE_PASS_SPEC, view); + if (srl->passflag & SCE_PASS_AO) + render_layer_add_pass(rr, rl, 3, SCE_PASS_AO, view); + if (srl->passflag & SCE_PASS_ENVIRONMENT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_ENVIRONMENT, view); + if (srl->passflag & SCE_PASS_INDIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_INDIRECT, view); + if (srl->passflag & SCE_PASS_SHADOW) + render_layer_add_pass(rr, rl, 3, SCE_PASS_SHADOW, view); + if (srl->passflag & SCE_PASS_REFLECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_REFLECT, view); + if (srl->passflag & SCE_PASS_REFRACT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_REFRACT, view); + if (srl->passflag & SCE_PASS_INDEXOB) + render_layer_add_pass(rr, rl, 1, SCE_PASS_INDEXOB, view); + if (srl->passflag & SCE_PASS_INDEXMA) + render_layer_add_pass(rr, rl, 1, SCE_PASS_INDEXMA, view); + if (srl->passflag & SCE_PASS_MIST) + render_layer_add_pass(rr, rl, 1, SCE_PASS_MIST, view); + if (rl->passflag & SCE_PASS_RAYHITS) + render_layer_add_pass(rr, rl, 4, SCE_PASS_RAYHITS, view); + if (srl->passflag & SCE_PASS_DIFFUSE_DIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE_DIRECT, view); + if (srl->passflag & SCE_PASS_DIFFUSE_INDIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE_INDIRECT, view); + if (srl->passflag & SCE_PASS_DIFFUSE_COLOR) + render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE_COLOR, view); + if (srl->passflag & SCE_PASS_GLOSSY_DIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_GLOSSY_DIRECT, view); + if (srl->passflag & SCE_PASS_GLOSSY_INDIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_GLOSSY_INDIRECT, view); + if (srl->passflag & SCE_PASS_GLOSSY_COLOR) + render_layer_add_pass(rr, rl, 3, SCE_PASS_GLOSSY_COLOR, view); + if (srl->passflag & SCE_PASS_TRANSM_DIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_TRANSM_DIRECT, view); + if (srl->passflag & SCE_PASS_TRANSM_INDIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_TRANSM_INDIRECT, view); + if (srl->passflag & SCE_PASS_TRANSM_COLOR) + render_layer_add_pass(rr, rl, 3, SCE_PASS_TRANSM_COLOR, view); + if (srl->passflag & SCE_PASS_SUBSURFACE_DIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_DIRECT, view); + if (srl->passflag & SCE_PASS_SUBSURFACE_INDIRECT) + render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_INDIRECT, view); + if (srl->passflag & SCE_PASS_SUBSURFACE_COLOR) + render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_COLOR, view); #ifdef WITH_CYCLES_DEBUG - if (BKE_scene_use_new_shading_nodes(re->scene)) { - render_layer_add_debug_pass(rr, rl, 1, SCE_PASS_DEBUG, - RENDER_PASS_DEBUG_BVH_TRAVERSAL_STEPS); - } + if (BKE_scene_use_new_shading_nodes(re->scene)) { + render_layer_add_debug_pass(rr, rl, 1, SCE_PASS_DEBUG, + RENDER_PASS_DEBUG_BVH_TRAVERSAL_STEPS, view); + } #endif + } } /* sss, previewrender and envmap don't do layers, so we make a default one */ if (BLI_listbase_is_empty(&rr->layers) && !(layername && layername[0])) { @@ -628,18 +716,27 @@ RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuf /* duplicate code... */ if (rr->do_exr_tile) { rl->display_buffer = MEM_mapallocN(rectx * recty * sizeof(unsigned int), "Combined display space rgba"); - rl->exrhandle = IMB_exr_get_handle(); - - IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.R", 0, 0, NULL); - IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.G", 0, 0, NULL); - IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.B", 0, 0, NULL); - IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.A", 0, 0, NULL); } - else { - rl->rectf = MEM_mapallocN(rectx * recty * sizeof(float) * 4, "Combined rgba"); + + for (rv = rr->views.first; rv; rv = rv->next) { + const char *view = rv->name; + + if (viewname && viewname[0]) + if (strcmp(view, viewname) != 0) + continue; + + if (rr->do_exr_tile) { + IMB_exr_add_view(rl->exrhandle, view); + + for (i=0; i < 4; i++) + IMB_exr_add_channel(rl->exrhandle, rl->name, name_from_passtype(SCE_PASS_COMBINED, i), view, 0, 0, NULL); + } + else { + render_layer_add_pass(rr, rl, 4, SCE_PASS_COMBINED, view); + } } - + /* note, this has to be in sync with scene.c */ rl->lay = (1 << 20) - 1; rl->layflag = 0x7FFF; /* solid ztra halo strand */ @@ -657,15 +754,15 @@ RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuf } /* allocate osa new results for samples */ -RenderResult *render_result_new_full_sample(Render *re, ListBase *lb, rcti *partrct, int crop, int savebuffers) +RenderResult *render_result_new_full_sample(Render *re, ListBase *lb, rcti *partrct, int crop, int savebuffers, const char *viewname) { int a; if (re->osa == 0) - return render_result_new(re, partrct, crop, savebuffers, RR_ALL_LAYERS); + return render_result_new(re, partrct, crop, savebuffers, RR_ALL_LAYERS, viewname); for (a = 0; a < re->osa; a++) { - RenderResult *rr = render_result_new(re, partrct, crop, savebuffers, RR_ALL_LAYERS); + RenderResult *rr = render_result_new(re, partrct, crop, savebuffers, RR_ALL_LAYERS, viewname); BLI_addtail(lb, rr); rr->sample_nr = a; } @@ -686,28 +783,98 @@ static void *ml_addlayer_cb(void *base, const char *str) return rl; } -static void ml_addpass_cb(void *UNUSED(base), void *lay, const char *str, float *rect, int totchan, const char *chan_id) +static void ml_addpass_cb(void *base, void *lay, const char *str, float *rect, int totchan, const char *chan_id, const char *view) { + RenderResult *rr = base; RenderLayer *rl = lay; RenderPass *rpass = MEM_callocN(sizeof(RenderPass), "loaded pass"); int a; BLI_addtail(&rl->passes, rpass); rpass->channels = totchan; - rpass->passtype = passtype_from_name(str); if (rpass->passtype == 0) printf("unknown pass %s\n", str); rl->passflag |= rpass->passtype; - BLI_strncpy(rpass->name, str, EXR_PASS_MAXNAME); /* channel id chars */ for (a = 0; a < totchan; a++) rpass->chan_id[a] = chan_id[a]; - + rpass->rect = rect; + if (view[0] != '\0') { + BLI_snprintf(rpass->name, sizeof(rpass->name), "%s.%s", str, view); + rpass->view_id = BLI_findstringindex(&rr->views, view, offsetof(RenderView, name)); + } + else { + BLI_strncpy(rpass->name, str, sizeof(rpass->name)); + rpass->view_id = 0; + } + + BLI_strncpy(rpass->view, view, sizeof(rpass->view)); + BLI_strncpy(rpass->internal_name, str, sizeof(rpass->internal_name)); } -/* from imbuf, if a handle was returned we convert this to render result */ +static void *ml_addview_cb(void *base, const char *str) +{ + RenderResult *rr = base; + RenderView *rv; + + rv = MEM_callocN(sizeof(RenderView), "new render view"); + BLI_strncpy(rv->name, str, EXR_VIEW_MAXNAME); + + /* For stereo drawing we need to ensure: + * STEREO_LEFT_NAME == STEREO_LEFT_ID and + * STEREO_RIGHT_NAME == STEREO_RIGHT_ID */ + + if (STREQ(str, STEREO_LEFT_NAME)) { + BLI_addhead(&rr->views, rv); + } + else if (STREQ(str, STEREO_RIGHT_NAME)) { + RenderView *left_rv = BLI_findstring(&rr->views, STEREO_LEFT_NAME, offsetof(RenderView, name)); + + if (left_rv == NULL) { + BLI_addhead(&rr->views, rv); + } + else { + BLI_insertlinkafter(&rr->views, left_rv, rv); + } + } + else { + BLI_addtail(&rr->views, rv); + } + + return rv; +} + +static int order_render_passes(const void *a, const void *b) +{ + // 1 if a is after b + RenderPass *rpa = (RenderPass *) a; + RenderPass *rpb = (RenderPass *) b; + + if (rpa->passtype > rpb->passtype) + return 1; + else if (rpa->passtype < rpb->passtype) + return 0; + + /* they have the same type */ + /* left first */ + if (STREQ(rpa->view, STEREO_LEFT_NAME)) + return 0; + else if (STREQ(rpb->view, STEREO_LEFT_NAME)) + return 1; + + /* right second */ + if (STREQ(rpa->view, STEREO_RIGHT_NAME)) + return 0; + else if (STREQ(rpb->view, STEREO_RIGHT_NAME)) + return 1; + + /* remaining in ascending id order */ + return (rpa->view_id < rpb->view_id); +} + +/* from imbuf, if a handle was returned and it's not a singlelayer multiview we convert this to render result */ RenderResult *render_result_new_from_exr(void *exrhandle, const char *colorspace, bool predivide, int rectx, int recty) { RenderResult *rr = MEM_callocN(sizeof(RenderResult), __func__); @@ -718,13 +885,17 @@ RenderResult *render_result_new_from_exr(void *exrhandle, const char *colorspace rr->rectx = rectx; rr->recty = recty; - IMB_exr_multilayer_convert(exrhandle, rr, ml_addlayer_cb, ml_addpass_cb); + IMB_exr_multilayer_convert(exrhandle, rr, ml_addview_cb, ml_addlayer_cb, ml_addpass_cb); for (rl = rr->layers.first; rl; rl = rl->next) { + int c=0; rl->rectx = rectx; rl->recty = recty; + BLI_listbase_sort(&rl->passes, order_render_passes); + for (rpass = rl->passes.first; rpass; rpass = rpass->next) { + printf("%d: %s\n", c++, rpass->name); rpass->rectx = rectx; rpass->recty = recty; @@ -738,6 +909,33 @@ RenderResult *render_result_new_from_exr(void *exrhandle, const char *colorspace return rr; } +void render_result_views_new(RenderResult *rr, RenderData *rd) +{ + SceneRenderView *srv; + RenderView *rv; + + /* clear previously existing views - for sequencer */ + render_result_views_free(rr); + + /* check renderdata for amount of views */ + if ((rd->scemode & R_MULTIVIEW)) { + for (srv = rd->views.first; srv; srv = srv->next) { + if (BKE_scene_multiview_is_render_view_active(rd, srv) == false) continue; + + rv = MEM_callocN(sizeof(RenderView), "new render view"); + BLI_addtail(&rr->views, rv); + + BLI_strncpy(rv->name, srv->name, sizeof(rv->name)); + } + } + + /* we always need at least one view */ + if (BLI_listbase_count_ex(&rr->views, 1) == 0) { + rv = MEM_callocN(sizeof(RenderView), "new render view"); + BLI_addtail(&rr->views, rv); + } +} + /*********************************** Merge ***********************************/ static void do_merge_tile(RenderResult *rr, RenderResult *rrpart, float *target, float *tile, int pixsize) @@ -783,16 +981,19 @@ void render_result_merge(RenderResult *rr, RenderResult *rrpart) for (rl = rr->layers.first; rl; rl = rl->next) { rlp = RE_GetRenderLayer(rrpart, rl->name); if (rlp) { - /* combined */ - if (rl->rectf && rlp->rectf) - do_merge_tile(rr, rrpart, rl->rectf, rlp->rectf, 4); - /* passes are allocated in sync */ for (rpass = rl->passes.first, rpassp = rlp->passes.first; rpass && rpassp; - rpass = rpass->next, rpassp = rpassp->next) + rpass = rpass->next) { + /* renderresult have all passes, renderpart only the active view's passes */ + if (strcmp(rpassp->name, rpass->name) != 0) + continue; + do_merge_tile(rr, rrpart, rpass->rect, rpassp->rect, rpass->channels); + + /* manually get next render pass */ + rpassp = rpassp->next; } } } @@ -801,7 +1002,7 @@ void render_result_merge(RenderResult *rr, RenderResult *rrpart) /* for passes read from files, these have names stored */ static char *make_pass_name(RenderPass *rpass, int chan) { - static char name[16]; + static char name[EXR_PASS_MAXNAME]; int len; BLI_strncpy(name, rpass->name, EXR_PASS_MAXNAME); @@ -813,65 +1014,111 @@ static char *make_pass_name(RenderPass *rpass, int chan) return name; } -/* filename already made absolute */ -/* called from within UI, saves both rendered result as a file-read result */ -bool RE_WriteRenderResult(ReportList *reports, RenderResult *rr, const char *filename, int compress) +/* called from within UI and render pipeline, saves both rendered result as a file-read result + * if multiview is true saves all views in a multiview exr + * else if view is not NULL saves single view + * else saves stereo3d + */ +bool RE_WriteRenderResult(ReportList *reports, RenderResult *rr, const char *filename, ImageFormatData *imf, const bool multiview, const char *view) { RenderLayer *rl; RenderPass *rpass; + RenderView *rview; void *exrhandle = IMB_exr_get_handle(); - bool success; - - BLI_make_existing_file(filename); - - /* composite result */ - if (rr->rectf) { - IMB_exr_add_channel(exrhandle, "Composite", "Combined.R", 4, 4 * rr->rectx, rr->rectf); - IMB_exr_add_channel(exrhandle, "Composite", "Combined.G", 4, 4 * rr->rectx, rr->rectf + 1); - IMB_exr_add_channel(exrhandle, "Composite", "Combined.B", 4, 4 * rr->rectx, rr->rectf + 2); - IMB_exr_add_channel(exrhandle, "Composite", "Combined.A", 4, 4 * rr->rectx, rr->rectf + 3); + bool success = false; + int a, nr; + const char *chan_view = NULL; + int compress = (imf ? imf->exr_codec : 0); + size_t width, height; + + const bool is_mono = view && !multiview; + + width = rr->rectx; + height = rr->recty; + + if (imf && imf->imtype == R_IMF_IMTYPE_OPENEXR && multiview) { + /* single layer OpenEXR */ + const char *RGBAZ[] = {"R", "G", "B", "A", "Z"}; + for (nr = 0, rview = rr->views.first; rview; rview = rview->next, nr++) { + IMB_exr_add_view(exrhandle, rview->name); + + if (rview->rectf) { + for (a = 0; a < 4; a++) + IMB_exr_add_channel(exrhandle, "", RGBAZ[a], + rview->name, 4, 4 * width, rview->rectf + a); + if (rview->rectz) + IMB_exr_add_channel(exrhandle, "", RGBAZ[4], + rview->name, 1, width, rview->rectz); + } + } } - - /* add layers/passes and assign channels */ - for (rl = rr->layers.first; rl; rl = rl->next) { - - /* combined */ - if (rl->rectf) { - int a, xstride = 4; - for (a = 0; a < xstride; a++) { - IMB_exr_add_channel(exrhandle, rl->name, get_pass_name(SCE_PASS_COMBINED, a), - xstride, xstride * rr->rectx, rl->rectf + a); + else { + for (nr = 0, rview = rr->views.first; rview; rview = rview->next, nr++) { + if (is_mono) { + if (!STREQ(view, rview->name)) { + continue; + } + chan_view = ""; + } + else { + /* if rendered only one view, we treat as a a non-view render */ + chan_view = rview->name; + } + + IMB_exr_add_view(exrhandle, rview->name); + + if (rview->rectf) { + for (a = 0; a < 4; a++) + IMB_exr_add_channel(exrhandle, "Composite", name_from_passtype(SCE_PASS_COMBINED, a), + chan_view, 4, 4 * width, rview->rectf + a); } } - - /* passes are allocated in sync */ - for (rpass = rl->passes.first; rpass; rpass = rpass->next) { - int a, xstride = rpass->channels; - for (a = 0; a < xstride; a++) { - if (rpass->passtype) { - IMB_exr_add_channel(exrhandle, rl->name, get_pass_name(rpass->passtype, a), - xstride, xstride * rr->rectx, rpass->rect + a); + + /* add layers/passes and assign channels */ + for (rl = rr->layers.first; rl; rl = rl->next) { + + /* passes are allocated in sync */ + for (rpass = rl->passes.first; rpass; rpass = rpass->next) { + const int xstride = rpass->channels; + + if (is_mono) { + if (!STREQ(view, rpass->view)) { + continue; + } + chan_view = ""; } else { - IMB_exr_add_channel(exrhandle, rl->name, make_pass_name(rpass, a), - xstride, xstride * rr->rectx, rpass->rect + a); + /* if rendered only one view, we treat as a a non-view render */ + chan_view = (nr > 1 ? rpass->view :""); + } + + for (a = 0; a < xstride; a++) { + + if (rpass->passtype) { + IMB_exr_add_channel(exrhandle, rl->name, name_from_passtype(rpass->passtype, a), chan_view, + xstride, xstride * width, rpass->rect + a); + } + else { + IMB_exr_add_channel(exrhandle, rl->name, make_pass_name(rpass, a), chan_view, + xstride, xstride * width, rpass->rect + a); + } } } } } - /* when the filename has no permissions, this can fail */ - if (IMB_exr_begin_write(exrhandle, filename, rr->rectx, rr->recty, compress)) { + BLI_make_existing_file(filename); + + if (IMB_exr_begin_write(exrhandle, filename, width, height, compress)) { IMB_exr_write_channels(exrhandle); - success = true; } else { /* TODO, get the error from openexr's exception */ BKE_report(reports, RPT_ERROR, "Error writing render result (see console)"); success = false; } - IMB_exr_close(exrhandle); + IMB_exr_close(exrhandle); return success; } @@ -932,7 +1179,7 @@ void render_result_single_layer_end(Render *re) /************************* EXR Tile File Rendering ***************************/ -static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart) +static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart, const char *viewname) { RenderLayer *rlp, *rl; RenderPass *rpassp; @@ -955,21 +1202,17 @@ static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart) else { offs = 0; } - - /* combined */ - if (rlp->rectf) { - int a, xstride = 4; - for (a = 0; a < xstride; a++) { - IMB_exr_set_channel(rl->exrhandle, rlp->name, get_pass_name(SCE_PASS_COMBINED, a), - xstride, xstride * rrpart->rectx, rlp->rectf + a + xstride * offs); - } - } - + /* passes are allocated in sync */ for (rpassp = rlp->passes.first; rpassp; rpassp = rpassp->next) { - int a, xstride = rpassp->channels; + const int xstride = rpassp->channels; + int a; + char passname[EXR_PASS_MAXNAME]; + for (a = 0; a < xstride; a++) { - IMB_exr_set_channel(rl->exrhandle, rlp->name, get_pass_name(rpassp->passtype, a), + set_pass_name(passname, rpassp->passtype, a, rpassp->view); + + IMB_exr_set_channel(rl->exrhandle, rlp->name, passname, xstride, xstride * rrpart->rectx, rpassp->rect + a + xstride * offs); } } @@ -988,7 +1231,7 @@ static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart) continue; } - IMB_exrtile_write_channels(rl->exrhandle, partx, party, 0); + IMB_exrtile_write_channels(rl->exrhandle, partx, party, 0, viewname); } BLI_unlock_thread(LOCK_IMAGE); @@ -1002,13 +1245,13 @@ static void save_empty_result_tiles(Render *re) for (rr = re->result; rr; rr = rr->next) { for (rl = rr->layers.first; rl; rl = rl->next) { - IMB_exrtile_clear_channels(rl->exrhandle); + IMB_exr_clear_channels(rl->exrhandle); for (pa = re->parts.first; pa; pa = pa->next) { if (pa->status != PART_STATUS_READY) { int party = pa->disprect.ymin - re->disprect.ymin + pa->crop; int partx = pa->disprect.xmin - re->disprect.xmin + pa->crop; - IMB_exrtile_write_channels(rl->exrhandle, partx, party, 0); + IMB_exrtile_write_channels(rl->exrhandle, partx, party, 0, re->viewname); } } } @@ -1055,10 +1298,10 @@ void render_result_exr_file_end(Render *re) } /* save part into exr file */ -void render_result_exr_file_merge(RenderResult *rr, RenderResult *rrpart) +void render_result_exr_file_merge(RenderResult *rr, RenderResult *rrpart, const char *viewname) { for (; rr && rrpart; rr = rr->next, rrpart = rrpart->next) - save_render_result_tile(rr, rrpart); + save_render_result_tile(rr, rrpart, viewname); } /* path to temporary exr file */ @@ -1088,7 +1331,7 @@ int render_result_exr_file_read_sample(Render *re, int sample) bool success = true; RE_FreeRenderResult(re->result); - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS); + re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); for (rl = re->result->layers.first; rl; rl = rl->next) { render_result_exr_file_path(re->scene, rl->name, sample, str); @@ -1129,23 +1372,20 @@ int render_result_exr_file_read_path(RenderResult *rr, RenderLayer *rl_single, c for (rl = rr->layers.first; rl; rl = rl->next) { if (rl_single && rl_single != rl) continue; - - /* combined */ - if (rl->rectf) { - int a, xstride = 4; - for (a = 0; a < xstride; a++) - IMB_exr_set_channel(exrhandle, rl->name, get_pass_name(SCE_PASS_COMBINED, a), - xstride, xstride * rectx, rl->rectf + a); - } /* passes are allocated in sync */ for (rpass = rl->passes.first; rpass; rpass = rpass->next) { - int a, xstride = rpass->channels; - for (a = 0; a < xstride; a++) - IMB_exr_set_channel(exrhandle, rl->name, get_pass_name(rpass->passtype, a), + const int xstride = rpass->channels; + int a; + char passname[EXR_PASS_MAXNAME]; + + for (a = 0; a < xstride; a++) { + set_pass_name(passname, rpass->passtype, a, rpass->view); + IMB_exr_set_channel(exrhandle, rl->name, passname, xstride, xstride * rectx, rpass->rect + a); + } - BLI_strncpy(rpass->name, get_pass_name(rpass->passtype, -1), sizeof(rpass->name)); + set_pass_name(rpass->name, rpass->passtype, -1, rpass->view); } } @@ -1191,7 +1431,8 @@ void render_result_exr_file_cache_write(Render *re) render_result_exr_file_cache_path(re->scene, root, str); printf("Caching exr file, %dx%d, %s\n", rr->rectx, rr->recty, str); - RE_WriteRenderResult(NULL, rr, str, 0); + + RE_WriteRenderResult(NULL, rr, str, NULL, true, NULL); } /* For cache, makes exact copy of render result */ @@ -1201,7 +1442,7 @@ bool render_result_exr_file_cache_read(Render *re) char *root = U.render_cachedir; RE_FreeRenderResult(re->result); - re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS); + re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS); /* First try cache. */ render_result_exr_file_cache_path(re->scene, root, str); @@ -1216,14 +1457,14 @@ bool render_result_exr_file_cache_read(Render *re) /*************************** Combined Pixel Rect *****************************/ -ImBuf *render_result_rect_to_ibuf(RenderResult *rr, RenderData *rd) +ImBuf *render_result_rect_to_ibuf(RenderResult *rr, RenderData *rd, const int view_id) { ImBuf *ibuf = IMB_allocImBuf(rr->rectx, rr->recty, rd->im_format.planes, 0); /* if not exists, BKE_imbuf_write makes one */ - ibuf->rect = (unsigned int *)rr->rect32; - ibuf->rect_float = rr->rectf; - ibuf->zbuf_float = rr->rectz; + ibuf->rect = (unsigned int *) RE_RenderViewGetRect32(rr, view_id); + ibuf->rect_float = RE_RenderViewGetRectf(rr, view_id); + ibuf->zbuf_float = RE_RenderViewGetRectz(rr, view_id); /* float factor for random dither, imbuf takes care of it */ ibuf->dither = rd->dither_intensity; @@ -1259,53 +1500,73 @@ ImBuf *render_result_rect_to_ibuf(RenderResult *rr, RenderData *rd) return ibuf; } -void render_result_rect_from_ibuf(RenderResult *rr, RenderData *UNUSED(rd), ImBuf *ibuf) +void render_result_rect_from_ibuf(RenderResult *rr, RenderData *UNUSED(rd), ImBuf *ibuf, const int view_id) { + RenderView *rv = BLI_findlink(&rr->views, view_id); + if (ibuf->rect_float) { - if (!rr->rectf) - rr->rectf = MEM_mallocN(4 * sizeof(float) * rr->rectx * rr->recty, "render_seq rectf"); + if (!rv->rectf) + rv->rectf = MEM_mallocN(4 * sizeof(float) * rr->rectx * rr->recty, "render_seq rectf"); - memcpy(rr->rectf, ibuf->rect_float, 4 * sizeof(float) * rr->rectx * rr->recty); + memcpy(rv->rectf, ibuf->rect_float, 4 * sizeof(float) * rr->rectx * rr->recty); /* TSK! Since sequence render doesn't free the *rr render result, the old rect32 * can hang around when sequence render has rendered a 32 bits one before */ - if (rr->rect32) { - MEM_freeN(rr->rect32); - rr->rect32 = NULL; + if (rv->rect32) { + MEM_freeN(rv->rect32); + rv->rect32 = NULL; } } else if (ibuf->rect) { - if (!rr->rect32) - rr->rect32 = MEM_mallocN(sizeof(int) * rr->rectx * rr->recty, "render_seq rect"); + if (!rv->rect32) + rv->rect32 = MEM_mallocN(sizeof(int) * rr->rectx * rr->recty, "render_seq rect"); - memcpy(rr->rect32, ibuf->rect, 4 * rr->rectx * rr->recty); + memcpy(rv->rect32, ibuf->rect, 4 * rr->rectx * rr->recty); /* Same things as above, old rectf can hang around from previous render. */ + if (rv->rectf) { + MEM_freeN(rv->rectf); + rv->rectf = NULL; + } + } + + /* clean up non-view buffers */ + if (rr) { if (rr->rectf) { MEM_freeN(rr->rectf); rr->rectf = NULL; } + + if (rr->rect32) { + MEM_freeN(rr->rect32); + rr->rect32 = NULL; + } } } -void render_result_rect_fill_zero(RenderResult *rr) +void render_result_rect_fill_zero(RenderResult *rr, const int view_id) { - if (rr->rectf) - memset(rr->rectf, 0, 4 * sizeof(float) * rr->rectx * rr->recty); - else if (rr->rect32) - memset(rr->rect32, 0, 4 * rr->rectx * rr->recty); + RenderView *rv = BLI_findlink(&rr->views, view_id); + + if (rv->rectf) + memset(rv->rectf, 0, 4 * sizeof(float) * rr->rectx * rr->recty); + else if (rv->rect32) + memset(rv->rect32, 0, 4 * rr->rectx * rr->recty); else - rr->rect32 = MEM_callocN(sizeof(int) * rr->rectx * rr->recty, "render_seq rect"); + rv->rect32 = MEM_callocN(sizeof(int) * rr->rectx * rr->recty, "render_seq rect"); } void render_result_rect_get_pixels(RenderResult *rr, unsigned int *rect, int rectx, int recty, - const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings) + const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings, + const int view_id) { if (rr->rect32) { - memcpy(rect, rr->rect32, sizeof(int) * rr->rectx * rr->recty); + int *rect32 = RE_RenderViewGetRect32(rr, view_id); + memcpy(rect, (rect32 ? rect32 : rr->rect32), sizeof(int) * rr->rectx * rr->recty); } else if (rr->rectf) { - IMB_display_buffer_transform_apply((unsigned char *) rect, rr->rectf, rr->rectx, rr->recty, 4, + float *rectf = RE_RenderViewGetRectf(rr, view_id); + IMB_display_buffer_transform_apply((unsigned char *) rect, (rectf ? rectf : rr->rectf), rr->rectx, rr->recty, 4, view_settings, display_settings, true); } else @@ -1313,3 +1574,75 @@ void render_result_rect_get_pixels(RenderResult *rr, unsigned int *rect, int rec memset(rect, 0, sizeof(int) * rectx * recty); } + +/*************************** multiview functions *****************************/ + +bool RE_HasFakeLayer(RenderResult *res) +{ + RenderView *rv; + + if (res == NULL) + return false; + + rv = res->views.first; + if (rv == NULL) + return false; + + return (rv->rect32 || rv->rectf); +} + +bool RE_RenderResult_is_stereo(RenderResult *res) +{ + if (! BLI_findstring(&res->views, STEREO_LEFT_NAME, offsetof(RenderView, name))) + return false; + + if (! BLI_findstring(&res->views, STEREO_RIGHT_NAME, offsetof(RenderView, name))) + return false; + + return true; +} + +void RE_RenderViewSetRectf(RenderResult *res, const int view_id, float *rect) +{ + RenderView *rv = BLI_findlink(&res->views, view_id); + if (rv) { + rv->rectf = rect; + } +} + +void RE_RenderViewSetRectz(RenderResult *res, const int view_id, float *rect) +{ + RenderView *rv = BLI_findlink(&res->views, view_id); + if (rv) { + rv->rectz = rect; + } +} + +float *RE_RenderViewGetRectz(RenderResult *res, const int view_id) +{ + RenderView *rv = BLI_findlink(&res->views, view_id); + if (rv) { + return rv->rectz; + } + return res->rectz; +} + +float *RE_RenderViewGetRectf(RenderResult *res, const int view_id) +{ + RenderView *rv = BLI_findlink(&res->views, view_id); + if (rv) { + return rv->rectf; + } + return res->rectf; +} + +int *RE_RenderViewGetRect32(RenderResult *res, const int view_id) +{ + RenderView *rv = BLI_findlink(&res->views, view_id); + if (rv) { + return rv->rect32; + } + return res->rect32; +} + + diff --git a/source/blender/render/intern/source/rendercore.c b/source/blender/render/intern/source/rendercore.c index 5b054005bac..99611a9c5a8 100644 --- a/source/blender/render/intern/source/rendercore.c +++ b/source/blender/render/intern/source/rendercore.c @@ -181,8 +181,10 @@ static void halo_pixelstruct(HaloRen *har, RenderLayer **rlpp, int totsample, in if (fullsample) { for (sample=0; sample<totsample; sample++) - if (ps->mask & (1 << sample)) - addalphaAddfacFloat(rlpp[sample]->rectf + od*4, col, har->add); + if (ps->mask & (1 << sample)) { + float *pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + addalphaAddfacFloat(pass + od*4, col, har->add); + } } else { fac= ((float)amountm)/(float)R.osa; @@ -214,8 +216,10 @@ static void halo_pixelstruct(HaloRen *har, RenderLayer **rlpp, int totsample, in if (fullsample) { for (sample=0; sample<totsample; sample++) - if (!(mask & (1 << sample))) - addalphaAddfacFloat(rlpp[sample]->rectf + od*4, col, har->add); + if (!(mask & (1 << sample))) { + float *pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + addalphaAddfacFloat(pass + od*4, col, har->add); + } } else { col[0]= accol[0]; @@ -223,8 +227,10 @@ static void halo_pixelstruct(HaloRen *har, RenderLayer **rlpp, int totsample, in col[2]= accol[2]; col[3]= accol[3]; - for (sample=0; sample<totsample; sample++) - addalphaAddfacFloat(rlpp[sample]->rectf + od*4, col, har->add); + for (sample=0; sample<totsample; sample++) { + float *pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + addalphaAddfacFloat(pass + od*4, col, har->add); + } } } @@ -305,8 +311,10 @@ static void halo_tile(RenderPart *pa, RenderLayer *rl) zz= calchalo_z(har, *rz); if ((zz> har->zs) || (har->mat && (har->mat->mode & MA_HALO_SOFT))) { if (shadeHaloFloat(har, col, zz, dist, xn, yn, har->flarec)) { - for (sample=0; sample<totsample; sample++) - addalphaAddfacFloat(rlpp[sample]->rectf + od*4, col, har->add); + for (sample=0; sample<totsample; sample++) { + float * rect= RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + addalphaAddfacFloat(rect + od*4, col, har->add); + } } } } @@ -359,7 +367,8 @@ static void lamphalo_tile(RenderPart *pa, RenderLayer *rl) if (fullsample) { for (sample=0; sample<totsample; sample++) { if (ps->mask & (1 << sample)) { - pass= rlpp[sample]->rectf + od*4; + pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + pass += od * 4; pass[0]+= col[0]; pass[1]+= col[1]; pass[2]+= col[2]; @@ -370,7 +379,8 @@ static void lamphalo_tile(RenderPart *pa, RenderLayer *rl) } else { fac= ((float)count)/(float)R.osa; - pass= rl->rectf + od*4; + pass = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, R.viewname); + pass += od * 4; pass[0]+= fac*col[0]; pass[1]+= fac*col[1]; pass[2]+= fac*col[2]; @@ -390,7 +400,9 @@ static void lamphalo_tile(RenderPart *pa, RenderLayer *rl) if (fullsample) { for (sample=0; sample<totsample; sample++) { if (!(mask & (1 << sample))) { - pass= rlpp[sample]->rectf + od*4; + + pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + pass += od * 4; pass[0]+= col[0]; pass[1]+= col[1]; pass[2]+= col[2]; @@ -401,7 +413,8 @@ static void lamphalo_tile(RenderPart *pa, RenderLayer *rl) } else { fac= ((float)R.osa-totsamp)/(float)R.osa; - pass= rl->rectf + od*4; + pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + pass += od * 4; pass[0]+= fac*col[0]; pass[1]+= fac*col[1]; pass[2]+= fac*col[2]; @@ -420,7 +433,8 @@ static void lamphalo_tile(RenderPart *pa, RenderLayer *rl) renderspothalo(&shi, col, 1.0f); for (sample=0; sample<totsample; sample++) { - pass= rlpp[sample]->rectf + od*4; + pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + pass += od * 4; pass[0]+= col[0]; pass[1]+= col[1]; pass[2]+= col[2]; @@ -444,14 +458,14 @@ static void add_filt_passes(RenderLayer *rl, int curmask, int rectx, int offset, { RenderPass *rpass; - /* combined rgb */ - add_filt_fmask(curmask, shr->combined, rl->rectf + 4*offset, rectx); - for (rpass= rl->passes.first; rpass; rpass= rpass->next) { float *fp, *col= NULL; int pixsize= 3; switch (rpass->passtype) { + case SCE_PASS_COMBINED: + add_filt_fmask(curmask, shr->combined, rpass->rect + 4*offset, rectx); + break; case SCE_PASS_Z: fp= rpass->rect + offset; *fp= shr->z; @@ -555,15 +569,16 @@ static void add_passes(RenderLayer *rl, int offset, ShadeInput *shi, ShadeResult { RenderPass *rpass; float *fp; - - fp= rl->rectf + 4*offset; - copy_v4_v4(fp, shr->combined); - + for (rpass= rl->passes.first; rpass; rpass= rpass->next) { float *col= NULL, uvcol[3]; int a, pixsize= 3; switch (rpass->passtype) { + case SCE_PASS_COMBINED: + /* copy combined to use for preview */ + copy_v4_v4(rpass->rect + 4*offset, shr->combined); + break; case SCE_PASS_Z: fp= rpass->rect + offset; *fp= shr->z; @@ -681,7 +696,8 @@ static void sky_tile(RenderPart *pa, RenderLayer *rl) bool done = false; for (sample= 0; sample<totsample; sample++) { - float *pass= rlpp[sample]->rectf + od; + float *pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + pass += od; if (pass[3]<1.0f) { @@ -742,8 +758,8 @@ static void atm_tile(RenderPart *pa, RenderLayer *rl) int sample; for (sample=0; sample<totsample; sample++) { - const float *zrect= RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_Z) + od; - float *rgbrect = rlpp[sample]->rectf + 4*od; + const float *zrect = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_Z, R.viewname) + od; + float *rgbrect = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname) + 4*od; float rgb[3] = {0}; bool done = false; @@ -978,8 +994,8 @@ static void clamp_alpha_rgb_range(RenderPart *pa, RenderLayer *rl) return; for (sample= 0; sample<totsample; sample++) { - float *rectf= rlpp[sample]->rectf; - + float *rectf = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname); + for (y= pa->rectx*pa->recty; y>0; y--, rectf+=4) { rectf[0] = MAX2(rectf[0], 0.0f); rectf[1] = MAX2(rectf[1], 0.0f); @@ -1060,7 +1076,7 @@ static void reset_sky_speed(RenderPart *pa, RenderLayer *rl) totsample= get_sample_layers(pa, rl, rlpp); for (sample= 0; sample<totsample; sample++) { - fp= RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_VECTOR); + fp= RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_VECTOR, R.viewname); if (fp==NULL) break; for (a= 4*pa->rectx*pa->recty - 1; a>=0; a--) @@ -1171,6 +1187,8 @@ void zbufshadeDA_tile(RenderPart *pa) pa->rectp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectp"); pa->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz"); for (rl= rr->layers.first; rl; rl= rl->next) { + float *rect = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, R.viewname); + if ((rl->layflag & SCE_LAY_ZMASK) && (rl->layflag & SCE_LAY_NEG_ZMASK)) pa->rectmask= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectmask"); @@ -1211,7 +1229,7 @@ void zbufshadeDA_tile(RenderPart *pa) if (R.flag & R_ZTRA || R.totstrand) { if (rl->layflag & (SCE_LAY_ZTRA|SCE_LAY_STRAND)) { if (pa->fullresult.first) { - zbuffer_transp_shade(pa, rl, rl->rectf, &psmlist); + zbuffer_transp_shade(pa, rl, rect, &psmlist); } else { unsigned short *ztramask, *solidmask= NULL; /* 16 bits, MAX_OSA */ @@ -1220,9 +1238,9 @@ void zbufshadeDA_tile(RenderPart *pa) rl->acolrect= MEM_callocN(4*sizeof(float)*pa->rectx*pa->recty, "alpha layer"); /* swap for live updates, and it is used in zbuf.c!!! */ - SWAP(float *, rl->acolrect, rl->rectf); - ztramask= zbuffer_transp_shade(pa, rl, rl->rectf, &psmlist); - SWAP(float *, rl->acolrect, rl->rectf); + SWAP(float *, rl->acolrect, rect); + ztramask = zbuffer_transp_shade(pa, rl, rect, &psmlist); + SWAP(float *, rl->acolrect, rect); /* zbuffer transp only returns ztramask if there's solid rendered */ if (ztramask) @@ -1231,7 +1249,8 @@ void zbufshadeDA_tile(RenderPart *pa) if (ztramask && solidmask) { unsigned short *sps= solidmask, *spz= ztramask; unsigned short fullmask= (1<<R.osa)-1; - float *fcol= rl->rectf; float *acol= rl->acolrect; + float *fcol= rect; + float *acol= rl->acolrect; int x; for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4, sps++, spz++) { @@ -1242,7 +1261,8 @@ void zbufshadeDA_tile(RenderPart *pa) } } else { - float *fcol= rl->rectf; float *acol= rl->acolrect; + float *fcol= rect; + float *acol= rl->acolrect; int x; for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4) { addAlphaOverFloat(fcol, acol); @@ -1265,7 +1285,7 @@ void zbufshadeDA_tile(RenderPart *pa) /* extra layers */ if (rl->layflag & SCE_LAY_EDGE) if (R.r.mode & R_EDGE) - edge_enhance_add(pa, rl->rectf, edgerect); + edge_enhance_add(pa, rect, edgerect); if (rl->passflag & SCE_PASS_VECTOR) reset_sky_speed(pa, rl); @@ -1319,6 +1339,7 @@ void zbufshade_tile(RenderPart *pa) pa->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz"); for (rl= rr->layers.first; rl; rl= rl->next) { + float *rect= RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, R.viewname); if ((rl->layflag & SCE_LAY_ZMASK) && (rl->layflag & SCE_LAY_NEG_ZMASK)) pa->rectmask= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectmask"); @@ -1342,7 +1363,7 @@ void zbufshade_tile(RenderPart *pa) rr->renlay= rl; if (rl->layflag & SCE_LAY_SOLID) { - const float *fcol= rl->rectf; + const float *fcol = rect; const int *ro= pa->recto, *rp= pa->rectp, *rz= pa->rectz; int x, y, offs=0, seed; @@ -1405,11 +1426,11 @@ void zbufshade_tile(RenderPart *pa) rl->acolrect= MEM_callocN(4*sizeof(float)*pa->rectx*pa->recty, "alpha layer"); /* swap for live updates */ - SWAP(float *, rl->acolrect, rl->rectf); - zbuffer_transp_shade(pa, rl, rl->rectf, NULL); - SWAP(float *, rl->acolrect, rl->rectf); + SWAP(float *, rl->acolrect, rect); + zbuffer_transp_shade(pa, rl, rect, NULL); + SWAP(float *, rl->acolrect, rect); - fcol= rl->rectf; acol= rl->acolrect; + fcol= rect; acol= rl->acolrect; for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4) { addAlphaOverFloat(fcol, acol); } @@ -1427,7 +1448,7 @@ void zbufshade_tile(RenderPart *pa) if (!R.test_break(R.tbh)) { if (rl->layflag & SCE_LAY_EDGE) if (R.r.mode & R_EDGE) - edge_enhance_add(pa, rl->rectf, edgerect); + edge_enhance_add(pa, rect, edgerect); } if (rl->passflag & SCE_PASS_VECTOR) @@ -1654,7 +1675,7 @@ void zbufshade_sss_tile(RenderPart *pa) return; } - fcol= rl->rectf; + fcol= RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, R.viewname); co= MEM_mallocN(sizeof(float)*3*handle.totps, "SSSCo"); color= MEM_mallocN(sizeof(float)*3*handle.totps, "SSSColor"); @@ -1938,6 +1959,7 @@ void add_halo_flare(Render *re) RenderLayer *rl; HaloRen *har; int a, mode; + float *rect; /* for now, we get the first renderlayer in list with halos set */ for (rl= rr->layers.first; rl; rl= rl->next) { @@ -1945,8 +1967,11 @@ void add_halo_flare(Render *re) if ((rl->layflag & SCE_LAY_HALO) == 0) continue; - if (rl->rectf==NULL) - continue; + + rect = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, re->viewname); + + if (rl==NULL || rect) + return; mode= R.r.mode; R.r.mode &= ~R_PANORAMA; @@ -1958,7 +1983,7 @@ void add_halo_flare(Render *re) if (har->flarec && (har->lay & rl->lay)) { do_draw = true; - renderflare(rr, rl->rectf, har); + renderflare(rr, rect, har); } } diff --git a/source/blender/render/intern/source/zbuf.c b/source/blender/render/intern/source/zbuf.c index d5d8595bf3f..b6628796e0d 100644 --- a/source/blender/render/intern/source/zbuf.c +++ b/source/blender/render/intern/source/zbuf.c @@ -3959,7 +3959,7 @@ static void reset_sky_speedvectors(RenderPart *pa, RenderLayer *rl, float *rectf float *fp, *col; int a; - fp= RE_RenderLayerGetPass(rl, SCE_PASS_VECTOR); + fp = RE_RenderLayerGetPass(rl, SCE_PASS_VECTOR, R.viewname); if (fp==NULL) return; col= rectf+3; @@ -4052,9 +4052,10 @@ unsigned short *zbuffer_transp_shade(RenderPart *pa, RenderLayer *rl, float *pas /* zero alpha pixels get speed vector max again */ if (addpassflag & SCE_PASS_VECTOR) - if (rl->layflag & SCE_LAY_SOLID) - reset_sky_speedvectors(pa, rl, rl->acolrect?rl->acolrect:rl->rectf); /* if acolrect is set we use it */ - + if (rl->layflag & SCE_LAY_SOLID) { + float *rect = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, R.viewname); + reset_sky_speedvectors(pa, rl, rl->acolrect ? rl->acolrect : rect); /* if acolrect is set we use it */ + } /* filtered render, for now we assume only 1 filter size */ if (pa->crop) { crop= 1; @@ -4239,8 +4240,9 @@ unsigned short *zbuffer_transp_shade(RenderPart *pa, RenderLayer *rl, float *pas alpha= samp_shr[a].combined[3]; if (alpha!=0.0f) { RenderLayer *rl= ssamp.rlpp[a]; - - addAlphaOverFloat(rl->rectf + 4*od, samp_shr[a].combined); + + float *rect = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, R.viewname); + addAlphaOverFloat(rect + 4 * od, samp_shr[a].combined); add_transp_passes(rl, od, &samp_shr[a], alpha); if (addpassflag & SCE_PASS_VECTOR) diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt index 78b5d499644..86f5fff4aef 100644 --- a/source/blender/windowmanager/CMakeLists.txt +++ b/source/blender/windowmanager/CMakeLists.txt @@ -64,6 +64,7 @@ set(SRC intern/wm_operators.c intern/wm_subwindow.c intern/wm_window.c + intern/wm_stereo.c WM_api.h WM_keymap.h diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index e64d08f38e1..dbeea80ba82 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -103,6 +103,7 @@ void WM_window_open_temp (struct bContext *C, struct rcti *position, int type); /* returns true if draw method is triple buffer */ bool WM_is_draw_triple(struct wmWindow *win); +bool WM_stereo3d_enabled(struct wmWindow *win, bool only_fullscreen_test); /* files */ diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c index 7440570f4a0..16fe9ca5142 100644 --- a/source/blender/windowmanager/intern/wm_draw.c +++ b/source/blender/windowmanager/intern/wm_draw.c @@ -48,9 +48,11 @@ #include "BIF_gl.h" #include "BKE_context.h" +#include "BKE_image.h" #include "GHOST_C-api.h" +#include "ED_node.h" #include "ED_view3d.h" #include "ED_screen.h" @@ -165,6 +167,7 @@ static void wm_method_draw_full(bContext *C, wmWindow *win) if (ar->swinid) { CTX_wm_region_set(C, ar); ED_region_do_draw(C, ar); + ar->do_draw = false; wm_paintcursor_draw(C, ar); CTX_wm_region_set(C, NULL); } @@ -175,12 +178,14 @@ static void wm_method_draw_full(bContext *C, wmWindow *win) } ED_screen_draw(win); + win->screen->do_draw = false; /* draw overlapping regions */ for (ar = screen->regionbase.first; ar; ar = ar->next) { if (ar->swinid) { CTX_wm_menu_set(C, ar); ED_region_do_draw(C, ar); + ar->do_draw = false; CTX_wm_menu_set(C, NULL); } } @@ -278,6 +283,7 @@ static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange) if (ar->do_draw) { CTX_wm_region_set(C, ar); ED_region_do_draw(C, ar); + ar->do_draw = false; wm_paintcursor_draw(C, ar); CTX_wm_region_set(C, NULL); @@ -288,6 +294,7 @@ static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange) if (ar->swap == WIN_FRONT_OK) { CTX_wm_region_set(C, ar); ED_region_do_draw(C, ar); + ar->do_draw = false; wm_paintcursor_draw(C, ar); CTX_wm_region_set(C, NULL); @@ -300,7 +307,7 @@ static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange) } } } - + wm_area_mark_invalid_backbuf(sa); CTX_wm_area_set(C, NULL); } @@ -308,6 +315,7 @@ static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange) /* after area regions so we can do area 'overlay' drawing */ if (screen->do_draw) { ED_screen_draw(win); + win->screen->do_draw = false; if (exchange) screen->swap = WIN_FRONT_OK; @@ -315,6 +323,7 @@ static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange) else if (exchange) { if (screen->swap == WIN_FRONT_OK) { ED_screen_draw(win); + win->screen->do_draw = false; screen->swap = WIN_BOTH_OK; } else if (screen->swap == WIN_BACK_OK) @@ -328,6 +337,7 @@ static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange) if (ar->swinid && ar->do_draw) { CTX_wm_menu_set(C, ar); ED_region_do_draw(C, ar); + ar->do_draw = false; CTX_wm_menu_set(C, NULL); } } @@ -360,15 +370,6 @@ static void wm_method_draw_damage(bContext *C, wmWindow *win) /* used. if not, multiple smaller ones are used, with */ /* worst case wasted space being 23.4% for 3x3 textures */ -#define MAX_N_TEX 3 - -typedef struct wmDrawTriple { - GLuint bind[MAX_N_TEX * MAX_N_TEX]; - int x[MAX_N_TEX], y[MAX_N_TEX]; - int nx, ny; - GLenum target; -} wmDrawTriple; - static void split_width(int x, int n, int *splitx, int *nx) { int a, newnx, waste; @@ -405,16 +406,13 @@ static void split_width(int x, int n, int *splitx, int *nx) } } -static void wm_draw_triple_free(wmWindow *win) +static void wm_draw_triple_free(wmDrawTriple *triple) { - if (win->drawdata) { - wmDrawTriple *triple = win->drawdata; + if (triple) { glDeleteTextures(triple->nx * triple->ny, triple->bind); MEM_freeN(triple); - - win->drawdata = NULL; } } @@ -499,7 +497,7 @@ static int wm_triple_gen_textures(wmWindow *win, wmDrawTriple *triple) return 1; } -static void wm_triple_draw_textures(wmWindow *win, wmDrawTriple *triple, float alpha) +void wm_triple_draw_textures(wmWindow *win, wmDrawTriple *triple, float alpha) { const int winsize_x = WM_window_pixels_x(win); const int winsize_y = WM_window_pixels_y(win); @@ -571,7 +569,7 @@ static void wm_triple_copy_textures(wmWindow *win, wmDrawTriple *triple) glBindTexture(triple->target, 0); } -static void wm_draw_region_blend(wmWindow *win, ARegion *ar) +static void wm_draw_region_blend(wmWindow *win, ARegion *ar, wmDrawTriple *triple) { float fac = ED_region_blend_factor(ar); @@ -580,7 +578,7 @@ static void wm_draw_region_blend(wmWindow *win, ARegion *ar) wmSubWindowScissorSet(win, win->screen->mainwin, &ar->winrct, true); glEnable(GL_BLEND); - wm_triple_draw_textures(win, win->drawdata, 1.0f - fac); + wm_triple_draw_textures(win, triple, 1.0f - fac); glDisable(GL_BLEND); } } @@ -589,29 +587,46 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win) { wmWindowManager *wm = CTX_wm_manager(C); wmDrawTriple *triple; + wmDrawData *dd, *dd_next, *drawdata = win->drawdata.first; bScreen *screen = win->screen; ScrArea *sa; ARegion *ar; - int copytex = 0; + int copytex = false; - if (win->drawdata) { + if (drawdata && drawdata->triple) { glClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); wmSubWindowSet(win, screen->mainwin); - wm_triple_draw_textures(win, win->drawdata, 1.0f); + wm_triple_draw_textures(win, drawdata->triple, 1.0f); } else { - win->drawdata = MEM_callocN(sizeof(wmDrawTriple), "wmDrawTriple"); + /* we run it when we start OR when we turn stereo on */ + if (drawdata == NULL) { + drawdata = MEM_callocN(sizeof(wmDrawData), "wmDrawData"); + BLI_addhead(&win->drawdata, drawdata); + } + + drawdata->triple = MEM_callocN(sizeof(wmDrawTriple), "wmDrawTriple"); - if (!wm_triple_gen_textures(win, win->drawdata)) { + if (!wm_triple_gen_textures(win, drawdata->triple)) { wm_draw_triple_fail(C, win); return; } } - triple = win->drawdata; + /* it means stereo was just turned off */ + /* note: we are removing all drawdatas that are not the first */ + for (dd = drawdata->next; dd; dd = dd_next) { + dd_next = dd->next; + + BLI_remlink(&win->drawdata, dd); + wm_draw_triple_free(dd->triple); + MEM_freeN(dd); + } + + triple = drawdata->triple; /* draw marked area regions */ for (sa = screen->areabase.first; sa; sa = sa->next) { @@ -619,16 +634,17 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win) for (ar = sa->regionbase.first; ar; ar = ar->next) { if (ar->swinid && ar->do_draw) { - - if (ar->overlap == 0) { + + if (ar->overlap == false) { CTX_wm_region_set(C, ar); ED_region_do_draw(C, ar); + ar->do_draw = false; CTX_wm_region_set(C, NULL); - copytex = 1; + copytex = true; } } } - + wm_area_mark_invalid_backbuf(sa); CTX_wm_area_set(C, NULL); } @@ -662,14 +678,15 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win) /* draw overlapping area regions (always like popups) */ for (sa = screen->areabase.first; sa; sa = sa->next) { CTX_wm_area_set(C, sa); - + for (ar = sa->regionbase.first; ar; ar = ar->next) { if (ar->swinid && ar->overlap) { CTX_wm_region_set(C, ar); ED_region_do_draw(C, ar); + ar->do_draw = false; CTX_wm_region_set(C, NULL); - - wm_draw_region_blend(win, ar); + + wm_draw_region_blend(win, ar, triple); } } @@ -678,12 +695,14 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win) /* after area regions so we can do area 'overlay' drawing */ ED_screen_draw(win); + win->screen->do_draw = false; /* draw floating regions (menus) */ for (ar = screen->regionbase.first; ar; ar = ar->next) { if (ar->swinid) { CTX_wm_menu_set(C, ar); ED_region_do_draw(C, ar); + ar->do_draw = false; CTX_wm_menu_set(C, NULL); } } @@ -691,13 +710,188 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win) /* always draw, not only when screen tagged */ if (win->gesture.first) wm_gesture_draw(win); - + /* needs pixel coords in screen */ if (wm->drags.first) { wm_drags_draw(C, win, NULL); } } +static void wm_method_draw_triple_multiview(bContext *C, wmWindow *win, StereoViews sview) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wmDrawData *drawdata; + wmDrawTriple *triple_data, *triple_all; + bScreen *screen = win->screen; + ScrArea *sa; + ARegion *ar; + int copytex = false; + int id; + + /* we store the triple_data in sequence to triple_all */ + for (id = 0; id < 2; id++) { + drawdata = BLI_findlink(&win->drawdata, (sview * 2) + id); + + if (drawdata && drawdata->triple) { + if (id == 0) { + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + wmSubWindowSet(win, screen->mainwin); + + wm_triple_draw_textures(win, drawdata->triple, 1.0f); + } + } + else { + /* we run it when we start OR when we turn stereo on */ + if (drawdata == NULL) { + drawdata = MEM_callocN(sizeof(wmDrawData), "wmDrawData"); + BLI_addtail(&win->drawdata, drawdata); + } + + drawdata->triple = MEM_callocN(sizeof(wmDrawTriple), "wmDrawTriple"); + + if (!wm_triple_gen_textures(win, drawdata->triple)) { + wm_draw_triple_fail(C, win); + return; + } + } + } + + triple_data = ((wmDrawData *) BLI_findlink(&win->drawdata, sview * 2))->triple; + triple_all = ((wmDrawData *) BLI_findlink(&win->drawdata, (sview * 2) + 1))->triple; + + /* draw marked area regions */ + for (sa = screen->areabase.first; sa; sa = sa->next) { + CTX_wm_area_set(C, sa); + + switch (sa->spacetype) { + case SPACE_IMAGE: + { + SpaceImage *sima = sa->spacedata.first; + sima->iuser.multiview_eye = sview; + break; + } + case SPACE_VIEW3D: + { + View3D *v3d = sa->spacedata.first; + BGpic *bgpic = v3d->bgpicbase.first; + v3d->multiview_eye = sview; + if (bgpic) bgpic->iuser.multiview_eye = sview; + break; + } + case SPACE_NODE: + { + SpaceNode *snode = sa->spacedata.first; + if ((snode->flag & SNODE_BACKDRAW) && ED_node_is_compositor(snode)) { + Image *ima = BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node"); + ima->eye = sview; + } + break; + } + case SPACE_SEQ: + { + SpaceSeq *sseq = sa->spacedata.first; + sseq->multiview_eye = sview; + break; + } + } + + /* draw marked area regions */ + for (ar = sa->regionbase.first; ar; ar = ar->next) { + if (ar->swinid && ar->do_draw) { + + if (ar->overlap == false) { + CTX_wm_region_set(C, ar); + ED_region_do_draw(C, ar); + + if (sview == STEREO_RIGHT_ID) + ar->do_draw = false; + + CTX_wm_region_set(C, NULL); + copytex = true; + } + } + } + + wm_area_mark_invalid_backbuf(sa); + CTX_wm_area_set(C, NULL); + } + + if (copytex) { + wmSubWindowSet(win, screen->mainwin); + + wm_triple_copy_textures(win, triple_data); + } + + if (wm->paintcursors.first) { + for (sa = screen->areabase.first; sa; sa = sa->next) { + for (ar = sa->regionbase.first; ar; ar = ar->next) { + if (ar->swinid && ar->swinid == screen->subwinactive) { + CTX_wm_area_set(C, sa); + CTX_wm_region_set(C, ar); + + /* make region ready for draw, scissor, pixelspace */ + ED_region_set(C, ar); + wm_paintcursor_draw(C, ar); + + CTX_wm_region_set(C, NULL); + CTX_wm_area_set(C, NULL); + } + } + } + + wmSubWindowSet(win, screen->mainwin); + } + + /* draw overlapping area regions (always like popups) */ + for (sa = screen->areabase.first; sa; sa = sa->next) { + CTX_wm_area_set(C, sa); + + for (ar = sa->regionbase.first; ar; ar = ar->next) { + if (ar->swinid && ar->overlap) { + CTX_wm_region_set(C, ar); + ED_region_do_draw(C, ar); + if (sview == STEREO_RIGHT_ID) + ar->do_draw = false; + CTX_wm_region_set(C, NULL); + + wm_draw_region_blend(win, ar, triple_data); + } + } + + CTX_wm_area_set(C, NULL); + } + + /* after area regions so we can do area 'overlay' drawing */ + ED_screen_draw(win); + if (sview == STEREO_RIGHT_ID) + win->screen->do_draw = false; + + /* draw floating regions (menus) */ + for (ar = screen->regionbase.first; ar; ar = ar->next) { + if (ar->swinid) { + CTX_wm_menu_set(C, ar); + ED_region_do_draw(C, ar); + if (sview == STEREO_RIGHT_ID) + ar->do_draw = false; + CTX_wm_menu_set(C, NULL); + } + } + + /* always draw, not only when screen tagged */ + if (win->gesture.first) + wm_gesture_draw(win); + + /* needs pixel coords in screen */ + if (wm->drags.first) { + wm_drags_draw(C, win, NULL); + } + + /* copy the ui + overlays */ + wmSubWindowSet(win, screen->mainwin); + wm_triple_copy_textures(win, triple_all); +} /****************** main update call **********************/ @@ -848,8 +1042,16 @@ void wm_draw_update(bContext *C) wm_method_draw_overlap_all(C, win, 0); else if (drawmethod == USER_DRAW_OVERLAP_FLIP) wm_method_draw_overlap_all(C, win, 1); - else // if (drawmethod == USER_DRAW_TRIPLE) - wm_method_draw_triple(C, win); + else { /* USER_DRAW_TRIPLE */ + if ((WM_stereo3d_enabled(win, false)) == false) { + wm_method_draw_triple(C, win); + } + else { + wm_method_draw_triple_multiview(C, win, STEREO_LEFT_ID); + wm_method_draw_triple_multiview(C, win, STEREO_RIGHT_ID); + wm_method_draw_stereo3d(C, win); + } + } win->screen->do_draw_gesture = false; win->screen->do_draw_paintcursor = false; @@ -862,15 +1064,23 @@ void wm_draw_update(bContext *C) } } +void wm_draw_data_free(wmWindow *win) +{ + wmDrawData *dd; + + for (dd = win->drawdata.first; dd; dd = dd->next) { + wm_draw_triple_free(dd->triple); + } + BLI_freelistN(&win->drawdata); +} + void wm_draw_window_clear(wmWindow *win) { bScreen *screen = win->screen; ScrArea *sa; ARegion *ar; - int drawmethod = wm_automatic_draw_method(win); - if (drawmethod == USER_DRAW_TRIPLE) - wm_draw_triple_free(win); + wm_draw_data_free(win); /* clear screen swap flags */ if (screen) { diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 3fd1fe427c9..f3e9c416e7b 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -846,11 +846,11 @@ static ImBuf *blend_file_thumb(Scene *scene, bScreen *screen, int **thumb_pt) if (scene->camera) { ibuf = ED_view3d_draw_offscreen_imbuf_simple(scene, scene->camera, BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2, - IB_rect, OB_SOLID, false, false, false, R_ALPHAPREMUL, err_out); + IB_rect, OB_SOLID, false, false, false, R_ALPHAPREMUL, NULL, err_out); } else { ibuf = ED_view3d_draw_offscreen_imbuf(scene, v3d, ar, BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2, - IB_rect, false, R_ALPHAPREMUL, err_out); + IB_rect, false, R_ALPHAPREMUL, NULL, err_out); } if (ibuf) { diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c index c2cd96165f3..499adf5704d 100644 --- a/source/blender/windowmanager/intern/wm_operators.c +++ b/source/blender/windowmanager/intern/wm_operators.c @@ -1268,6 +1268,13 @@ void WM_operator_properties_filesel(wmOperatorType *ot, int filter, short type, if (flag & WM_FILESEL_RELPATH) RNA_def_boolean(ot->srna, "relative_path", true, "Relative Path", "Select the file relative to the blend file"); + if ((filter & FILE_TYPE_IMAGE) || (filter & FILE_TYPE_MOVIE)) { + prop = RNA_def_boolean(ot->srna, "show_multiview", 0, "Enable Multi-View", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "use_multiview", 0, "Use Multi-View", ""); + RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); + } + prop = RNA_def_enum(ot->srna, "display_type", file_display_items, display, "Display Type", ""); RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE); } @@ -4592,8 +4599,10 @@ static int redraw_timer_exec(bContext *C, wmOperator *op) for (a = 0; a < iter; a++) { if (type == 0) { - if (ar) + if (ar) { ED_region_do_draw(C, ar); + ar->do_draw = false; + } } else if (type == 1) { wmWindow *win = CTX_wm_window(C); @@ -4621,6 +4630,7 @@ static int redraw_timer_exec(bContext *C, wmOperator *op) if (ar_iter->swinid) { CTX_wm_region_set(C, ar_iter); ED_region_do_draw(C, ar_iter); + ar->do_draw = false; } } } @@ -4832,6 +4842,36 @@ static void operatortype_ghash_free_cb(wmOperatorType *ot) } /* ******************************************************* */ +/* toggle 3D for current window, turning it fullscreen if needed */ +static void WM_OT_stereo3d_set(wmOperatorType *ot) +{ + PropertyRNA *prop; + + ot->name = "Set Stereo 3D"; + ot->idname = "WM_OT_set_stereo_3d"; + ot->description = "Toggle 3D stereo support for current window (or change the display mode)"; + + ot->exec = wm_stereo3d_set_exec; + ot->invoke = wm_stereo3d_set_invoke; + ot->poll = WM_operator_winactive; + ot->ui = wm_stereo3d_set_draw; + ot->cancel = wm_stereo3d_set_cancel; + + prop = RNA_def_enum(ot->srna, "display_mode", stereo3d_display_items, S3D_DISPLAY_ANAGLYPH, "Display Mode", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "anaglyph_type", stereo3d_anaglyph_type_items, S3D_ANAGLYPH_REDCYAN, "Anaglyph Type", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_enum(ot->srna, "interlace_type", stereo3d_interlace_type_items, S3D_INTERLACE_ROW, "Interlace Type", ""); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "use_interlace_swap", false, "Swap Left/Right", + "Swap left and right stereo channels"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + prop = RNA_def_boolean(ot->srna, "use_sidebyside_crosseyed", false, "Cross-Eyed", + "Right eye should see left image and vice-versa"); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); +} + +/* ******************************************************* */ /* called on initialize WM_exit() */ void wm_operatortype_free(void) { @@ -4873,6 +4913,7 @@ void wm_operatortype_init(void) WM_operatortype_append(WM_OT_call_menu); WM_operatortype_append(WM_OT_call_menu_pie); WM_operatortype_append(WM_OT_radial_control); + WM_operatortype_append(WM_OT_stereo3d_set); #if defined(WIN32) WM_operatortype_append(WM_OT_console_toggle); #endif diff --git a/source/blender/windowmanager/intern/wm_stereo.c b/source/blender/windowmanager/intern/wm_stereo.c new file mode 100644 index 00000000000..5d7b11272c6 --- /dev/null +++ b/source/blender/windowmanager/intern/wm_stereo.c @@ -0,0 +1,534 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2015 by Blender Foundation + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): Dalai Felinto + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/windowmanager/intern/wm_stereo.c + * \ingroup wm + */ + + +#include <stdlib.h> +#include <string.h> + +#include "DNA_listBase.h" + +#include "RNA_access.h" + +#include "MEM_guardedalloc.h" + +#include "BLI_listbase.h" +#include "BLI_rect.h" +#include "BLI_utildefines.h" + +#include "BIF_gl.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_report.h" + +#include "GHOST_C-api.h" + +#include "ED_screen.h" + +#include "GPU_glew.h" + +#include "WM_api.h" +#include "WM_types.h" +#include "wm.h" +#include "wm_draw.h" /* wmDrawTriple */ +#include "wm_window.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +static void wm_method_draw_stereo3d_pageflip(wmWindow *win) +{ + wmDrawData *drawdata; + int view; + + for (view = 0; view < 2; view ++) { + drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1); + + if (view == STEREO_LEFT_ID) + glDrawBuffer(GL_BACK_LEFT); + else //STEREO_RIGHT_ID + glDrawBuffer(GL_BACK_RIGHT); + + wm_triple_draw_textures(win, drawdata->triple, 1.0f); + } +} + +static GLuint left_interlace_mask[32]; +static GLuint right_interlace_mask[32]; +static enum eStereo3dInterlaceType interlace_prev_type = -1; +static char interlace_prev_swap = -1; + +static void wm_interlace_masks_create(wmWindow *win) +{ + GLuint pattern; + char i; + bool swap = (win->stereo3d_format->flag & S3D_INTERLACE_SWAP) != 0; + enum eStereo3dInterlaceType interlace_type = win->stereo3d_format->interlace_type; + + if (interlace_prev_type == interlace_type && interlace_prev_swap == swap) + return; + + switch (interlace_type) { + case S3D_INTERLACE_ROW: + pattern = 0x00000000; + pattern = swap ? ~pattern : pattern; + for (i = 0; i < 32; i += 2) { + left_interlace_mask[i] = pattern; + right_interlace_mask[i] = ~pattern; + } + for (i = 1; i < 32; i += 2) { + left_interlace_mask[i] = ~pattern; + right_interlace_mask[i] = pattern; + } + break; + case S3D_INTERLACE_COLUMN: + pattern = 0x55555555; + pattern = swap ? ~pattern : pattern; + for (i = 0; i < 32; i++) { + left_interlace_mask[i] = pattern; + right_interlace_mask[i] = ~pattern; + } + break; + case S3D_INTERLACE_CHECKERBOARD: + default: + pattern = 0x55555555; + pattern = swap ? ~pattern : pattern; + for (i = 0; i < 32; i += 2) { + left_interlace_mask[i] = pattern; + right_interlace_mask[i] = ~pattern; + } + for (i = 1; i < 32; i += 2) { + left_interlace_mask[i] = ~pattern; + right_interlace_mask[i] = pattern; + } + break; + } + interlace_prev_type = interlace_type; + interlace_prev_swap = swap; +} + +static void wm_method_draw_stereo3d_interlace(wmWindow *win) +{ + wmDrawData *drawdata; + int view; + + wm_interlace_masks_create(win); + + for (view = 0; view < 2; view ++) { + drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1); + + glEnable(GL_POLYGON_STIPPLE); + glPolygonStipple(view ? (GLubyte *) right_interlace_mask : (GLubyte *) left_interlace_mask); + + wm_triple_draw_textures(win, drawdata->triple, 1.0f); + glDisable(GL_POLYGON_STIPPLE); + } +} + +static void wm_method_draw_stereo3d_anaglyph(wmWindow *win) +{ + wmDrawData *drawdata; + int view, bit; + + for (view = 0; view < 2; view ++) { + drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1); + + bit = view + 1; + switch (win->stereo3d_format->anaglyph_type) { + case S3D_ANAGLYPH_REDCYAN: + glColorMask((1&bit) ? GL_TRUE : GL_FALSE, + (2&bit) ? GL_TRUE : GL_FALSE, + (2&bit) ? GL_TRUE : GL_FALSE, + GL_FALSE); + break; + case S3D_ANAGLYPH_GREENMAGENTA: + glColorMask((2&bit) ? GL_TRUE : GL_FALSE, + (1&bit) ? GL_TRUE : GL_FALSE, + (2&bit) ? GL_TRUE : GL_FALSE, + GL_FALSE); + break; + case S3D_ANAGLYPH_YELLOWBLUE: + glColorMask((1&bit) ? GL_TRUE : GL_FALSE, + (1&bit) ? GL_TRUE : GL_FALSE, + (2&bit) ? GL_TRUE : GL_FALSE, + GL_FALSE); + break; + } + + wm_triple_draw_textures(win, drawdata->triple, 1.0f); + + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } +} + +static void wm_method_draw_stereo3d_sidebyside(wmWindow *win) +{ + wmDrawData *drawdata; + wmDrawTriple *triple; + float halfx, halfy, ratiox, ratioy; + int x, y, offx, offy; + float alpha = 1.0f; + int view; + int soffx; + bool cross_eyed = (win->stereo3d_format->flag & S3D_SIDEBYSIDE_CROSSEYED) != 0; + + for (view = 0; view < 2; view ++) { + drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1); + triple = drawdata->triple; + + soffx = WM_window_pixels_x(win) * 0.5f; + if (view == STEREO_LEFT_ID) { + if (!cross_eyed) + soffx = 0; + } + else { //RIGHT_LEFT_ID + if (cross_eyed) + soffx = 0; + } + + glEnable(triple->target); + + for (y = 0, offy = 0; y < triple->ny; offy += triple->y[y], y++) { + for (x = 0, offx = 0; x < triple->nx; offx += triple->x[x], x++) { + const int sizex = triple->x[x]; + const int sizey = triple->y[y]; + + /* wmOrtho for the screen has this same offset */ + ratiox = sizex; + ratioy = sizey; + halfx = GLA_PIXEL_OFS; + halfy = GLA_PIXEL_OFS; + + /* texture rectangle has unnormalized coordinates */ + if (triple->target == GL_TEXTURE_2D) { + ratiox /= triple->x[x]; + ratioy /= triple->y[y]; + halfx /= triple->x[x]; + halfy /= triple->y[y]; + } + + glBindTexture(triple->target, triple->bind[x + y * triple->nx]); + + glColor4f(1.0f, 1.0f, 1.0f, alpha); + glBegin(GL_QUADS); + glTexCoord2f(halfx, halfy); + glVertex2f(soffx + (offx * 0.5f), offy); + + glTexCoord2f(ratiox + halfx, halfy); + glVertex2f(soffx + ((offx + sizex) * 0.5f), offy); + + glTexCoord2f(ratiox + halfx, ratioy + halfy); + glVertex2f(soffx + ((offx + sizex) * 0.5f), offy + sizey); + + glTexCoord2f(halfx, ratioy + halfy); + glVertex2f(soffx + (offx * 0.5f), offy + sizey); + glEnd(); + } + } + + glBindTexture(triple->target, 0); + glDisable(triple->target); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } +} + +static void wm_method_draw_stereo3d_topbottom(wmWindow *win) +{ + wmDrawData *drawdata; + wmDrawTriple *triple; + float halfx, halfy, ratiox, ratioy; + int x, y, offx, offy; + float alpha = 1.0f; + int view; + int soffy; + + for (view = 0; view < 2; view ++) { + drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1); + triple = drawdata->triple; + + if (view == STEREO_LEFT_ID) { + soffy = WM_window_pixels_y(win) * 0.5f; + } + else { /* STEREO_RIGHT_ID */ + soffy = 0; + } + + glEnable(triple->target); + + for (y = 0, offy = 0; y < triple->ny; offy += triple->y[y], y++) { + for (x = 0, offx = 0; x < triple->nx; offx += triple->x[x], x++) { + const int sizex = triple->x[x]; + const int sizey = triple->y[y]; + + /* wmOrtho for the screen has this same offset */ + ratiox = sizex; + ratioy = sizey; + halfx = GLA_PIXEL_OFS; + halfy = GLA_PIXEL_OFS; + + /* texture rectangle has unnormalized coordinates */ + if (triple->target == GL_TEXTURE_2D) { + ratiox /= triple->x[x]; + ratioy /= triple->y[y]; + halfx /= triple->x[x]; + halfy /= triple->y[y]; + } + + glBindTexture(triple->target, triple->bind[x + y * triple->nx]); + + glColor4f(1.0f, 1.0f, 1.0f, alpha); + glBegin(GL_QUADS); + glTexCoord2f(halfx, halfy); + glVertex2f(offx, soffy + (offy * 0.5f)); + + glTexCoord2f(ratiox + halfx, halfy); + glVertex2f(offx + sizex, soffy + (offy * 0.5f)); + + glTexCoord2f(ratiox + halfx, ratioy + halfy); + glVertex2f(offx + sizex, soffy + ((offy + sizey) * 0.5f)); + + glTexCoord2f(halfx, ratioy + halfy); + glVertex2f(offx, soffy + ((offy + sizey) * 0.5f)); + glEnd(); + } + } + + glBindTexture(triple->target, 0); + glDisable(triple->target); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + } +} + +void wm_method_draw_stereo3d(const bContext *UNUSED(C), wmWindow *win) +{ + switch (win->stereo3d_format->display_mode) { + case S3D_DISPLAY_ANAGLYPH: + wm_method_draw_stereo3d_anaglyph(win); + break; + case S3D_DISPLAY_INTERLACE: + wm_method_draw_stereo3d_interlace(win); + break; + case S3D_DISPLAY_PAGEFLIP: + wm_method_draw_stereo3d_pageflip(win); + break; + case S3D_DISPLAY_SIDEBYSIDE: + wm_method_draw_stereo3d_sidebyside(win); + break; + case S3D_DISPLAY_TOPBOTTOM: + wm_method_draw_stereo3d_topbottom(win); + break; + default: + break; + } +} + +static bool wm_stereo3d_is_fullscreen_required(eStereoDisplayMode stereo_display) +{ + return ELEM(stereo_display, + S3D_DISPLAY_SIDEBYSIDE, + S3D_DISPLAY_TOPBOTTOM, + S3D_DISPLAY_PAGEFLIP); +} + +bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check) +{ + bScreen *screen = win->screen; + + if ((skip_stereo3d_check == false) && (ED_screen_stereo3d_required(screen) == false)) + return false; + + if (wm_stereo3d_is_fullscreen_required(win->stereo3d_format->display_mode)) + return WM_window_is_fullscreen(win); + + return true; +} + +/************************** Stereo 3D operator **********************************/ +typedef struct Stereo3dData { + Stereo3dFormat stereo3d_format; +} Stereo3dData; + +static bool wm_stereo3d_set_properties(bContext *C, wmOperator *op) +{ + wmWindow *win = CTX_wm_window(C); + Stereo3dFormat *s3d = win->stereo3d_format; + PropertyRNA *prop; + bool is_set = false; + + prop = RNA_struct_find_property(op->ptr, "display_mode"); + if (RNA_property_is_set(op->ptr, prop)) { + s3d->display_mode = RNA_property_enum_get(op->ptr, prop); + is_set = true; + } + + prop = RNA_struct_find_property(op->ptr, "anaglyph_type"); + if (RNA_property_is_set(op->ptr, prop)) { + s3d->anaglyph_type = RNA_property_enum_get(op->ptr, prop); + is_set = true; + } + + prop = RNA_struct_find_property(op->ptr, "interlace_type"); + if (RNA_property_is_set(op->ptr, prop)) { + s3d->interlace_type = RNA_property_enum_get(op->ptr, prop); + is_set = true; + } + + prop = RNA_struct_find_property(op->ptr, "use_interlace_swap"); + if (RNA_property_is_set(op->ptr, prop)) { + if (RNA_property_boolean_get(op->ptr, prop)) + s3d->flag |= S3D_INTERLACE_SWAP; + else + s3d->flag &= ~S3D_INTERLACE_SWAP; + is_set = true; + } + + prop = RNA_struct_find_property(op->ptr, "use_sidebyside_crosseyed"); + if (RNA_property_is_set(op->ptr, prop)) { + if (RNA_property_boolean_get(op->ptr, prop)) + s3d->flag |= S3D_SIDEBYSIDE_CROSSEYED; + else + s3d->flag &= ~S3D_SIDEBYSIDE_CROSSEYED; + is_set = true; + } + + return is_set; +} + +static void wm_stereo3d_set_init(bContext *C, wmOperator *op) +{ + Stereo3dData *s3dd; + wmWindow *win = CTX_wm_window(C); + + op->customdata = s3dd = MEM_callocN(sizeof(Stereo3dData), __func__); + + /* store the original win stereo 3d settings in case of cancel */ + s3dd->stereo3d_format = *win->stereo3d_format; +} + +int wm_stereo3d_set_exec(bContext *C, wmOperator *op) +{ + wmWindowManager *wm = CTX_wm_manager(C); + wmWindow *win = CTX_wm_window(C); + const bool is_fullscreen = WM_window_is_fullscreen(win); + + if (G.background) + return OPERATOR_CANCELLED; + + /* pagelfip requires a new window to be created with the proper OS flags */ + if (win->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) { + if (wm_window_duplicate_exec(C, op) == OPERATOR_FINISHED) { + wm_window_close(C, wm, win); + win = wm->windows.last; + } + else { + BKE_reportf(op->reports, RPT_ERROR, + "Fail to create a window compatible with time sequential (page-flip) display method"); + return OPERATOR_CANCELLED; + } + } + + if (wm_stereo3d_is_fullscreen_required(win->stereo3d_format->display_mode)) { + if (!is_fullscreen) { + wm_window_fullscreen_toggle_exec(C, op); + } + } + + if (op->customdata) { + MEM_freeN(op->customdata); + } + + WM_event_add_notifier(C, NC_WINDOW, NULL); + return OPERATOR_FINISHED; +} + +int wm_stereo3d_set_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + wm_stereo3d_set_init(C, op); + + if (wm_stereo3d_set_properties(C, op)) + return wm_stereo3d_set_exec(C, op); + else + return WM_operator_props_dialog_popup(C, op, 250, 100); +} + +void wm_stereo3d_set_draw(bContext *C, wmOperator *op) +{ + wmWindow *win = CTX_wm_window(C); + Stereo3dFormat *stereo3d_format; + PointerRNA stereo3d_format_ptr; + uiLayout *layout = op->layout; + uiLayout *col; + + stereo3d_format = win->stereo3d_format; + RNA_pointer_create(NULL, &RNA_Stereo3dDisplay, stereo3d_format, &stereo3d_format_ptr); + + 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_PAGEFLIP: + case S3D_DISPLAY_TOPBOTTOM: + default: + { + break; + } + } +} + +void wm_stereo3d_set_cancel(bContext *C, wmOperator *op) +{ + Stereo3dData *s3dd = op->customdata; + wmWindow *win = CTX_wm_window(C); + + /* roll back to the original */ + if (win) { + *win->stereo3d_format = s3dd->stereo3d_format; + } + + MEM_freeN(op->customdata); + op->customdata = NULL; +} diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c index 1af20e6036d..4b98f8bf71d 100644 --- a/source/blender/windowmanager/intern/wm_window.c +++ b/source/blender/windowmanager/intern/wm_window.c @@ -206,12 +206,13 @@ void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win) wm_event_free_all(win); wm_subwindows_free(win); - - if (win->drawdata) - MEM_freeN(win->drawdata); - + + wm_draw_data_free(win); + wm_ghostwindow_destroy(win); - + + MEM_freeN(win->stereo3d_format); + MEM_freeN(win); } @@ -236,6 +237,8 @@ wmWindow *wm_window_new(bContext *C) BLI_addtail(&wm->windows, win); win->winid = find_free_winid(wm); + win->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Stereo 3D Format (window)"); + return win; } @@ -259,8 +262,11 @@ wmWindow *wm_window_copy(bContext *C, wmWindow *winorig) win->screen->do_draw = true; win->drawmethod = U.wmdrawmethod; - win->drawdata = NULL; - + + BLI_listbase_clear(&win->drawdata); + + *win->stereo3d_format = *winorig->stereo3d_format; + return win; } @@ -367,6 +373,10 @@ static void wm_window_add_ghostwindow(wmWindowManager *wm, const char *title, wm glSettings.numOfAASamples = multisamples; + /* a new window is created when pageflip mode is required for a window */ + if (win->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) + glSettings.flags |= GHOST_glStereoVisual; + if (!(U.uiflag2 & USER_OPENGL_NO_WARN_SUPPORT)) glSettings.flags |= GHOST_glWarnSupport; @@ -519,8 +529,7 @@ wmWindow *WM_window_open(bContext *C, const rcti *rect) win->sizey = BLI_rcti_size_y(rect); win->drawmethod = U.wmdrawmethod; - win->drawdata = NULL; - + WM_check(C); return win; diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h index de04129a0ef..bc777b74b51 100644 --- a/source/blender/windowmanager/wm.h +++ b/source/blender/windowmanager/wm.h @@ -76,6 +76,13 @@ void wm_autosave_delete(void); void wm_autosave_read(bContext *C, struct ReportList *reports); void wm_autosave_location(char *filepath); +/* wm_stereo.c */ +void wm_method_draw_stereo3d(const bContext *C, wmWindow *win); +int wm_stereo3d_set_exec(bContext *C, wmOperator *op); +int wm_stereo3d_set_invoke(bContext *C, wmOperator *op, const wmEvent *event); +void wm_stereo3d_set_draw(bContext *C, wmOperator *op); +void wm_stereo3d_set_cancel(bContext *C, wmOperator *op); + /* init operator properties */ void wm_open_init_load_ui(wmOperator *op, bool use_prefs); void wm_open_init_use_scripts(wmOperator *op, bool use_prefs); diff --git a/source/blender/windowmanager/wm_draw.h b/source/blender/windowmanager/wm_draw.h index 3d72fe17c79..5dc52b2e4fb 100644 --- a/source/blender/windowmanager/wm_draw.h +++ b/source/blender/windowmanager/wm_draw.h @@ -32,6 +32,23 @@ #ifndef __WM_DRAW_H__ #define __WM_DRAW_H__ +#include "GPU_glew.h" + + +#define MAX_N_TEX 3 + +typedef struct wmDrawTriple { + GLuint bind[MAX_N_TEX * MAX_N_TEX]; + int x[MAX_N_TEX], y[MAX_N_TEX]; + int nx, ny; + GLenum target; +} wmDrawTriple; + +typedef struct wmDrawData { + struct wmDrawData *next, *prev; + wmDrawTriple *triple; +} wmDrawData; + struct bContext; struct wmWindow; struct ARegion; @@ -43,5 +60,9 @@ void wm_draw_region_clear (struct wmWindow *win, struct ARegion *ar); void wm_tag_redraw_overlay (struct wmWindow *win, struct ARegion *ar); +void wm_triple_draw_textures (struct wmWindow *win, struct wmDrawTriple *triple, float alpha); + +void wm_draw_data_free (struct wmWindow *win); + #endif /* __WM_DRAW_H__ */ diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c index 893eae8485a..8fbc3c5be9c 100644 --- a/source/blenderplayer/bad_level_call_stubs/stubs.c +++ b/source/blenderplayer/bad_level_call_stubs/stubs.c @@ -221,12 +221,20 @@ void EDBM_mesh_normals_update(struct BMEditMesh *em) RET_NONE void *g_system; bool EDBM_mtexpoly_check(struct BMEditMesh *em) RET_ZERO -float *RE_RenderLayerGetPass(struct RenderLayer *rl, int passtype) RET_NULL +float *RE_RenderLayerGetPass(volatile struct RenderLayer *rl, int passtype, const char *viewname) RET_NULL float RE_filter_value(int type, float x) RET_ZERO struct RenderLayer *RE_GetRenderLayer(struct RenderResult *rr, const char *name) RET_NULL void RE_init_texture_rng() RET_NONE void RE_exit_texture_rng() RET_NONE +float *RE_RenderViewGetRectf(struct RenderResult *rr, int view_id) {STUB_ASSERT(0); return (float *) NULL;} +float *RE_RenderViewGetRectz(struct RenderResult *rr, int view_id) {STUB_ASSERT(0); return (float *) NULL;} +bool RE_layers_have_name(struct RenderResult *result) {STUB_ASSERT(0); return 0;} +void RE_engine_active_view_set(struct RenderEngine *engine, const char *viewname) {STUB_ASSERT(0);} +void RE_engine_get_camera_model_matrix(struct RenderEngine *engine, struct Object *camera, float *r_modelmat) {STUB_ASSERT(0);} +float RE_engine_get_camera_shift_x(struct RenderEngine *engine, struct Object *camera) RET_ZERO +void RE_SetActiveRenderView(struct Render *re, const char *viewname) {STUB_ASSERT(0);} + /* zbuf.c stub */ void antialias_tagbuf(int xsize, int ysize, char *rectmove) RET_NONE void RE_zbuf_accumulate_vecblur(struct NodeBlurData *nbd, int xsize, int ysize, float *newrect, float *imgrect, float *vecbufrect, float *zbufrect) RET_NONE @@ -250,6 +258,7 @@ void RE_sample_material_color(struct Material *mat, float color[3], float *alpha /* nodes */ struct Render *RE_GetRender(const char *name) RET_NULL +struct Object *RE_GetCamera(struct Render *re) RET_NULL float RE_lamp_get_data(struct ShadeInput *shi, struct Object *lamp_obj, float col[4], float lv[3], float *dist, float shadow[4]) RET_ZERO /* blenkernel */ @@ -584,9 +593,10 @@ void uiTemplateColormanagedViewSettings(struct uiLayout *layout, struct bContext void uiTemplateComponentMenu(struct uiLayout *layout, struct PointerRNA *ptr, const char *propname, const char *name) RET_NONE void uiTemplateNodeSocket(struct uiLayout *layout, struct bContext *C, float *color) RET_NONE void uiTemplatePalette(struct uiLayout *layout, struct PointerRNA *ptr, const char *propname, int color) RET_NONE +void uiTemplateImageStereo3d(struct uiLayout *layout, struct PointerRNA *stereo3d_format_ptr) RET_NONE /* rna render */ -struct RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h, const char *layername) RET_NULL +struct RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h, const char *layername, const char *viewname) RET_NULL struct RenderResult *RE_AcquireResultRead(struct Render *re) RET_NULL struct RenderResult *RE_AcquireResultWrite(struct Render *re) RET_NULL struct RenderStats *RE_GetStats(struct Render *re) RET_NULL @@ -598,7 +608,7 @@ void RE_engine_end_result(RenderEngine *engine, struct RenderResult *result, int void RE_engine_update_stats(RenderEngine *engine, const char *stats, const char *info) RET_NONE void RE_layer_load_from_file(struct RenderLayer *layer, struct ReportList *reports, const char *filename, int x, int y) RET_NONE void RE_result_load_from_file(struct RenderResult *result, struct ReportList *reports, const char *filename) RET_NONE -void RE_AcquireResultImage(struct Render *re, struct RenderResult *rr) RET_NONE +void RE_AcquireResultImage(struct Render *re, struct RenderResult *rr, const int view_id) RET_NONE void RE_ReleaseResult(struct Render *re) RET_NONE void RE_ReleaseResultImage(struct Render *re) RET_NONE int RE_engine_test_break(struct RenderEngine *engine) RET_ZERO @@ -713,6 +723,11 @@ struct CCLDeviceInfo *CCL_compute_device_list(int opencl) RET_NULL /* compositor */ void COM_execute(RenderData *rd, Scene *scene, bNodeTree *editingtree, int rendering, - const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings) RET_NONE + const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings, + const char *viewName) RET_NONE + +/*multiview*/ +bool RE_RenderResult_is_stereo(RenderResult *res) RET_ZERO +void uiTemplateImageViews(uiLayout *layout, struct PointerRNA *imfptr) RET_NONE #endif // WITH_GAMEENGINE |