Multi-View and Stereo 3D
authorDalai Felinto <dfelinto@gmail.com>
Mon, 6 Apr 2015 13:40:12 +0000 (10:40 -0300)
committerDalai Felinto <dfelinto@gmail.com>
Mon, 6 Apr 2015 13:40:12 +0000 (10:40 -0300)
Official Documentation:
http://www.blender.org/manual/render/workflows/multiview.html

Implemented Features
====================
Builtin Stereo Camera
* Convergence Mode
* Interocular Distance
* Convergence Distance
* Pivot Mode

Viewport
* Cameras
* Plane
* Volume

Compositor
* View Switch Node
* Image Node Multi-View OpenEXR support

Sequencer
* Image/Movie Strips 'Use Multiview'

UV/Image Editor
* Option to see Multi-View images in Stereo-3D or its individual images
* Save/Open Multi-View (OpenEXR, Stereo3D, individual views) images

I/O
* Save/Open Multi-View (OpenEXR, Stereo3D, individual views) images

Scene Render Views
* Ability to have an arbitrary number of views in the scene

Missing Bits
============
First rule of Multi-View bug report: If something is not working as it should *when Views is off* this is a severe bug, do mention this in the report.

Second rule is, if something works *when Views is off* but doesn't (or crashes) when *Views is on*, this is a important bug. Do mention this in the report.

Everything else is likely small todos, and may wait until we are sure none of the above is happening.

Apart from that there are those known issues:
* Compositor Image Node poorly working for Multi-View OpenEXR
(this was working prefectly before the 'Use Multi-View' functionality)
* Selecting camera from Multi-View when looking from camera is problematic
* Animation Playback (ctrl+F11) doesn't support stereo formats
* Wrong filepath when trying to play back animated scene
* Viewport Rendering doesn't support Multi-View
* Overscan Rendering
* Fullscreen display modes need to warn the user
* Object copy should be aware of views suffix

Acknowledgments
===============
* Francesco Siddi for the help with the original feature specs and design
* Brecht Van Lommel for the original review of the code and design early on
* Blender Foundation for the Development Fund to support the project wrap up

Final patch reviewers:
* Antony Riakiotakis (psy-fi)
* Campbell Barton (ideasman42)
* Julian Eisel (Severin)
* Sergey Sharybin (nazgul)
* Thomas Dinged (dingto)

Code contributors of the original branch in github:
* Alexey Akishin
* Gabriel Caraballo

175 files changed:
intern/cycles/blender/addon/ui.py
intern/cycles/blender/blender_camera.cpp
intern/cycles/blender/blender_session.cpp
intern/cycles/blender/blender_session.h
release/scripts/startup/bl_ui/properties_data_camera.py
release/scripts/startup/bl_ui/properties_render.py
release/scripts/startup/bl_ui/properties_render_layer.py
release/scripts/startup/bl_ui/space_image.py
release/scripts/startup/bl_ui/space_info.py
release/scripts/startup/bl_ui/space_sequencer.py
release/scripts/startup/bl_ui/space_view3d.py
release/scripts/startup/nodeitems_builtins.py
source/blender/blenkernel/BKE_blender.h
source/blender/blenkernel/BKE_camera.h
source/blender/blenkernel/BKE_image.h
source/blender/blenkernel/BKE_node.h
source/blender/blenkernel/BKE_scene.h
source/blender/blenkernel/BKE_sequencer.h
source/blender/blenkernel/BKE_writeavi.h
source/blender/blenkernel/BKE_writeffmpeg.h
source/blender/blenkernel/BKE_writeframeserver.h
source/blender/blenkernel/intern/bpath.c
source/blender/blenkernel/intern/camera.c
source/blender/blenkernel/intern/image.c
source/blender/blenkernel/intern/node.c
source/blender/blenkernel/intern/ocean.c
source/blender/blenkernel/intern/packedFile.c
source/blender/blenkernel/intern/scene.c
source/blender/blenkernel/intern/seqcache.c
source/blender/blenkernel/intern/sequencer.c
source/blender/blenkernel/intern/writeavi.c
source/blender/blenkernel/intern/writeffmpeg.c
source/blender/blenkernel/intern/writeframeserver.c
source/blender/blenlib/BLI_threads.h
source/blender/blenlib/intern/threads.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/versioning_270.c
source/blender/blenloader/intern/writefile.c
source/blender/collada/ImageExporter.cpp
source/blender/compositor/CMakeLists.txt
source/blender/compositor/COM_compositor.h
source/blender/compositor/intern/COM_CompositorContext.h
source/blender/compositor/intern/COM_Converter.cpp
source/blender/compositor/intern/COM_ExecutionSystem.cpp
source/blender/compositor/intern/COM_ExecutionSystem.h
source/blender/compositor/intern/COM_compositor.cpp
source/blender/compositor/nodes/COM_CompositorNode.cpp
source/blender/compositor/nodes/COM_ImageNode.cpp
source/blender/compositor/nodes/COM_ImageNode.h
source/blender/compositor/nodes/COM_OutputFileNode.cpp
source/blender/compositor/nodes/COM_RenderLayersNode.cpp
source/blender/compositor/nodes/COM_SplitViewerNode.cpp
source/blender/compositor/nodes/COM_SwitchViewNode.cpp [new file with mode: 0644]
source/blender/compositor/nodes/COM_SwitchViewNode.h [new file with mode: 0644]
source/blender/compositor/nodes/COM_ViewerNode.cpp
source/blender/compositor/operations/COM_CompositorOperation.cpp
source/blender/compositor/operations/COM_CompositorOperation.h
source/blender/compositor/operations/COM_ImageOperation.cpp
source/blender/compositor/operations/COM_ImageOperation.h
source/blender/compositor/operations/COM_MultilayerImageOperation.cpp
source/blender/compositor/operations/COM_MultilayerImageOperation.h
source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cpp [new file with mode: 0644]
source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h [new file with mode: 0644]
source/blender/compositor/operations/COM_OutputFileOperation.cpp
source/blender/compositor/operations/COM_OutputFileOperation.h
source/blender/compositor/operations/COM_RenderLayersProg.cpp
source/blender/compositor/operations/COM_RenderLayersProg.h
source/blender/compositor/operations/COM_ViewerOperation.cpp
source/blender/compositor/operations/COM_ViewerOperation.h
source/blender/editors/include/ED_screen.h
source/blender/editors/include/ED_view3d.h
source/blender/editors/include/UI_interface.h
source/blender/editors/interface/interface_ops.c
source/blender/editors/object/object_bake_api.c
source/blender/editors/render/render_intern.h
source/blender/editors/render/render_internal.c
source/blender/editors/render/render_opengl.c
source/blender/editors/render/render_ops.c
source/blender/editors/render/render_preview.c
source/blender/editors/render/render_shading.c
source/blender/editors/screen/area.c
source/blender/editors/screen/screen_edit.c
source/blender/editors/screen/screen_ops.c
source/blender/editors/screen/screendump.c
source/blender/editors/sculpt_paint/paint_image.c
source/blender/editors/sculpt_paint/paint_image_2d.c
source/blender/editors/sculpt_paint/paint_image_proj.c
source/blender/editors/space_image/image_buttons.c
source/blender/editors/space_image/image_draw.c
source/blender/editors/space_image/image_ops.c
source/blender/editors/space_image/space_image.c
source/blender/editors/space_node/drawnode.c
source/blender/editors/space_node/node_edit.c
source/blender/editors/space_node/node_intern.h
source/blender/editors/space_node/node_ops.c
source/blender/editors/space_sequencer/sequencer_add.c
source/blender/editors/space_sequencer/sequencer_draw.c
source/blender/editors/space_sequencer/sequencer_edit.c
source/blender/editors/space_sequencer/sequencer_intern.h
source/blender/editors/space_sequencer/sequencer_view.c
source/blender/editors/space_view3d/drawobject.c
source/blender/editors/space_view3d/space_view3d.c
source/blender/editors/space_view3d/view3d_draw.c
source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp
source/blender/gpu/GPU_draw.h
source/blender/gpu/intern/gpu_draw.c
source/blender/imbuf/CMakeLists.txt
source/blender/imbuf/IMB_imbuf.h
source/blender/imbuf/IMB_imbuf_types.h
source/blender/imbuf/intern/IMB_anim.h
source/blender/imbuf/intern/anim_movie.c
source/blender/imbuf/intern/indexer.c
source/blender/imbuf/intern/openexr/openexr_api.cpp
source/blender/imbuf/intern/openexr/openexr_multi.h
source/blender/imbuf/intern/openexr/openexr_stub.cpp
source/blender/imbuf/intern/stereoimbuf.c [new file with mode: 0644]
source/blender/imbuf/intern/thumbs.c
source/blender/imbuf/intern/util.c
source/blender/imbuf/intern/writeimage.c
source/blender/makesdna/DNA_camera_types.h
source/blender/makesdna/DNA_image_types.h
source/blender/makesdna/DNA_node_types.h
source/blender/makesdna/DNA_scene_types.h
source/blender/makesdna/DNA_sequence_types.h
source/blender/makesdna/DNA_space_types.h
source/blender/makesdna/DNA_view3d_types.h
source/blender/makesdna/DNA_windowmanager_types.h
source/blender/makesrna/RNA_access.h
source/blender/makesrna/RNA_enum_types.h
source/blender/makesrna/intern/rna_camera.c
source/blender/makesrna/intern/rna_color.c
source/blender/makesrna/intern/rna_image.c
source/blender/makesrna/intern/rna_image_api.c
source/blender/makesrna/intern/rna_main_api.c
source/blender/makesrna/intern/rna_nodetree.c
source/blender/makesrna/intern/rna_render.c
source/blender/makesrna/intern/rna_scene.c
source/blender/makesrna/intern/rna_scene_api.c
source/blender/makesrna/intern/rna_sequencer.c
source/blender/makesrna/intern/rna_sequencer_api.c
source/blender/makesrna/intern/rna_space.c
source/blender/makesrna/intern/rna_ui_api.c
source/blender/makesrna/intern/rna_userdef.c
source/blender/makesrna/intern/rna_wm.c
source/blender/nodes/CMakeLists.txt
source/blender/nodes/NOD_composite.h
source/blender/nodes/NOD_static_types.h
source/blender/nodes/composite/node_composite_tree.c
source/blender/nodes/composite/nodes/node_composite_image.c
source/blender/nodes/composite/nodes/node_composite_outputFile.c
source/blender/nodes/composite/nodes/node_composite_switchview.c [new file with mode: 0644]
source/blender/quicktime/apple/qtkit_export.m
source/blender/quicktime/quicktime_export.h
source/blender/render/extern/include/RE_engine.h
source/blender/render/extern/include/RE_pipeline.h
source/blender/render/intern/include/render_result.h
source/blender/render/intern/include/render_types.h
source/blender/render/intern/source/convertblender.c
source/blender/render/intern/source/envmap.c
source/blender/render/intern/source/external_engine.c
source/blender/render/intern/source/initrender.c
source/blender/render/intern/source/pipeline.c
source/blender/render/intern/source/render_result.c
source/blender/render/intern/source/rendercore.c
source/blender/render/intern/source/zbuf.c
source/blender/windowmanager/CMakeLists.txt
source/blender/windowmanager/WM_api.h
source/blender/windowmanager/intern/wm_draw.c
source/blender/windowmanager/intern/wm_files.c
source/blender/windowmanager/intern/wm_operators.c
source/blender/windowmanager/intern/wm_stereo.c [new file with mode: 0644]
source/blender/windowmanager/intern/wm_window.c
source/blender/windowmanager/wm.h
source/blender/windowmanager/wm_draw.h
source/blenderplayer/bad_level_call_stubs/stubs.c

index 956d8b9..74de318 100644 (file)
@@ -18,7 +18,7 @@
 
 import bpy
 
-from bpy.types import Panel, Menu, Operator
+from bpy.types import Panel, Menu, Operator, UIList
 
 
 class CYCLES_MT_sampling_presets(Menu):
@@ -413,6 +413,49 @@ class CyclesRender_PT_layer_passes(CyclesButtonsPanel, Panel):
         col.prop(rl, "use_pass_environment")
 
 
+class CyclesRender_PT_views(CyclesButtonsPanel, Panel):
+    bl_label = "Views"
+    bl_context = "render_layer"
+
+    def draw_header(self, context):
+        rd = context.scene.render
+        self.layout.prop(rd, "use_multiview", text="")
+
+    def draw(self, context):
+        layout = self.layout
+
+        scene = context.scene
+        rd = scene.render
+        rv = rd.views.active
+
+
+        layout.active = rd.use_multiview
+        basic_stereo = (rd.views_format == 'STEREO_3D')
+
+        row = layout.row()
+        row.prop(rd, "views_format", expand=True)
+
+        if basic_stereo:
+            row = layout.row()
+            row.template_list("RENDERLAYER_UL_renderviews", "name", rd, "stereo_views", rd.views, "active_index", rows=2)
+
+            row = layout.row()
+            row.label(text="File Suffix:")
+            row.prop(rv, "file_suffix", text="")
+
+        else:
+            row = layout.row()
+            row.template_list("RENDERLAYER_UL_renderviews", "name", rd, "views", rd.views, "active_index", rows=2)
+
+            col = row.column(align=True)
+            col.operator("scene.render_view_add", icon='ZOOMIN', text="")
+            col.operator("scene.render_view_remove", icon='ZOOMOUT', text="")
+
+            row = layout.row()
+            row.label(text="Camera Suffix:")
+            row.prop(rv, "camera_suffix", text="")
+
+
 class Cycles_PT_post_processing(CyclesButtonsPanel, Panel):
     bl_label = "Post Processing"
     bl_options = {'DEFAULT_CLOSED'}
@@ -1428,6 +1471,7 @@ def get_panels():
         "DATA_PT_vertex_colors",
         "DATA_PT_camera",
         "DATA_PT_camera_display",
+        "DATA_PT_camera_stereoscopy",
         "DATA_PT_camera_safe_areas",
         "DATA_PT_lens",
         "DATA_PT_speaker",
index 5b3e666..fffc46e 100644 (file)
@@ -95,7 +95,7 @@ static void blender_camera_init(BlenderCamera *bcam, BL::RenderSettings b_render
        bcam->full_height = render_resolution_y(b_render);
 }
 
-static float blender_camera_focal_distance(BL::Object b_ob, BL::Camera b_camera)
+static float blender_camera_focal_distance(BL::RenderEngine b_engine, BL::Object b_ob, BL::Camera b_camera)
 {
        BL::Object b_dof_object = b_camera.dof_object();
 
@@ -103,14 +103,16 @@ static float blender_camera_focal_distance(BL::Object b_ob, BL::Camera b_camera)
                return b_camera.dof_distance();
        
        /* for dof object, return distance along camera Z direction */
-       Transform obmat = transform_clear_scale(get_transform(b_ob.matrix_world()));
+       BL::Array<float, 16> b_ob_matrix;
+       b_engine.camera_model_matrix(b_ob, b_ob_matrix);
+       Transform obmat = get_transform(b_ob_matrix);
        Transform dofmat = get_transform(b_dof_object.matrix_world());
        Transform mat = transform_inverse(obmat) * dofmat;
 
        return fabsf(transform_get_column(&mat, 3).z);
 }
 
-static void blender_camera_from_object(BlenderCamera *bcam, BL::Object b_ob, bool skip_panorama = false)
+static void blender_camera_from_object(BlenderCamera *bcam, BL::RenderEngine b_engine, BL::Object b_ob, bool skip_panorama = false)
 {
        BL::ID b_ob_data = b_ob.data();
 
@@ -181,10 +183,10 @@ static void blender_camera_from_object(BlenderCamera *bcam, BL::Object b_ob, boo
 
                bcam->apertureblades = RNA_int_get(&ccamera, "aperture_blades");
                bcam->aperturerotation = RNA_float_get(&ccamera, "aperture_rotation");
-               bcam->focaldistance = blender_camera_focal_distance(b_ob, b_camera);
+               bcam->focaldistance = blender_camera_focal_distance(b_engine, b_ob, b_camera);
                bcam->aperture_ratio = RNA_float_get(&ccamera, "aperture_ratio");
 
-               bcam->shift.x = b_camera.shift_x();
+               bcam->shift.x = b_engine.camera_shift_x(b_ob);
                bcam->shift.y = b_camera.shift_y();
 
                bcam->sensor_width = b_camera.sensor_width();
@@ -402,8 +404,10 @@ void BlenderSync::sync_camera(BL::RenderSettings b_render, BL::Object b_override
                b_ob = b_override;
 
        if(b_ob) {
-               blender_camera_from_object(&bcam, b_ob);
-               bcam.matrix = get_transform(b_ob.matrix_world());
+               BL::Array<float, 16> b_ob_matrix;
+               blender_camera_from_object(&bcam, b_engine, b_ob);
+               b_engine.camera_model_matrix(b_ob, b_ob_matrix);
+               bcam.matrix = get_transform(b_ob_matrix);
        }
 
        /* sync */
@@ -414,8 +418,9 @@ void BlenderSync::sync_camera(BL::RenderSettings b_render, BL::Object b_override
 void BlenderSync::sync_camera_motion(BL::Object b_ob, float motion_time)
 {
        Camera *cam = scene->camera;
-
-       Transform tfm = get_transform(b_ob.matrix_world());
+       BL::Array<float, 16> b_ob_matrix;
+       b_engine.camera_model_matrix(b_ob, b_ob_matrix);
+       Transform tfm = get_transform(b_ob_matrix);
        tfm = blender_camera_matrix(tfm, cam->type);
 
        if(tfm != cam->matrix) {
@@ -433,10 +438,10 @@ void BlenderSync::sync_camera_motion(BL::Object b_ob, float motion_time)
 
 /* Sync 3D View Camera */
 
-static void blender_camera_view_subset(BL::RenderSettings b_render, BL::Scene b_scene, BL::Object b_ob, BL::SpaceView3D b_v3d,
+static void blender_camera_view_subset(BL::RenderEngine b_engine, BL::RenderSettings b_render, BL::Scene b_scene, BL::Object b_ob, BL::SpaceView3D b_v3d,
        BL::RegionView3D b_rv3d, int width, int height, BoundBox2D *view_box, BoundBox2D *cam_box);
 
-static void blender_camera_from_view(BlenderCamera *bcam, BL::Scene b_scene, BL::SpaceView3D b_v3d, BL::RegionView3D b_rv3d, int width, int height, bool skip_panorama = false)
+static void blender_camera_from_view(BlenderCamera *bcam, BL::RenderEngine b_engine, BL::Scene b_scene, BL::SpaceView3D b_v3d, BL::RegionView3D b_rv3d, int width, int height, bool skip_panorama = false)
 {
        /* 3d view parameters */
        bcam->nearclip = b_v3d.clip_start();
@@ -449,13 +454,13 @@ static void blender_camera_from_view(BlenderCamera *bcam, BL::Scene b_scene, BL:
                BL::Object b_ob = (b_v3d.lock_camera_and_layers())? b_scene.camera(): b_v3d.camera();
 
                if(b_ob) {
-                       blender_camera_from_object(bcam, b_ob, skip_panorama);
+                       blender_camera_from_object(bcam, b_engine, b_ob, skip_panorama);
 
                        if(!skip_panorama && bcam->type == CAMERA_PANORAMA) {
                                /* in panorama camera view, we map viewplane to camera border */
                                BoundBox2D view_box, cam_box;
 
-                               blender_camera_view_subset(b_scene.render(), b_scene, b_ob, b_v3d, b_rv3d, width, height,
+                               blender_camera_view_subset(b_engine, b_scene.render(), b_scene, b_ob, b_v3d, b_rv3d, width, height,
                                        &view_box, &cam_box);
 
                                bcam->pano_viewplane = view_box.make_relative_to(cam_box);
@@ -493,7 +498,7 @@ static void blender_camera_from_view(BlenderCamera *bcam, BL::Scene b_scene, BL:
        bcam->matrix = transform_inverse(get_transform(b_rv3d.view_matrix()));
 }
 
-static void blender_camera_view_subset(BL::RenderSettings b_render, BL::Scene b_scene, BL::Object b_ob, BL::SpaceView3D b_v3d,
+static void blender_camera_view_subset(BL::RenderEngine b_engine, BL::RenderSettings b_render, BL::Scene b_scene, BL::Object b_ob, BL::SpaceView3D b_v3d,
        BL::RegionView3D b_rv3d, int width, int height, BoundBox2D *view_box, BoundBox2D *cam_box)
 {
        BoundBox2D cam, view;
@@ -502,7 +507,7 @@ static void blender_camera_view_subset(BL::RenderSettings b_render, BL::Scene b_
        /* get viewport viewplane */
        BlenderCamera view_bcam;
        blender_camera_init(&view_bcam, b_render);
-       blender_camera_from_view(&view_bcam, b_scene, b_v3d, b_rv3d, width, height, true);
+       blender_camera_from_view(&view_bcam, b_engine, b_scene, b_v3d, b_rv3d, width, height, true);
 
        blender_camera_viewplane(&view_bcam, width, height,
                &view, &view_aspect, &sensor_size);
@@ -510,7 +515,7 @@ static void blender_camera_view_subset(BL::RenderSettings b_render, BL::Scene b_
        /* get camera viewplane */
        BlenderCamera cam_bcam;
        blender_camera_init(&cam_bcam, b_render);
-       blender_camera_from_object(&cam_bcam, b_ob, true);
+       blender_camera_from_object(&cam_bcam, b_engine, b_ob, true);
 
        blender_camera_viewplane(&cam_bcam, cam_bcam.full_width, cam_bcam.full_height,
                &cam, &cam_aspect, &sensor_size);
@@ -520,7 +525,8 @@ static void blender_camera_view_subset(BL::RenderSettings b_render, BL::Scene b_
        *cam_box = cam * (1.0f/cam_aspect);
 }
 
-static void blender_camera_border_subset(BL::RenderSettings b_render,
+static void blender_camera_border_subset(BL::RenderEngine b_engine,
+                                         BL::RenderSettings b_render,
                                          BL::Scene b_scene,
                                          BL::SpaceView3D b_v3d,
                                          BL::RegionView3D b_rv3d,
@@ -531,7 +537,7 @@ static void blender_camera_border_subset(BL::RenderSettings b_render,
 {
        /* Determine camera viewport subset. */
        BoundBox2D view_box, cam_box;
-       blender_camera_view_subset(b_render, b_scene, b_ob, b_v3d, b_rv3d, width, height,
+       blender_camera_view_subset(b_engine, b_render, b_scene, b_ob, b_v3d, b_rv3d, width, height,
                                   &view_box, &cam_box);
 
        /* Determine viewport subset matching given border. */
@@ -539,7 +545,7 @@ static void blender_camera_border_subset(BL::RenderSettings b_render,
        *result = cam_box.subset(border);
 }
 
-static void blender_camera_border(BlenderCamera *bcam, BL::RenderSettings b_render, BL::Scene b_scene, BL::SpaceView3D b_v3d,
+static void blender_camera_border(BlenderCamera *bcam, BL::RenderEngine b_engine, BL::RenderSettings b_render, BL::Scene b_scene, BL::SpaceView3D b_v3d,
        BL::RegionView3D b_rv3d, int width, int height)
 {
        bool is_camera_view;
@@ -568,7 +574,8 @@ static void blender_camera_border(BlenderCamera *bcam, BL::RenderSettings b_rend
 
        /* Determine camera border inside the viewport. */
        BoundBox2D full_border;
-       blender_camera_border_subset(b_render,
+       blender_camera_border_subset(b_engine,
+                                    b_render,
                                     b_scene,
                                     b_v3d,
                                     b_rv3d,
@@ -587,7 +594,8 @@ static void blender_camera_border(BlenderCamera *bcam, BL::RenderSettings b_rend
        bcam->border.top = b_render.border_max_y();
 
        /* Determine viewport subset matching camera border. */
-       blender_camera_border_subset(b_render,
+       blender_camera_border_subset(b_engine,
+                                    b_render,
                                     b_scene,
                                     b_v3d,
                                     b_rv3d,
@@ -602,8 +610,8 @@ void BlenderSync::sync_view(BL::SpaceView3D b_v3d, BL::RegionView3D b_rv3d, int
 {
        BlenderCamera bcam;
        blender_camera_init(&bcam, b_scene.render());
-       blender_camera_from_view(&bcam, b_scene, b_v3d, b_rv3d, width, height);
-       blender_camera_border(&bcam, b_scene.render(), b_scene, b_v3d, b_rv3d, width, height);
+       blender_camera_from_view(&bcam, b_engine, b_scene, b_v3d, b_rv3d, width, height);
+       blender_camera_border(&bcam, b_engine, b_scene.render(), b_scene, b_v3d, b_rv3d, width, height);
 
        blender_camera_sync(scene->camera, &bcam, width, height);
 }
index 4f428bb..17bab0a 100644 (file)
@@ -331,9 +331,9 @@ static ShaderEvalType get_shader_type(const string& pass_type)
                return SHADER_EVAL_BAKE;
 }
 
-static BL::RenderResult begin_render_result(BL::RenderEngine b_engine, int x, int y, int w, int h, const char *layername)
+static BL::RenderResult begin_render_result(BL::RenderEngine b_engine, int x, int y, int w, int h, const char *layername, const char *viewname)
 {
-       return b_engine.begin_result(x, y, w, h, layername);
+       return b_engine.begin_result(x, y, w, h, layername, viewname);
 }
 
 static void end_render_result(BL::RenderEngine b_engine, BL::RenderResult b_rr, bool cancel, bool do_merge_results)
@@ -350,7 +350,7 @@ void BlenderSession::do_write_update_render_tile(RenderTile& rtile, bool do_upda
        int h = params.height;
 
        /* get render result */
-       BL::RenderResult b_rr = begin_render_result(b_engine, x, y, w, h, b_rlay_name.c_str());
+       BL::RenderResult b_rr = begin_render_result(b_engine, x, y, w, h, b_rlay_name.c_str(), b_rview_name.c_str());
 
        /* can happen if the intersected rectangle gives 0 width or height */
        if(b_rr.ptr.data == NULL) {
@@ -415,13 +415,14 @@ void BlenderSession::render()
 
        /* render each layer */
        BL::RenderSettings r = b_scene.render();
-       BL::RenderSettings::layers_iterator b_iter;
+       BL::RenderSettings::layers_iterator b_layer_iter;
+       BL::RenderResult::views_iterator b_view_iter;
        
-       for(r.layers.begin(b_iter); b_iter != r.layers.end(); ++b_iter) {
-               b_rlay_name = b_iter->name();
+       for(r.layers.begin(b_layer_iter); b_layer_iter != r.layers.end(); ++b_layer_iter) {
+               b_rlay_name = b_layer_iter->name();
 
-               /* temporary render result to find needed passes */
-               BL::RenderResult b_rr = begin_render_result(b_engine, 0, 0, 1, 1, b_rlay_name.c_str());
+               /* temporary render result to find needed passes and views */
+               BL::RenderResult b_rr = begin_render_result(b_engine, 0, 0, 1, 1, b_rlay_name.c_str(), NULL);
                BL::RenderResult::layers_iterator b_single_rlay;
                b_rr.layers.begin(b_single_rlay);
 
@@ -456,31 +457,41 @@ void BlenderSession::render()
                        }
                }
 
-               /* free result without merging */
-               end_render_result(b_engine, b_rr, true, false);
-
                buffer_params.passes = passes;
-               scene->film->pass_alpha_threshold = b_iter->pass_alpha_threshold();
+               scene->film->pass_alpha_threshold = b_layer_iter->pass_alpha_threshold();
                scene->film->tag_passes_update(scene, passes);
                scene->film->tag_update(scene);
                scene->integrator->tag_update(scene);
 
-               /* update scene */
-               sync->sync_camera(b_render, b_engine.camera_override(), width, height);
-               sync->sync_data(b_v3d, b_engine.camera_override(), &python_thread_state, b_rlay_name.c_str());
+               for(b_rr.views.begin(b_view_iter); b_view_iter != b_rr.views.end(); ++b_view_iter) {
+                       b_rview_name = b_view_iter->name();
 
-               /* update number of samples per layer */
-               int samples = sync->get_layer_samples();
-               bool bound_samples = sync->get_layer_bound_samples();
+                       /* set the current view */
+                       b_engine.active_view_set(b_rview_name.c_str());
 
-               if(samples != 0 && (!bound_samples || (samples < session_params.samples)))
-                       session->reset(buffer_params, samples);
-               else
-                       session->reset(buffer_params, session_params.samples);
+                       /* update scene */
+                       sync->sync_camera(b_render, b_engine.camera_override(), width, height);
+                       sync->sync_data(b_v3d, b_engine.camera_override(), &python_thread_state, b_rlay_name.c_str());
 
-               /* render */
-               session->start();
-               session->wait();
+                       /* update number of samples per layer */
+                       int samples = sync->get_layer_samples();
+                       bool bound_samples = sync->get_layer_bound_samples();
+
+                       if(samples != 0 && (!bound_samples || (samples < session_params.samples)))
+                               session->reset(buffer_params, samples);
+                       else
+                               session->reset(buffer_params, session_params.samples);
+
+                       /* render */
+                       session->start();
+                       session->wait();
+
+                       if(session->progress.get_cancel())
+                               break;
+               }
+
+               /* free result without merging */
+               end_render_result(b_engine, b_rr, true, false);
 
                if(session->progress.get_cancel())
                        break;
@@ -619,10 +630,12 @@ void BlenderSession::do_write_update_render_result(BL::RenderResult b_rr, BL::Re
                        b_pass.rect(&pixels[0]);
                }
        }
-
-       /* copy combined pass */
-       if(buffers->get_pass_rect(PASS_COMBINED, exposure, rtile.sample, 4, &pixels[0]))
-               b_rlay.rect(&pixels[0]);
+       else {
+               /* copy combined pass */
+                BL::RenderPass b_combined_pass(b_rlay.passes.find_by_type(BL::RenderPass::type_COMBINED, b_rview_name.c_str()));
+               if(buffers->get_pass_rect(PASS_COMBINED, exposure, rtile.sample, 4, &pixels[0]))
+                       b_combined_pass.rect(&pixels[0]);
+       }
 
        /* tag result as updated */
        b_engine.update_result(b_rr);
@@ -841,6 +854,9 @@ void BlenderSession::update_status_progress()
                scene += " | " + b_scene.name();
                if(b_rlay_name != "")
                        scene += ", "  + b_rlay_name;
+
+               if(b_rview_name != "")
+                       scene += ", " + b_rview_name;
        }
        else {
                BLI_timestr(total_time, time_str, sizeof(time_str));
index c807028..b6fc709 100644 (file)
@@ -90,6 +90,7 @@ public:
        BL::SpaceView3D b_v3d;
        BL::RegionView3D b_rv3d;
        string b_rlay_name;
+       string b_rview_name;
 
        string last_status;
        string last_error;
index e425596..6c87af8 100644 (file)
@@ -135,6 +135,35 @@ class DATA_PT_lens(CameraButtonsPanel, Panel):
         col.prop(cam, "clip_end", text="End")
 
 
+class DATA_PT_camera_stereoscopy(CameraButtonsPanel, Panel):
+    bl_label = "Stereoscopy"
+    COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+    @classmethod
+    def poll(cls, context):
+        render = context.scene.render
+        return (super().poll(context) and render.use_multiview \
+                and render.views_format == 'STEREO_3D')
+
+    def draw(self, context):
+        layout = self.layout
+        render = context.scene.render
+        st = context.camera.stereo
+
+        col = layout.column()
+        col.row().prop(st, "convergence_mode", expand=True)
+
+        if st.convergence_mode == 'PARALLEL':
+            col.prop(st, "viewport_convergence")
+        else:
+            col.prop(st, "convergence_distance")
+
+        col.prop(st, "interocular_distance")
+
+        col.label(text="Pivot:")
+        col.row().prop(st, "pivot", expand=True)
+
+
 class DATA_PT_camera(CameraButtonsPanel, Panel):
     bl_label = "Camera"
     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME'}
index b596fe4..35f77e0 100644 (file)
@@ -404,6 +404,8 @@ class RENDER_PT_output(RenderButtonsPanel, Panel):
         col.prop(rd, "use_render_cache")
 
         layout.template_image_settings(image_settings, color_management=False)
+        if rd.use_multiview:
+            layout.template_image_views(image_settings)
 
         if file_format == 'QUICKTIME':
             quicktime = rd.quicktime
index 35032d3..18c75cc 100644 (file)
@@ -167,5 +167,63 @@ class RENDERLAYER_PT_layer_passes(RenderLayerButtonsPanel, Panel):
         self.draw_pass_type_buttons(col, rl, "refraction")
 
 
+class RENDERLAYER_UL_renderviews(UIList):
+    def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
+        # assert(isinstance(item, bpy.types.SceneRenderView)
+        view = item
+        if self.layout_type in {'DEFAULT', 'COMPACT'}:
+            if view.name in {'left', 'right'}:
+                layout.label(view.name, icon_value=icon + (not view.use))
+            else:
+                layout.prop(view, "name", text="", index=index, icon_value=icon, emboss=False)
+            layout.prop(view, "use", text="", index=index)
+
+        elif self.layout_type == 'GRID':
+            layout.alignment = 'CENTER'
+            layout.label("", icon_value=icon + (not view.use))
+
+
+class RENDERLAYER_PT_views(RenderLayerButtonsPanel, Panel):
+    bl_label = "Views"
+    COMPAT_ENGINES = {'BLENDER_RENDER'}
+
+    def draw_header(self, context):
+        rd = context.scene.render
+        self.layout.prop(rd, "use_multiview", text="")
+
+    def draw(self, context):
+        layout = self.layout
+
+        scene = context.scene
+        rd = scene.render
+        rv = rd.views.active
+
+        layout.active = rd.use_multiview
+        basic_stereo = rd.views_format == 'STEREO_3D'
+
+        row = layout.row()
+        row.prop(rd, "views_format", expand=True)
+
+        if basic_stereo:
+            row = layout.row()
+            row.template_list("RENDERLAYER_UL_renderviews", "name", rd, "stereo_views", rd.views, "active_index", rows=2)
+
+            row = layout.row()
+            row.label(text="File Suffix:")
+            row.prop(rv, "file_suffix", text="")
+
+        else:
+            row = layout.row()
+            row.template_list("RENDERLAYER_UL_renderviews", "name", rd, "views", rd.views, "active_index", rows=2)
+
+            col = row.column(align=True)
+            col.operator("scene.render_view_add", icon='ZOOMIN', text="")
+            col.operator("scene.render_view_remove", icon='ZOOMOUT', text="")
+
+            row = layout.row()
+            row.label(text="Camera Suffix:")
+            row.prop(rv, "camera_suffix", text="")
+
+
 if __name__ == "__main__":  # only for live edit.
     bpy.utils.register_module(__name__)
index e2c2783..a1101bb 100644 (file)
@@ -458,6 +458,10 @@ class IMAGE_HT_header(Header):
             layout.prop_search(mesh.uv_textures, "active", mesh, "uv_textures", text="")
 
         if ima:
+            if ima.is_stereo_3d:
+                row = layout.row()
+                row.prop(sima, "show_stereo_3d", text="")
+
             # layers
             layout.template_image_layers(ima, iuser)
 
index 927967c..48a1b28 100644 (file)
@@ -282,6 +282,10 @@ class INFO_MT_window(Menu):
             layout.separator()
             layout.operator("wm.console_toggle", icon='CONSOLE')
 
+        if context.scene.render.use_multiview:
+            layout.separator()
+            layout.operator("wm.set_stereo_3d", icon='CAMERA_STEREO')
+
 
 class INFO_MT_help(Menu):
     bl_label = "Help"
index da477e4..e19cf82 100644 (file)
@@ -659,6 +659,7 @@ class SEQUENCER_PT_input(SequencerButtonsPanel, Panel):
 
     def draw(self, context):
         layout = self.layout
+        scene = context.scene
 
         strip = act_strip(context)
 
@@ -672,7 +673,7 @@ class SEQUENCER_PT_input(SequencerButtonsPanel, Panel):
 
             # Current element for the filename
 
-            elem = strip.strip_elem_from_frame(context.scene.frame_current)
+            elem = strip.strip_elem_from_frame(scene.frame_current)
             if elem:
                 split = layout.split(percentage=0.2)
                 split.label(text="File:")
@@ -718,6 +719,19 @@ class SEQUENCER_PT_input(SequencerButtonsPanel, Panel):
         col.prop(strip, "frame_offset_start", text="Start")
         col.prop(strip, "frame_offset_end", text="End")
 
+        if scene.render.use_multiview and seq_type in {'IMAGE', 'MOVIE'}:
+            layout.prop(strip, "use_multiview")
+
+            col = layout.column()
+            col.active = strip.use_multiview
+
+            col.label(text="Views Format:")
+            col.row().prop(strip, "views_format", expand=True)
+
+            box = col.box()
+            box.active = strip.views_format == 'STEREO_3D'
+            box.template_image_stereo_3d(strip.stereo_3d_format)
+
 
 class SEQUENCER_PT_sound(SequencerButtonsPanel, Panel):
     bl_label = "Sound"
index 6b9e4b8..a23ad42 100644 (file)
@@ -2936,16 +2936,50 @@ class VIEW3D_PT_view3d_display(Panel):
             row.prop(region, "use_box_clip")
 
 
-class VIEW3D_PT_view3d_shading(Panel):
+class VIEW3D_PT_view3d_stereo(Panel):
     bl_space_type = 'VIEW_3D'
     bl_region_type = 'UI'
-    bl_label = "Shading"
+    bl_label = "Stereoscopy"
     bl_options = {'DEFAULT_CLOSED'}
 
     @classmethod
     def poll(cls, context):
+        scene = context.scene
+
+        multiview = scene.render.use_multiview
+        return context.space_data and multiview
+
+    def draw(self, context):
+        layout = self.layout
         view = context.space_data
-        return (view)
+
+        basic_stereo = context.scene.render.views_format == 'STEREO_3D'
+
+        col = layout.column()
+        col.row().prop(view, "stereo_3d_camera", expand=True)
+
+        col.label(text="Display:")
+        row = col.row()
+        row.active = basic_stereo
+        row.prop(view, "show_stereo_3d_cameras")
+        row = col.row()
+        row.active = basic_stereo
+        split = row.split()
+        split.prop(view, "show_stereo_3d_convergence_plane")
+        split = row.split()
+        split.prop(view, "stereo_3d_convergence_plane_alpha", text="Alpha")
+        split.active = view.show_stereo_3d_convergence_plane
+        row = col.row()
+        split = row.split()
+        split.prop(view, "show_stereo_3d_volume")
+        split = row.split()
+        split.prop(view, "stereo_3d_volume_alpha", text="Alpha")
+
+
+class VIEW3D_PT_view3d_shading(Panel):
+    bl_space_type = 'VIEW_3D'
+    bl_region_type = 'UI'
+    bl_label = "Shading"
 
     def draw(self, context):
         layout = self.layout
index 1590bd4..98c524b 100644 (file)
@@ -328,6 +328,7 @@ compositor_node_categories = [
         NodeItem("CompositorNodeCombYUVA"),
         NodeItem("CompositorNodeSepYCCA"),
         NodeItem("CompositorNodeCombYCCA"),
+        NodeItem("CompositorNodeSwitchView"),
         ]),
     CompositorNodeCategory("CMP_OP_FILTER", "Filter", items=[
         NodeItem("CompositorNodeBlur"),
index a790715..86576f9 100644 (file)
@@ -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
index 4849c66..aacb7a4 100644 (file)
@@ -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
index 3b7ba24..ad19196 100644 (file)
@@ -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);
index ee63771..eca15a6 100644 (file)
@@ -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);
index 27c89dd..79778d5 100644 (file)
@@ -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
index 8481827..4a45489 100644 (file)
@@ -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 */
index bc06be0..ca295c5 100644 (file)
@@ -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
 }
index 951fa50..a40c310 100644 (file)
@@ -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
index b7d601e..2f8bce3 100644 (file)
@@ -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
 }
index e94f30d..bb37312 100644 (file)
@@ -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);
                                                }
index 5b4d7ba..fec6542 100644 (file)
  */
 
 #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(&params, &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) {
index 9b00a01..f852d88 100644 (file)
 
 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);
+               }
+       }
+}
index 7f3db70..0d375b0 100644 (file)
@@ -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();
index 81460ec..b89b5ab 100644 (file)
@@ -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 */
index 9b429b3..61e39d0 100644 (file)
@@ -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;
index 3b48de1..443671f 100644 (file)
@@ -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);
+       }
+}
index 41f1766..abfc858 100644 (file)
@@ -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;
 }
index 7215550..942426f 100644 (file)
 #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 =
index a86ffac..cec455e 100644 (file)
 
 /* ********************** 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';
 }
index 0717369..af71f19 100644 (file)
 
 #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;
@@