Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--intern/cycles/blender/addon/ui.py46
-rw-r--r--intern/cycles/blender/blender_camera.cpp54
-rw-r--r--intern/cycles/blender/blender_session.cpp74
-rw-r--r--intern/cycles/blender/blender_session.h1
-rw-r--r--release/scripts/startup/bl_ui/properties_data_camera.py29
-rw-r--r--release/scripts/startup/bl_ui/properties_render.py2
-rw-r--r--release/scripts/startup/bl_ui/properties_render_layer.py58
-rw-r--r--release/scripts/startup/bl_ui/space_image.py4
-rw-r--r--release/scripts/startup/bl_ui/space_info.py4
-rw-r--r--release/scripts/startup/bl_ui/space_sequencer.py16
-rw-r--r--release/scripts/startup/bl_ui/space_view3d.py40
-rw-r--r--release/scripts/startup/nodeitems_builtins.py1
-rw-r--r--source/blender/blenkernel/BKE_blender.h2
-rw-r--r--source/blender/blenkernel/BKE_camera.h8
-rw-r--r--source/blender/blenkernel/BKE_image.h40
-rw-r--r--source/blender/blenkernel/BKE_node.h4
-rw-r--r--source/blender/blenkernel/BKE_scene.h21
-rw-r--r--source/blender/blenkernel/BKE_sequencer.h11
-rw-r--r--source/blender/blenkernel/BKE_writeavi.h18
-rw-r--r--source/blender/blenkernel/BKE_writeffmpeg.h13
-rw-r--r--source/blender/blenkernel/BKE_writeframeserver.h13
-rw-r--r--source/blender/blenkernel/intern/bpath.c4
-rw-r--r--source/blender/blenkernel/intern/camera.c254
-rw-r--r--source/blender/blenkernel/intern/image.c1157
-rw-r--r--source/blender/blenkernel/intern/node.c1
-rw-r--r--source/blender/blenkernel/intern/ocean.c2
-rw-r--r--source/blender/blenkernel/intern/packedFile.c64
-rw-r--r--source/blender/blenkernel/intern/scene.c350
-rw-r--r--source/blender/blenkernel/intern/seqcache.c7
-rw-r--r--source/blender/blenkernel/intern/sequencer.c902
-rw-r--r--source/blender/blenkernel/intern/writeavi.c88
-rw-r--r--source/blender/blenkernel/intern/writeffmpeg.c408
-rw-r--r--source/blender/blenkernel/intern/writeframeserver.c132
-rw-r--r--source/blender/blenlib/BLI_threads.h1
-rw-r--r--source/blender/blenlib/intern/threads.c5
-rw-r--r--source/blender/blenloader/intern/readfile.c41
-rw-r--r--source/blender/blenloader/intern/versioning_270.c88
-rw-r--r--source/blender/blenloader/intern/writefile.c31
-rw-r--r--source/blender/collada/ImageExporter.cpp2
-rw-r--r--source/blender/compositor/CMakeLists.txt4
-rw-r--r--source/blender/compositor/COM_compositor.h3
-rw-r--r--source/blender/compositor/intern/COM_CompositorContext.h17
-rw-r--r--source/blender/compositor/intern/COM_Converter.cpp4
-rw-r--r--source/blender/compositor/intern/COM_ExecutionSystem.cpp4
-rw-r--r--source/blender/compositor/intern/COM_ExecutionSystem.h3
-rw-r--r--source/blender/compositor/intern/COM_compositor.cpp7
-rw-r--r--source/blender/compositor/nodes/COM_CompositorNode.cpp1
-rw-r--r--source/blender/compositor/nodes/COM_ImageNode.cpp50
-rw-r--r--source/blender/compositor/nodes/COM_ImageNode.h2
-rw-r--r--source/blender/compositor/nodes/COM_OutputFileNode.cpp46
-rw-r--r--source/blender/compositor/nodes/COM_RenderLayersNode.cpp3
-rw-r--r--source/blender/compositor/nodes/COM_SplitViewerNode.cpp4
-rw-r--r--source/blender/compositor/nodes/COM_SwitchViewNode.cpp42
-rw-r--r--source/blender/compositor/nodes/COM_SwitchViewNode.h37
-rw-r--r--source/blender/compositor/nodes/COM_ViewerNode.cpp5
-rw-r--r--source/blender/compositor/operations/COM_CompositorOperation.cpp15
-rw-r--r--source/blender/compositor/operations/COM_CompositorOperation.h6
-rw-r--r--source/blender/compositor/operations/COM_ImageOperation.cpp11
-rw-r--r--source/blender/compositor/operations/COM_ImageOperation.h7
-rw-r--r--source/blender/compositor/operations/COM_MultilayerImageOperation.cpp23
-rw-r--r--source/blender/compositor/operations/COM_MultilayerImageOperation.h11
-rw-r--r--source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cpp317
-rw-r--r--source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h74
-rw-r--r--source/blender/compositor/operations/COM_OutputFileOperation.cpp116
-rw-r--r--source/blender/compositor/operations/COM_OutputFileOperation.h15
-rw-r--r--source/blender/compositor/operations/COM_RenderLayersProg.cpp10
-rw-r--r--source/blender/compositor/operations/COM_RenderLayersProg.h9
-rw-r--r--source/blender/compositor/operations/COM_ViewerOperation.cpp15
-rw-r--r--source/blender/compositor/operations/COM_ViewerOperation.h6
-rw-r--r--source/blender/editors/include/ED_screen.h1
-rw-r--r--source/blender/editors/include/ED_view3d.h7
-rw-r--r--source/blender/editors/include/UI_interface.h3
-rw-r--r--source/blender/editors/interface/interface_ops.c1
-rw-r--r--source/blender/editors/object/object_bake_api.c2
-rw-r--r--source/blender/editors/render/render_intern.h3
-rw-r--r--source/blender/editors/render/render_internal.c21
-rw-r--r--source/blender/editors/render/render_opengl.c347
-rw-r--r--source/blender/editors/render/render_ops.c3
-rw-r--r--source/blender/editors/render/render_preview.c10
-rw-r--r--source/blender/editors/render/render_shading.c64
-rw-r--r--source/blender/editors/screen/area.c1
-rw-r--r--source/blender/editors/screen/screen_edit.c82
-rw-r--r--source/blender/editors/screen/screen_ops.c1
-rw-r--r--source/blender/editors/screen/screendump.c29
-rw-r--r--source/blender/editors/sculpt_paint/paint_image.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_2d.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c4
-rw-r--r--source/blender/editors/space_image/image_buttons.c266
-rw-r--r--source/blender/editors/space_image/image_draw.c12
-rw-r--r--source/blender/editors/space_image/image_ops.c491
-rw-r--r--source/blender/editors/space_image/space_image.c3
-rw-r--r--source/blender/editors/space_node/drawnode.c56
-rw-r--r--source/blender/editors/space_node/node_edit.c56
-rw-r--r--source/blender/editors/space_node/node_intern.h2
-rw-r--r--source/blender/editors/space_node/node_ops.c2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_add.c92
-rw-r--r--source/blender/editors/space_sequencer/sequencer_draw.c10
-rw-r--r--source/blender/editors/space_sequencer/sequencer_edit.c20
-rw-r--r--source/blender/editors/space_sequencer/sequencer_intern.h2
-rw-r--r--source/blender/editors/space_sequencer/sequencer_view.c2
-rw-r--r--source/blender/editors/space_view3d/drawobject.c263
-rw-r--r--source/blender/editors/space_view3d/space_view3d.c8
-rw-r--r--source/blender/editors/space_view3d/view3d_draw.c162
-rw-r--r--source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp9
-rw-r--r--source/blender/gpu/GPU_draw.h2
-rw-r--r--source/blender/gpu/intern/gpu_draw.c4
-rw-r--r--source/blender/imbuf/CMakeLists.txt1
-rw-r--r--source/blender/imbuf/IMB_imbuf.h40
-rw-r--r--source/blender/imbuf/IMB_imbuf_types.h4
-rw-r--r--source/blender/imbuf/intern/IMB_anim.h1
-rw-r--r--source/blender/imbuf/intern/anim_movie.c5
-rw-r--r--source/blender/imbuf/intern/indexer.c12
-rw-r--r--source/blender/imbuf/intern/openexr/openexr_api.cpp1030
-rw-r--r--source/blender/imbuf/intern/openexr/openexr_multi.h39
-rw-r--r--source/blender/imbuf/intern/openexr/openexr_stub.cpp46
-rw-r--r--source/blender/imbuf/intern/stereoimbuf.c1292
-rw-r--r--source/blender/imbuf/intern/thumbs.c2
-rw-r--r--source/blender/imbuf/intern/util.c12
-rw-r--r--source/blender/imbuf/intern/writeimage.c30
-rw-r--r--source/blender/makesdna/DNA_camera_types.h27
-rw-r--r--source/blender/makesdna/DNA_image_types.h52
-rw-r--r--source/blender/makesdna/DNA_node_types.h1
-rw-r--r--source/blender/makesdna/DNA_scene_types.h94
-rw-r--r--source/blender/makesdna/DNA_sequence_types.h15
-rw-r--r--source/blender/makesdna/DNA_space_types.h3
-rw-r--r--source/blender/makesdna/DNA_view3d_types.h16
-rw-r--r--source/blender/makesdna/DNA_windowmanager_types.h6
-rw-r--r--source/blender/makesrna/RNA_access.h2
-rw-r--r--source/blender/makesrna/RNA_enum_types.h7
-rw-r--r--source/blender/makesrna/intern/rna_camera.c67
-rw-r--r--source/blender/makesrna/intern/rna_color.c11
-rw-r--r--source/blender/makesrna/intern/rna_image.c109
-rw-r--r--source/blender/makesrna/intern/rna_image_api.c2
-rw-r--r--source/blender/makesrna/intern/rna_main_api.c5
-rw-r--r--source/blender/makesrna/intern/rna_nodetree.c97
-rw-r--r--source/blender/makesrna/intern/rna_render.c128
-rw-r--r--source/blender/makesrna/intern/rna_scene.c393
-rw-r--r--source/blender/makesrna/intern/rna_scene_api.c8
-rw-r--r--source/blender/makesrna/intern/rna_sequencer.c41
-rw-r--r--source/blender/makesrna/intern/rna_sequencer_api.c7
-rw-r--r--source/blender/makesrna/intern/rna_space.c133
-rw-r--r--source/blender/makesrna/intern/rna_ui_api.c10
-rw-r--r--source/blender/makesrna/intern/rna_userdef.c4
-rw-r--r--source/blender/makesrna/intern/rna_wm.c39
-rw-r--r--source/blender/nodes/CMakeLists.txt1
-rw-r--r--source/blender/nodes/NOD_composite.h1
-rw-r--r--source/blender/nodes/NOD_static_types.h1
-rw-r--r--source/blender/nodes/composite/node_composite_tree.c7
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_image.c11
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_outputFile.c4
-rw-r--r--source/blender/nodes/composite/nodes/node_composite_switchview.c153
-rw-r--r--source/blender/quicktime/apple/qtkit_export.m43
-rw-r--r--source/blender/quicktime/quicktime_export.h10
-rw-r--r--source/blender/render/extern/include/RE_engine.h6
-rw-r--r--source/blender/render/extern/include/RE_pipeline.h65
-rw-r--r--source/blender/render/intern/include/render_result.h21
-rw-r--r--source/blender/render/intern/include/render_types.h3
-rw-r--r--source/blender/render/intern/source/convertblender.c6
-rw-r--r--source/blender/render/intern/source/envmap.c6
-rw-r--r--source/blender/render/intern/source/external_engine.c27
-rw-r--r--source/blender/render/intern/source/initrender.c20
-rw-r--r--source/blender/render/intern/source/pipeline.c1099
-rw-r--r--source/blender/render/intern/source/render_result.c815
-rw-r--r--source/blender/render/intern/source/rendercore.c111
-rw-r--r--source/blender/render/intern/source/zbuf.c14
-rw-r--r--source/blender/windowmanager/CMakeLists.txt1
-rw-r--r--source/blender/windowmanager/WM_api.h1
-rw-r--r--source/blender/windowmanager/intern/wm_draw.c284
-rw-r--r--source/blender/windowmanager/intern/wm_files.c4
-rw-r--r--source/blender/windowmanager/intern/wm_operators.c43
-rw-r--r--source/blender/windowmanager/intern/wm_stereo.c534
-rw-r--r--source/blender/windowmanager/intern/wm_window.c27
-rw-r--r--source/blender/windowmanager/wm.h7
-rw-r--r--source/blender/windowmanager/wm_draw.h21
-rw-r--r--source/blenderplayer/bad_level_call_stubs/stubs.c23
175 files changed, 11910 insertions, 2158 deletions
diff --git a/intern/cycles/blender/addon/ui.py b/intern/cycles/blender/addon/ui.py
index 956d8b962e9..74de31850ee 100644
--- a/intern/cycles/blender/addon/ui.py
+++ b/intern/cycles/blender/addon/ui.py
@@ -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",
diff --git a/intern/cycles/blender/blender_camera.cpp b/intern/cycles/blender/blender_camera.cpp
index 5b3e666e7c7..fffc46ec9a6 100644
--- a/intern/cycles/blender/blender_camera.cpp
+++ b/intern/cycles/blender/blender_camera.cpp
@@ -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);
}
diff --git a/intern/cycles/blender/blender_session.cpp b/intern/cycles/blender/blender_session.cpp
index 4f428bbea57..17bab0ab525 100644
--- a/intern/cycles/blender/blender_session.cpp
+++ b/intern/cycles/blender/blender_session.cpp
@@ -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));
diff --git a/intern/cycles/blender/blender_session.h b/intern/cycles/blender/blender_session.h
index c8070286006..b6fc709b92e 100644
--- a/intern/cycles/blender/blender_session.h
+++ b/intern/cycles/blender/blender_session.h
@@ -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;
diff --git a/release/scripts/startup/bl_ui/properties_data_camera.py b/release/scripts/startup/bl_ui/properties_data_camera.py
index e4255966a5f..6c87af8ad68 100644
--- a/release/scripts/startup/bl_ui/properties_data_camera.py
+++ b/release/scripts/startup/bl_ui/properties_data_camera.py
@@ -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'}
diff --git a/release/scripts/startup/bl_ui/properties_render.py b/release/scripts/startup/bl_ui/properties_render.py
index b596fe48b49..35f77e02834 100644
--- a/release/scripts/startup/bl_ui/properties_render.py
+++ b/release/scripts/startup/bl_ui/properties_render.py
@@ -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
diff --git a/release/scripts/startup/bl_ui/properties_render_layer.py b/release/scripts/startup/bl_ui/properties_render_layer.py
index 35032d38933..18c75ccb20c 100644
--- a/release/scripts/startup/bl_ui/properties_render_layer.py
+++ b/release/scripts/startup/bl_ui/properties_render_layer.py
@@ -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__)
diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py
index e2c2783fae1..a1101bb3efb 100644
--- a/release/scripts/startup/bl_ui/space_image.py
+++ b/release/scripts/startup/bl_ui/space_image.py
@@ -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)
diff --git a/release/scripts/startup/bl_ui/space_info.py b/release/scripts/startup/bl_ui/space_info.py
index 927967c8bc6..48a1b28289e 100644
--- a/release/scripts/startup/bl_ui/space_info.py
+++ b/release/scripts/startup/bl_ui/space_info.py
@@ -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"
diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py
index da477e4e6be..e19cf8239d7 100644
--- a/release/scripts/startup/bl_ui/space_sequencer.py
+++ b/release/scripts/startup/bl_ui/space_sequencer.py
@@ -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"
diff --git a/release/scripts/startup/bl_ui/space_view3d.py b/release/scripts/startup/bl_ui/space_view3d.py
index 6b9e4b85875..a23ad4209d5 100644
--- a/release/scripts/startup/bl_ui/space_view3d.py
+++ b/release/scripts/startup/bl_ui/space_view3d.py
@@ -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
diff --git a/release/scripts/startup/nodeitems_builtins.py b/release/scripts/startup/nodeitems_builtins.py
index 1590bd49763..98c524b0b37 100644
--- a/release/scripts/startup/nodeitems_builtins.py
+++ b/release/scripts/startup/nodeitems_builtins.py
@@ -328,6 +328,7 @@ compositor_node_categories = [
NodeItem("CompositorNodeCombYUVA"),
NodeItem("CompositorNodeSepYCCA"),
NodeItem("CompositorNodeCombYCCA"),
+ NodeItem("CompositorNodeSwitchView"),
]),
CompositorNodeCategory("CMP_OP_FILTER", "Filter", items=[
NodeItem("CompositorNodeBlur"),
diff --git a/source/blender/blenkernel/BKE_blender.h b/source/blender/blenkernel/BKE_blender.h
index a790715ac98..86576f9a11b 100644
--- a/source/blender/blenkernel/BKE_blender.h
+++ b/source/blender/blenkernel/BKE_blender.h
@@ -42,7 +42,7 @@ extern "C" {
* and keep comment above the defines.
* Use STRINGIFY() rather than defining with quotes */
#define BLENDER_VERSION 274
-#define BLENDER_SUBVERSION 3
+#define BLENDER_SUBVERSION 4
/* Several breakages with 270, e.g. constraint deg vs rad */
#define BLENDER_MINVERSION 270
#define BLENDER_MINSUBVERSION 5
diff --git a/source/blender/blenkernel/BKE_camera.h b/source/blender/blenkernel/BKE_camera.h
index 4849c66df1b..aacb7a4066b 100644
--- a/source/blender/blenkernel/BKE_camera.h
+++ b/source/blender/blenkernel/BKE_camera.h
@@ -136,6 +136,14 @@ bool BKE_camera_view_frame_fit_to_coords(
void BKE_camera_to_gpu_dof(struct Object *camera, struct GPUFXSettings *r_fx_settings);
+/* Camera multi-view API */
+
+struct Object *BKE_camera_multiview_render(struct Scene *scene, struct Object *camera, const char *viewname);
+void BKE_camera_multiview_view_matrix(struct RenderData *rd, struct Object *camera, const bool is_left, float r_viewmat[4][4]);
+void BKE_camera_multiview_model_matrix(struct RenderData *rd, struct Object *camera, const char *viewname, float r_modelmat[4][4]);
+float BKE_camera_multiview_shift_x(struct RenderData *rd, struct Object *camera, const char *viewname);
+void BKE_camera_multiview_params(struct RenderData *rd, struct CameraParams *params, struct Object *camera, const char *viewname);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h
index 3b7ba24d20b..ad191968a40 100644
--- a/source/blender/blenkernel/BKE_image.h
+++ b/source/blender/blenkernel/BKE_image.h
@@ -45,34 +45,34 @@ struct Object;
struct ImageFormatData;
struct ImagePool;
struct Main;
+struct ReportList;
#define IMA_MAX_SPACE 64
void BKE_images_init(void);
void BKE_images_exit(void);
+void BKE_image_free_packedfiles(struct Image *image);
+void BKE_image_free_views(struct Image *image);
void BKE_image_free_buffers(struct Image *image);
/* call from library */
void BKE_image_free(struct Image *image);
void BKE_imbuf_stamp_info(struct Scene *scene, struct Object *camera, struct ImBuf *ibuf);
-void BKE_image_stamp_buf(
- struct Scene *scene, struct Object *camera,
- unsigned char *rect, float *rectf, int width, int height, int channels);
+void BKE_image_stamp_buf(struct Scene *scene, struct Object *camera, unsigned char *rect, float *rectf, int width, int height, int channels);
bool BKE_imbuf_alpha_test(struct ImBuf *ibuf);
int BKE_imbuf_write_stamp(struct Scene *scene, struct Object *camera, struct ImBuf *ibuf, const char *name, struct ImageFormatData *imf);
+void BKE_imbuf_write_prepare(struct ImBuf *ibuf, struct ImageFormatData *imf);
int BKE_imbuf_write(struct ImBuf *ibuf, const char *name, struct ImageFormatData *imf);
int BKE_imbuf_write_as(struct ImBuf *ibuf, const char *name, struct ImageFormatData *imf, const bool is_copy);
-
void BKE_image_path_from_imformat(
char *string, const char *base, const char *relbase, int frame,
- const struct ImageFormatData *im_format, const bool use_ext, const bool use_frames);
+ const struct ImageFormatData *im_format, const bool use_ext, const bool use_frames, const char *suffix);
void BKE_image_path_from_imtype(
char *string, const char *base, const char *relbase, int frame,
- const char imtype, const bool use_ext, const bool use_frames);
-
-bool BKE_image_path_ensure_ext_from_imformat(char *string, const struct ImageFormatData *im_format);
-bool BKE_image_path_ensure_ext_from_imtype(char *string, const char imtype);
+ const char imtype, const bool use_ext, const bool use_frames, const char *suffix);
+int BKE_image_path_ensure_ext_from_imformat(char *string, const struct ImageFormatData *im_format);
+int BKE_image_path_ensure_ext_from_imtype(char *string, const char imtype);
char BKE_image_ftype_to_imtype(const int ftype);
int BKE_image_imtype_to_ftype(const char imtype);
@@ -103,6 +103,7 @@ void BKE_image_tag_time(struct Image *ima);
/* ImageUser is in Texture, in Nodes, Background Image, Image Window, .... */
/* should be used in conjunction with an ID * to Image. */
struct ImageUser;
+struct RenderData;
struct RenderPass;
struct RenderResult;
@@ -172,11 +173,12 @@ struct Image *BKE_image_load_exists(const char *filepath);
/* adds image, adds ibuf, generates color or pattern */
struct Image *BKE_image_add_generated(
- struct Main *bmain, unsigned int width, unsigned int height, const char *name, int depth, int floatbuf, short gen_type, const float color[4]);
+ struct Main *bmain, unsigned int width, unsigned int height, const char *name, int depth, int floatbuf, short gen_type, const float color[4], const bool stereo3d);
/* adds image from imbuf, owns imbuf */
struct Image *BKE_image_add_from_imbuf(struct ImBuf *ibuf);
/* for reload, refresh, pack */
+void BKE_image_init_imageuser(struct Image *ima, struct ImageUser *iuser);
void BKE_image_signal(struct Image *ima, struct ImageUser *iuser, int signal);
void BKE_image_walk_all_users(const struct Main *mainp, void *customdata,
@@ -184,6 +186,8 @@ void BKE_image_walk_all_users(const struct Main *mainp, void *customdata,
/* ensures an Image exists for viewing nodes or render */
struct Image *BKE_image_verify_viewer(int type, const char *name);
+/* ensures the view node cache is compatible with the scene views */
+void BKE_image_verify_viewer_views(const struct RenderData *rd, struct Image *ima, struct ImageUser *iuser);
/* called on frame change or before render */
void BKE_image_user_frame_calc(struct ImageUser *iuser, int cfra, int fieldnr);
@@ -195,13 +199,23 @@ void BKE_image_update_frame(const struct Main *bmain, int cfra);
/* sets index offset for multilayer files */
struct RenderPass *BKE_image_multilayer_index(struct RenderResult *rr, struct ImageUser *iuser);
+/* sets index offset for multiview files */
+void BKE_image_multiview_index(struct Image *ima, struct ImageUser *iuser);
+
/* for multilayer images as well as for render-viewer */
+bool BKE_image_is_multilayer(struct Image *ima);
struct RenderResult *BKE_image_acquire_renderresult(struct Scene *scene, struct Image *ima);
void BKE_image_release_renderresult(struct Scene *scene, struct Image *ima);
+/* for multilayer images as well as for singlelayer */
+bool BKE_image_is_openexr(struct Image *ima);
+
/* for multiple slot render, call this before render */
void BKE_image_backup_render(struct Scene *scene, struct Image *ima);
-
+
+/* for singlelayer openexr saving */
+bool BKE_image_save_openexr_multiview(struct Image *ima, struct ImBuf *ibuf, const char *filepath, const int flags);
+
/* goes over all textures that use images */
void BKE_image_free_all_textures(void);
@@ -212,6 +226,7 @@ void BKE_image_free_anim_ibufs(struct Image *ima, int except_frame);
void BKE_image_all_free_anim_ibufs(int except_frame);
void BKE_image_memorypack(struct Image *ima);
+void BKE_image_packfiles(struct ReportList *reports, struct Image *ima, const char *basepath);
/* prints memory statistics for images */
void BKE_image_print_memlist(void);
@@ -243,7 +258,8 @@ float *BKE_image_get_float_pixels_for_frame(struct Image *image, int frame);
/* Guess offset for the first frame in the sequence */
int BKE_image_sequence_guess_offset(struct Image *image);
-
+bool BKE_image_has_anim(struct Image *image);
+bool BKE_image_has_packedfile(struct Image *image);
bool BKE_image_is_animated(struct Image *image);
bool BKE_image_is_dirty(struct Image *image);
void BKE_image_file_format_set(struct Image *image, int ftype);
diff --git a/source/blender/blenkernel/BKE_node.h b/source/blender/blenkernel/BKE_node.h
index ee63771406e..eca15a62c70 100644
--- a/source/blender/blenkernel/BKE_node.h
+++ b/source/blender/blenkernel/BKE_node.h
@@ -941,6 +941,7 @@ void ntreeGPUMaterialNodes(struct bNodeTree *ntree, struct GPUMateria
#define CMP_NODE_MAP_RANGE 319
#define CMP_NODE_PLANETRACKDEFORM 320
#define CMP_NODE_CORNERPIN 321
+#define CMP_NODE_SWITCH_VIEW 322
/* channel toggles */
#define CMP_CHAN_RGB 1
@@ -975,7 +976,8 @@ void ntreeGPUMaterialNodes(struct bNodeTree *ntree, struct GPUMateria
/* API */
void ntreeCompositExecTree(struct Scene *scene, struct bNodeTree *ntree, struct RenderData *rd, int rendering, int do_previews,
- const struct ColorManagedViewSettings *view_settings, const struct ColorManagedDisplaySettings *display_settings);
+ const struct ColorManagedViewSettings *view_settings, const struct ColorManagedDisplaySettings *display_settings,
+ const char *view_name);
void ntreeCompositTagRender(struct Scene *sce);
int ntreeCompositTagAnimated(struct bNodeTree *ntree);
void ntreeCompositTagGenerators(struct bNodeTree *ntree);
diff --git a/source/blender/blenkernel/BKE_scene.h b/source/blender/blenkernel/BKE_scene.h
index 27c89dd3a09..79778d504a5 100644
--- a/source/blender/blenkernel/BKE_scene.h
+++ b/source/blender/blenkernel/BKE_scene.h
@@ -70,6 +70,7 @@ void BKE_scene_free(struct Scene *sce);
struct Scene *BKE_scene_add(struct Main *bmain, const char *name);
/* base functions */
+struct Base *BKE_scene_base_find_by_name(struct Scene *scene, const char *name);
struct Base *BKE_scene_base_find(struct Scene *scene, struct Object *ob);
struct Base *BKE_scene_base_add(struct Scene *sce, struct Object *ob);
void BKE_scene_base_unlink(struct Scene *sce, struct Base *base);
@@ -122,6 +123,9 @@ void BKE_scene_update_for_newframe_ex(struct EvaluationContext *eval_ctx, struct
struct SceneRenderLayer *BKE_scene_add_render_layer(struct Scene *sce, const char *name);
bool BKE_scene_remove_render_layer(struct Main *main, struct Scene *scene, struct SceneRenderLayer *srl);
+struct SceneRenderView *BKE_scene_add_render_view(struct Scene *sce, const char *name);
+bool BKE_scene_remove_render_view(struct Scene *scene, struct SceneRenderView *srv);
+
/* render profile */
int get_render_subsurf_level(const struct RenderData *r, int level);
int get_render_child_particle_number(const struct RenderData *r, int num);
@@ -142,6 +146,23 @@ int BKE_render_num_threads(const struct RenderData *r);
double BKE_scene_unit_scale(const struct UnitSettings *unit, const int unit_type, double value);
+/* multiview */
+bool BKE_scene_multiview_is_stereo3d(const struct RenderData *rd);
+bool BKE_scene_multiview_is_render_view_active(const struct RenderData *rd, const struct SceneRenderView *srv);
+bool BKE_scene_multiview_is_render_view_first(const struct RenderData *rd, const char *viewname);
+bool BKE_scene_multiview_is_render_view_last(const struct RenderData *rd, const char *viewname);
+size_t BKE_scene_multiview_num_views_get(const struct RenderData *rd);
+struct SceneRenderView *BKE_scene_multiview_render_view_findindex(const struct RenderData *rd, const int view_id);
+const char *BKE_scene_multiview_render_view_name_get(const struct RenderData *rd, const int view_id);
+size_t BKE_scene_multiview_view_id_get(const struct RenderData *rd, const char *viewname);
+void BKE_scene_multiview_filepath_get(struct SceneRenderView *srv, const char *filepath, char *r_filepath);
+void BKE_scene_multiview_view_filepath_get(const struct RenderData *rd, const char *filepath, const char *view, char *r_filepath);
+const char *BKE_scene_multiview_view_suffix_get(const struct RenderData *rd, const char *viewname);
+const char *BKE_scene_multiview_view_id_suffix_get(const struct RenderData *rd, const size_t view_id);
+void BKE_scene_multiview_view_prefix_get(struct Scene *scene, const char *name, char *rprefix, char **rext);
+void BKE_scene_multiview_videos_dimensions_get(const struct RenderData *rd, const size_t width, const size_t height, size_t *r_width, size_t *r_height);
+size_t BKE_scene_multiview_num_videos_get(const struct RenderData *rd);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_sequencer.h b/source/blender/blenkernel/BKE_sequencer.h
index 84818276e7b..4a45489933c 100644
--- a/source/blender/blenkernel/BKE_sequencer.h
+++ b/source/blender/blenkernel/BKE_sequencer.h
@@ -41,6 +41,7 @@ struct Mask;
struct Scene;
struct Sequence;
struct SequenceModifierData;
+struct Stereo3dFormat;
struct StripElem;
struct bSound;
@@ -99,6 +100,7 @@ typedef struct SeqRenderData {
float motion_blur_shutter;
bool skip_cache;
bool is_proxy_render;
+ size_t view_id;
} SeqRenderData;
void BKE_sequencer_new_render_data(
@@ -223,6 +225,7 @@ void BKE_sequencer_base_clipboard_pointers_store(struct ListBase *seqbase);
void BKE_sequencer_base_clipboard_pointers_restore(struct ListBase *seqbase, struct Main *bmain);
void BKE_sequence_free(struct Scene *scene, struct Sequence *seq);
+void BKE_sequence_free_anim(struct Sequence *seq);
const char *BKE_sequence_give_name(struct Sequence *seq);
void BKE_sequence_calc(struct Scene *scene, struct Sequence *seq);
void BKE_sequence_calc_disp(struct Scene *scene, struct Sequence *seq);
@@ -235,7 +238,7 @@ struct StripElem *BKE_sequencer_give_stripelem(struct Sequence *seq, int cfra);
void BKE_sequencer_update_changed_seq_and_deps(struct Scene *scene, struct Sequence *changed_seq, int len_change, int ibuf_change);
bool BKE_sequencer_input_have_to_preprocess(const SeqRenderData *context, struct Sequence *seq, float cfra);
-struct SeqIndexBuildContext *BKE_sequencer_proxy_rebuild_context(struct Main *bmain, struct Scene *scene, struct Sequence *seq, struct GSet *file_list);
+void BKE_sequencer_proxy_rebuild_context(struct Main *bmain, struct Scene *scene, struct Sequence *seq, struct GSet *file_list, ListBase *queue);
void BKE_sequencer_proxy_rebuild(struct SeqIndexBuildContext *context, short *stop, short *do_update, float *progress);
void BKE_sequencer_proxy_rebuild_finish(struct SeqIndexBuildContext *context, bool stop);
@@ -355,6 +358,10 @@ typedef struct SeqLoadInfo {
int len; /* only for image strips */
char path[1024]; /* 1024 = FILE_MAX */
+ /* multiview */
+ char views_format;
+ struct Stereo3dFormat *stereo3d_format;
+
/* return values */
char name[64];
struct Sequence *seq_sound; /* for movie's */
@@ -399,7 +406,7 @@ struct Sequence *BKE_sequencer_add_sound_strip(struct bContext *C, ListBase *seq
struct Sequence *BKE_sequencer_add_movie_strip(struct bContext *C, ListBase *seqbasep, struct SeqLoadInfo *seq_load);
/* view3d draw callback, run when not in background view */
-typedef struct ImBuf *(*SequencerDrawView)(struct Scene *, struct Object *, int, int, unsigned int, int, bool, bool, bool, int, char[256]);
+typedef struct ImBuf *(*SequencerDrawView)(struct Scene *, struct Object *, int, int, unsigned int, int, bool, bool, bool, int, const char *, char[256]);
extern SequencerDrawView sequencer_view3d_cb;
/* copy/paste */
diff --git a/source/blender/blenkernel/BKE_writeavi.h b/source/blender/blenkernel/BKE_writeavi.h
index bc06be0295a..ca295c51f5d 100644
--- a/source/blender/blenkernel/BKE_writeavi.h
+++ b/source/blender/blenkernel/BKE_writeavi.h
@@ -43,16 +43,20 @@ struct ReportList;
struct Scene;
typedef struct bMovieHandle {
- int (*start_movie)(struct Scene *scene, struct RenderData *rd, int rectx, int recty, struct ReportList *reports, bool preview);
- int (*append_movie)(struct RenderData *rd, int start_frame, int frame, int *pixels,
- int rectx, int recty, struct ReportList *reports);
- void (*end_movie)(void);
- int (*get_next_frame)(struct RenderData *rd, struct ReportList *reports); /* optional */
- void (*get_movie_path)(char *string, struct RenderData *rd, bool preview); /* optional */
+ int (*start_movie)(void *context_v, struct Scene *scene, struct RenderData *rd, int rectx, int recty,
+ struct ReportList *reports, bool preview, const char *suffix);
+ int (*append_movie)(void *context_v, struct RenderData *rd, int start_frame, int frame, int *pixels,
+ int rectx, int recty, const char *suffix, struct ReportList *reports);
+ void (*end_movie)(void *context_v);
+ int (*get_next_frame)(void *context_v, struct RenderData *rd, struct ReportList *reports); /* optional */
+ void (*get_movie_path)(char *string, struct RenderData *rd, bool preview, const char *suffix); /* optional */
+ void *(*context_create)(void);
+ void (*context_free)(void *context_v);
} bMovieHandle;
bMovieHandle *BKE_movie_handle_get(const char imtype);
-void BKE_movie_filepath_get(char *string, struct RenderData *rd, bool preview);
+void BKE_movie_filepath_get(char *string, struct RenderData *rd, bool preview, const char *suffix);
+void BKE_context_create(bMovieHandle *mh);
#ifdef __cplusplus
}
diff --git a/source/blender/blenkernel/BKE_writeffmpeg.h b/source/blender/blenkernel/BKE_writeffmpeg.h
index 951fa501f9c..a40c31022e3 100644
--- a/source/blender/blenkernel/BKE_writeffmpeg.h
+++ b/source/blender/blenkernel/BKE_writeffmpeg.h
@@ -68,11 +68,11 @@ struct RenderData;
struct ReportList;
struct Scene;
-int BKE_ffmpeg_start(struct Scene *scene, struct RenderData *rd, int rectx, int recty, struct ReportList *reports, bool preview);
-void BKE_ffmpeg_end(void);
-int BKE_ffmpeg_append(struct RenderData *rd, int start_frame, int frame, int *pixels,
- int rectx, int recty, struct ReportList *reports);
-void BKE_ffmpeg_filepath_get(char *string, struct RenderData *rd, bool preview);
+int BKE_ffmpeg_start(void *context_v, struct Scene *scene, struct RenderData *rd, int rectx, int recty, struct ReportList *reports, bool preview, const char *suffix);
+void BKE_ffmpeg_end(void *context_v);
+int BKE_ffmpeg_append(void *context_v, struct RenderData *rd, int start_frame, int frame, int *pixels,
+ int rectx, int recty, const char *suffix, struct ReportList *reports);
+void BKE_ffmpeg_filepath_get(char *string, struct RenderData *rd, bool preview, const char *suffix);
void BKE_ffmpeg_preset_set(struct RenderData *rd, int preset);
void BKE_ffmpeg_image_type_verify(struct RenderData *rd, struct ImageFormatData *imf);
@@ -82,6 +82,9 @@ bool BKE_ffmpeg_alpha_channel_is_supported(struct RenderData *rd);
int BKE_ffmpeg_property_add_string(struct RenderData *rd, const char *type, const char *str);
void BKE_ffmpeg_property_del(struct RenderData *rd, void *type, void *prop_);
+void *BKE_ffmpeg_context_create(void);
+void BKE_ffmpeg_context_free(void *context_v);
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/BKE_writeframeserver.h b/source/blender/blenkernel/BKE_writeframeserver.h
index b7d601ea769..2f8bce31e41 100644
--- a/source/blender/blenkernel/BKE_writeframeserver.h
+++ b/source/blender/blenkernel/BKE_writeframeserver.h
@@ -40,11 +40,14 @@ struct RenderData;
struct ReportList;
struct Scene;
-int BKE_frameserver_start(struct Scene *scene, struct RenderData *rd, int rectx, int recty, struct ReportList *reports, bool preview);
-void BKE_frameserver_end(void);
-int BKE_frameserver_append(struct RenderData *rd, int start_frame, int frame, int *pixels,
- int rectx, int recty, struct ReportList *reports);
-int BKE_frameserver_loop(struct RenderData *rd, struct ReportList *reports);
+int BKE_frameserver_start(void *context_v, struct Scene *scene, struct RenderData *rd, int rectx, int recty,
+ struct ReportList *reports, bool preview, const char *suffix);
+void BKE_frameserver_end(void *context_v);
+int BKE_frameserver_append(void *context_v, struct RenderData *rd, int start_frame, int frame, int *pixels,
+ int rectx, int recty, const char*suffix, struct ReportList *reports);
+int BKE_frameserver_loop(void *context_v, struct RenderData *rd, struct ReportList *reports);
+void *BKE_frameserver_context_create(void);
+void BKE_frameserver_context_free(void *context_v);
#ifdef __cplusplus
}
diff --git a/source/blender/blenkernel/intern/bpath.c b/source/blender/blenkernel/intern/bpath.c
index e94f30da0c5..bb3731218ed 100644
--- a/source/blender/blenkernel/intern/bpath.c
+++ b/source/blender/blenkernel/intern/bpath.c
@@ -423,10 +423,10 @@ void BKE_bpath_traverse_id(Main *bmain, ID *id, BPathVisitor visit_cb, const int
{
Image *ima;
ima = (Image *)id;
- if (ima->packedfile == NULL || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) {
+ if (BKE_image_has_packedfile(ima) == false || (flag & BKE_BPATH_TRAVERSE_SKIP_PACKED) == 0) {
if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE)) {
if (rewrite_path_fixed(ima->name, visit_cb, absbase, bpath_user_data)) {
- if (!ima->packedfile) {
+ if (!BKE_image_has_packedfile(ima)) {
BKE_image_signal(ima, NULL, IMA_SIGNAL_RELOAD);
BKE_image_walk_all_users(bmain, ima, bpath_traverse_image_user_cb);
}
diff --git a/source/blender/blenkernel/intern/camera.c b/source/blender/blenkernel/intern/camera.c
index 5b4d7ba8214..fec6542ac47 100644
--- a/source/blender/blenkernel/intern/camera.c
+++ b/source/blender/blenkernel/intern/camera.c
@@ -30,16 +30,19 @@
*/
#include <stdlib.h>
+#include <stddef.h>
#include "DNA_camera_types.h"
#include "DNA_lamp_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_view3d_types.h"
+#include "DNA_ID.h"
#include "BLI_math.h"
-#include "BLI_utildefines.h"
#include "BLI_rect.h"
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
#include "BKE_animsys.h"
#include "BKE_camera.h"
@@ -47,6 +50,7 @@
#include "BKE_global.h"
#include "BKE_library.h"
#include "BKE_main.h"
+#include "BKE_scene.h"
#include "BKE_screen.h"
#include "GPU_compositing.h"
@@ -71,6 +75,10 @@ void *BKE_camera_add(Main *bmain, const char *name)
GPU_fx_compositor_init_dof_settings(&cam->gpu_dof);
+ /* stereoscopy 3d */
+ cam->stereo.interocular_distance = 0.065f;
+ cam->stereo.convergence_distance = 30.f * 0.065f;
+
return cam;
}
@@ -695,6 +703,250 @@ bool BKE_camera_view_frame_fit_to_coords(
return camera_frame_fit_calc_from_data(&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) {
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index 9b00a0150ad..f852d886ae9 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -99,6 +99,12 @@
static SpinLock image_spin;
+/* prototypes */
+static size_t image_num_files(struct Image *ima);
+static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **lock_r);
+static void image_update_views_format(Image *ima, ImageUser *iuser);
+static void image_add_view(Image *ima, const char *viewname, const char *filepath);
+
/* max int, to indicate we don't store sequences in ibuf */
#define IMA_NO_INDEX 0x7FEFEFEF
@@ -257,6 +263,46 @@ static void image_free_cached_frames(Image *image)
}
}
+static void image_free_packedfiles(Image *ima)
+{
+ while (ima->packedfiles.last) {
+ ImagePackedFile *imapf = ima->packedfiles.last;
+ if (imapf->packedfile) {
+ freePackedFile(imapf->packedfile);
+ }
+ BLI_remlink(&ima->packedfiles, imapf);
+ MEM_freeN(imapf);
+ }
+}
+
+void BKE_image_free_packedfiles(Image *ima)
+{
+ image_free_packedfiles(ima);
+}
+
+static void image_free_views(Image *ima)
+{
+ BLI_freelistN(&ima->views);
+}
+
+void BKE_image_free_views(Image *image)
+{
+ image_free_views(image);
+}
+
+static void image_free_anims(Image *ima)
+{
+ while (ima->anims.last) {
+ ImageAnim *ia = ima->anims.last;
+ if (ia->anim) {
+ IMB_free_anim(ia->anim);
+ ia->anim = NULL;
+ }
+ BLI_remlink(&ima->anims, ia);
+ MEM_freeN(ia);
+ }
+}
+
/**
* Simply free the image data from memory,
* on display the image can load again (except for render buffers).
@@ -265,8 +311,7 @@ void BKE_image_free_buffers(Image *ima)
{
image_free_cached_frames(ima);
- if (ima->anim) IMB_free_anim(ima->anim);
- ima->anim = NULL;
+ image_free_anims(ima);
if (ima->rr) {
RE_FreeRenderResult(ima->rr);
@@ -290,10 +335,9 @@ void BKE_image_free(Image *ima)
int a;
BKE_image_free_buffers(ima);
- if (ima->packedfile) {
- freePackedFile(ima->packedfile);
- ima->packedfile = NULL;
- }
+
+ image_free_packedfiles(ima);
+
BKE_icon_delete(&ima->id);
ima->id.icon_id = 0;
@@ -305,6 +349,9 @@ void BKE_image_free(Image *ima)
ima->renders[a] = NULL;
}
}
+
+ image_free_views(ima);
+ MEM_freeN(ima->stereo3d_format);
}
/* only image block itself */
@@ -328,7 +375,9 @@ static Image *image_alloc(Main *bmain, const char *name, short source, short typ
ima->flag |= IMA_VIEW_AS_RENDER;
BKE_color_managed_colorspace_settings_init(&ima->colorspace_settings);
+ ima->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Image Stereo Format");
}
+
return ima;
}
@@ -359,6 +408,22 @@ static void image_assign_ibuf(Image *ima, ImBuf *ibuf, int index, int frame)
}
}
+static void copy_image_packedfiles(ListBase *lb_dst, const ListBase *lb_src)
+{
+ const ImagePackedFile *imapf_src;
+
+ BLI_listbase_clear(lb_dst);
+ for (imapf_src = lb_src->first; imapf_src; imapf_src = imapf_src->next) {
+ ImagePackedFile *imapf_dst = MEM_mallocN(sizeof(ImagePackedFile), "Image Packed Files (copy)");
+ BLI_strncpy(imapf_dst->filepath, imapf_src->filepath, sizeof(imapf_dst->filepath));
+
+ if (imapf_src->packedfile)
+ imapf_dst->packedfile = dupPackedFile(imapf_src->packedfile);
+
+ BLI_addtail(lb_dst, imapf_dst);
+ }
+}
+
/* empty image block, of similar type and filename */
Image *BKE_image_copy(Main *bmain, Image *ima)
{
@@ -381,8 +446,10 @@ Image *BKE_image_copy(Main *bmain, Image *ima)
BKE_color_managed_colorspace_settings_copy(&nima->colorspace_settings, &ima->colorspace_settings);
- if (ima->packedfile)
- nima->packedfile = dupPackedFile(ima->packedfile);
+ copy_image_packedfiles(&nima->packedfiles, &ima->packedfiles);
+
+ nima->stereo3d_format = MEM_dupallocN(ima->stereo3d_format);
+ BLI_duplicatelist(&nima->views, &ima->views);
if (ima->id.lib) {
BKE_id_lib_local_paths(bmain, ima->id.lib, &nima->id);
@@ -686,7 +753,9 @@ Image *BKE_image_load_exists_ex(const char *filepath, bool *r_exists)
BLI_path_abs(strtest, ID_BLEND_PATH(G.main, &ima->id));
if (BLI_path_cmp(strtest, str) == 0) {
- if (ima->anim == NULL || ima->id.us == 0) {
+ if ((BKE_image_has_anim(ima) == false) ||
+ (ima->id.us == 0))
+ {
ima->id.us++; /* officially should not, it doesn't link here! */
if (ima->ok == 0)
ima->ok = IMA_OK;
@@ -774,13 +843,14 @@ static ImBuf *add_ibuf_size(unsigned int width, unsigned int height, const char
}
/* adds new image block, creates ImBuf and initializes color */
-Image *BKE_image_add_generated(Main *bmain, unsigned int width, unsigned int height, const char *name, int depth, int floatbuf, short gen_type, const float color[4])
+Image *BKE_image_add_generated(Main *bmain, unsigned int width, unsigned int height, const char *name, int depth, int floatbuf, short gen_type, const float color[4], const bool stereo3d)
{
/* on save, type is changed to FILE in editsima.c */
Image *ima = image_alloc(bmain, name, IMA_SRC_GENERATED, IMA_TYPE_UV_TEST);
if (ima) {
- ImBuf *ibuf;
+ size_t view_id;
+ const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME};
/* BLI_strncpy(ima->name, name, FILE_MAX); */ /* don't do this, this writes in ain invalid filepath! */
ima->gen_x = width;
@@ -790,13 +860,21 @@ Image *BKE_image_add_generated(Main *bmain, unsigned int width, unsigned int hei
ima->gen_depth = depth;
copy_v4_v4(ima->gen_color, color);
- ibuf = add_ibuf_size(width, height, ima->name, depth, floatbuf, gen_type, color, &ima->colorspace_settings);
- image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0);
+ for (view_id = 0; view_id < 2; view_id++) {
+ ImBuf *ibuf;
+ ibuf = add_ibuf_size(width, height, ima->name, depth, floatbuf, gen_type, color, &ima->colorspace_settings);
+ image_assign_ibuf(ima, ibuf, stereo3d ? view_id : IMA_NO_INDEX, 0);
- /* image_assign_ibuf puts buffer to the cache, which increments user counter. */
- IMB_freeImBuf(ibuf);
+ /* image_assign_ibuf puts buffer to the cache, which increments user counter. */
+ IMB_freeImBuf(ibuf);
+ if (!stereo3d) break;
+
+ image_add_view(ima, names[view_id], "");
+ }
ima->ok = IMA_OK_LOADED;
+ if (stereo3d)
+ ima->flag |= IMA_IS_STEREO | IMA_IS_MULTIVIEW;
}
return ima;
@@ -821,17 +899,76 @@ Image *BKE_image_add_from_imbuf(ImBuf *ibuf)
return ima;
}
+/* packs rects from memory as PNG
+ * convert multiview images to R_IMF_VIEWS_INDIVIDUAL
+ */
+static void image_memorypack_multiview(Image *ima)
+{
+ ImageView *iv;
+ size_t i;
+
+ image_free_packedfiles(ima);
+
+ for (i = 0, iv = ima->views.first; iv; iv = iv->next, i++) {
+ ImBuf *ibuf = image_get_cached_ibuf_for_index_frame(ima, i, 0);
+
+ ibuf->ftype = PNG;
+ ibuf->planes = R_IMF_PLANES_RGBA;
+
+ /* if the image was a R_IMF_VIEWS_STEREO_3D we force _L, _R suffices */
+ if (ima->views_format == R_IMF_VIEWS_STEREO_3D) {
+ const char *suffix[2] = {STEREO_LEFT_SUFFIX, STEREO_RIGHT_SUFFIX};
+ BLI_path_suffix(iv->filepath, FILE_MAX, suffix[i], "");
+ }
+
+ IMB_saveiff(ibuf, iv->filepath, IB_rect | IB_mem);
+
+ if (ibuf->encodedbuffer == NULL) {
+ printf("memory save for pack error\n");
+ IMB_freeImBuf(ibuf);
+ image_free_packedfiles(ima);
+ return;
+ }
+ else {
+ ImagePackedFile *imapf;
+ PackedFile *pf = MEM_callocN(sizeof(*pf), "PackedFile");
+
+ pf->data = ibuf->encodedbuffer;
+ pf->size = ibuf->encodedsize;
+
+ imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image PackedFile");
+ BLI_strncpy(imapf->filepath, iv->filepath, sizeof(imapf->filepath));
+ imapf->packedfile = pf;
+ BLI_addtail(&ima->packedfiles, imapf);
+
+ ibuf->encodedbuffer = NULL;
+ ibuf->encodedsize = 0;
+ ibuf->userflags &= ~IB_BITMAPDIRTY;
+ }
+ IMB_freeImBuf(ibuf);
+ }
+
+ if (ima->source == IMA_SRC_GENERATED) {
+ ima->source = IMA_SRC_FILE;
+ ima->type = IMA_TYPE_IMAGE;
+ }
+ ima->views_format = R_IMF_VIEWS_INDIVIDUAL;
+}
+
/* packs rect from memory as PNG */
void BKE_image_memorypack(Image *ima)
{
- ImBuf *ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0);
+ ImBuf *ibuf;
+
+ if ((ima->flag & IMA_IS_MULTIVIEW))
+ return image_memorypack_multiview(ima);
+
+ ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0);
if (ibuf == NULL)
return;
- if (ima->packedfile) {
- freePackedFile(ima->packedfile);
- ima->packedfile = NULL;
- }
+
+ image_free_packedfiles(ima);
ibuf->ftype = PNG;
ibuf->planes = R_IMF_PLANES_RGBA;
@@ -841,11 +978,17 @@ void BKE_image_memorypack(Image *ima)
printf("memory save for pack error\n");
}
else {
+ ImagePackedFile *imapf;
PackedFile *pf = MEM_callocN(sizeof(*pf), "PackedFile");
pf->data = ibuf->encodedbuffer;
pf->size = ibuf->encodedsize;
- ima->packedfile = pf;
+
+ imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image PackedFile");
+ BLI_strncpy(imapf->filepath, ima->name, sizeof(imapf->filepath));
+ imapf->packedfile = pf;
+ BLI_addtail(&ima->packedfiles, imapf);
+
ibuf->encodedbuffer = NULL;
ibuf->encodedsize = 0;
ibuf->userflags &= ~IB_BITMAPDIRTY;
@@ -859,6 +1002,28 @@ void BKE_image_memorypack(Image *ima)
IMB_freeImBuf(ibuf);
}
+void BKE_image_packfiles(ReportList *reports, Image *ima, const char *basepath)
+{
+ const size_t totfiles = image_num_files(ima);
+
+ if (totfiles == 1) {
+ ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image packed file");
+ BLI_addtail(&ima->packedfiles, imapf);
+ imapf->packedfile = newPackedFile(reports, ima->name, basepath);
+ BLI_strncpy(imapf->filepath, ima->name, sizeof(imapf->filepath));
+ }
+ else {
+ ImageView *iv;
+ for (iv = ima->views.first; iv; iv = iv->next) {
+ ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image packed file");
+ BLI_addtail(&ima->packedfiles, imapf);
+
+ imapf->packedfile = newPackedFile(reports, iv->filepath, basepath);
+ BLI_strncpy(imapf->filepath, iv->filepath, sizeof(imapf->filepath));
+ }
+ }
+}
+
void BKE_image_tag_time(Image *ima)
{
ima->lastused = PIL_check_seconds_timer_i();
@@ -1259,7 +1424,7 @@ char BKE_imtype_from_arg(const char *imtype_arg)
else return R_IMF_IMTYPE_INVALID;
}
-static bool image_path_ensure_ext(char *string, const char imtype, const ImageFormatData *im_format)
+static bool do_add_image_extension(char *string, const char imtype, const ImageFormatData *im_format)
{
const char *extension = NULL;
const char *extension_test;
@@ -1311,7 +1476,7 @@ static bool image_path_ensure_ext(char *string, const char imtype, const ImageFo
}
#endif
#ifdef WITH_OPENEXR
- else if (ELEM(imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) {
+ else if (imtype == R_IMF_IMTYPE_OPENEXR || imtype == R_IMF_IMTYPE_MULTILAYER) {
if (!BLI_testextensie(string, extension_test = ".exr"))
extension = extension_test;
}
@@ -1369,14 +1534,14 @@ static bool image_path_ensure_ext(char *string, const char imtype, const ImageFo
}
}
-bool BKE_image_path_ensure_ext_from_imformat(char *string, const ImageFormatData *im_format)
+int BKE_image_path_ensure_ext_from_imformat(char *string, const ImageFormatData *im_format)
{
- return image_path_ensure_ext(string, im_format->imtype, im_format);
+ return do_add_image_extension(string, im_format->imtype, im_format);
}
-bool BKE_image_path_ensure_ext_from_imtype(char *string, const char imtype)
+int BKE_image_path_ensure_ext_from_imtype(char *string, const char imtype)
{
- return image_path_ensure_ext(string, imtype, NULL);
+ return do_add_image_extension(string, imtype, NULL);
}
void BKE_imformat_defaults(ImageFormatData *im_format)
@@ -1908,14 +2073,12 @@ bool BKE_imbuf_alpha_test(ImBuf *ibuf)
/* note: imf->planes is ignored here, its assumed the image channels
* are already set */
-int BKE_imbuf_write(ImBuf *ibuf, const char *name, ImageFormatData *imf)
+void BKE_imbuf_write_prepare(ImBuf *ibuf, ImageFormatData *imf)
{
char imtype = imf->imtype;
char compress = imf->compress;
char quality = imf->quality;
- int ok;
-
if (imtype == R_IMF_IMTYPE_IRIS) {
ibuf->ftype = IMAGIC;
}
@@ -1952,7 +2115,7 @@ int BKE_imbuf_write(ImBuf *ibuf, const char *name, ImageFormatData *imf)
}
#endif
#ifdef WITH_OPENEXR
- else if (imtype == R_IMF_IMTYPE_OPENEXR || imtype == R_IMF_IMTYPE_MULTILAYER) {
+ else if (ELEM(imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER)) {
ibuf->ftype = OPENEXR;
if (imf->depth == R_IMF_CHAN_DEPTH_16)
ibuf->ftype |= OPENEXR_HALF;
@@ -2036,6 +2199,13 @@ int BKE_imbuf_write(ImBuf *ibuf, const char *name, ImageFormatData *imf)
if (quality < 10) quality = 90;
ibuf->ftype = JPG | quality;
}
+}
+
+int BKE_imbuf_write(ImBuf *ibuf, const char *name, ImageFormatData *imf)
+{
+ int ok;
+
+ BKE_imbuf_write_prepare(ibuf, imf);
BLI_make_existing_file(name);
@@ -2079,9 +2249,10 @@ int BKE_imbuf_write_stamp(Scene *scene, struct Object *camera, ImBuf *ibuf, cons
}
-static void image_path_makepicstring(
+static void do_makepicstring(
char *string, const char *base, const char *relbase, int frame, const char imtype,
- const ImageFormatData *im_format, const short use_ext, const short use_frames)
+ const ImageFormatData *im_format, const short use_ext, const short use_frames,
+ const char *suffix)
{
if (string == NULL) return;
BLI_strncpy(string, base, FILE_MAX - 10); /* weak assumption */
@@ -2090,22 +2261,25 @@ static void image_path_makepicstring(
if (use_frames)
BLI_path_frame(string, frame, 4);
+ if (suffix)
+ BLI_path_suffix(string, FILE_MAX, suffix, "");
+
if (use_ext)
- image_path_ensure_ext(string, imtype, im_format);
+ do_add_image_extension(string, imtype, im_format);
}
void BKE_image_path_from_imformat(
char *string, const char *base, const char *relbase, int frame,
- const ImageFormatData *im_format, const bool use_ext, const bool use_frames)
+ const ImageFormatData *im_format, const bool use_ext, const bool use_frames, const char *suffix)
{
- image_path_makepicstring(string, base, relbase, frame, im_format->imtype, im_format, use_ext, use_frames);
+ do_makepicstring(string, base, relbase, frame, im_format->imtype, im_format, use_ext, use_frames, suffix);
}
void BKE_image_path_from_imtype(
char *string, const char *base, const char *relbase, int frame,
- const char imtype, const bool use_ext, const bool use_frames)
+ const char imtype, const bool use_ext, const bool use_frames, const char *view)
{
- image_path_makepicstring(string, base, relbase, frame, imtype, NULL, use_ext, use_frames);
+ do_makepicstring(string, base, relbase, frame, imtype, NULL, use_ext, use_frames, view);
}
struct anim *openanim_noload(const char *name, int flags, int streamindex, char colorspace[IMA_MAX_SPACE])
@@ -2181,6 +2355,59 @@ Image *BKE_image_verify_viewer(int type, const char *name)
return ima;
}
+static void image_viewer_create_views(const RenderData *rd, Image *ima)
+{
+ SceneRenderView *srv;
+ for (srv = rd->views.first; srv; srv = srv->next) {
+ if (BKE_scene_multiview_is_render_view_active(rd, srv) == false)
+ continue;
+ image_add_view(ima, srv->name, "");
+ }
+}
+
+/* Reset the image cache and views when the Viewer Nodes views don't match the scene views */
+void BKE_image_verify_viewer_views(const RenderData *rd, Image *ima, ImageUser *iuser)
+{
+ bool do_reset;
+
+ BLI_lock_thread(LOCK_DRAW_IMAGE);
+
+ if (BKE_scene_multiview_is_stereo3d(rd)) {
+ ima->flag |= IMA_IS_STEREO;
+ ima->flag |= IMA_IS_MULTIVIEW;
+ }
+ else {
+ ima->flag &= ~IMA_IS_STEREO;
+ ima->flag &= ~IMA_IS_MULTIVIEW;
+ iuser->flag &= ~IMA_SHOW_STEREO;
+ }
+
+ /* see if all scene render views are in the image view list */
+ do_reset = (BKE_scene_multiview_num_views_get(rd) != BLI_listbase_count(&ima->views));
+ if (!do_reset) {
+ SceneRenderView *srv;
+ ImageView *iv;
+
+ for (iv = ima->views.first; iv; iv = iv->next) {
+ srv = BLI_findstring(&rd->views, iv->name, offsetof(SceneRenderView, name));
+ if ((srv == NULL) || (BKE_scene_multiview_is_render_view_active(rd, srv) == false)) {
+ do_reset = true;
+ break;
+ }
+ }
+ }
+
+ if (do_reset) {
+ image_free_cached_frames(ima);
+ BKE_image_free_views(ima);
+
+ /* add new views */
+ image_viewer_create_views(rd, ima);
+ }
+
+ BLI_unlock_thread(LOCK_DRAW_IMAGE);
+}
+
void BKE_image_walk_all_users(const Main *mainp, void *customdata,
void callback(Image *ima, ImageUser *iuser, void *customdata))
{
@@ -2238,6 +2465,33 @@ static void image_tag_frame_recalc(Image *ima, ImageUser *iuser, void *customdat
}
}
+static void image_init_imageuser(Image *ima, ImageUser *iuser)
+{
+ RenderResult *rr = ima->rr;
+
+ iuser->multi_index = 0;
+ iuser->layer = iuser->pass = iuser->view = 0;
+ iuser->passtype = SCE_PASS_COMBINED;
+
+ if (rr) {
+ RenderLayer *rl = rr->layers.first;
+
+ if (rl) {
+ RenderPass *rp = rl->passes.first;
+
+ if (rp)
+ iuser->passtype = rp->passtype;
+ }
+
+ BKE_image_multilayer_index(rr, iuser);
+ }
+}
+
+void BKE_image_init_imageuser(Image *ima, ImageUser *iuser)
+{
+ return image_init_imageuser(ima, iuser);
+}
+
void BKE_image_signal(Image *ima, ImageUser *iuser, int signal)
{
if (ima == NULL)
@@ -2248,8 +2502,13 @@ void BKE_image_signal(Image *ima, ImageUser *iuser, int signal)
switch (signal) {
case IMA_SIGNAL_FREE:
BKE_image_free_buffers(ima);
- if (iuser)
+
+ if (iuser) {
iuser->ok = 1;
+ if (iuser->scene) {
+ image_update_views_format(ima, iuser);
+ }
+ }
break;
case IMA_SIGNAL_SRC_CHANGE:
if (ima->type == IMA_TYPE_UV_TEST)
@@ -2301,23 +2560,41 @@ void BKE_image_signal(Image *ima, ImageUser *iuser, int signal)
case IMA_SIGNAL_RELOAD:
/* try to repack file */
- if (ima->packedfile) {
- PackedFile *pf;
- pf = newPackedFile(NULL, ima->name, ID_BLEND_PATH(G.main, &ima->id));
- if (pf) {
- freePackedFile(ima->packedfile);
- ima->packedfile = pf;
- BKE_image_free_buffers(ima);
+ if (BKE_image_has_packedfile(ima)) {
+ const size_t totfiles = image_num_files(ima);
+
+ if (totfiles != BLI_listbase_count_ex(&ima->packedfiles, totfiles + 1)) {
+ /* in case there are new available files to be loaded */
+ image_free_packedfiles(ima);
+ BKE_image_packfiles(NULL, ima, ID_BLEND_PATH(G.main, &ima->id));
}
else {
- printf("ERROR: Image not available. Keeping packed image\n");
+ ImagePackedFile *imapf;
+ for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) {
+ PackedFile *pf;
+ pf = newPackedFile(NULL, imapf->filepath, ID_BLEND_PATH(G.main, &ima->id));
+ if (pf) {
+ freePackedFile(imapf->packedfile);
+ imapf->packedfile = pf;
+ }
+ else {
+ printf("ERROR: Image \"%s\" not available. Keeping packed image\n", imapf->filepath);
+ }
+ }
}
+
+ if (BKE_image_has_packedfile(ima))
+ BKE_image_free_buffers(ima);
}
else
BKE_image_free_buffers(ima);
- if (iuser)
+ if (iuser) {
iuser->ok = 1;
+ if (iuser->scene) {
+ image_update_views_format(ima, iuser);
+ }
+ }
break;
case IMA_SIGNAL_USER_NEW_IMAGE:
@@ -2325,8 +2602,7 @@ void BKE_image_signal(Image *ima, ImageUser *iuser, int signal)
iuser->ok = 1;
if (ima->source == IMA_SRC_FILE || ima->source == IMA_SRC_SEQUENCE) {
if (ima->type == IMA_TYPE_MULTILAYER) {
- iuser->multi_index = 0;
- iuser->layer = iuser->pass = 0;
+ image_init_imageuser(ima, iuser);
}
}
}
@@ -2368,21 +2644,34 @@ RenderPass *BKE_image_multilayer_index(RenderResult *rr, ImageUser *iuser)
return NULL;
if (iuser) {
- short index = 0, rl_index = 0, rp_index;
+ short index = 0, rv_index, rl_index = 0, rp_index;
+ bool is_stereo = (iuser->flag & IMA_SHOW_STEREO) && RE_RenderResult_is_stereo(rr);
+
+ rv_index = is_stereo ? iuser->multiview_eye : iuser->view;
for (rl = rr->layers.first; rl; rl = rl->next, rl_index++) {
rp_index = 0;
- for (rpass = rl->passes.first; rpass; rpass = rpass->next, index++, rp_index++)
- if (iuser->layer == rl_index && iuser->pass == rp_index)
+
+ for (rpass = rl->passes.first; rpass; rpass = rpass->next, index++, rp_index++) {
+ if (iuser->layer == rl_index &&
+ iuser->passtype == rpass->passtype &&
+ rv_index == rpass->view_id)
+ {
break;
+ }
+ }
if (rpass)
break;
}
- if (rpass)
+ if (rpass) {
iuser->multi_index = index;
- else
+ iuser->pass = rp_index;
+ }
+ else {
iuser->multi_index = 0;
+ iuser->pass = 0;
+ }
}
if (rpass == NULL) {
rl = rr->layers.first;
@@ -2393,19 +2682,80 @@ RenderPass *BKE_image_multilayer_index(RenderResult *rr, ImageUser *iuser)
return rpass;
}
+void BKE_image_multiview_index(Image *ima, ImageUser *iuser)
+{
+ if (iuser) {
+ bool is_stereo = (ima->flag & IMA_IS_STEREO) && (iuser->flag & IMA_SHOW_STEREO);
+ if (is_stereo) {
+ iuser->multi_index = iuser->multiview_eye;
+ }
+ else {
+ if ((iuser->view < 0) || (iuser->view >= BLI_listbase_count_ex(&ima->views, iuser->view + 1))) {
+ iuser->multi_index = iuser->view = 0;
+ }
+ else {
+ iuser->multi_index = iuser->view;
+ }
+ }
+ }
+}
+
+/* if layer or pass changes, we need an index for the imbufs list */
+/* note it is called for rendered results, but it doesnt use the index! */
+/* and because rendered results use fake layer/passes, don't correct for wrong indices here */
+bool BKE_image_is_multilayer(Image *ima)
+{
+ if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE)) {
+ if (ima->type == IMA_TYPE_MULTILAYER) {
+ return true;
+ }
+ }
+ else if (ima->source == IMA_SRC_VIEWER) {
+ if (ima->type == IMA_TYPE_R_RESULT) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static void image_init_multilayer_multiview_flag(Image *ima, RenderResult *rr)
+{
+ if (rr) {
+ if (RE_RenderResult_is_stereo(rr)) {
+ ima->flag |= IMA_IS_STEREO;
+ ima->flag |= IMA_IS_MULTIVIEW;
+ }
+ else {
+ ima->flag &= ~IMA_IS_STEREO;
+ if (BLI_listbase_count_ex(&rr->views, 2) > 1)
+ ima->flag |= IMA_IS_MULTIVIEW;
+ else
+ ima->flag &= ~IMA_IS_MULTIVIEW;
+ }
+ }
+ else {
+ ima->flag &= ~IMA_IS_STEREO;
+ ima->flag &= ~IMA_IS_MULTIVIEW;
+ }
+}
+
RenderResult *BKE_image_acquire_renderresult(Scene *scene, Image *ima)
{
+ RenderResult *rr = NULL;
if (ima->rr) {
- return ima->rr;
+ rr = ima->rr;
}
else if (ima->type == IMA_TYPE_R_RESULT) {
if (ima->render_slot == ima->last_render_slot)
- return RE_AcquireResultRead(RE_GetRender(scene->id.name));
+ rr = RE_AcquireResultRead(RE_GetRender(scene->id.name));
else
- return ima->renders[ima->render_slot];
+ rr = ima->renders[ima->render_slot];
+
+ /* set proper multiview flag */
+ image_init_multilayer_multiview_flag(ima, rr);
}
- else
- return NULL;
+
+ return rr;
}
void BKE_image_release_renderresult(Scene *scene, Image *ima)
@@ -2419,6 +2769,18 @@ void BKE_image_release_renderresult(Scene *scene, Image *ima)
}
}
+bool BKE_image_is_openexr(struct Image *ima)
+{
+#ifdef WITH_OPENEXR
+ if (ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE)) {
+ return BLI_testextensie(ima->name, ".exr");
+ }
+#else
+ UNUSED_VARS(ima);
+#endif
+ return false;
+}
+
void BKE_image_backup_render(Scene *scene, Image *ima)
{
/* called right before rendering, ima->renders contains render
@@ -2439,8 +2801,155 @@ void BKE_image_backup_render(Scene *scene, Image *ima)
ima->last_render_slot = slot;
}
+/**************************** multiview save openexr *********************************/
+#ifdef WITH_OPENEXR
+static const char *image_get_view_cb(void *base, const size_t view_id)
+{
+ Image *ima = base;
+ ImageView *iv = BLI_findlink(&ima->views, view_id);
+ return iv ? iv->name : "";
+}
+#endif /* WITH_OPENEXR */
+
+#ifdef WITH_OPENEXR
+static ImBuf *image_get_buffer_cb(void *base, const size_t view_id)
+{
+ Image *ima = base;
+ ImageUser iuser = {0};
+
+ iuser.view = view_id;
+ iuser.ok = 1;
+
+ BKE_image_multiview_index(ima, &iuser);
+
+ return image_acquire_ibuf(ima, &iuser, NULL);
+}
+#endif /* WITH_OPENEXR */
+
+bool BKE_image_save_openexr_multiview(Image *ima, ImBuf *ibuf, const char *filepath, const int flags)
+{
+#ifdef WITH_OPENEXR
+ char name[FILE_MAX];
+ bool ok;
+
+ BLI_strncpy(name, filepath, sizeof(name));
+ BLI_path_abs(name, G.main->name);
+
+ ibuf->userdata = ima;
+ ok = IMB_exr_multiview_save(ibuf, name, flags, BLI_listbase_count(&ima->views), image_get_view_cb, image_get_buffer_cb);
+ ibuf->userdata = NULL;
+
+ return ok;
+#else
+ UNUSED_VARS(ima, ibuf, filepath, flags);
+ return false;
+#endif
+}
+
+/**************************** multiview load openexr *********************************/
+
+static void image_add_view(Image *ima, const char *viewname, const char *filepath)
+{
+ ImageView *iv;
+
+ iv = MEM_mallocN(sizeof(ImageView), "Viewer Image View");
+ BLI_strncpy(iv->name, viewname, sizeof(iv->name));
+ BLI_strncpy(iv->filepath, filepath, sizeof(iv->filepath));
+
+ /* For stereo drawing we need to ensure:
+ * STEREO_LEFT_NAME == STEREO_LEFT_ID and
+ * STEREO_RIGHT_NAME == STEREO_RIGHT_ID */
+
+ if (STREQ(viewname, STEREO_LEFT_NAME)) {
+ BLI_addhead(&ima->views, iv);
+ }
+ else if (STREQ(viewname, STEREO_RIGHT_NAME)) {
+ ImageView *left_iv = BLI_findstring(&ima->views, STEREO_LEFT_NAME, offsetof(ImageView, name));
+
+ if (left_iv == NULL) {
+ BLI_addhead(&ima->views, iv);
+ }
+ else {
+ BLI_insertlinkafter(&ima->views, left_iv, iv);
+ }
+ }
+ else {
+ BLI_addtail(&ima->views, iv);
+ }
+}
+
+#ifdef WITH_OPENEXR
+static void image_add_view_cb(void *base, const char *str)
+{
+ Image *ima = base;
+ image_add_view(ima, str, ima->name);
+}
+
+static void image_add_buffer_cb(void *base, const char *str, ImBuf *ibuf, const int frame)
+{
+ Image *ima = base;
+ size_t id;
+ bool predivide = (ima->alpha_mode == IMA_ALPHA_PREMUL);
+ const char *colorspace = ima->colorspace_settings.name;
+ const char *to_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR);
+
+ if (ibuf == NULL)
+ return;
+
+ id = BLI_findstringindex(&ima->views, str, offsetof(ImageView, name));
+
+ if (id == -1)
+ return;
+
+ if (ibuf->channels >= 3)
+ IMB_colormanagement_transform(ibuf->rect_float, ibuf->x, ibuf->y, ibuf->channels,
+ colorspace, to_colorspace, predivide);
+
+ image_assign_ibuf(ima, ibuf, id, frame);
+ IMB_freeImBuf(ibuf);
+}
+#endif /* WITH_OPENEXR */
+
+#ifdef WITH_OPENEXR
+static void image_update_multiview_flags(Image *ima)
+{
+ if (BLI_listbase_count_ex(&ima->views, 2) > 1) {
+ ima->flag |= IMA_IS_MULTIVIEW;
+
+ if (BLI_findstring(&ima->views, STEREO_LEFT_NAME, offsetof(ImageView, name)) &&
+ BLI_findstring(&ima->views, STEREO_RIGHT_NAME, offsetof(ImageView, name)))
+ {
+ ima->flag |= IMA_IS_STEREO;
+ }
+ else {
+ ima->flag &= ~IMA_IS_STEREO;
+ }
+ }
+ else {
+ ima->flag &= ~IMA_IS_STEREO;
+ ima->flag &= ~IMA_IS_MULTIVIEW;
+ }
+}
+#endif /* WITH_OPENEXR */
+
/* after imbuf load, openexr type can return with a exrhandle open */
/* in that case we have to build a render-result */
+#ifdef WITH_OPENEXR
+static void image_create_multiview(Image *ima, ImBuf *ibuf, const int frame)
+{
+ image_free_views(ima);
+
+ IMB_exr_multiview_convert(ibuf->userdata, ima, image_add_view_cb, image_add_buffer_cb, frame);
+
+ image_update_multiview_flags(ima);
+
+ IMB_exr_close(ibuf->userdata);
+}
+#endif /* WITH_OPENEXR */
+
+/* after imbuf load, openexr type can return with a exrhandle open */
+/* in that case we have to build a render-result */
+#ifdef WITH_OPENEXR
static void image_create_multilayer(Image *ima, ImBuf *ibuf, int framenr)
{
const char *colorspace = ima->colorspace_settings.name;
@@ -2448,14 +2957,16 @@ static void image_create_multilayer(Image *ima, ImBuf *ibuf, int framenr)
ima->rr = RE_MultilayerConvert(ibuf->userdata, colorspace, predivide, ibuf->x, ibuf->y);
-#ifdef WITH_OPENEXR
IMB_exr_close(ibuf->userdata);
-#endif
ibuf->userdata = NULL;
if (ima->rr)
ima->rr->framenr = framenr;
+
+ /* set proper multiview flag */
+ image_init_multilayer_multiview_flag(ima, ima->rr);
}
+#endif /* WITH_OPENEXR */
/* common stuff to do with images after loading */
static void image_initialize_after_load(Image *ima, ImBuf *ibuf)
@@ -2488,18 +2999,41 @@ static int imbuf_alpha_flags_for_image(Image *ima)
return flag;
}
-static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int frame)
+/* the number of files will vary according to the stereo format */
+static size_t image_num_files(Image *ima)
+{
+ const bool is_multiview = (ima->flag & IMA_IS_MULTIVIEW) != 0;
+
+ if (!is_multiview) {
+ return 1;
+ }
+ else if (ima->views_format == R_IMF_VIEWS_STEREO_3D) {
+ return 1;
+ }
+ /* R_IMF_VIEWS_INDIVIDUAL */
+ else {
+ return BLI_listbase_count(&ima->views);
+ }
+}
+
+static ImBuf *load_sequence_single(Image *ima, ImageUser *iuser, int frame, const size_t view_id, bool *r_assign)
{
struct ImBuf *ibuf;
char name[FILE_MAX];
int flag;
+ ImageUser iuser_t;
/* XXX temp stuff? */
if (ima->lastframe != frame)
ima->tpageflag |= IMA_TPAGE_REFRESH;
ima->lastframe = frame;
- BKE_image_user_file_path(iuser, ima, name);
+
+ if (iuser)
+ iuser_t = *iuser;
+
+ iuser_t.view = view_id;
+ BKE_image_user_file_path(&iuser_t, ima, name);
flag = IB_rect | IB_multilayer;
flag |= imbuf_alpha_flags_for_image(ima);
@@ -2520,25 +3054,78 @@ static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int frame)
#ifdef WITH_OPENEXR
/* handle multilayer case, don't assign ibuf. will be handled in BKE_image_acquire_ibuf */
if (ibuf->ftype == OPENEXR && ibuf->userdata) {
- image_create_multilayer(ima, ibuf, frame);
- ima->type = IMA_TYPE_MULTILAYER;
- IMB_freeImBuf(ibuf);
- ibuf = NULL;
+ /* handle singlelayer multiview case assign ibuf based on available views */
+ if (IMB_exr_has_singlelayer_multiview(ibuf->userdata)) {
+ image_create_multiview(ima, ibuf, frame);
+ IMB_freeImBuf(ibuf);
+ ibuf = NULL;
+ }
+ else if (IMB_exr_has_multilayer(ibuf->userdata)) {
+ /* handle multilayer case, don't assign ibuf. will be handled in BKE_image_acquire_ibuf */
+ image_create_multilayer(ima, ibuf, frame);
+ ima->type = IMA_TYPE_MULTILAYER;
+ IMB_freeImBuf(ibuf);
+ ibuf = NULL;
+ }
}
else {
image_initialize_after_load(ima, ibuf);
- image_assign_ibuf(ima, ibuf, 0, frame);
+ *r_assign = true;
}
#else
image_initialize_after_load(ima, ibuf);
- image_assign_ibuf(ima, ibuf, 0, frame);
+ *r_assign = true;
#endif
}
- else
- ima->ok = 0;
- if (iuser)
- iuser->ok = ima->ok;
+ return ibuf;
+}
+
+static ImBuf *image_load_sequence_file(Image *ima, ImageUser *iuser, int frame)
+{
+ struct ImBuf *ibuf = NULL;
+ const bool is_multiview = (ima->flag & IMA_IS_MULTIVIEW) != 0;
+ const size_t totfiles = image_num_files(ima);
+ bool assign = false;
+
+ if (!is_multiview) {
+ ibuf = load_sequence_single(ima, iuser, frame, 0, &assign);
+ if (assign) {
+ image_assign_ibuf(ima, ibuf, 0, frame);
+ }
+ }
+ else {
+ size_t i;
+ struct ImBuf **ibuf_arr;
+ const size_t totviews = BLI_listbase_count(&ima->views);
+
+ ibuf_arr = MEM_mallocN(sizeof(ImBuf *) * totviews, "Image Views Imbufs");
+
+ for (i = 0; i < totfiles; i++)
+ ibuf_arr[i] = load_sequence_single(ima, iuser, frame, i, &assign);
+
+ if ((ima->flag & IMA_IS_STEREO) && ima->views_format == R_IMF_VIEWS_STEREO_3D)
+ IMB_ImBufFromStereo3d(ima->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]);
+
+ /* return the original requested ImBuf */
+ ibuf = ibuf_arr[(iuser ? iuser->multi_index : 0)];
+
+ if (assign) {
+ for (i = 0; i < totviews; i++) {
+ image_assign_ibuf(ima, ibuf_arr[i], i, frame);
+ }
+ }
+
+ /* "remove" the others (decrease their refcount) */
+ for (i = 0; i < totviews; i++) {
+ if (ibuf_arr[i] != ibuf) {
+ IMB_freeImBuf(ibuf_arr[i]);
+ }
+ }
+
+ /* cleanup */
+ MEM_freeN(ibuf_arr);
+ }
return ibuf;
}
@@ -2595,46 +3182,52 @@ static ImBuf *image_load_sequence_multilayer(Image *ima, ImageUser *iuser, int f
return ibuf;
}
-
-static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame)
+static ImBuf *load_movie_single(Image *ima, ImageUser *iuser, int frame, const size_t view_id)
{
struct ImBuf *ibuf = NULL;
+ ImageAnim *ia;
- ima->lastframe = frame;
+ ia = BLI_findlink(&ima->anims, view_id);
- if (ima->anim == NULL) {
+ if (ia->anim == NULL) {
char str[FILE_MAX];
int flags = IB_rect;
+ ImageUser iuser_t;
+
if (ima->flag & IMA_DEINTERLACE) {
flags |= IB_animdeinterlace;
}
- BKE_image_user_file_path(iuser, ima, str);
+ if (iuser)
+ iuser_t = *iuser;
+
+ iuser_t.view = view_id;
+
+ BKE_image_user_file_path(&iuser_t, ima, str);
/* FIXME: make several stream accessible in image editor, too*/
- ima->anim = openanim(str, flags, 0, ima->colorspace_settings.name);
+ ia->anim = openanim(str, flags, 0, ima->colorspace_settings.name);
/* let's initialize this user */
- if (ima->anim && iuser && iuser->frames == 0)
- iuser->frames = IMB_anim_get_duration(ima->anim,
+ if (ia->anim && iuser && iuser->frames == 0)
+ iuser->frames = IMB_anim_get_duration(ia->anim,
IMB_TC_RECORD_RUN);
}
- if (ima->anim) {
- int dur = IMB_anim_get_duration(ima->anim,
+ if (ia->anim) {
+ int dur = IMB_anim_get_duration(ia->anim,
IMB_TC_RECORD_RUN);
int fra = frame - 1;
if (fra < 0) fra = 0;
if (fra > (dur - 1)) fra = dur - 1;
ibuf = IMB_makeSingleUser(
- IMB_anim_absolute(ima->anim, fra,
+ IMB_anim_absolute(ia->anim, fra,
IMB_TC_RECORD_RUN,
IMB_PROXY_NONE));
if (ibuf) {
image_initialize_after_load(ima, ibuf);
- image_assign_ibuf(ima, ibuf, 0, frame);
}
else
ima->ok = 0;
@@ -2642,67 +3235,223 @@ static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame)
else
ima->ok = 0;
+ return ibuf;
+}
+
+static ImBuf *image_load_movie_file(Image *ima, ImageUser *iuser, int frame)
+{
+ struct ImBuf *ibuf = NULL;
+ const bool is_multiview = (ima->flag & IMA_IS_MULTIVIEW) != 0;
+ const size_t totfiles = image_num_files(ima);
+ size_t i;
+
+ if (totfiles != BLI_listbase_count_ex(&ima->anims, totfiles + 1)) {
+ image_free_anims(ima);
+
+ for (i = 0; i < totfiles; i++) {
+ /* allocate the ImageAnim */
+ ImageAnim *ia = MEM_callocN(sizeof(ImageAnim), "Image Anim");
+ BLI_addtail(&ima->anims, ia);
+ }
+ }
+
+ if (!is_multiview) {
+ ibuf = load_movie_single(ima, iuser, frame, 0);
+ image_assign_ibuf(ima, ibuf, 0, frame);
+ }
+ else {
+ struct ImBuf **ibuf_arr;
+ const size_t totviews = BLI_listbase_count(&ima->views);
+
+ ibuf_arr = MEM_mallocN(sizeof(ImBuf *) * totviews, "Image Views (movie) Imbufs");
+
+ for (i = 0; i < totfiles; i++) {
+ ibuf_arr[i] = load_movie_single(ima, iuser, frame, i);
+ }
+
+ if ((ima->flag & IMA_IS_STEREO) && ima->views_format == R_IMF_VIEWS_STEREO_3D)
+ IMB_ImBufFromStereo3d(ima->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]);
+
+ for (i = 0; i < totviews; i++) {
+ if (ibuf_arr[i]) {
+ image_assign_ibuf(ima, ibuf_arr[i], i, frame);
+ }
+ else {
+ ima->ok = 0;
+ }
+ }
+
+ /* return the original requested ImBuf */
+ ibuf = ibuf_arr[(iuser ? iuser->multi_index : 0)];
+
+ /* "remove" the others (decrease their refcount) */
+ for (i = 0; i < totviews; i++) {
+ if (ibuf_arr[i] != ibuf) {
+ IMB_freeImBuf(ibuf_arr[i]);
+ }
+ }
+
+ /* cleanup */
+ MEM_freeN(ibuf_arr);
+ }
+
if (iuser)
iuser->ok = ima->ok;
return ibuf;
}
-/* warning, 'iuser' can be NULL */
-static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra)
+static ImBuf *load_image_single(
+ Image *ima, ImageUser *iuser, int cfra,
+ const size_t view_id,
+ const bool has_packed,
+ bool *r_assign)
{
- struct ImBuf *ibuf;
- char str[FILE_MAX];
- int assign = 0, flag;
-
- /* always ensure clean ima */
- BKE_image_free_buffers(ima);
+ char filepath[FILE_MAX];
+ struct ImBuf *ibuf = NULL;
+ int flag;
/* is there a PackedFile with this image ? */
- if (ima->packedfile) {
+ if (has_packed) {
+ ImagePackedFile *imapf;
+
flag = IB_rect | IB_multilayer;
flag |= imbuf_alpha_flags_for_image(ima);
- ibuf = IMB_ibImageFromMemory((unsigned char *)ima->packedfile->data, ima->packedfile->size, flag,
- ima->colorspace_settings.name, "<packed data>");
+ imapf = BLI_findlink(&ima->packedfiles, view_id);
+ ibuf = IMB_ibImageFromMemory(
+ (unsigned char *)imapf->packedfile->data, imapf->packedfile->size, flag,
+ ima->colorspace_settings.name, "<packed data>");
}
else {
+ ImageUser iuser_t;
+
flag = IB_rect | IB_multilayer | IB_metadata;
flag |= imbuf_alpha_flags_for_image(ima);
- /* get the right string */
+ /* get the correct filepath */
BKE_image_user_frame_calc(iuser, cfra, 0);
- BKE_image_user_file_path(iuser, ima, str);
+
+ if (iuser)
+ iuser_t = *iuser;
+ else
+ iuser_t.framenr = ima->lastframe;
+
+ iuser_t.view = view_id;
+
+ BKE_image_user_file_path(&iuser_t, ima, filepath);
/* read ibuf */
- ibuf = IMB_loadiffname(str, flag, ima->colorspace_settings.name);
+ ibuf = IMB_loadiffname(filepath, flag, ima->colorspace_settings.name);
}
if (ibuf) {
- /* handle multilayer case, don't assign ibuf. will be handled in BKE_image_acquire_ibuf */
+#ifdef WITH_OPENEXR
if (ibuf->ftype == OPENEXR && ibuf->userdata) {
- image_create_multilayer(ima, ibuf, cfra);
- ima->type = IMA_TYPE_MULTILAYER;
- IMB_freeImBuf(ibuf);
- ibuf = NULL;
+ if (IMB_exr_has_singlelayer_multiview(ibuf->userdata)) {
+ /* handle singlelayer multiview case assign ibuf based on available views */
+ image_create_multiview(ima, ibuf, cfra);
+ IMB_freeImBuf(ibuf);
+ ibuf = NULL;
+ }
+ else if (IMB_exr_has_multilayer(ibuf->userdata)) {
+ /* handle multilayer case, don't assign ibuf. will be handled in BKE_image_acquire_ibuf */
+ image_create_multilayer(ima, ibuf, cfra);
+ ima->type = IMA_TYPE_MULTILAYER;
+ IMB_freeImBuf(ibuf);
+ ibuf = NULL;
+ }
}
else {
image_initialize_after_load(ima, ibuf);
- assign = 1;
+ *r_assign = true;
/* check if the image is a font image... */
detectBitmapFont(ibuf);
/* make packed file for autopack */
- if ((ima->packedfile == NULL) && (G.fileflags & G_AUTOPACK))
- ima->packedfile = newPackedFile(NULL, str, ID_BLEND_PATH(G.main, &ima->id));
+ if ((has_packed == false) && (G.fileflags & G_AUTOPACK)) {
+ ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image Packefile");
+ BLI_addtail(&ima->packedfiles, imapf);
+
+ BLI_strncpy(imapf->filepath, filepath, sizeof(imapf->filepath));
+ imapf->packedfile = newPackedFile(NULL, filepath, ID_BLEND_PATH(G.main, &ima->id));
+ }
}
+#else
+ image_initialize_after_load(ima, ibuf);
+ *r_assign = true;
+#endif
}
- else
+ else {
ima->ok = 0;
+ }
- if (assign)
- image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0);
+ return ibuf;
+}
+
+/* warning, 'iuser' can be NULL
+ * note: Image->views was already populated (in image_update_views_format)
+ */
+static ImBuf *image_load_image_file(Image *ima, ImageUser *iuser, int cfra)
+{
+ struct ImBuf *ibuf = NULL;
+ bool assign = false;
+ const bool is_multiview = (ima->flag & IMA_IS_MULTIVIEW) != 0;
+ const size_t totfiles = image_num_files(ima);
+ bool has_packed = BKE_image_has_packedfile(ima);
+
+ /* always ensure clean ima */
+ BKE_image_free_buffers(ima);
+
+ /* this should never happen, but just playing safe */
+ if (has_packed) {
+ if (totfiles != BLI_listbase_count_ex(&ima->packedfiles, totfiles + 1)) {
+ image_free_packedfiles(ima);
+ has_packed = false;
+ }
+ }
+
+ if (!is_multiview) {
+ ibuf = load_image_single(ima, iuser, cfra, 0, has_packed, &assign);
+ if (assign) {
+ image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0);
+ }
+ }
+ else {
+ size_t i;
+ struct ImBuf **ibuf_arr;
+ const size_t totviews = BLI_listbase_count(&ima->views);
+ BLI_assert(totviews > 0);
+
+ ibuf_arr = MEM_mallocN(sizeof(ImBuf *) * totviews, "Image Views Imbufs");
+
+ for (i = 0; i < totfiles; i++)
+ ibuf_arr[i] = load_image_single(ima, iuser, cfra, i, has_packed, &assign);
+
+ if ((ima->flag & IMA_IS_STEREO) && ima->views_format == R_IMF_VIEWS_STEREO_3D)
+ IMB_ImBufFromStereo3d(ima->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]);
+
+ /* return the original requested ImBuf */
+ i = iuser && iuser->multi_index < totviews ? iuser->multi_index : 0;
+ ibuf = ibuf_arr[i];
+
+ if (assign) {
+ for (i = 0; i < totviews; i++) {
+ image_assign_ibuf(ima, ibuf_arr[i], i, 0);
+ }
+ }
+
+ /* "remove" the others (decrease their refcount) */
+ for (i = 0; i < totviews; i++) {
+ if (ibuf_arr[i] != ibuf) {
+ IMB_freeImBuf(ibuf_arr[i]);
+ }
+ }
+
+ /* cleanup */
+ MEM_freeN(ibuf_arr);
+ }
if (iuser)
iuser->ok = ima->ok;
@@ -2756,9 +3505,10 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **lock_
float *rectf, *rectz;
unsigned int *rect;
float dither;
- int channels, layer, pass;
+ int channels, layer, passtype;
ImBuf *ibuf;
int from_render = (ima->render_slot == ima->last_render_slot);
+ int actview;
bool byte_buffer_in_display_space = false;
if (!(iuser && iuser->scene))
@@ -2772,14 +3522,18 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **lock_
channels = 4;
layer = iuser->layer;
- pass = iuser->pass;
+ passtype = iuser->passtype;
+ actview = iuser->view;
+
+ if ((ima->flag & IMA_IS_STEREO) && (iuser->flag & IMA_SHOW_STEREO))
+ actview = iuser->multiview_eye;
if (from_render) {
- RE_AcquireResultImage(re, &rres);
+ RE_AcquireResultImage(re, &rres, actview);
}
else if (ima->renders[ima->render_slot]) {
rres = *(ima->renders[ima->render_slot]);
- rres.have_combined = rres.rectf != NULL;
+ rres.have_combined = RE_RenderViewGetRectf(&rres, actview) != NULL;
}
else
memset(&rres, 0, sizeof(RenderResult));
@@ -2819,24 +3573,18 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **lock_
if (rl) {
RenderPass *rpass;
- /* there's no combined pass, is in renderlayer itself */
- if (pass == 0) {
- rectf = rl->rectf;
- if (rectf == NULL) {
- /* Happens when Save Buffers is enabled.
- * Use display buffer stored in the render layer.
- */
- rect = (unsigned int *) rl->display_buffer;
- byte_buffer_in_display_space = true;
+ for (rpass = rl->passes.first; rpass; rpass = rpass->next) {
+ if (passtype == rpass->passtype &&
+ actview == rpass->view_id)
+ {
+ break;
}
}
- else {
- rpass = BLI_findlink(&rl->passes, pass - 1);
- if (rpass) {
- channels = rpass->channels;
- rectf = rpass->rect;
- dither = 0.0f; /* don't dither passes */
- }
+
+ if (rpass) {
+ channels = rpass->channels;
+ rectf = rpass->rect;
+ dither = 0.0f; /* don't dither passes */
}
for (rpass = rl->passes.first; rpass; rpass = rpass->next)
@@ -2925,9 +3673,31 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **lock_
return ibuf;
}
+static size_t image_get_multiview_index(Image *ima, ImageUser *iuser)
+{
+ const bool is_multilayer = BKE_image_is_multilayer(ima);
+ const bool is_backdrop = (ima->source == IMA_SRC_VIEWER) && (ima->type == IMA_TYPE_COMPOSITE) && (iuser == NULL);
+
+ if (is_multilayer) {
+ return iuser ? iuser->multi_index : IMA_NO_INDEX;
+ }
+ else if (is_backdrop) {
+ if ((ima->flag & IMA_IS_STEREO)) {
+ /* backdrop hackaround (since there is no iuser */
+ return ima->eye;
+ }
+ }
+ else if ((ima->flag & IMA_IS_MULTIVIEW)) {
+ return iuser ? iuser->multi_index : 0;
+ }
+
+ return IMA_NO_INDEX;
+}
+
static void image_get_frame_and_index(Image *ima, ImageUser *iuser, int *r_frame, int *r_index)
{
int frame = 0, index = 0;
+ index = image_get_multiview_index(ima, iuser);
/* see if we already have an appropriate ibuf, with image source and type */
if (ima->source == IMA_SRC_MOVIE) {
@@ -2939,7 +3709,6 @@ static void image_get_frame_and_index(Image *ima, ImageUser *iuser, int *r_frame
}
else if (ima->type == IMA_TYPE_MULTILAYER) {
frame = iuser ? iuser->framenr : ima->lastframe;
- index = iuser ? iuser->multi_index : IMA_NO_INDEX;
}
}
@@ -2958,10 +3727,12 @@ static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_frame,
ImBuf *ibuf = NULL;
int frame = 0, index = 0;
+ index = image_get_multiview_index(ima, iuser);
+
/* see if we already have an appropriate ibuf, with image source and type */
if (ima->source == IMA_SRC_MOVIE) {
frame = iuser ? iuser->framenr : ima->lastframe;
- ibuf = image_get_cached_ibuf_for_index_frame(ima, 0, frame);
+ ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame);
/* XXX temp stuff? */
if (ima->lastframe != frame)
ima->tpageflag |= IMA_TPAGE_REFRESH;
@@ -2970,7 +3741,7 @@ static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_frame,
else if (ima->source == IMA_SRC_SEQUENCE) {
if (ima->type == IMA_TYPE_IMAGE) {
frame = iuser ? iuser->framenr : ima->lastframe;
- ibuf = image_get_cached_ibuf_for_index_frame(ima, 0, frame);
+ ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame);
/* XXX temp stuff? */
if (ima->lastframe != frame) {
@@ -2990,18 +3761,17 @@ static ImBuf *image_get_cached_ibuf(Image *ima, ImageUser *iuser, int *r_frame,
}
else if (ima->type == IMA_TYPE_MULTILAYER) {
frame = iuser ? iuser->framenr : ima->lastframe;
- index = iuser ? iuser->multi_index : IMA_NO_INDEX;
ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame);
}
}
else if (ima->source == IMA_SRC_FILE) {
if (ima->type == IMA_TYPE_IMAGE)
- ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0);
+ ibuf = image_get_cached_ibuf_for_index_frame(ima, index, 0);
else if (ima->type == IMA_TYPE_MULTILAYER)
- ibuf = image_get_cached_ibuf_for_index_frame(ima, iuser ? iuser->multi_index : IMA_NO_INDEX, 0);
+ ibuf = image_get_cached_ibuf_for_index_frame(ima, index, 0);
}
else if (ima->source == IMA_SRC_GENERATED) {
- ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0);
+ ibuf = image_get_cached_ibuf_for_index_frame(ima, index, 0);
}
else if (ima->source == IMA_SRC_VIEWER) {
/* always verify entirely, not that this shouldn't happen
@@ -3085,7 +3855,7 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **lock_r)
if (ima->gen_depth == 0) ima->gen_depth = 24;
ibuf = add_ibuf_size(ima->gen_x, ima->gen_y, ima->name, ima->gen_depth, (ima->gen_flag & IMA_GEN_FLOAT) != 0, ima->gen_type,
ima->gen_color, &ima->colorspace_settings);
- image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0);
+ image_assign_ibuf(ima, ibuf, index, 0);
ima->ok = IMA_OK_LOADED;
}
else if (ima->source == IMA_SRC_VIEWER) {
@@ -3103,13 +3873,13 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **lock_r)
/* XXX anim play for viewer nodes not yet supported */
frame = 0; // XXX iuser ? iuser->framenr : 0;
- ibuf = image_get_cached_ibuf_for_index_frame(ima, 0, frame);
+ ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame);
if (!ibuf) {
/* Composite Viewer, all handled in compositor */
/* fake ibuf, will be filled in compositor */
- ibuf = IMB_allocImBuf(256, 256, 32, IB_rect);
- image_assign_ibuf(ima, ibuf, 0, frame);
+ ibuf = IMB_allocImBuf(256, 256, 32, IB_rect | IB_rectfloat);
+ image_assign_ibuf(ima, ibuf, index, frame);
}
}
}
@@ -3409,7 +4179,13 @@ void BKE_image_update_frame(const Main *bmain, int cfra)
void BKE_image_user_file_path(ImageUser *iuser, Image *ima, char *filepath)
{
- BLI_strncpy(filepath, ima->name, FILE_MAX);
+ if ((ima->flag & IMA_IS_MULTIVIEW) && (ima->rr == NULL)) {
+ ImageView *iv = BLI_findlink(&ima->views, iuser->view);
+ BLI_strncpy(filepath, iv->filepath, FILE_MAX);
+ }
+ else {
+ BLI_strncpy(filepath, ima->name, FILE_MAX);
+ }
if (ima->source == IMA_SRC_SEQUENCE) {
char head[FILE_MAX], tail[FILE_MAX];
@@ -3545,6 +4321,16 @@ int BKE_image_sequence_guess_offset(Image *image)
return atoi(num);
}
+bool BKE_image_has_anim(Image *ima)
+{
+ return (BLI_listbase_is_empty(&ima->anims) == false);
+}
+
+bool BKE_image_has_packedfile(Image *ima)
+{
+ return (BLI_listbase_is_empty(&ima->packedfiles) == false);
+}
+
/**
* Checks the image buffer changes (not keyframed values)
*
@@ -3675,3 +4461,88 @@ ImBuf *BKE_image_get_first_ibuf(Image *image)
return ibuf;
}
+
+static void image_update_views_format(Image *ima, ImageUser *iuser)
+{
+ SceneRenderView *srv;
+ ImageView *iv;
+ Scene *scene = iuser->scene;
+ const bool is_multiview = ((scene->r.scemode & R_MULTIVIEW) != 0) &&
+ ((ima->flag & IMA_USE_VIEWS) != 0);
+
+ /* reset the image views */
+ BKE_image_free_views(ima);
+
+ if (!is_multiview) {
+ goto monoview;
+ }
+ else if (ima->views_format == R_IMF_VIEWS_STEREO_3D) {
+ size_t i;
+ const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME};
+
+ ima->flag |= IMA_IS_MULTIVIEW;
+ ima->flag |= IMA_IS_STEREO;
+
+ for (i = 0; i < 2; i++) {
+ image_add_view(ima, names[i], ima->name);
+ }
+ return;
+ }
+ else {
+ /* R_IMF_VIEWS_INDIVIDUAL */
+ char prefix[FILE_MAX] = {'\0'};
+ char *name = ima->name;
+ char *ext = NULL;
+
+ BKE_scene_multiview_view_prefix_get(scene, name, prefix, &ext);
+
+ if (prefix[0] == '\0') {
+ goto monoview;
+ }
+
+ /* create all the image views */
+ for (srv = scene->r.views.first; srv; srv = srv->next) {
+ if (BKE_scene_multiview_is_render_view_active(&scene->r, srv)) {
+ char filepath[FILE_MAX];
+ BLI_snprintf(filepath, sizeof(filepath), "%s%s%s", prefix, srv->suffix, ext);
+ image_add_view(ima, srv->name, filepath);
+ }
+ }
+
+ /* check if the files are all available */
+ iv = ima->views.last;
+ while (iv) {
+ int file;
+ char str[FILE_MAX];
+
+ BLI_strncpy(str, iv->filepath, sizeof(str));
+ BLI_path_abs(str, G.main->name);
+
+ /* exists? */
+ file = BLI_open(str, O_BINARY | O_RDONLY, 0);
+ if (file == -1) {
+ ImageView *iv_del = iv;
+ iv = iv->prev;
+ BLI_remlink(&ima->views, iv_del);
+ MEM_freeN(iv_del);
+ }
+ else {
+ iv = iv->prev;
+ }
+ close(file);
+ }
+
+ /* all good */
+ if (BLI_listbase_count_ex(&ima->views, 2) > 1) {
+ ima->flag |= IMA_IS_MULTIVIEW;
+ if (BKE_scene_multiview_is_stereo3d(&scene->r))
+ ima->flag |= IMA_IS_STEREO;
+ }
+ else {
+monoview:
+ ima->flag &= ~IMA_IS_STEREO;
+ ima->flag &= ~IMA_IS_MULTIVIEW;
+ BKE_image_free_views(ima);
+ }
+ }
+}
diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c
index 7f3db700534..0d375b0dbeb 100644
--- a/source/blender/blenkernel/intern/node.c
+++ b/source/blender/blenkernel/intern/node.c
@@ -3475,6 +3475,7 @@ static void registerCompositNodes(void)
register_node_type_cmp_bokehimage();
register_node_type_cmp_bokehblur();
register_node_type_cmp_switch();
+ register_node_type_cmp_switch_view();
register_node_type_cmp_pixelate();
register_node_type_cmp_mask();
diff --git a/source/blender/blenkernel/intern/ocean.c b/source/blender/blenkernel/intern/ocean.c
index 81460ecdcc9..b89b5ab4bbc 100644
--- a/source/blender/blenkernel/intern/ocean.c
+++ b/source/blender/blenkernel/intern/ocean.c
@@ -1002,7 +1002,7 @@ static void cache_filename(char *string, const char *path, const char *relbase,
BLI_join_dirfile(cachepath, sizeof(cachepath), path, fname);
- BKE_image_path_from_imtype(string, cachepath, relbase, frame, R_IMF_IMTYPE_OPENEXR, true, true);
+ BKE_image_path_from_imtype(string, cachepath, relbase, frame, R_IMF_IMTYPE_OPENEXR, true, true, "");
}
/* silly functions but useful to inline when the args do a lot of indirections */
diff --git a/source/blender/blenkernel/intern/packedFile.c b/source/blender/blenkernel/intern/packedFile.c
index 9b429b356b4..61e39d0ad47 100644
--- a/source/blender/blenkernel/intern/packedFile.c
+++ b/source/blender/blenkernel/intern/packedFile.c
@@ -128,8 +128,8 @@ int countPackedFiles(Main *bmain)
/* let's check if there are packed files... */
for (ima = bmain->image.first; ima; ima = ima->id.next)
- if (ima->packedfile)
- count++;
+ if (BKE_image_has_packedfile(ima))
+ count ++;
for (vf = bmain->vfont.first; vf; vf = vf->id.next)
if (vf->packedfile)
@@ -232,9 +232,9 @@ void packAll(Main *bmain, ReportList *reports)
int tot = 0;
for (ima = bmain->image.first; ima; ima = ima->id.next) {
- if (ima->packedfile == NULL && ima->id.lib == NULL) {
+ if (BKE_image_has_packedfile(ima) == false && ima->id.lib == NULL) {
if (ima->source == IMA_SRC_FILE) {
- ima->packedfile = newPackedFile(reports, ima->name, ID_BLEND_PATH(bmain, &ima->id));
+ BKE_image_packfiles(reports, ima, ID_BLEND_PATH(bmain, &ima->id));
tot ++;
}
else if (BKE_image_is_animated(ima)) {
@@ -564,23 +564,47 @@ int unpackSound(Main *bmain, ReportList *reports, bSound *sound, int how)
int unpackImage(ReportList *reports, Image *ima, int how)
{
- char localname[FILE_MAX], absname[FILE_MAX];
- char *newname;
int ret_value = RET_ERROR;
-
+
if (ima != NULL && ima->name[0]) {
- unpack_generate_paths(ima->name, (ID *)ima, absname, localname, sizeof(absname), sizeof(localname));
- newname = unpackFile(reports, absname, localname, ima->packedfile, how);
- if (newname != NULL) {
- ret_value = RET_OK;
- freePackedFile(ima->packedfile);
- ima->packedfile = NULL;
- BLI_strncpy(ima->name, newname, sizeof(ima->name));
- MEM_freeN(newname);
- BKE_image_signal(ima, NULL, IMA_SIGNAL_RELOAD);
+ while (ima->packedfiles.last) {
+ char localname[FILE_MAX], absname[FILE_MAX];
+ char *newname;
+ ImagePackedFile *imapf = ima->packedfiles.last;
+
+ unpack_generate_paths(imapf->filepath, (ID *)ima, absname, localname, sizeof(absname), sizeof(localname));
+ newname = unpackFile(reports, absname, localname, imapf->packedfile, how);
+
+ if (newname != NULL) {
+ ImageView *iv;
+
+ ret_value = ret_value == RET_ERROR ? RET_ERROR : RET_OK;
+ freePackedFile(imapf->packedfile);
+ imapf->packedfile = NULL;
+
+ /* update the new corresponding view filepath */
+ iv = BLI_findstring(&ima->views, imapf->filepath, offsetof(ImageView, filepath));
+ if (iv) {
+ BLI_strncpy(iv->filepath, newname, sizeof(imapf->filepath));
+ }
+
+ /* keep the new name in the image for non-pack specific reasons */
+ BLI_strncpy(ima->name, newname, sizeof(imapf->filepath));
+ MEM_freeN(newname);
+ }
+ else {
+ ret_value = RET_ERROR;
+ }
+
+ BLI_remlink(&ima->packedfiles, imapf);
+ MEM_freeN(imapf);
}
}
-
+
+ if (ret_value == RET_OK) {
+ BKE_image_signal(ima, NULL, IMA_SIGNAL_RELOAD);
+ }
+
return(ret_value);
}
@@ -636,7 +660,7 @@ void unpackAll(Main *bmain, ReportList *reports, int how)
bSound *sound;
for (ima = bmain->image.first; ima; ima = ima->id.next)
- if (ima->packedfile)
+ if (BKE_image_has_packedfile(ima))
unpackImage(reports, ima, how);
for (vf = bmain->vfont.first; vf; vf = vf->id.next)
@@ -655,7 +679,7 @@ bool BKE_pack_check(ID *id)
case ID_IM:
{
Image *ima = (Image *)id;
- return ima->packedfile != NULL;
+ return BKE_image_has_packedfile(ima);
}
case ID_VF:
{
@@ -683,7 +707,7 @@ void BKE_unpack_id(Main *bmain, ID *id, ReportList *reports, int how)
case ID_IM:
{
Image *ima = (Image *)id;
- if (ima->packedfile) {
+ if (BKE_image_has_packedfile(ima)) {
unpackImage(reports, ima, how);
}
break;
diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c
index 3b48de13553..443671f5a61 100644
--- a/source/blender/blenkernel/intern/scene.c
+++ b/source/blender/blenkernel/intern/scene.c
@@ -92,6 +92,7 @@
#include "PIL_time.h"
#include "IMB_colormanagement.h"
+#include "IMB_imbuf.h"
#include "bmesh.h"
@@ -157,14 +158,16 @@ Scene *BKE_scene_copy(Scene *sce, int type)
Base *base, *obase;
if (type == SCE_COPY_EMPTY) {
- ListBase lb;
+ ListBase rl, rv;
/* XXX. main should become an arg */
scen = BKE_scene_add(G.main, sce->id.name + 2);
- lb = scen->r.layers;
+ rl = scen->r.layers;
+ rv = scen->r.views;
scen->r = sce->r;
- scen->r.layers = lb;
+ scen->r.layers = rl;
scen->r.actlay = 0;
+ scen->r.views = rv;
scen->unit = sce->unit;
scen->physics_settings = sce->physics_settings;
scen->gm = sce->gm;
@@ -197,6 +200,7 @@ Scene *BKE_scene_copy(Scene *sce, int type)
BLI_duplicatelist(&(scen->markers), &(sce->markers));
BLI_duplicatelist(&(scen->transform_spaces), &(sce->transform_spaces));
BLI_duplicatelist(&(scen->r.layers), &(sce->r.layers));
+ BLI_duplicatelist(&(scen->r.views), &(sce->r.views));
BKE_keyingsets_copy(&(scen->keyingsets), &(sce->keyingsets));
if (sce->nodetree) {
@@ -390,6 +394,7 @@ void BKE_scene_free(Scene *sce)
BLI_freelistN(&sce->markers);
BLI_freelistN(&sce->transform_spaces);
BLI_freelistN(&sce->r.layers);
+ BLI_freelistN(&sce->r.views);
if (sce->toolsettings) {
if (sce->toolsettings->vpaint) {
@@ -437,6 +442,7 @@ Scene *BKE_scene_add(Main *bmain, const char *name)
ParticleEditSettings *pset;
int a;
const char *colorspace_name;
+ SceneRenderView *srv;
sce = BKE_libblock_alloc(bmain, ID_SCE, name);
sce->lay = sce->layact = 1;
@@ -628,7 +634,16 @@ Scene *BKE_scene_add(Main *bmain, const char *name)
/* note; in header_info.c the scene copy happens..., if you add more to renderdata it has to be checked there */
BKE_scene_add_render_layer(sce, NULL);
-
+
+ /* multiview - stereo */
+ BKE_scene_add_render_view(sce, STEREO_LEFT_NAME);
+ srv = sce->r.views.first;
+ BLI_strncpy(srv->suffix, STEREO_LEFT_SUFFIX, sizeof(srv->suffix));
+
+ BKE_scene_add_render_view(sce, STEREO_RIGHT_NAME);
+ srv = sce->r.views.last;
+ BLI_strncpy(srv->suffix, STEREO_RIGHT_SUFFIX, sizeof(srv->suffix));
+
/* game data */
sce->gm.stereoflag = STEREO_NOSTEREO;
sce->gm.stereomode = STEREO_ANAGLYPH;
@@ -701,6 +716,19 @@ Scene *BKE_scene_add(Main *bmain, const char *name)
return sce;
}
+Base *BKE_scene_base_find_by_name(struct Scene *scene, const char *name)
+{
+ Base *base;
+
+ for (base = scene->base.first; base; base = base->next) {
+ if (STREQ(base->object->id.name + 2, name)) {
+ break;
+ }
+ }
+
+ return base;
+}
+
Base *BKE_scene_base_find(Scene *scene, Object *ob)
{
return BLI_findptr(&scene->base, ob, offsetof(Base, object));
@@ -1892,6 +1920,42 @@ bool BKE_scene_remove_render_layer(Main *bmain, Scene *scene, SceneRenderLayer *
return true;
}
+/* return default view */
+SceneRenderView *BKE_scene_add_render_view(Scene *sce, const char *name)
+{
+ SceneRenderView *srv;
+
+ if (!name)
+ name = DATA_("RenderView");
+
+ srv = MEM_callocN(sizeof(SceneRenderView), "new render view");
+ BLI_strncpy(srv->name, name, sizeof(srv->name));
+ BLI_uniquename(&sce->r.views, srv, DATA_("RenderView"), '.', offsetof(SceneRenderView, name), sizeof(srv->name));
+ BLI_addtail(&sce->r.views, srv);
+
+ return srv;
+}
+
+bool BKE_scene_remove_render_view(Scene *scene, SceneRenderView *srv)
+{
+ const int act = BLI_findindex(&scene->r.views, srv);
+
+ if (act == -1) {
+ return false;
+ }
+ else if (scene->r.views.first == scene->r.views.last) {
+ /* ensure 1 view is kept */
+ return false;
+ }
+
+ BLI_remlink(&scene->r.views, srv);
+ MEM_freeN(srv);
+
+ scene->r.actview = 0;
+
+ return true;
+}
+
/* render simplification */
int get_render_subsurf_level(const RenderData *r, int lvl)
@@ -2062,3 +2126,281 @@ double BKE_scene_unit_scale(const UnitSettings *unit, const int unit_type, doubl
return value;
}
}
+
+/******************** multiview *************************/
+
+size_t BKE_scene_multiview_num_views_get(const RenderData *rd)
+{
+ SceneRenderView *srv;
+ size_t totviews = 0;
+
+ if ((rd->scemode & R_MULTIVIEW) == 0)
+ return 1;
+
+ if (rd->views_format == SCE_VIEWS_FORMAT_STEREO_3D) {
+ if (BLI_findstring(&rd->views, STEREO_LEFT_NAME, offsetof(SceneRenderView, name))) {
+ totviews++;
+ }
+
+ if (BLI_findstring(&rd->views, STEREO_RIGHT_NAME, offsetof(SceneRenderView, name))) {
+ totviews++;
+ }
+ }
+ else {
+ for (srv = rd->views.first; srv; srv = srv->next) {
+ if ((srv->viewflag & SCE_VIEW_DISABLE) == 0) {
+ totviews++;
+ }
+ }
+ }
+ return totviews;
+}
+
+bool BKE_scene_multiview_is_stereo3d(const RenderData *rd)
+{
+ SceneRenderView *srv[2];
+
+ if ((rd->scemode & R_MULTIVIEW) == 0)
+ return false;
+
+ srv[0] = (SceneRenderView *)BLI_findstring(&rd->views, STEREO_LEFT_NAME, offsetof(SceneRenderView, name));
+ srv[1] = (SceneRenderView *)BLI_findstring(&rd->views, STEREO_RIGHT_NAME, offsetof(SceneRenderView, name));
+
+ return (srv[0] && ((srv[0]->viewflag & SCE_VIEW_DISABLE) == 0) &&
+ srv[1] && ((srv[1]->viewflag & SCE_VIEW_DISABLE) == 0));
+}
+
+/* return whether to render this SceneRenderView */
+bool BKE_scene_multiview_is_render_view_active(const RenderData *rd, const SceneRenderView *srv)
+{
+ if (srv == NULL)
+ return false;
+
+ if ((rd->scemode & R_MULTIVIEW) == 0)
+ return false;
+
+ if ((srv->viewflag & SCE_VIEW_DISABLE))
+ return false;
+
+ if (rd->views_format == SCE_VIEWS_FORMAT_MULTIVIEW)
+ return true;
+
+ /* SCE_VIEWS_SETUP_BASIC */
+ if (STREQ(srv->name, STEREO_LEFT_NAME) ||
+ STREQ(srv->name, STEREO_RIGHT_NAME))
+ {
+ return true;
+ }
+
+ return false;
+}
+
+/* return true if viewname is the first or if the name is NULL or not found */
+bool BKE_scene_multiview_is_render_view_first(const RenderData *rd, const char *viewname)
+{
+ SceneRenderView *srv;
+
+ if ((rd->scemode & R_MULTIVIEW) == 0)
+ return true;
+
+ if ((!viewname) || (!viewname[0]))
+ return true;
+
+ for (srv = rd->views.first; srv; srv = srv->next) {
+ if (BKE_scene_multiview_is_render_view_active(rd, srv)) {
+ return STREQ(viewname, srv->name);
+ }
+ }
+
+ return true;
+}
+
+/* return true if viewname is the last or if the name is NULL or not found */
+bool BKE_scene_multiview_is_render_view_last(const RenderData *rd, const char *viewname)
+{
+ SceneRenderView *srv;
+
+ if ((rd->scemode & R_MULTIVIEW) == 0)
+ return true;
+
+ if ((!viewname) || (!viewname[0]))
+ return true;
+
+ for (srv = rd->views.last; srv; srv = srv->prev) {
+ if (BKE_scene_multiview_is_render_view_active(rd, srv)) {
+ return STREQ(viewname, srv->name);
+ }
+ }
+
+ return true;
+}
+
+SceneRenderView *BKE_scene_multiview_render_view_findindex(const RenderData *rd, const int view_id)
+{
+ SceneRenderView *srv;
+ size_t nr;
+
+ if ((rd->scemode & R_MULTIVIEW) == 0)
+ return NULL;
+
+ nr = 0;
+ for (srv = rd->views.first, nr = 0; srv; srv = srv->next) {
+ if (BKE_scene_multiview_is_render_view_active(rd, srv)) {
+ if (nr++ == view_id)
+ return srv;
+ }
+ }
+ return srv;
+}
+
+const char *BKE_scene_multiview_render_view_name_get(const RenderData *rd, const int view_id)
+{
+ SceneRenderView *srv = BKE_scene_multiview_render_view_findindex(rd, view_id);
+
+ if (srv)
+ return srv->name;
+ else
+ return "";
+}
+
+size_t BKE_scene_multiview_view_id_get(const RenderData *rd, const char *viewname)
+{
+ SceneRenderView *srv;
+ size_t nr;
+
+ if ((!rd) || ((rd->scemode & R_MULTIVIEW) == 0))
+ return 0;
+
+ if ((!viewname) || (!viewname[0]))
+ return 0;
+
+ nr = 0;
+ for (srv = rd->views.first, nr = 0; srv; srv = srv->next) {
+ if (BKE_scene_multiview_is_render_view_active(rd, srv)) {
+ if (STREQ(viewname, srv->name)) {
+ return nr;
+ }
+ else {
+ nr += 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+void BKE_scene_multiview_filepath_get(
+ SceneRenderView *srv, const char *filepath,
+ char *r_filepath)
+{
+ BLI_strncpy(r_filepath, filepath, FILE_MAX);
+ BLI_path_suffix(r_filepath, FILE_MAX, srv->suffix, "");
+}
+
+/**
+ * When multiview is not used the filepath is as usual (e.g., ``Image.jpg``).
+ * When multiview is on, even if only one view is enabled the view is incorporated
+ * into the file name (e.g., ``Image_L.jpg``). That allows for the user to re-render
+ * individual views.
+ */
+void BKE_scene_multiview_view_filepath_get(
+ const RenderData *rd, const char *filepath, const char *viewname,
+ char *r_filepath)
+{
+ SceneRenderView *srv;
+ char suffix[FILE_MAX];
+
+ srv = BLI_findstring(&rd->views, viewname, offsetof(SceneRenderView, name));
+ if (srv)
+ BLI_strncpy(suffix, srv->suffix, sizeof(suffix));
+ else
+ BLI_strncpy(suffix, viewname, sizeof(suffix));
+
+ BLI_strncpy(r_filepath, filepath, FILE_MAX);
+ BLI_path_suffix(r_filepath, FILE_MAX, suffix, "");
+}
+
+const char *BKE_scene_multiview_view_suffix_get(const RenderData *rd, const char *viewname)
+{
+ SceneRenderView *srv;
+
+ if ((viewname == NULL) || (viewname[0] == '\0'))
+ return viewname;
+
+ srv = BLI_findstring(&rd->views, viewname, offsetof(SceneRenderView, name));
+ if (srv)
+ return srv->suffix;
+ else
+ return viewname;
+}
+
+const char *BKE_scene_multiview_view_id_suffix_get(const RenderData *rd, const size_t view_id)
+{
+ if ((rd->scemode & R_MULTIVIEW) == 0) {
+ return "";
+ }
+ else {
+ const char *viewname = BKE_scene_multiview_render_view_name_get(rd, view_id);
+ return BKE_scene_multiview_view_suffix_get(rd, viewname);
+ }
+}
+
+void BKE_scene_multiview_view_prefix_get(Scene *scene, const char *name, char *rprefix, char **rext)
+{
+ SceneRenderView *srv;
+ size_t index_act;
+ char *suf_act;
+ const char delims[] = {'.', '\0'};
+
+ rprefix[0] = '\0';
+
+ /* begin of extension */
+ index_act = BLI_str_rpartition(name, delims, rext, &suf_act);
+ BLI_assert(index_act > 0);
+
+ for (srv = scene->r.views.first; srv; srv = srv->next) {
+ if (BKE_scene_multiview_is_render_view_active(&scene->r, srv)) {
+ size_t len = strlen(srv->suffix);
+ if (STREQLEN(*rext - len, srv->suffix, len)) {
+ BLI_strncpy(rprefix, name, strlen(name) - strlen(*rext) - len + 1);
+ break;
+ }
+ }
+ }
+}
+
+void BKE_scene_multiview_videos_dimensions_get(
+ const RenderData *rd, const size_t width, const size_t height,
+ size_t *r_width, size_t *r_height)
+{
+ if ((rd->scemode & R_MULTIVIEW) &&
+ rd->im_format.views_format == R_IMF_VIEWS_STEREO_3D)
+ {
+ IMB_stereo3d_write_dimensions(
+ rd->im_format.stereo3d_format.display_mode,
+ (rd->im_format.stereo3d_format.flag & S3D_SQUEEZED_FRAME) != 0,
+ width, height,
+ r_width, r_height);
+ }
+ else {
+ *r_width = width;
+ *r_height = height;
+ }
+}
+
+size_t BKE_scene_multiview_num_videos_get(const RenderData *rd)
+{
+ if (BKE_imtype_is_movie(rd->im_format.imtype) == false)
+ return 0;
+
+ if ((rd->scemode & R_MULTIVIEW) == 0)
+ return 1;
+
+ if (rd->im_format.views_format == R_IMF_VIEWS_STEREO_3D) {
+ return 1;
+ }
+ else {
+ /* R_IMF_VIEWS_INDIVIDUAL */
+ return BKE_scene_multiview_num_views_get(rd);
+ }
+}
diff --git a/source/blender/blenkernel/intern/seqcache.c b/source/blender/blenkernel/intern/seqcache.c
index 41f17665065..abfc858fd03 100644
--- a/source/blender/blenkernel/intern/seqcache.c
+++ b/source/blender/blenkernel/intern/seqcache.c
@@ -33,6 +33,7 @@
#include "MEM_guardedalloc.h"
#include "DNA_sequence_types.h"
+#include "DNA_scene_types.h"
#include "IMB_moviecache.h"
#include "IMB_imbuf.h"
@@ -41,6 +42,7 @@
#include "BLI_listbase.h"
#include "BKE_sequencer.h"
+#include "BKE_scene.h"
typedef struct SeqCacheKey {
struct Sequence *seq;
@@ -77,7 +79,9 @@ static bool seq_cmp_render_data(const SeqRenderData *a, const SeqRenderData *b)
(a->bmain != b->bmain) ||
(a->scene != b->scene) ||
(a->motion_blur_shutter != b->motion_blur_shutter) ||
- (a->motion_blur_samples != b->motion_blur_samples));
+ (a->motion_blur_samples != b->motion_blur_samples) ||
+ (a->scene->r.views_format != b->scene->r.views_format) ||
+ (a->view_id != b->view_id));
}
static unsigned int seq_hash_render_data(const SeqRenderData *a)
@@ -89,6 +93,7 @@ static unsigned int seq_hash_render_data(const SeqRenderData *a)
rval ^= ((intptr_t) a->scene) << 6;
rval ^= (int)(a->motion_blur_shutter * 100.0f) << 10;
rval ^= a->motion_blur_samples << 24;
+ rval ^= ((a->scene->r.views_format * 2) + a->view_id) << 32;
return rval;
}
diff --git a/source/blender/blenkernel/intern/sequencer.c b/source/blender/blenkernel/intern/sequencer.c
index 721555097d7..942426febdd 100644
--- a/source/blender/blenkernel/intern/sequencer.c
+++ b/source/blender/blenkernel/intern/sequencer.c
@@ -53,6 +53,12 @@
#include "BLI_threads.h"
#include "BLI_utildefines.h"
+#ifdef WIN32
+# include "BLI_winstuff.h"
+#else
+# include <unistd.h>
+#endif
+
#include "BLF_translation.h"
#include "BKE_animsys.h"
@@ -89,6 +95,8 @@ static ImBuf *seq_render_strip_stack(const SeqRenderData *context, ListBase *seq
static ImBuf *seq_render_strip(const SeqRenderData *context, Sequence *seq, float cfra);
static void seq_free_animdata(Scene *scene, Sequence *seq);
static ImBuf *seq_render_mask(const SeqRenderData *context, Mask *mask, float nr, bool make_float);
+static size_t seq_num_files(Scene *scene, char views_format, const bool is_multiview);
+static void seq_anim_add_suffix(Scene *scene, struct anim *anim, const size_t view_id);
/* **** XXX ******** */
#define SELECT 1
@@ -181,10 +189,7 @@ static void BKE_sequence_free_ex(Scene *scene, Sequence *seq, const bool do_cach
if (seq->strip)
seq_free_strip(seq->strip);
- if (seq->anim) {
- IMB_free_anim(seq->anim);
- seq->anim = NULL;
- }
+ BKE_sequence_free_anim(seq);
if (seq->type & SEQ_TYPE_EFFECT) {
struct SeqEffectHandle sh = BKE_sequence_get_effect(seq);
@@ -196,6 +201,10 @@ static void BKE_sequence_free_ex(Scene *scene, Sequence *seq, const bool do_cach
((ID *)seq->sound)->us--;
}
+ if (seq->stereo3d_format) {
+ MEM_freeN(seq->stereo3d_format);
+ }
+
/* clipboard has no scene and will never have a sound handle or be active
* same goes to sequences copy for proxy rebuild job
*/
@@ -240,6 +249,22 @@ void BKE_sequence_free(Scene *scene, Sequence *seq)
BKE_sequence_free_ex(scene, seq, true);
}
+/* Function to free imbuf and anim data on changes */
+void BKE_sequence_free_anim(Sequence *seq)
+{
+ while (seq->anims.last) {
+ StripAnim *sanim = seq->anims.last;
+ BLI_remlink(&seq->anims, sanim);
+
+ if (sanim->anim) {
+ IMB_free_anim(sanim->anim);
+ sanim->anim = NULL;
+ }
+
+ MEM_freeN(sanim);
+ }
+}
+
/* cache must be freed before calling this function
* since it leaves the seqbase in an invalid state */
static void seq_free_sequence_recurse(Scene *scene, Sequence *seq)
@@ -537,6 +562,7 @@ void BKE_sequencer_new_render_data(
r_context->motion_blur_shutter = 0;
r_context->skip_cache = false;
r_context->is_proxy_render = false;
+ r_context->view_id = 0;
}
/* ************************* iterator ************************** */
@@ -771,10 +797,17 @@ void BKE_sequence_calc(Scene *scene, Sequence *seq)
}
}
+static void seq_multiview_name(Scene *scene, const size_t view_id, const char *prefix,
+ const char *ext, char *r_path, size_t r_size)
+{
+ const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, view_id);
+ BLI_snprintf(r_path, r_size, "%s%s%s", prefix, suffix, ext);
+}
+
/* note: caller should run BKE_sequence_calc(scene, seq) after */
void BKE_sequence_reload_new_file(Scene *scene, Sequence *seq, const bool lock_range)
{
- char str[FILE_MAX];
+ char path[FILE_MAX];
int prev_startdisp = 0, prev_enddisp = 0;
/* note: don't rename the strip, will break animation curves */
@@ -807,22 +840,67 @@ void BKE_sequence_reload_new_file(Scene *scene, Sequence *seq, const bool lock_r
break;
}
case SEQ_TYPE_MOVIE:
- BLI_join_dirfile(str, sizeof(str), seq->strip->dir,
+ {
+ StripAnim *sanim;
+ bool is_multiview_loaded = false;
+ const bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 &&
+ (scene->r.scemode & R_MULTIVIEW) != 0;
+
+ BLI_join_dirfile(path, sizeof(path), seq->strip->dir,
seq->strip->stripdata->name);
- BLI_path_abs(str, G.main->name);
+ BLI_path_abs(path, G.main->name);
+
+ BKE_sequence_free_anim(seq);
+
+ if (is_multiview && (seq->views_format == R_IMF_VIEWS_INDIVIDUAL)) {
+ char prefix[FILE_MAX];
+ char *ext = NULL;
+ size_t totfiles = seq_num_files(scene, seq->views_format, true);
+ int i = 0;
+
+ BKE_scene_multiview_view_prefix_get(scene, path, prefix, &ext);
- if (seq->anim) IMB_free_anim(seq->anim);
+ if (prefix[0] != '\0') {
+ for (i = 0; i < totfiles; i++) {
+ struct anim *anim;
+ char str[FILE_MAX];
- seq->anim = openanim(str, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0),
- seq->streamindex, seq->strip->colorspace_settings.name);
+ seq_multiview_name(scene, i, prefix, ext, str, FILE_MAX);
+ anim = openanim(str, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0),
+ seq->streamindex, seq->strip->colorspace_settings.name);
+ seq_anim_add_suffix(scene, anim, i);
- if (!seq->anim) {
+ if (anim) {
+ sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim");
+ BLI_addtail(&seq->anims, sanim);
+ sanim->anim = anim;
+ }
+ }
+ is_multiview_loaded = true;
+ }
+ }
+
+ if (is_multiview_loaded == false) {
+ struct anim *anim;
+ anim = openanim(path, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0),
+ seq->streamindex, seq->strip->colorspace_settings.name);
+ if (anim) {
+ sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim");
+ BLI_addtail(&seq->anims, sanim);
+ sanim->anim = anim;
+ }
+ }
+
+ /* use the first video as reference for everything */
+ sanim = seq->anims.first;
+
+ if ((!sanim) || (!sanim->anim)) {
return;
}
- seq->len = IMB_anim_get_duration(seq->anim, seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN);
-
- seq->anim_preseek = IMB_anim_get_preseek(seq->anim);
+ seq->len = IMB_anim_get_duration(sanim->anim, seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN);
+
+ seq->anim_preseek = IMB_anim_get_preseek(sanim->anim);
seq->len -= seq->anim_startofs;
seq->len -= seq->anim_endofs;
@@ -830,6 +908,7 @@ void BKE_sequence_reload_new_file(Scene *scene, Sequence *seq, const bool lock_r
seq->len = 0;
}
break;
+ }
case SEQ_TYPE_MOVIECLIP:
if (seq->clip == NULL)
return;
@@ -1297,6 +1376,7 @@ typedef struct SeqIndexBuildContext {
int size_flags;
int quality;
bool overwrite;
+ size_t view_id;
Main *bmain;
Scene *scene;
@@ -1336,44 +1416,59 @@ static double seq_rendersize_to_scale_factor(int size)
return 0.25;
}
-static void seq_open_anim_file(Editing *ed, Sequence *seq, bool openfile)
+/* the number of files will vary according to the stereo format */
+static size_t seq_num_files(Scene *scene, char views_format, const bool is_multiview)
+{
+ if (!is_multiview) {
+ return 1;
+ }
+ else if (views_format == R_IMF_VIEWS_STEREO_3D) {
+ return 1;
+ }
+ /* R_IMF_VIEWS_INDIVIDUAL */
+ else {
+ return BKE_scene_multiview_num_views_get(&scene->r);
+ }
+}
+
+static void seq_proxy_index_dir_set(struct anim *anim, const char *base_dir)
+{
+ char dir[FILE_MAX];
+ char fname[FILE_MAXFILE];
+
+ IMB_anim_get_fname(anim, fname, FILE_MAXFILE);
+ BLI_strncpy(dir, base_dir, sizeof(dir));
+ BLI_path_append(dir, sizeof(dir), fname);
+ IMB_anim_set_index_dir(anim, dir);
+}
+
+static void seq_open_anim_file(Scene *scene, Sequence *seq, bool openfile)
{
+ char dir[FILE_MAX];
char name[FILE_MAX];
StripProxy *proxy;
+ bool use_proxy;
+ bool is_multiview_loaded = false;
+ Editing *ed = scene->ed;
+ const bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 && (scene->r.scemode & R_MULTIVIEW) != 0;
- if (seq->anim != NULL) {
+ if ((seq->anims.first != NULL) && (((StripAnim *)seq->anims.first)->anim != NULL)) {
return;
}
+ /* reset all the previously created anims */
+ BKE_sequence_free_anim(seq);
+
BLI_join_dirfile(name, sizeof(name),
seq->strip->dir, seq->strip->stripdata->name);
BLI_path_abs(name, G.main->name);
-
- if (openfile) {
- seq->anim = openanim(name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0),
- seq->streamindex, seq->strip->colorspace_settings.name);
- }
- else {
- seq->anim = openanim_noload(name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0),
- seq->streamindex, seq->strip->colorspace_settings.name);
- }
-
- if (seq->anim == NULL) {
- return;
- }
proxy = seq->strip->proxy;
- if (proxy == NULL) {
- return;
- }
-
- if ((proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR) ||
- (ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE))
- {
- char dir[FILE_MAX];
- char fname[FILE_MAXFILE];
+ use_proxy = proxy && ((proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR) != 0 ||
+ (ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE));
+ if (use_proxy) {
if (ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE) {
if (ed->proxy_dir[0] == 0)
BLI_strncpy(dir, "//BL_proxy", sizeof(dir));
@@ -1383,21 +1478,93 @@ static void seq_open_anim_file(Editing *ed, Sequence *seq, bool openfile)
else {
BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir));
}
+ BLI_path_abs(dir, G.main->name);
+ }
- IMB_anim_get_fname(seq->anim, fname, FILE_MAXFILE);
- BLI_path_append(dir, sizeof(dir), fname);
+ if (is_multiview && seq->views_format == R_IMF_VIEWS_INDIVIDUAL) {
+ size_t totfiles = seq_num_files(scene, seq->views_format, true);
+ char prefix[FILE_MAX];
+ char *ext = NULL;
+ int i;
- BLI_path_abs(dir, G.main->name);
+ BKE_scene_multiview_view_prefix_get(scene, name, prefix, &ext);
+
+ if (prefix[0] != '\0') {
+ for (i = 0; i < totfiles; i++) {
+ const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, i);
+ char str[FILE_MAX];
+ StripAnim *sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim");
- IMB_anim_set_index_dir(seq->anim, dir);
+ BLI_addtail(&seq->anims, sanim);
+
+ BLI_snprintf(str, sizeof(str), "%s%s%s", prefix, suffix, ext);
+
+ if (openfile) {
+ sanim->anim = openanim(
+ str, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0),
+ seq->streamindex, seq->strip->colorspace_settings.name);
+ }
+ else {
+ sanim->anim = openanim_noload(
+ str, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0),
+ seq->streamindex, seq->strip->colorspace_settings.name);
+ }
+
+ seq_anim_add_suffix(scene, sanim->anim, i);
+
+ if (sanim->anim == NULL) {
+ if (openfile) {
+ sanim->anim = openanim(
+ name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0),
+ seq->streamindex, seq->strip->colorspace_settings.name);
+ }
+ else {
+ sanim->anim = openanim_noload(
+ name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0),
+ seq->streamindex, seq->strip->colorspace_settings.name);
+ }
+
+ /* no individual view files - monoscopic, stereo 3d or exr multiview */
+ totfiles = 1;
+ }
+
+ if (sanim->anim && use_proxy) {
+ seq_proxy_index_dir_set(sanim->anim, dir);
+ }
+ }
+ is_multiview_loaded = true;
+ }
}
-}
+ if (is_multiview_loaded == false) {
+ StripAnim *sanim;
+
+ sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim");
+ BLI_addtail(&seq->anims, sanim);
-static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render_size, char *name)
+ if (openfile) {
+ sanim->anim = openanim(
+ name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0),
+ seq->streamindex, seq->strip->colorspace_settings.name);
+ }
+ else {
+ sanim->anim = openanim_noload(
+ name, IB_rect | ((seq->flag & SEQ_FILTERY) ? IB_animdeinterlace : 0),
+ seq->streamindex, seq->strip->colorspace_settings.name);
+ }
+
+ if (sanim->anim && use_proxy) {
+ seq_proxy_index_dir_set(sanim->anim, dir);
+ }
+ }
+}
+
+static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render_size, char *name, const size_t view_id)
{
int frameno;
char dir[PROXY_MAXFILE];
+ StripAnim *sanim;
+ char suffix[24] = {'\0'};
StripProxy *proxy = seq->strip->proxy;
if (!proxy) {
@@ -1412,23 +1579,25 @@ static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render
* have both, a directory full of jpeg files and proxy avis, so
* sorry folks, please rebuild your proxies... */
- if (seq->anim && ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE) {
+ sanim = BLI_findlink(&seq->anims, view_id);
+
+ if (sanim && sanim->anim && ed->proxy_storage == SEQ_EDIT_PROXY_DIR_STORAGE) {
char fname[FILE_MAXFILE];
if (ed->proxy_dir[0] == 0)
BLI_strncpy(dir, "//BL_proxy", sizeof(dir));
else
BLI_strncpy(dir, ed->proxy_dir, sizeof(dir));
- IMB_anim_get_fname(seq->anim, fname, FILE_MAXFILE);
+ IMB_anim_get_fname(sanim->anim, fname, FILE_MAXFILE);
BLI_path_append(dir, sizeof(dir), fname);
BLI_path_abs(name, G.main->name);
}
else if ((proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR) && (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE)) {
BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir));
}
- else if (seq->anim && (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR)) {
+ else if (sanim && sanim->anim && (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_DIR)) {
char fname[FILE_MAXFILE];
BLI_strncpy(dir, seq->strip->proxy->dir, sizeof(dir));
- IMB_anim_get_fname(seq->anim, fname, FILE_MAXFILE);
+ IMB_anim_get_fname(sanim->anim, fname, FILE_MAXFILE);
BLI_path_append(dir, sizeof(dir), fname);
}
else if (seq->type == SEQ_TYPE_IMAGE) {
@@ -1438,12 +1607,16 @@ static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render
return false;
}
- if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE && seq->anim &&
+ if (view_id > 0)
+ BLI_snprintf(suffix, sizeof(suffix), "_%zu", view_id);
+
+ if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE && sanim && sanim->anim &&
ed->proxy_storage != SEQ_EDIT_PROXY_DIR_STORAGE)
{
BLI_join_dirfile(name, PROXY_MAXFILE,
dir, proxy->file);
BLI_path_abs(name, G.main->name);
+ BLI_snprintf(name, PROXY_MAXFILE, "%s_%s", name, suffix);
return true;
}
@@ -1451,13 +1624,13 @@ static bool seq_proxy_get_fname(Editing *ed, Sequence *seq, int cfra, int render
/* generate a separate proxy directory for each preview size */
if (seq->type == SEQ_TYPE_IMAGE) {
- BLI_snprintf(name, PROXY_MAXFILE, "%s/images/%d/%s_proxy", dir, render_size,
- BKE_sequencer_give_stripelem(seq, cfra)->name);
+ BLI_snprintf(name, PROXY_MAXFILE, "%s/images/%d/%s_proxy%s", dir, render_size,
+ BKE_sequencer_give_stripelem(seq, cfra)->name, suffix);
frameno = 1;
}
else {
frameno = (int)give_stripelem_index(seq, cfra) + seq->anim_startofs;
- BLI_snprintf(name, PROXY_MAXFILE, "%s/proxy_misc/%d/####", dir, render_size);
+ BLI_snprintf(name, PROXY_MAXFILE, "%s/proxy_misc/%d/####%s", dir, render_size, suffix);
}
BLI_path_abs(name, G.main->name);
@@ -1476,6 +1649,7 @@ static ImBuf *seq_proxy_fetch(const SeqRenderData *context, Sequence *seq, int c
int render_size = context->preview_render_size;
StripProxy *proxy = seq->strip->proxy;
Editing *ed = context->scene->ed;
+ StripAnim *sanim;
if (!(seq->flag & SEQ_USE_PROXY)) {
return NULL;
@@ -1496,7 +1670,7 @@ static ImBuf *seq_proxy_fetch(const SeqRenderData *context, Sequence *seq, int c
if (proxy->storage & SEQ_STORAGE_PROXY_CUSTOM_FILE) {
int frameno = (int)give_stripelem_index(seq, cfra) + seq->anim_startofs;
if (proxy->anim == NULL) {
- if (seq_proxy_get_fname(ed, seq, cfra, render_size, name) == 0) {
+ if (seq_proxy_get_fname(ed, seq, cfra, render_size, name, context->view_id) == 0) {
return NULL;
}
@@ -1506,14 +1680,15 @@ static ImBuf *seq_proxy_fetch(const SeqRenderData *context, Sequence *seq, int c
return NULL;
}
- seq_open_anim_file(context->scene->ed, seq, true);
+ seq_open_anim_file(context->scene, seq, true);
+ sanim = seq->anims.first;
- frameno = IMB_anim_index_get_frame_index(seq->anim, proxy->tc, frameno);
+ frameno = IMB_anim_index_get_frame_index(sanim ? sanim->anim : NULL, seq->strip->proxy->tc, frameno);
return IMB_anim_absolute(proxy->anim, frameno, IMB_TC_NONE, IMB_PROXY_NONE);
}
- if (seq_proxy_get_fname(ed, seq, cfra, render_size, name) == 0) {
+ if (seq_proxy_get_fname(ed, seq, cfra, render_size, name, context->view_id) == 0) {
return NULL;
}
@@ -1540,7 +1715,7 @@ static void seq_proxy_build_frame(const SeqRenderData *context, Sequence *seq, i
ImBuf *ibuf_tmp, *ibuf;
Editing *ed = context->scene->ed;
- if (!seq_proxy_get_fname(ed, seq, cfra, proxy_render_size, name)) {
+ if (!seq_proxy_get_fname(ed, seq, cfra, proxy_render_size, name, context->view_id)) {
return;
}
@@ -1581,44 +1756,130 @@ static void seq_proxy_build_frame(const SeqRenderData *context, Sequence *seq, i
IMB_freeImBuf(ibuf);
}
-SeqIndexBuildContext *BKE_sequencer_proxy_rebuild_context(Main *bmain, Scene *scene, Sequence *seq, struct GSet *file_list)
+/* returns whether the file this context would read from even exist, if not, don't create the context
+*/
+static bool seq_proxy_multiview_context_invalid(Sequence *seq, Scene *scene, const size_t view_id)
+{
+ if ((scene->r.scemode & R_MULTIVIEW) == 0)
+ return false;
+
+ if ((seq->type == SEQ_TYPE_IMAGE) && (seq->views_format == R_IMF_VIEWS_INDIVIDUAL)) {
+ static char prefix[FILE_MAX];
+ static char *ext = NULL;
+ char str[FILE_MAX];
+
+ if (view_id == 0) {
+ char path[FILE_MAX];
+ BLI_join_dirfile(path, sizeof(path), seq->strip->dir,
+ seq->strip->stripdata->name);
+ BLI_path_abs(path, G.main->name);
+ BKE_scene_multiview_view_prefix_get(scene, path, prefix, &ext);
+ }
+
+ if (prefix[0] == '\0')
+ return view_id != 0;
+
+ seq_multiview_name(scene, view_id, prefix, ext, str, FILE_MAX);
+
+ if (BLI_access(str, R_OK) == 0)
+ return false;
+ else
+ return view_id != 0;
+ }
+ return false;
+}
+
+/** This returns the maximum possible number of required contexts
+*/
+static size_t seq_proxy_context_count(Sequence *seq, Scene *scene)
+{
+ size_t num_views = 1;
+
+ if ((scene->r.scemode & R_MULTIVIEW) == 0)
+ return 1;
+
+ switch (seq->type) {
+ case SEQ_TYPE_MOVIE:
+ {
+ num_views = BLI_listbase_count(&seq->anims);
+ break;
+ }
+ case SEQ_TYPE_IMAGE:
+ {
+ switch (seq->views_format) {
+ case R_IMF_VIEWS_INDIVIDUAL:
+ num_views = BKE_scene_multiview_num_views_get(&scene->r);
+ break;
+ case R_IMF_VIEWS_STEREO_3D:
+ num_views = 2;
+ break;
+ case R_IMF_VIEWS_MULTIVIEW:
+ /* not supported at the moment */
+ /* pass through */
+ default:
+ num_views = 1;
+ }
+ break;
+ }
+ }
+
+ return num_views;
+}
+
+void BKE_sequencer_proxy_rebuild_context(Main *bmain, Scene *scene, Sequence *seq, struct GSet *file_list, ListBase *queue)
{
SeqIndexBuildContext *context;
Sequence *nseq;
+ LinkData *link;
+ size_t i;
+ size_t num_files;
if (!seq->strip || !seq->strip->proxy) {
- return NULL;
+ return;
}
if (!(seq->flag & SEQ_USE_PROXY)) {
- return NULL;
+ return;
}
- context = MEM_callocN(sizeof(SeqIndexBuildContext), "seq proxy rebuild context");
+ num_files = seq_proxy_context_count(seq, scene);
+
+ for (i = 0; i < num_files; i++) {
+ if (seq_proxy_multiview_context_invalid(seq, scene, i))
+ continue;
+
+ context = MEM_callocN(sizeof(SeqIndexBuildContext), "seq proxy rebuild context");
+
+ nseq = BKE_sequence_dupli_recursive(scene, scene, seq, 0);
- nseq = BKE_sequence_dupli_recursive(scene, scene, seq, 0);
+ context->tc_flags = nseq->strip->proxy->build_tc_flags;
+ context->size_flags = nseq->strip->proxy->build_size_flags;
+ context->quality = nseq->strip->proxy->quality;
+ context->overwrite = (nseq->strip->proxy->build_flags & SEQ_PROXY_SKIP_EXISTING) == 0;
- context->tc_flags = nseq->strip->proxy->build_tc_flags;
- context->size_flags = nseq->strip->proxy->build_size_flags;
- context->quality = nseq->strip->proxy->quality;
- context->overwrite = (nseq->strip->proxy->build_flags & SEQ_PROXY_SKIP_EXISTING) == 0;
+ context->bmain = bmain;
+ context->scene = scene;
+ context->orig_seq = seq;
+ context->seq = nseq;
- context->bmain = bmain;
- context->scene = scene;
- context->orig_seq = seq;
- context->seq = nseq;
+ context->view_id = i; /* only for images */
- if (nseq->type == SEQ_TYPE_MOVIE) {
- seq_open_anim_file(scene->ed, nseq, true);
+ link = BLI_genericNodeN(context);
+ BLI_addtail(queue, link);
- if (nseq->anim) {
- context->index_context = IMB_anim_index_rebuild_context(nseq->anim,
- context->tc_flags, context->size_flags, context->quality,
- context->overwrite, file_list);
+ if (nseq->type == SEQ_TYPE_MOVIE) {
+ StripAnim *sanim;
+
+ seq_open_anim_file(scene, nseq, true);
+ sanim = BLI_findlink(&nseq->anims, i);
+
+ if (sanim->anim) {
+ context->index_context = IMB_anim_index_rebuild_context(sanim->anim,
+ context->tc_flags, context->size_flags, context->quality,
+ context->overwrite, file_list);
+ }
}
}
-
- return context;
}
void BKE_sequencer_proxy_rebuild(SeqIndexBuildContext *context, short *stop, short *do_update, float *progress)
@@ -1657,6 +1918,7 @@ void BKE_sequencer_proxy_rebuild(SeqIndexBuildContext *context, short *stop, sho
render_context.skip_cache = true;
render_context.is_proxy_render = true;
+ render_context.view_id = context->view_id;
for (cfra = seq->startdisp + seq->startstill; cfra < seq->enddisp - seq->endstill; cfra++) {
if (context->size_flags & IMB_PROXY_25) {
@@ -1683,8 +1945,14 @@ void BKE_sequencer_proxy_rebuild(SeqIndexBuildContext *context, short *stop, sho
void BKE_sequencer_proxy_rebuild_finish(SeqIndexBuildContext *context, bool stop)
{
if (context->index_context) {
- IMB_close_anim_proxies(context->seq->anim);
- IMB_close_anim_proxies(context->orig_seq->anim);
+ StripAnim *sanim;
+
+ for (sanim = context->seq->anims.first; sanim; sanim = sanim->next)
+ IMB_close_anim_proxies(sanim->anim);
+
+ for (sanim = context->orig_seq->anims.first; sanim; sanim = sanim->next)
+ IMB_close_anim_proxies(sanim->anim);
+
IMB_anim_index_rebuild_finish(context->index_context, stop);
}
@@ -2429,6 +2697,234 @@ static ImBuf *seq_render_effect_strip_impl(const SeqRenderData *context, Sequenc
return out;
}
+static ImBuf *seq_render_image_strip(const SeqRenderData *context, Sequence *seq, float nr, float cfra)
+{
+ ImBuf *ibuf = NULL;
+ char name[FILE_MAX];
+ bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 &&
+ (context->scene->r.scemode & R_MULTIVIEW) != 0;
+ StripElem *s_elem = BKE_sequencer_give_stripelem(seq, cfra);
+ int flag;
+
+ if (s_elem) {
+ BLI_join_dirfile(name, sizeof(name), seq->strip->dir, s_elem->name);
+ BLI_path_abs(name, G.main->name);
+ }
+
+ flag = IB_rect;
+ if (seq->alpha_mode == SEQ_ALPHA_PREMUL)
+ flag |= IB_alphamode_premul;
+
+ if (!s_elem) {
+ /* don't do anything */
+ }
+ else if (is_multiview) {
+ size_t totfiles = seq_num_files(context->scene, seq->views_format, true);
+ size_t totviews;
+ struct ImBuf **ibufs_arr;
+ char prefix[FILE_MAX];
+ char *ext = NULL;
+ int i;
+
+ if (totfiles > 1) {
+ BKE_scene_multiview_view_prefix_get(context->scene, name, prefix, &ext);
+ if (prefix[0] == '\0') {
+ goto monoview_image;
+ }
+ }
+
+ totviews = BKE_scene_multiview_num_views_get(&context->scene->r);
+ ibufs_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Sequence Image Views Imbufs");
+
+ for (i = 0; i < totfiles; i++) {
+
+ if (prefix[0] == '\0') {
+ ibufs_arr[i] = IMB_loadiffname(name, flag, seq->strip->colorspace_settings.name);
+ }
+ else {
+ char str[FILE_MAX];
+ seq_multiview_name(context->scene, i, prefix, ext, str, FILE_MAX);
+ ibufs_arr[i] = IMB_loadiffname(str, flag, seq->strip->colorspace_settings.name);
+ }
+
+ if (ibufs_arr[i]) {
+ /* we don't need both (speed reasons)! */
+ if (ibufs_arr[i]->rect_float && ibufs_arr[i]->rect)
+ imb_freerectImBuf(ibufs_arr[i]);
+ }
+ }
+
+ if (seq->views_format == R_IMF_VIEWS_STEREO_3D && ibufs_arr[0])
+ IMB_ImBufFromStereo3d(seq->stereo3d_format, ibufs_arr[0], &ibufs_arr[0], &ibufs_arr[1]);
+
+ for (i = 0; i < totviews; i++) {
+ if (ibufs_arr[i]) {
+ SeqRenderData localcontext = *context;
+ localcontext.view_id = i;
+
+ /* all sequencer color is done in SRGB space, linear gives odd crossfades */
+ BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibufs_arr[i], false);
+
+ if (i != context->view_id) {
+ copy_to_ibuf_still(&localcontext, seq, nr, ibufs_arr[i]);
+ BKE_sequencer_cache_put(&localcontext, seq, cfra, SEQ_STRIPELEM_IBUF, ibufs_arr[i]);
+ }
+ }
+ }
+
+ /* return the original requested ImBuf */
+ ibuf = ibufs_arr[context->view_id];
+ if (ibuf) {
+ s_elem->orig_width = ibufs_arr[0]->x;
+ s_elem->orig_height = ibufs_arr[0]->y;
+ }
+
+ /* "remove" the others (decrease their refcount) */
+ for (i = 0; i < totviews; i++) {
+ if (ibufs_arr[i] != ibuf) {
+ IMB_freeImBuf(ibufs_arr[i]);
+ }
+ }
+
+ MEM_freeN(ibufs_arr);
+ }
+ else {
+monoview_image:
+ if ((ibuf = IMB_loadiffname(name, flag, seq->strip->colorspace_settings.name))) {
+ /* we don't need both (speed reasons)! */
+ if (ibuf->rect_float && ibuf->rect)
+ imb_freerectImBuf(ibuf);
+
+ /* all sequencer color is done in SRGB space, linear gives odd crossfades */
+ BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false);
+
+ s_elem->orig_width = ibuf->x;
+ s_elem->orig_height = ibuf->y;
+ }
+ }
+
+ return ibuf;
+}
+
+static ImBuf *seq_render_movie_strip(const SeqRenderData *context, Sequence *seq, float nr, float cfra)
+{
+ ImBuf *ibuf = NULL;
+ StripAnim *sanim;
+ bool is_multiview = (seq->flag & SEQ_USE_VIEWS) != 0 &&
+ (context->scene->r.scemode & R_MULTIVIEW) != 0;
+
+ /* load all the videos */
+ seq_open_anim_file(context->scene, seq, false);
+
+ if (is_multiview) {
+ ImBuf **ibuf_arr;
+ size_t totviews;
+ size_t totfiles = seq_num_files(context->scene, seq->views_format, true);
+ int i;
+
+ if (totfiles != BLI_listbase_count_ex(&seq->anims, totfiles + 1))
+ goto monoview_movie;
+
+ totviews = BKE_scene_multiview_num_views_get(&context->scene->r);
+ ibuf_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Sequence Image Views Imbufs");
+
+ for (i = 0, sanim = seq->anims.first; sanim; sanim = sanim->next, i++) {
+ if (sanim->anim) {
+ IMB_Proxy_Size proxy_size = seq_rendersize_to_proxysize(context->preview_render_size);
+ IMB_anim_set_preseek(sanim->anim, seq->anim_preseek);
+
+ ibuf_arr[i] = IMB_anim_absolute(sanim->anim, nr + seq->anim_startofs,
+ seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN,
+ proxy_size);
+
+ /* fetching for requested proxy size failed, try fetching the original instead */
+ if (!ibuf_arr[i] && proxy_size != IMB_PROXY_NONE) {
+ ibuf_arr[i] = IMB_anim_absolute(sanim->anim, nr + seq->anim_startofs,
+ seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN,
+ IMB_PROXY_NONE);
+ }
+ if (ibuf_arr[i]) {
+ /* we don't need both (speed reasons)! */
+ if (ibuf_arr[i]->rect_float && ibuf_arr[i]->rect)
+ imb_freerectImBuf(ibuf_arr[i]);
+ }
+ }
+ }
+
+ if (seq->views_format == R_IMF_VIEWS_STEREO_3D) {
+ if (ibuf_arr[0]) {
+ IMB_ImBufFromStereo3d(seq->stereo3d_format, ibuf_arr[0], &ibuf_arr[0], &ibuf_arr[1]);
+ }
+ else {
+ /* probably proxy hasn't been created yet */
+ MEM_freeN(ibuf_arr);
+ return NULL;
+ }
+ }
+
+ for (i = 0; i < totviews; i++) {
+ SeqRenderData localcontext = *context;
+ localcontext.view_id = i;
+
+ if (ibuf_arr[i]) {
+ /* all sequencer color is done in SRGB space, linear gives odd crossfades */
+ BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf_arr[i], false);
+ }
+ if (i != context->view_id) {
+ copy_to_ibuf_still(&localcontext, seq, nr, ibuf_arr[i]);
+ BKE_sequencer_cache_put(&localcontext, seq, cfra, SEQ_STRIPELEM_IBUF, ibuf_arr[i]);
+ }
+ }
+
+ /* return the original requested ImBuf */
+ ibuf = ibuf_arr[context->view_id];
+ if (ibuf) {
+ seq->strip->stripdata->orig_width = ibuf->x;
+ seq->strip->stripdata->orig_height = ibuf->y;
+ }
+
+ /* "remove" the others (decrease their refcount) */
+ for (i = 0; i < totviews; i++) {
+ if (ibuf_arr[i] != ibuf) {
+ IMB_freeImBuf(ibuf_arr[i]);
+ }
+ }
+
+ MEM_freeN(ibuf_arr);
+ }
+ else {
+monoview_movie:
+ sanim = seq->anims.first;
+ if (sanim && sanim->anim) {
+ IMB_Proxy_Size proxy_size = seq_rendersize_to_proxysize(context->preview_render_size);
+ IMB_anim_set_preseek(sanim->anim, seq->anim_preseek);
+
+ ibuf = IMB_anim_absolute(sanim->anim, nr + seq->anim_startofs,
+ seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN,
+ proxy_size);
+
+ /* fetching for requested proxy size failed, try fetching the original instead */
+ if (!ibuf && proxy_size != IMB_PROXY_NONE) {
+ ibuf = IMB_anim_absolute(sanim->anim, nr + seq->anim_startofs,
+ seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN,
+ IMB_PROXY_NONE);
+ }
+ if (ibuf) {
+ BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false);
+
+ /* we don't need both (speed reasons)! */
+ if (ibuf->rect_float && ibuf->rect) {
+ imb_freerectImBuf(ibuf);
+ }
+
+ seq->strip->stripdata->orig_width = ibuf->x;
+ seq->strip->stripdata->orig_height = ibuf->y;
+ }
+ }
+ }
+ return ibuf;
+}
+
static ImBuf *seq_render_movieclip_strip(const SeqRenderData *context, Sequence *seq, float nr)
{
ImBuf *ibuf = NULL;
@@ -2560,7 +3056,7 @@ static ImBuf *seq_render_mask_strip(const SeqRenderData *context, Sequence *seq,
return seq_render_mask(context, seq->mask, nr, make_float);
}
-static ImBuf *seq_render_scene_strip(const SeqRenderData *context, Sequence *seq, float nr)
+static ImBuf *seq_render_scene_strip(const SeqRenderData *context, Sequence *seq, float nr, float cfra)
{
ImBuf *ibuf = NULL;
float frame;
@@ -2658,6 +3154,7 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, Sequence *seq
char err_out[256] = "unknown";
int width = (scene->r.xsch * scene->r.size) / 100;
int height = (scene->r.ysch * scene->r.size) / 100;
+ const char *viewname = BKE_scene_multiview_render_view_name_get(&scene->r, context->view_id);
/* for old scened this can be uninitialized,
* should probably be added to do_versions at some point if the functionality stays */
@@ -2669,14 +3166,18 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, Sequence *seq
ibuf = sequencer_view3d_cb(scene, camera, width, height, IB_rect,
context->scene->r.seq_prev_type,
(context->scene->r.seq_flag & R_SEQ_SOLID_TEX) != 0,
- use_gpencil, true, scene->r.alphamode, err_out);
+ use_gpencil, true, scene->r.alphamode, viewname, err_out);
if (ibuf == NULL) {
fprintf(stderr, "seq_render_scene_strip failed to get opengl buffer: %s\n", err_out);
}
}
else {
Render *re = RE_GetRender(scene->id.name);
- RenderResult rres;
+ size_t totviews = BKE_scene_multiview_num_views_get(&scene->r);
+ int i;
+ ImBuf **ibufs_arr;
+
+ ibufs_arr = MEM_callocN(sizeof(ImBuf *) * totviews, "Sequence Image Views Imbufs");
/* XXX: this if can be removed when sequence preview rendering uses the job system
*
@@ -2696,27 +3197,51 @@ static ImBuf *seq_render_scene_strip(const SeqRenderData *context, Sequence *seq
/* restore previous state after it was toggled on & off by RE_BlenderFrame */
G.is_rendering = is_rendering;
}
-
- RE_AcquireResultImage(re, &rres);
-
- if (rres.rectf) {
- ibuf = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rectfloat);
- memcpy(ibuf->rect_float, rres.rectf, 4 * sizeof(float) * rres.rectx * rres.recty);
- if (rres.rectz) {
- addzbuffloatImBuf(ibuf);
- memcpy(ibuf->zbuf_float, rres.rectz, sizeof(float) * rres.rectx * rres.recty);
+
+ for (i = 0; i < totviews; i++) {
+ SeqRenderData localcontext = *context;
+ RenderResult rres;
+
+ localcontext.view_id = i;
+
+ RE_AcquireResultImage(re, &rres, i);
+
+ if (rres.rectf) {
+ ibufs_arr[i] = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rectfloat);
+ memcpy(ibufs_arr[i]->rect_float, rres.rectf, 4 * sizeof(float) * rres.rectx * rres.recty);
+
+ if (rres.rectz) {
+ addzbuffloatImBuf(ibufs_arr[i]);
+ memcpy(ibufs_arr[i]->zbuf_float, rres.rectz, sizeof(float) * rres.rectx * rres.recty);
+ }
+
+ /* float buffers in the sequencer are not linear */
+ BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibufs_arr[i], false);
+ }
+ else if (rres.rect32) {
+ ibufs_arr[i] = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rect);
+ memcpy(ibufs_arr[i]->rect, rres.rect32, 4 * rres.rectx * rres.recty);
}
- /* float buffers in the sequencer are not linear */
- BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false);
+ if (i != context->view_id) {
+ copy_to_ibuf_still(&localcontext, seq, nr, ibufs_arr[i]);
+ BKE_sequencer_cache_put(&localcontext, seq, cfra, SEQ_STRIPELEM_IBUF, ibufs_arr[i]);
+ }
+
+ RE_ReleaseResultImage(re);
}
- else if (rres.rect32) {
- ibuf = IMB_allocImBuf(rres.rectx, rres.recty, 32, IB_rect);
- memcpy(ibuf->rect, rres.rect32, 4 * rres.rectx * rres.recty);
+
+ /* return the original requested ImBuf */
+ ibuf = ibufs_arr[context->view_id];
+
+ /* "remove" the others (decrease their refcount) */
+ for (i = 0; i < totviews; i++) {
+ if (ibufs_arr[i] != ibuf) {
+ IMB_freeImBuf(ibufs_arr[i]);
+ }
}
-
- RE_ReleaseResultImage(re);
-
+ MEM_freeN(ibufs_arr);
+
// BIF_end_render_callbacks();
}
@@ -2743,7 +3268,6 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context, Sequence *s
float nr = give_stripelem_index(seq, cfra);
int type = (seq->type & SEQ_TYPE_EFFECT && seq->type != SEQ_TYPE_SPEED) ? SEQ_TYPE_EFFECT : seq->type;
bool use_preprocess = BKE_sequencer_input_have_to_preprocess(context, seq, cfra);
- char name[FILE_MAX];
switch (type) {
case SEQ_TYPE_META:
@@ -2802,64 +3326,14 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context, Sequence *s
case SEQ_TYPE_IMAGE:
{
- StripElem *s_elem = BKE_sequencer_give_stripelem(seq, cfra);
- int flag;
-
- if (s_elem) {
- BLI_join_dirfile(name, sizeof(name), seq->strip->dir, s_elem->name);
- BLI_path_abs(name, G.main->name);
- }
-
- flag = IB_rect;
- if (seq->alpha_mode == SEQ_ALPHA_PREMUL)
- flag |= IB_alphamode_premul;
-
- if (s_elem && (ibuf = IMB_loadiffname(name, flag, seq->strip->colorspace_settings.name))) {
- /* we don't need both (speed reasons)! */
- if (ibuf->rect_float && ibuf->rect)
- imb_freerectImBuf(ibuf);
-
- /* all sequencer color is done in SRGB space, linear gives odd crossfades */
- BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false);
-
- copy_to_ibuf_still(context, seq, nr, ibuf);
-
- s_elem->orig_width = ibuf->x;
- s_elem->orig_height = ibuf->y;
- }
+ ibuf = seq_render_image_strip(context, seq, nr, cfra);
+ copy_to_ibuf_still(context, seq, nr, ibuf);
break;
}
case SEQ_TYPE_MOVIE:
{
- seq_open_anim_file(context->scene->ed, seq, false);
-
- if (seq->anim) {
- IMB_Proxy_Size proxy_size = seq_rendersize_to_proxysize(context->preview_render_size);
- IMB_anim_set_preseek(seq->anim, seq->anim_preseek);
-
- ibuf = IMB_anim_absolute(seq->anim, nr + seq->anim_startofs,
- seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN,
- proxy_size);
-
- /* fetching for requested proxy size failed, try fetching the original instead */
- if (!ibuf && proxy_size != IMB_PROXY_NONE) {
- ibuf = IMB_anim_absolute(seq->anim, nr + seq->anim_startofs,
- seq->strip->proxy ? seq->strip->proxy->tc : IMB_TC_RECORD_RUN,
- IMB_PROXY_NONE);
- }
- if (ibuf) {
- BKE_sequencer_imbuf_to_sequencer_space(context->scene, ibuf, false);
-
- /* we don't need both (speed reasons)! */
- if (ibuf->rect_float && ibuf->rect) {
- imb_freerectImBuf(ibuf);
- }
-
- seq->strip->stripdata->orig_width = ibuf->x;
- seq->strip->stripdata->orig_height = ibuf->y;
- }
- }
+ ibuf = seq_render_movie_strip(context, seq, nr, cfra);
copy_to_ibuf_still(context, seq, nr, ibuf);
break;
}
@@ -2867,7 +3341,7 @@ static ImBuf *do_render_strip_uncached(const SeqRenderData *context, Sequence *s
case SEQ_TYPE_SCENE:
{
/* scene can be NULL after deletions */
- ibuf = seq_render_scene_strip(context, seq, nr);
+ ibuf = seq_render_scene_strip(context, seq, nr, cfra);
/* Scene strips update all animation, so we need to restore original state.*/
BKE_animsys_evaluate_all_animation(context->bmain, context->scene, cfra);
@@ -3364,16 +3838,6 @@ ImBuf *BKE_sequencer_give_ibuf_threaded(const SeqRenderData *context, float cfra
return e ? e->ibuf : NULL;
}
-/* Functions to free imbuf and anim data on changes */
-
-static void free_anim_seq(Sequence *seq)
-{
- if (seq->anim) {
- IMB_free_anim(seq->anim);
- seq->anim = NULL;
- }
-}
-
/* check whether sequence cur depends on seq */
bool BKE_sequence_check_depend(Sequence *seq, Sequence *cur)
{
@@ -3425,15 +3889,11 @@ static void sequence_invalidate_cache(Scene *scene, Sequence *seq, bool invalida
/* invalidate cache for current sequence */
if (invalidate_self) {
- if (seq->anim) {
- /* Animation structure holds some buffers inside,
- * so for proper cache invalidation we need to
- * re-open the animation.
- */
- IMB_free_anim(seq->anim);
- seq->anim = NULL;
- }
-
+ /* Animation structure holds some buffers inside,
+ * so for proper cache invalidation we need to
+ * re-open the animation.
+ */
+ BKE_sequence_free_anim(seq);
BKE_sequencer_cache_cleanup_sequence(seq);
}
@@ -3480,7 +3940,7 @@ void BKE_sequencer_free_imbuf(Scene *scene, ListBase *seqbase, bool for_render)
if (seq->strip) {
if (seq->type == SEQ_TYPE_MOVIE) {
- free_anim_seq(seq);
+ BKE_sequence_free_anim(seq);
}
if (seq->type == SEQ_TYPE_SPEED) {
BKE_sequence_effect_speed_rebuild_map(scene, seq, true);
@@ -3527,7 +3987,7 @@ static bool update_changed_seq_recurs(Scene *scene, Sequence *seq, Sequence *cha
if (free_imbuf) {
if (ibuf_change) {
if (seq->type == SEQ_TYPE_MOVIE)
- free_anim_seq(seq);
+ BKE_sequence_free_anim(seq);
if (seq->type == SEQ_TYPE_SPEED) {
BKE_sequence_effect_speed_rebuild_map(scene, seq, true);
}
@@ -4415,6 +4875,8 @@ Sequence *BKE_sequence_alloc(ListBase *lb, int cfra, int machine)
seq->pitch = 1.0f;
seq->scene_sound = NULL;
+ seq->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Sequence Stereo Format");
+
return seq;
}
@@ -4472,6 +4934,12 @@ Sequence *BKE_sequencer_add_image_strip(bContext *C, ListBase *seqbasep, SeqLoad
strip->stripdata = MEM_callocN(seq->len * sizeof(StripElem), "stripelem");
BLI_strncpy(strip->dir, seq_load->path, sizeof(strip->dir));
+ if (seq_load->stereo3d_format)
+ *seq->stereo3d_format = *seq_load->stereo3d_format;
+
+ seq->views_format = seq_load->views_format;
+ seq->flag |= seq_load->flag & SEQ_USE_VIEWS;
+
seq_load_apply(scene, seq, seq_load);
return seq;
@@ -4551,6 +5019,12 @@ Sequence *BKE_sequencer_add_sound_strip(bContext *C, ListBase *seqbasep, SeqLoad
}
#endif // WITH_AUDASPACE
+static void seq_anim_add_suffix(Scene *scene, struct anim *anim, const size_t view_id)
+{
+ const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, view_id);
+ IMB_suffix_anim(anim, suffix);
+}
+
Sequence *BKE_sequencer_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoadInfo *seq_load)
{
Scene *scene = CTX_data_scene(C); /* only for sound */
@@ -4560,29 +5034,84 @@ Sequence *BKE_sequencer_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoad
Strip *strip;
StripElem *se;
char colorspace[64] = "\0"; /* MAX_COLORSPACE_NAME */
-
- struct anim *an;
+ bool is_multiview_loaded = false;
+ const bool is_multiview = (seq_load->flag & SEQ_USE_VIEWS) != 0;
+ size_t totfiles = seq_num_files(scene, seq_load->views_format, is_multiview);
+ struct anim **anim_arr;
+ int i;
BLI_strncpy(path, seq_load->path, sizeof(path));
BLI_path_abs(path, G.main->name);
- an = openanim(path, IB_rect, 0, colorspace);
+ anim_arr = MEM_callocN(sizeof(struct anim *) * totfiles, "Video files");
- if (an == NULL)
- return NULL;
+ if (is_multiview && (seq_load->views_format == R_IMF_VIEWS_INDIVIDUAL)) {
+ char prefix[FILE_MAX];
+ char *ext = NULL;
+ size_t j = 0;
+
+ BKE_scene_multiview_view_prefix_get(scene, path, prefix, &ext);
+
+ if (prefix[0] != '\0') {
+ for (i = 0; i < totfiles; i++) {
+ char str[FILE_MAX];
+
+ seq_multiview_name(scene, i, prefix, ext, str, FILE_MAX);
+ anim_arr[j] = openanim(str, IB_rect, 0, colorspace);
+ seq_anim_add_suffix(scene, anim_arr[j], i);
+
+ if (anim_arr[j]) {
+ j++;
+ }
+ }
+
+ if (j == 0) {
+ MEM_freeN(anim_arr);
+ return NULL;
+ }
+ is_multiview_loaded = true;
+ }
+ }
+
+ if (is_multiview_loaded == false) {
+ anim_arr[0] = openanim(path, IB_rect, 0, colorspace);
+
+ if (anim_arr[0] == NULL) {
+ MEM_freeN(anim_arr);
+ return NULL;
+ }
+ }
seq = BKE_sequence_alloc(seqbasep, seq_load->start_frame, seq_load->channel);
+
+ /* multiview settings */
+ if (seq_load->stereo3d_format) {
+ *seq->stereo3d_format = *seq_load->stereo3d_format;
+ seq->views_format = seq_load->views_format;
+ }
+ seq->flag |= seq_load->flag & SEQ_USE_VIEWS;
+
seq->type = SEQ_TYPE_MOVIE;
seq->blend_mode = SEQ_TYPE_CROSS; /* so alpha adjustment fade to the strip below */
- seq->anim = an;
- seq->anim_preseek = IMB_anim_get_preseek(an);
+ for (i = 0; i < totfiles; i++) {
+ if (anim_arr[i]) {
+ StripAnim *sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim");
+ BLI_addtail(&seq->anims, sanim);
+ sanim->anim = anim_arr[i];
+ }
+ else {
+ break;
+ }
+ }
+
+ seq->anim_preseek = IMB_anim_get_preseek(anim_arr[0]);
BLI_strncpy(seq->name + 2, "Movie", SEQ_NAME_MAXSTR - 2);
BKE_sequence_base_unique_name_recursive(&scene->ed->seqbase, seq);
/* basic defaults */
seq->strip = strip = MEM_callocN(sizeof(Strip), "strip");
- seq->len = IMB_anim_get_duration(an, IMB_TC_RECORD_RUN);
+ seq->len = IMB_anim_get_duration(anim_arr[0], IMB_TC_RECORD_RUN);
strip->us = 1;
BLI_strncpy(seq->strip->colorspace_settings.name, colorspace, sizeof(seq->strip->colorspace_settings.name));
@@ -4610,6 +5139,7 @@ Sequence *BKE_sequencer_add_movie_strip(bContext *C, ListBase *seqbasep, SeqLoad
/* can be NULL */
seq_load_apply(scene, seq, seq_load);
+ MEM_freeN(anim_arr);
return seq;
}
@@ -4621,6 +5151,8 @@ static Sequence *seq_dupli(Scene *scene, Scene *scene_to, Sequence *seq, int dup
seq->tmp = seqn;
seqn->strip = MEM_dupallocN(seq->strip);
+ seqn->stereo3d_format = MEM_dupallocN(seq->stereo3d_format);
+
/* XXX: add F-Curve duplication stuff? */
if (seq->strip->crop) {
@@ -4667,7 +5199,7 @@ static Sequence *seq_dupli(Scene *scene, Scene *scene_to, Sequence *seq, int dup
else if (seq->type == SEQ_TYPE_MOVIE) {
seqn->strip->stripdata =
MEM_dupallocN(seq->strip->stripdata);
- seqn->anim = NULL;
+ BLI_listbase_clear(&seqn->anims);
}
else if (seq->type == SEQ_TYPE_SOUND_RAM) {
seqn->strip->stripdata =
diff --git a/source/blender/blenkernel/intern/writeavi.c b/source/blender/blenkernel/intern/writeavi.c
index a86ffacfdbd..cec455e01b9 100644
--- a/source/blender/blenkernel/intern/writeavi.c
+++ b/source/blender/blenkernel/intern/writeavi.c
@@ -50,26 +50,34 @@
/* ********************** general blender movie support ***************************** */
-static int start_stub(Scene *UNUSED(scene), RenderData *UNUSED(rd), int UNUSED(rectx), int UNUSED(recty),
- ReportList *UNUSED(reports), bool UNUSED(preview))
+static int start_stub(void *UNUSED(context_v), Scene *UNUSED(scene), RenderData *UNUSED(rd), int UNUSED(rectx), int UNUSED(recty),
+ ReportList *UNUSED(reports), bool UNUSED(preview), const char *UNUSED(suffix))
{ return 0; }
-static void end_stub(void)
+static void end_stub(void *UNUSED(context_v))
{}
-static int append_stub(RenderData *UNUSED(rd), int UNUSED(start_frame), int UNUSED(frame), int *UNUSED(pixels),
- int UNUSED(rectx), int UNUSED(recty), ReportList *UNUSED(reports))
+static int append_stub(void *UNUSED(context_v), RenderData *UNUSED(rd), int UNUSED(start_frame), int UNUSED(frame), int *UNUSED(pixels),
+ int UNUSED(rectx), int UNUSED(recty), const char *UNUSED(suffix), ReportList *UNUSED(reports))
{ return 0; }
+static void *context_create_stub(void)
+{ return NULL; }
+
+static void context_free_stub(void *UNUSED(context_v))
+{}
+
#ifdef WITH_AVI
# include "AVI_avi.h"
/* callbacks */
-static int start_avi(Scene *scene, RenderData *rd, int rectx, int recty, ReportList *reports, bool preview);
-static void end_avi(void);
-static int append_avi(RenderData *rd, int start_frame, int frame, int *pixels,
- int rectx, int recty, ReportList *reports);
-static void filepath_avi(char *string, RenderData *rd, bool preview);
+static int start_avi(void *context_v, Scene *scene, RenderData *rd, int rectx, int recty, ReportList *reports, bool preview, const char *suffix);
+static void end_avi(void *context_v);
+static int append_avi(void *context_v, RenderData *rd, int start_frame, int frame, int *pixels,
+ int rectx, int recty, const char *suffix, ReportList *reports);
+static void filepath_avi(char *string, RenderData *rd, bool preview, const char *suffix);
+static void *context_create_avi(void);
+static void context_free_avi(void *context_v);
#endif /* WITH_AVI */
#ifdef WITH_QUICKTIME
@@ -93,13 +101,17 @@ bMovieHandle *BKE_movie_handle_get(const char imtype)
mh.end_movie = end_stub;
mh.get_next_frame = NULL;
mh.get_movie_path = NULL;
-
+ mh.context_create = context_create_stub;
+ mh.context_free = context_free_stub;
+
/* set the default handle, as builtin */
#ifdef WITH_AVI
mh.start_movie = start_avi;
mh.append_movie = append_avi;
mh.end_movie = end_avi;
mh.get_movie_path = filepath_avi;
+ mh.context_create = context_create_avi;
+ mh.context_free = context_free_avi;
#endif
/* do the platform specific handles */
@@ -109,6 +121,8 @@ bMovieHandle *BKE_movie_handle_get(const char imtype)
mh.append_movie = append_qt;
mh.end_movie = end_qt;
mh.get_movie_path = filepath_qt;
+ mh.context_create = context_create_qt;
+ mh.context_free = context_free_qt;
}
#endif
#ifdef WITH_FFMPEG
@@ -117,6 +131,8 @@ bMovieHandle *BKE_movie_handle_get(const char imtype)
mh.append_movie = BKE_ffmpeg_append;
mh.end_movie = BKE_ffmpeg_end;
mh.get_movie_path = BKE_ffmpeg_filepath_get;
+ mh.context_create = BKE_ffmpeg_context_create;
+ mh.context_free = BKE_ffmpeg_context_free;
}
#endif
#ifdef WITH_FRAMESERVER
@@ -125,6 +141,8 @@ bMovieHandle *BKE_movie_handle_get(const char imtype)
mh.append_movie = BKE_frameserver_append;
mh.end_movie = BKE_frameserver_end;
mh.get_next_frame = BKE_frameserver_loop;
+ mh.context_create = BKE_frameserver_context_create;
+ mh.context_free = BKE_frameserver_context_free;
}
#endif
@@ -137,9 +155,7 @@ bMovieHandle *BKE_movie_handle_get(const char imtype)
#ifdef WITH_AVI
-static AviMovie *avi = NULL;
-
-static void filepath_avi(char *string, RenderData *rd, bool preview)
+static void filepath_avi(char *string, RenderData *rd, bool preview, const char *suffix)
{
int sfra, efra;
@@ -170,27 +186,27 @@ static void filepath_avi(char *string, RenderData *rd, bool preview)
BLI_path_frame_range(string, sfra, efra, 4);
}
}
+
+ BLI_path_suffix(string, FILE_MAX, suffix, "");
}
-static int start_avi(Scene *scene, RenderData *rd, int rectx, int recty, ReportList *reports, bool preview)
+static int start_avi(void *context_v, Scene *UNUSED(scene), RenderData *rd, int rectx, int recty,
+ ReportList *reports, bool preview, const char *suffix)
{
int x, y;
char name[256];
AviFormat format;
int quality;
double framerate;
-
- (void)scene; /* unused */
-
- filepath_avi(name, rd, preview);
+ AviMovie *avi = context_v;
+
+ filepath_avi(name, rd, preview, suffix);
x = rectx;
y = recty;
quality = rd->im_format.quality;
framerate = (double) rd->frs_sec / (double) rd->frs_sec_base;
-
- avi = MEM_mallocN(sizeof(AviMovie), "avimovie");
if (rd->im_format.imtype != R_IMF_IMTYPE_AVIJPEG) format = AVI_FORMAT_AVI_RGB;
else format = AVI_FORMAT_MJPEG;
@@ -216,12 +232,13 @@ static int start_avi(Scene *scene, RenderData *rd, int rectx, int recty, ReportL
return 1;
}
-static int append_avi(RenderData *UNUSED(rd), int start_frame, int frame, int *pixels,
- int rectx, int recty, ReportList *UNUSED(reports))
+static int append_avi(void *context_v, RenderData *UNUSED(rd), int start_frame, int frame, int *pixels,
+ int rectx, int recty, const char *UNUSED(suffix), ReportList *UNUSED(reports))
{
unsigned int *rt1, *rt2, *rectot;
int x, y;
char *cp, rt;
+ AviMovie *avi = context_v;
if (avi == NULL)
return 0;
@@ -252,22 +269,37 @@ static int append_avi(RenderData *UNUSED(rd), int start_frame, int frame, int *p
return 1;
}
-static void end_avi(void)
+static void end_avi(void *context_v)
{
+ AviMovie *avi = context_v;
+
if (avi == NULL) return;
AVI_close_compress(avi);
- MEM_freeN(avi);
- avi = NULL;
}
+
+static void *context_create_avi(void)
+{
+ AviMovie *avi = MEM_mallocN(sizeof(AviMovie), "avimovie");
+ return avi;
+}
+
+static void context_free_avi(void *context_v)
+{
+ AviMovie *avi = context_v;
+ if (avi) {
+ MEM_freeN(avi);
+ }
+}
+
#endif /* WITH_AVI */
/* similar to BKE_image_path_from_imformat() */
-void BKE_movie_filepath_get(char *string, RenderData *rd, bool preview)
+void BKE_movie_filepath_get(char *string, RenderData *rd, bool preview, const char *suffix)
{
bMovieHandle *mh = BKE_movie_handle_get(rd->im_format.imtype);
if (mh->get_movie_path)
- mh->get_movie_path(string, rd, preview);
+ mh->get_movie_path(string, rd, preview, suffix);
else
string[0] = '\0';
}
diff --git a/source/blender/blenkernel/intern/writeffmpeg.c b/source/blender/blenkernel/intern/writeffmpeg.c
index 0717369cde8..af71f19c226 100644
--- a/source/blender/blenkernel/intern/writeffmpeg.c
+++ b/source/blender/blenkernel/intern/writeffmpeg.c
@@ -61,36 +61,38 @@
#include "ffmpeg_compat.h"
-static int ffmpeg_type = 0;
-static int ffmpeg_codec = AV_CODEC_ID_MPEG4;
-static int ffmpeg_audio_codec = AV_CODEC_ID_NONE;
-static int ffmpeg_video_bitrate = 1150;
-static int ffmpeg_audio_bitrate = 128;
-static int ffmpeg_gop_size = 12;
-static int ffmpeg_autosplit = 0;
-static int ffmpeg_autosplit_count = 0;
-static bool ffmpeg_preview = false;
-
-static AVFormatContext *outfile = 0;
-static AVStream *video_stream = 0;
-static AVStream *audio_stream = 0;
-static AVFrame *current_frame = 0;
-static struct SwsContext *img_convert_ctx = 0;
-
-static uint8_t *audio_input_buffer = 0;
-static uint8_t *audio_deinterleave_buffer = 0;
-static int audio_input_samples = 0;
+typedef struct FFMpegContext {
+ int ffmpeg_type;
+ int ffmpeg_codec;
+ int ffmpeg_audio_codec;
+ int ffmpeg_video_bitrate;
+ int ffmpeg_audio_bitrate;
+ int ffmpeg_gop_size;
+ int ffmpeg_autosplit;
+ int ffmpeg_autosplit_count;
+ bool ffmpeg_preview;
+
+ AVFormatContext *outfile;
+ AVStream *video_stream;
+ AVStream *audio_stream;
+ AVFrame *current_frame;
+ struct SwsContext *img_convert_ctx;
+
+ uint8_t *audio_input_buffer;
+ uint8_t *audio_deinterleave_buffer;
+ int audio_input_samples;
#ifndef FFMPEG_HAVE_ENCODE_AUDIO2
-static uint8_t *audio_output_buffer = 0;
-static int audio_outbuf_size = 0;
+ uint8_t *audio_output_buffer;
+ int audio_outbuf_size;
#endif
-static double audio_time = 0.0f;
-static bool audio_deinterleave = false;
-static int audio_sample_size = 0;
+ double audio_time;
+ bool audio_deinterleave;
+ int audio_sample_size;
#ifdef WITH_AUDASPACE
-static AUD_Device *audio_mixdown_device = 0;
+ AUD_Device *audio_mixdown_device;
#endif
+} FFMpegContext;
#define FFMPEG_AUTOSPLIT_SIZE 2000000000
@@ -99,6 +101,7 @@ static AUD_Device *audio_mixdown_device = 0;
static void ffmpeg_dict_set_int(AVDictionary **dict, const char *key, int value);
static void ffmpeg_dict_set_float(AVDictionary **dict, const char *key, float value);
static void ffmpeg_set_expert_options(RenderData *rd);
+static void ffmpeg_filepath_get(FFMpegContext *context, char *string, struct RenderData *rd, bool preview, const char *suffix);
/* Delete a picture buffer */
@@ -117,50 +120,50 @@ static int request_float_audio_buffer(int codec_id)
}
#ifdef WITH_AUDASPACE
-static int write_audio_frame(void)
+static int write_audio_frame(FFMpegContext *context)
{
AVCodecContext *c = NULL;
AVPacket pkt;
AVFrame *frame = NULL;
int got_output = 0;
- c = audio_stream->codec;
+ c = context->audio_stream->codec;
av_init_packet(&pkt);
pkt.size = 0;
pkt.data = NULL;
- AUD_readDevice(audio_mixdown_device, audio_input_buffer, audio_input_samples);
- audio_time += (double) audio_input_samples / (double) c->sample_rate;
+ AUD_readDevice(context->audio_mixdown_device, context->audio_input_buffer, context->audio_input_samples);
+ context->audio_time += (double) context->audio_input_samples / (double) c->sample_rate;
#ifdef FFMPEG_HAVE_ENCODE_AUDIO2
frame = avcodec_alloc_frame();
avcodec_get_frame_defaults(frame);
- frame->pts = audio_time / av_q2d(c->time_base);
- frame->nb_samples = audio_input_samples;
+ frame->pts = context->audio_time / av_q2d(c->time_base);
+ frame->nb_samples = context->audio_input_samples;
frame->format = c->sample_fmt;
#ifdef FFMPEG_HAVE_FRAME_CHANNEL_LAYOUT
frame->channel_layout = c->channel_layout;
#endif
- if (audio_deinterleave) {
+ if (context->audio_deinterleave) {
int channel, i;
uint8_t *temp;
for (channel = 0; channel < c->channels; channel++) {
for (i = 0; i < frame->nb_samples; i++) {
- memcpy(audio_deinterleave_buffer + (i + channel * frame->nb_samples) * audio_sample_size,
- audio_input_buffer + (c->channels * i + channel) * audio_sample_size, audio_sample_size);
+ memcpy(context->audio_deinterleave_buffer + (i + channel * frame->nb_samples) * context->audio_sample_size,
+ context->audio_input_buffer + (c->channels * i + channel) * context->audio_sample_size, context->audio_sample_size);
}
}
- temp = audio_deinterleave_buffer;
- audio_deinterleave_buffer = audio_input_buffer;
- audio_input_buffer = temp;
+ temp = context->audio_deinterleave_buffer;
+ context->audio_deinterleave_buffer = context->audio_input_buffer;
+ context->audio_input_buffer = temp;
}
- avcodec_fill_audio_frame(frame, c->channels, c->sample_fmt, audio_input_buffer,
- audio_input_samples * c->channels * audio_sample_size, 1);
+ avcodec_fill_audio_frame(frame, c->channels, c->sample_fmt, context->audio_input_buffer,
+ context->audio_input_samples * c->channels * context->audio_sample_size, 1);
if (avcodec_encode_audio2(c, &pkt, frame, &got_output) < 0) {
// XXX error("Error writing audio packet");
@@ -172,30 +175,30 @@ static int write_audio_frame(void)
return 0;
}
#else
- pkt.size = avcodec_encode_audio(c, audio_output_buffer, audio_outbuf_size, (short *) audio_input_buffer);
+ pkt.size = avcodec_encode_audio(c, context->audio_output_buffer, context->audio_outbuf_size, (short *) context->audio_input_buffer);
if (pkt.size < 0) {
// XXX error("Error writing audio packet");
return -1;
}
- pkt.data = audio_output_buffer;
+ pkt.data = context->audio_output_buffer;
got_output = 1;
#endif
if (got_output) {
if (pkt.pts != AV_NOPTS_VALUE)
- pkt.pts = av_rescale_q(pkt.pts, c->time_base, audio_stream->time_base);
+ pkt.pts = av_rescale_q(pkt.pts, c->time_base, context->audio_stream->time_base);
if (pkt.dts != AV_NOPTS_VALUE)
- pkt.dts = av_rescale_q(pkt.dts, c->time_base, audio_stream->time_base);
+ pkt.dts = av_rescale_q(pkt.dts, c->time_base, context->audio_stream->time_base);
if (pkt.duration > 0)
- pkt.duration = av_rescale_q(pkt.duration, c->time_base, audio_stream->time_base);
+ pkt.duration = av_rescale_q(pkt.duration, c->time_base, context->audio_stream->time_base);
- pkt.stream_index = audio_stream->index;
+ pkt.stream_index = context->audio_stream->index;
pkt.flags |= AV_PKT_FLAG_KEY;
- if (av_interleaved_write_frame(outfile, &pkt) != 0) {
+ if (av_interleaved_write_frame(context->outfile, &pkt) != 0) {
fprintf(stderr, "Error writing audio packet!\n");
if (frame)
avcodec_free_frame(&frame);
@@ -302,11 +305,11 @@ static const char **get_file_extensions(int format)
}
/* Write a frame to the output file */
-static int write_video_frame(RenderData *rd, int cfra, AVFrame *frame, ReportList *reports)
+static int write_video_frame(FFMpegContext *context, RenderData *rd, int cfra, AVFrame *frame, ReportList *reports)
{
int got_output;
int ret, success = 1;
- AVCodecContext *c = video_stream->codec;
+ AVCodecContext *c = context->video_stream->codec;
AVPacket packet = { 0 };
av_init_packet(&packet);
@@ -321,22 +324,22 @@ static int write_video_frame(RenderData *rd, int cfra, AVFrame *frame, ReportLis
if (ret >= 0 && got_output) {
if (packet.pts != AV_NOPTS_VALUE) {
- packet.pts = av_rescale_q(packet.pts, c->time_base, video_stream->time_base);
+ packet.pts = av_rescale_q(packet.pts, c->time_base, context->video_stream->time_base);
PRINT("Video Frame PTS: %d\n", (int)packet.pts);
}
else {
PRINT("Video Frame PTS: not set\n");
}
if (packet.dts != AV_NOPTS_VALUE) {
- packet.dts = av_rescale_q(packet.dts, c->time_base, video_stream->time_base);
+ packet.dts = av_rescale_q(packet.dts, c->time_base, context->video_stream->time_base);
PRINT("Video Frame DTS: %d\n", (int)packet.dts);
}
else {
PRINT("Video Frame DTS: not set\n");
}
- packet.stream_index = video_stream->index;
- ret = av_interleaved_write_frame(outfile, &packet);
+ packet.stream_index = context->video_stream->index;
+ ret = av_interleaved_write_frame(context->outfile, &packet);
success = (ret == 0);
}
else if (ret < 0) {
@@ -350,11 +353,11 @@ static int write_video_frame(RenderData *rd, int cfra, AVFrame *frame, ReportLis
}
/* read and encode a frame of audio from the buffer */
-static AVFrame *generate_video_frame(uint8_t *pixels, ReportList *reports)
+static AVFrame *generate_video_frame(FFMpegContext *context, uint8_t *pixels, ReportList *reports)
{
uint8_t *rendered_frame;
- AVCodecContext *c = video_stream->codec;
+ AVCodecContext *c = context->video_stream->codec;
int width = c->width;
int height = c->height;
AVFrame *rgb_frame;
@@ -367,7 +370,7 @@ static AVFrame *generate_video_frame(uint8_t *pixels, ReportList *reports)
}
}
else {
- rgb_frame = current_frame;
+ rgb_frame = context->current_frame;
}
rendered_frame = pixels;
@@ -411,17 +414,17 @@ static AVFrame *generate_video_frame(uint8_t *pixels, ReportList *reports)
}
if (c->pix_fmt != PIX_FMT_BGR32) {
- sws_scale(img_convert_ctx, (const uint8_t *const *) rgb_frame->data,
+ sws_scale(context->img_convert_ctx, (const uint8_t *const *) rgb_frame->data,
rgb_frame->linesize, 0, c->height,
- current_frame->data, current_frame->linesize);
+ context->current_frame->data, context->current_frame->linesize);
delete_picture(rgb_frame);
}
- current_frame->format = PIX_FMT_BGR32;
- current_frame->width = width;
- current_frame->height = height;
+ context->current_frame->format = PIX_FMT_BGR32;
+ context->current_frame->width = width;
+ context->current_frame->height = height;
- return current_frame;
+ return context->current_frame;
}
static void set_ffmpeg_property_option(AVCodecContext *c, IDProperty *prop, AVDictionary **dictionary)
@@ -516,7 +519,7 @@ static void set_ffmpeg_properties(RenderData *rd, AVCodecContext *c, const char
/* prepare a video stream for the output file */
-static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContext *of,
+static AVStream *alloc_video_stream(FFMpegContext *context, RenderData *rd, int codec_id, AVFormatContext *of,
int rectx, int recty, char *error, int error_size)
{
AVStream *st;
@@ -542,7 +545,7 @@ static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContex
c->height = recty;
/* FIXME: Really bad hack (tm) for NTSC support */
- if (ffmpeg_type == FFMPEG_DV && rd->frs_sec != 25) {
+ if (context->ffmpeg_type == FFMPEG_DV && rd->frs_sec != 25) {
c->time_base.den = 2997;
c->time_base.num = 100;
}
@@ -555,8 +558,8 @@ static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContex
c->time_base.num = ((double) rd->frs_sec_base) * 100000;
}
- c->gop_size = ffmpeg_gop_size;
- c->bit_rate = ffmpeg_video_bitrate * 1000;
+ c->gop_size = context->ffmpeg_gop_size;
+ c->bit_rate = context->ffmpeg_video_bitrate * 1000;
c->rc_max_rate = rd->ffcodecdata.rc_max_rate * 1000;
c->rc_min_rate = rd->ffcodecdata.rc_min_rate * 1000;
c->rc_buffer_size = rd->ffcodecdata.rc_buffer_size * 1024;
@@ -585,7 +588,7 @@ static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContex
c->pix_fmt = PIX_FMT_YUV422P;
}
- if (ffmpeg_type == FFMPEG_XVID) {
+ if (context->ffmpeg_type == FFMPEG_XVID) {
/* arghhhh ... */
c->pix_fmt = PIX_FMT_YUV420P;
c->codec_tag = (('D' << 24) + ('I' << 16) + ('V' << 8) + 'X');
@@ -655,14 +658,14 @@ static AVStream *alloc_video_stream(RenderData *rd, int codec_id, AVFormatContex
}
av_dict_free(&opts);
- current_frame = alloc_picture(c->pix_fmt, c->width, c->height);
+ context->current_frame = alloc_picture(c->pix_fmt, c->width, c->height);
- img_convert_ctx = sws_getContext(c->width, c->height, PIX_FMT_BGR32, c->width, c->height, c->pix_fmt, SWS_BICUBIC,
+ context->img_convert_ctx = sws_getContext(c->width, c->height, PIX_FMT_BGR32, c->width, c->height, c->pix_fmt, SWS_BICUBIC,
NULL, NULL, NULL);
return st;
}
-static AVStream *alloc_audio_stream(RenderData *rd, int codec_id, AVFormatContext *of, char *error, int error_size)
+static AVStream *alloc_audio_stream(FFMpegContext *context, RenderData *rd, int codec_id, AVFormatContext *of, char *error, int error_size)
{
AVStream *st;
AVCodecContext *c;
@@ -680,7 +683,7 @@ static AVStream *alloc_audio_stream(RenderData *rd, int codec_id, AVFormatContex
c->codec_type = AVMEDIA_TYPE_AUDIO;
c->sample_rate = rd->ffcodecdata.audio_mixrate;
- c->bit_rate = ffmpeg_audio_bitrate * 1000;
+ c->bit_rate = context->ffmpeg_audio_bitrate * 1000;
c->sample_fmt = AV_SAMPLE_FMT_S16;
c->channels = rd->ffcodecdata.audio_channels;
@@ -747,35 +750,35 @@ static AVStream *alloc_audio_stream(RenderData *rd, int codec_id, AVFormatContex
st->codec->time_base.den = st->codec->sample_rate;
#ifndef FFMPEG_HAVE_ENCODE_AUDIO2
- audio_outbuf_size = FF_MIN_BUFFER_SIZE;
+ context->audio_outbuf_size = FF_MIN_BUFFER_SIZE;
#endif
if (c->frame_size == 0)
// used to be if ((c->codec_id >= CODEC_ID_PCM_S16LE) && (c->codec_id <= CODEC_ID_PCM_DVD))
// not sure if that is needed anymore, so let's try out if there are any
// complaints regarding some ffmpeg versions users might have
- audio_input_samples = FF_MIN_BUFFER_SIZE * 8 / c->bits_per_coded_sample / c->channels;
+ context->audio_input_samples = FF_MIN_BUFFER_SIZE * 8 / c->bits_per_coded_sample / c->channels;
else {
- audio_input_samples = c->frame_size;
+ context->audio_input_samples = c->frame_size;
#ifndef FFMPEG_HAVE_ENCODE_AUDIO2
- if (c->frame_size * c->channels * sizeof(int16_t) * 4 > audio_outbuf_size)
- audio_outbuf_size = c->frame_size * c->channels * sizeof(int16_t) * 4;
+ if (c->frame_size * c->channels * sizeof(int16_t) * 4 > context->audio_outbuf_size)
+ context->audio_outbuf_size = c->frame_size * c->channels * sizeof(int16_t) * 4;
#endif
}
- audio_deinterleave = av_sample_fmt_is_planar(c->sample_fmt);
+ context->audio_deinterleave = av_sample_fmt_is_planar(c->sample_fmt);
- audio_sample_size = av_get_bytes_per_sample(c->sample_fmt);
+ context->audio_sample_size = av_get_bytes_per_sample(c->sample_fmt);
- audio_input_buffer = (uint8_t *) av_malloc(audio_input_samples * c->channels * audio_sample_size);
+ context->audio_input_buffer = (uint8_t *) av_malloc(context->audio_input_samples * c->channels * context->audio_sample_size);
#ifndef FFMPEG_HAVE_ENCODE_AUDIO2
- audio_output_buffer = (uint8_t *) av_malloc(audio_outbuf_size);
+ context->audio_output_buffer = (uint8_t *) av_malloc(context->audio_outbuf_size);
#endif
- if (audio_deinterleave)
- audio_deinterleave_buffer = (uint8_t *) av_malloc(audio_input_samples * c->channels * audio_sample_size);
+ if (context->audio_deinterleave)
+ context->audio_deinterleave_buffer = (uint8_t *) av_malloc(context->audio_input_samples * c->channels * context->audio_sample_size);
- audio_time = 0.0f;
+ context->audio_time = 0.0f;
return st;
}
@@ -799,7 +802,7 @@ static void ffmpeg_dict_set_float(AVDictionary **dict, const char *key, float va
av_dict_set(dict, key, buffer, 0);
}
-static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, ReportList *reports)
+static int start_ffmpeg_impl(FFMpegContext *context, struct RenderData *rd, int rectx, int recty, const char *suffix, ReportList *reports)
{
/* Handle to the output file */
AVFormatContext *of;
@@ -808,26 +811,26 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report
char name[256], error[1024];
const char **exts;
- ffmpeg_type = rd->ffcodecdata.type;
- ffmpeg_codec = rd->ffcodecdata.codec;
- ffmpeg_audio_codec = rd->ffcodecdata.audio_codec;
- ffmpeg_video_bitrate = rd->ffcodecdata.video_bitrate;
- ffmpeg_audio_bitrate = rd->ffcodecdata.audio_bitrate;
- ffmpeg_gop_size = rd->ffcodecdata.gop_size;
- ffmpeg_autosplit = rd->ffcodecdata.flags & FFMPEG_AUTOSPLIT_OUTPUT;
-
+ context->ffmpeg_type = rd->ffcodecdata.type;
+ context->ffmpeg_codec = rd->ffcodecdata.codec;
+ context->ffmpeg_audio_codec = rd->ffcodecdata.audio_codec;
+ context->ffmpeg_video_bitrate = rd->ffcodecdata.video_bitrate;
+ context->ffmpeg_audio_bitrate = rd->ffcodecdata.audio_bitrate;
+ context->ffmpeg_gop_size = rd->ffcodecdata.gop_size;
+ context->ffmpeg_autosplit = rd->ffcodecdata.flags & FFMPEG_AUTOSPLIT_OUTPUT;
+
/* Determine the correct filename */
- BKE_ffmpeg_filepath_get(name, rd, ffmpeg_preview);
+ ffmpeg_filepath_get(context, name, rd, context->ffmpeg_preview, suffix);
PRINT("Starting output to %s(ffmpeg)...\n"
" Using type=%d, codec=%d, audio_codec=%d,\n"
" video_bitrate=%d, audio_bitrate=%d,\n"
" gop_size=%d, autosplit=%d\n"
" render width=%d, render height=%d\n",
- name, ffmpeg_type, ffmpeg_codec, ffmpeg_audio_codec,
- ffmpeg_video_bitrate, ffmpeg_audio_bitrate,
- ffmpeg_gop_size, ffmpeg_autosplit, rectx, recty);
+ name, context->ffmpeg_type, context->ffmpeg_codec, context->ffmpeg_audio_codec,
+ context->ffmpeg_video_bitrate, context->ffmpeg_audio_bitrate,
+ context->ffmpeg_gop_size, context->ffmpeg_autosplit, rectx, recty);
- exts = get_file_extensions(ffmpeg_type);
+ exts = get_file_extensions(context->ffmpeg_type);
if (!exts) {
BKE_report(reports, RPT_ERROR, "No valid formats found");
return 0;
@@ -846,7 +849,7 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report
of->oformat = fmt;
of->packet_size = rd->ffcodecdata.mux_packet_size;
- if (ffmpeg_audio_codec != AV_CODEC_ID_NONE) {
+ if (context->ffmpeg_audio_codec != AV_CODEC_ID_NONE) {
ffmpeg_dict_set_int(&opts, "muxrate", rd->ffcodecdata.mux_rate);
}
else {
@@ -857,15 +860,15 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report
of->max_delay = (int)(0.7 * AV_TIME_BASE);
- fmt->audio_codec = ffmpeg_audio_codec;
+ fmt->audio_codec = context->ffmpeg_audio_codec;
BLI_strncpy(of->filename, name, sizeof(of->filename));
/* set the codec to the user's selection */
- switch (ffmpeg_type) {
+ switch (context->ffmpeg_type) {
case FFMPEG_AVI:
case FFMPEG_MOV:
case FFMPEG_MKV:
- fmt->video_codec = ffmpeg_codec;
+ fmt->video_codec = context->ffmpeg_codec;
break;
case FFMPEG_OGG:
fmt->video_codec = AV_CODEC_ID_THEORA;
@@ -908,9 +911,9 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report
}
}
- if (ffmpeg_type == FFMPEG_DV) {
+ if (context->ffmpeg_type == FFMPEG_DV) {
fmt->audio_codec = AV_CODEC_ID_PCM_S16LE;
- if (ffmpeg_audio_codec != AV_CODEC_ID_NONE && rd->ffcodecdata.audio_mixrate != 48000 && rd->ffcodecdata.audio_channels != 2) {
+ if (context->ffmpeg_audio_codec != AV_CODEC_ID_NONE && rd->ffcodecdata.audio_mixrate != 48000 && rd->ffcodecdata.audio_channels != 2) {
BKE_report(reports, RPT_ERROR, "FFMPEG only supports 48khz / stereo audio for DV!");
av_dict_free(&opts);
return 0;
@@ -918,9 +921,9 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report
}
if (fmt->video_codec != AV_CODEC_ID_NONE) {
- video_stream = alloc_video_stream(rd, fmt->video_codec, of, rectx, recty, error, sizeof(error));
- PRINT("alloc video stream %p\n", video_stream);
- if (!video_stream) {
+ context->video_stream = alloc_video_stream(context, rd, fmt->video_codec, of, rectx, recty, error, sizeof(error));
+ PRINT("alloc video stream %p\n", context->video_stream);
+ if (!context->video_stream) {
if (error[0])
BKE_report(reports, RPT_ERROR, error);
else
@@ -931,9 +934,9 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report
}
}
- if (ffmpeg_audio_codec != AV_CODEC_ID_NONE) {
- audio_stream = alloc_audio_stream(rd, fmt->audio_codec, of, error, sizeof(error));
- if (!audio_stream) {
+ if (context->ffmpeg_audio_codec != AV_CODEC_ID_NONE) {
+ context->audio_stream = alloc_audio_stream(context, rd, fmt->audio_codec, of, error, sizeof(error));
+ if (!context->audio_stream) {
if (error[0])
BKE_report(reports, RPT_ERROR, error);
else
@@ -956,7 +959,7 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report
return 0;
}
- outfile = of;
+ context->outfile = of;
av_dump_format(of, 0, name, 1);
av_dict_free(&opts);
@@ -980,11 +983,11 @@ static int start_ffmpeg_impl(struct RenderData *rd, int rectx, int recty, Report
* parameter.
* </p>
*/
-static void flush_ffmpeg(void)
+static void flush_ffmpeg(FFMpegContext *context)
{
int ret = 0;
- AVCodecContext *c = video_stream->codec;
+ AVCodecContext *c = context->video_stream->codec;
/* get the delayed frames */
while (1) {
int got_output;
@@ -1000,28 +1003,28 @@ static void flush_ffmpeg(void)
break;
}
if (packet.pts != AV_NOPTS_VALUE) {
- packet.pts = av_rescale_q(packet.pts, c->time_base, video_stream->time_base);
+ packet.pts = av_rescale_q(packet.pts, c->time_base, context->video_stream->time_base);
PRINT("Video Frame PTS: %d\n", (int) packet.pts);
}
else {
PRINT("Video Frame PTS: not set\n");
}
if (packet.dts != AV_NOPTS_VALUE) {
- packet.dts = av_rescale_q(packet.dts, c->time_base, video_stream->time_base);
+ packet.dts = av_rescale_q(packet.dts, c->time_base, context->video_stream->time_base);
PRINT("Video Frame DTS: %d\n", (int) packet.dts);
}
else {
PRINT("Video Frame DTS: not set\n");
}
- packet.stream_index = video_stream->index;
- ret = av_interleaved_write_frame(outfile, &packet);
+ packet.stream_index = context->video_stream->index;
+ ret = av_interleaved_write_frame(context->outfile, &packet);
if (ret != 0) {
fprintf(stderr, "Error writing delayed frame %d\n", ret);
break;
}
}
- avcodec_flush_buffers(video_stream->codec);
+ avcodec_flush_buffers(context->video_stream->codec);
}
/* **********************************************************************
@@ -1029,7 +1032,7 @@ static void flush_ffmpeg(void)
* ********************************************************************** */
/* Get the output filename-- similar to the other output formats */
-void BKE_ffmpeg_filepath_get(char *string, RenderData *rd, bool preview)
+static void ffmpeg_filepath_get(FFMpegContext *context, char *string, RenderData *rd, bool preview, const char *suffix)
{
char autosplit[20];
@@ -1056,7 +1059,9 @@ void BKE_ffmpeg_filepath_get(char *string, RenderData *rd, bool preview)
autosplit[0] = 0;
if ((rd->ffcodecdata.flags & FFMPEG_AUTOSPLIT_OUTPUT) != 0) {
- sprintf(autosplit, "_%03d", ffmpeg_autosplit_count);
+ if (context) {
+ sprintf(autosplit, "_%03d", context->ffmpeg_autosplit_count);
+ }
}
if (rd->scemode & R_EXTENSION) {
@@ -1086,19 +1091,28 @@ void BKE_ffmpeg_filepath_get(char *string, RenderData *rd, bool preview)
strcat(string, autosplit);
}
+
+ BLI_path_suffix(string, FILE_MAX, suffix, "");
}
-int BKE_ffmpeg_start(struct Scene *scene, RenderData *rd, int rectx, int recty, ReportList *reports, bool preview)
+void BKE_ffmpeg_filepath_get(char *string, RenderData *rd, bool preview, const char *suffix)
+{
+ ffmpeg_filepath_get(NULL, string, rd, preview, suffix);
+}
+
+int BKE_ffmpeg_start(void *context_v, struct Scene *scene, RenderData *rd, int rectx, int recty,
+ ReportList *reports, bool preview, const char *suffix)
{
int success;
+ FFMpegContext *context = context_v;
- ffmpeg_autosplit_count = 0;
- ffmpeg_preview = preview;
+ context->ffmpeg_autosplit_count = 0;
+ context->ffmpeg_preview = preview;
- success = start_ffmpeg_impl(rd, rectx, recty, reports);
+ success = start_ffmpeg_impl(context, rd, rectx, recty, suffix, reports);
#ifdef WITH_AUDASPACE
- if (audio_stream) {
- AVCodecContext *c = audio_stream->codec;
+ if (context->audio_stream) {
+ AVCodecContext *c = context->audio_stream->codec;
AUD_DeviceSpecs specs;
specs.channels = c->channels;
@@ -1123,7 +1137,7 @@ int BKE_ffmpeg_start(struct Scene *scene, RenderData *rd, int rectx, int recty,
}
specs.rate = rd->ffcodecdata.audio_mixrate;
- audio_mixdown_device = BKE_sound_mixdown(scene, specs, preview ? rd->psfra : rd->sfra, rd->ffcodecdata.audio_volume);
+ context->audio_mixdown_device = BKE_sound_mixdown(scene, specs, preview ? rd->psfra : rd->sfra, rd->ffcodecdata.audio_volume);
#ifdef FFMPEG_CODEC_TIME_BASE
c->time_base.den = specs.rate;
c->time_base.num = 1;
@@ -1133,16 +1147,16 @@ int BKE_ffmpeg_start(struct Scene *scene, RenderData *rd, int rectx, int recty,
return success;
}
-static void end_ffmpeg_impl(int is_autosplit);
+static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit);
#ifdef WITH_AUDASPACE
-static void write_audio_frames(double to_pts)
+static void write_audio_frames(FFMpegContext *context, double to_pts)
{
int finished = 0;
- while (audio_stream && !finished) {
- if ((audio_time >= to_pts) ||
- (write_audio_frame()))
+ while (context->audio_stream && !finished) {
+ if ((context->audio_time >= to_pts) ||
+ (write_audio_frame(context)))
{
finished = 1;
}
@@ -1150,8 +1164,10 @@ static void write_audio_frames(double to_pts)
}
#endif
-int BKE_ffmpeg_append(RenderData *rd, int start_frame, int frame, int *pixels, int rectx, int recty, ReportList *reports)
+int BKE_ffmpeg_append(void *context_v, RenderData *rd, int start_frame, int frame, int *pixels,
+ int rectx, int recty, const char *suffix, ReportList *reports)
{
+ FFMpegContext *context = context_v;
AVFrame *avframe;
int success = 1;
@@ -1160,111 +1176,112 @@ int BKE_ffmpeg_append(RenderData *rd, int start_frame, int frame, int *pixels, i
/* why is this done before writing the video frame and again at end_ffmpeg? */
// write_audio_frames(frame / (((double)rd->frs_sec) / rd->frs_sec_base));
- if (video_stream) {
- avframe = generate_video_frame((unsigned char *) pixels, reports);
- success = (avframe && write_video_frame(rd, frame - start_frame, avframe, reports));
+ if (context->video_stream) {
+ avframe = generate_video_frame(context, (unsigned char *) pixels, reports);
+ success = (avframe && write_video_frame(context, rd, frame - start_frame, avframe, reports));
- if (ffmpeg_autosplit) {
- if (avio_tell(outfile->pb) > FFMPEG_AUTOSPLIT_SIZE) {
- end_ffmpeg_impl(true);
- ffmpeg_autosplit_count++;
- success &= start_ffmpeg_impl(rd, rectx, recty, reports);
+ if (context->ffmpeg_autosplit) {
+ if (avio_tell(context->outfile->pb) > FFMPEG_AUTOSPLIT_SIZE) {
+ end_ffmpeg_impl(context, true);
+ context->ffmpeg_autosplit_count++;
+ success &= start_ffmpeg_impl(context, rd, rectx, recty, suffix, reports);
}
}
}
#ifdef WITH_AUDASPACE
- write_audio_frames((frame - start_frame) / (((double)rd->frs_sec) / (double)rd->frs_sec_base));
+ write_audio_frames(context, (frame - start_frame) / (((double)rd->frs_sec) / (double)rd->frs_sec_base));
#endif
return success;
}
-static void end_ffmpeg_impl(int is_autosplit)
+static void end_ffmpeg_impl(FFMpegContext *context, int is_autosplit)
{
unsigned int i;
PRINT("Closing ffmpeg...\n");
#if 0
- if (audio_stream) { /* SEE UPPER */
- write_audio_frames();
+ if (context->audio_stream) { /* SEE UPPER */
+ write_audio_frames(context);
}
#endif
#ifdef WITH_AUDASPACE
if (is_autosplit == false) {
- if (audio_mixdown_device) {
- AUD_closeReadDevice(audio_mixdown_device);
- audio_mixdown_device = 0;
+ if (context->audio_mixdown_device) {
+ AUD_closeReadDevice(context->audio_mixdown_device);
+ context->audio_mixdown_device = 0;
}
}
#endif
- if (video_stream && video_stream->codec) {
+ if (context->video_stream && context->video_stream->codec) {
PRINT("Flushing delayed frames...\n");
- flush_ffmpeg();
+ flush_ffmpeg(context);
}
- if (outfile) {
- av_write_trailer(outfile);
+ if (context->outfile) {
+ av_write_trailer(context->outfile);
}
/* Close the video codec */
- if (video_stream && video_stream->codec) {
- avcodec_close(video_stream->codec);
- PRINT("zero video stream %p\n", video_stream);
- video_stream = 0;
+ if (context->video_stream && context->video_stream->codec) {
+ avcodec_close(context->video_stream->codec);
+ PRINT("zero video stream %p\n", context->video_stream);
+ context->video_stream = 0;
}
/* Close the output file */
- if (outfile) {
- for (i = 0; i < outfile->nb_streams; i++) {
- if (&outfile->streams[i]) {
- av_freep(&outfile->streams[i]);
+ if (context->outfile) {
+ for (i = 0; i < context->outfile->nb_streams; i++) {
+ if (&context->outfile->streams[i]) {
+ av_freep(&context->outfile->streams[i]);
}
}
}
/* free the temp buffer */
- if (current_frame) {
- delete_picture(current_frame);
- current_frame = 0;
+ if (context->current_frame) {
+ delete_picture(context->current_frame);
+ context->current_frame = 0;
}
- if (outfile && outfile->oformat) {
- if (!(outfile->oformat->flags & AVFMT_NOFILE)) {
- avio_close(outfile->pb);
+ if (context->outfile && context->outfile->oformat) {
+ if (!(context->outfile->oformat->flags & AVFMT_NOFILE)) {
+ avio_close(context->outfile->pb);
}
}
- if (outfile) {
- av_free(outfile);
- outfile = 0;
+ if (context->outfile) {
+ av_free(context->outfile);
+ context->outfile = 0;
}
- if (audio_input_buffer) {
- av_free(audio_input_buffer);
- audio_input_buffer = 0;
+ if (context->audio_input_buffer) {
+ av_free(context->audio_input_buffer);
+ context->audio_input_buffer = 0;
}
#ifndef FFMPEG_HAVE_ENCODE_AUDIO2
- if (audio_output_buffer) {
- av_free(audio_output_buffer);
- audio_output_buffer = 0;
+ if (context->audio_output_buffer) {
+ av_free(context->audio_output_buffer);
+ context->audio_output_buffer = 0;
}
#endif
- if (audio_deinterleave_buffer) {
- av_free(audio_deinterleave_buffer);
- audio_deinterleave_buffer = 0;
+ if (context->audio_deinterleave_buffer) {
+ av_free(context->audio_deinterleave_buffer);
+ context->audio_deinterleave_buffer = 0;
}
- if (img_convert_ctx) {
- sws_freeContext(img_convert_ctx);
- img_convert_ctx = 0;
+ if (context->img_convert_ctx) {
+ sws_freeContext(context->img_convert_ctx);
+ context->img_convert_ctx = 0;
}
}
-void BKE_ffmpeg_end(void)
+void BKE_ffmpeg_end(void *context_v)
{
- end_ffmpeg_impl(false);
+ FFMpegContext *context = context_v;
+ end_ffmpeg_impl(context, false);
}
/* properties */
@@ -1658,4 +1675,31 @@ bool BKE_ffmpeg_alpha_channel_is_supported(RenderData *rd)
return false;
}
-#endif
+void *BKE_ffmpeg_context_create(void)
+{
+ FFMpegContext *context;
+
+ /* new ffmpeg data struct */
+ context = MEM_callocN(sizeof(FFMpegContext), "new ffmpeg context");
+
+ context->ffmpeg_codec = AV_CODEC_ID_MPEG4;
+ context->ffmpeg_audio_codec = AV_CODEC_ID_NONE;
+ context->ffmpeg_video_bitrate = 1150;
+ context->ffmpeg_audio_bitrate = 128;
+ context->ffmpeg_gop_size = 12;
+ context->ffmpeg_autosplit = 0;
+ context->ffmpeg_autosplit_count = 0;
+ context->ffmpeg_preview = false;
+
+ return context;
+}
+
+void BKE_ffmpeg_context_free(void *context_v)
+{
+ FFMpegContext *context = context_v;
+ if (context) {
+ MEM_freeN(context);
+ }
+}
+
+#endif /* WITH_FFMPEG */
diff --git a/source/blender/blenkernel/intern/writeframeserver.c b/source/blender/blenkernel/intern/writeframeserver.c
index 9cac86d62bd..ba58038bbd8 100644
--- a/source/blender/blenkernel/intern/writeframeserver.c
+++ b/source/blender/blenkernel/intern/writeframeserver.c
@@ -66,12 +66,15 @@
#include "BKE_report.h"
#include "DNA_scene_types.h"
+#include "MEM_guardedalloc.h"
-static int sock;
-static int connsock;
-static int write_ppm;
-static int render_width;
-static int render_height;
+typedef struct FrameserverContext {
+ int sock;
+ int connsock;
+ int write_ppm;
+ int render_width;
+ int render_height;
+} FrameserverContext;
#if defined(_WIN32)
@@ -110,10 +113,11 @@ static int closesocket(int fd)
}
#endif
-int BKE_frameserver_start(struct Scene *scene, RenderData *UNUSED(rd), int rectx, int recty, ReportList *reports, bool UNUSED(preview))
+int BKE_frameserver_start(void *context_v, struct Scene *scene, RenderData *UNUSED(rd), int rectx, int recty, ReportList *reports, bool UNUSED(preview), const char *UNUSED(suffix))
{
struct sockaddr_in addr;
int arg = 1;
+ FrameserverContext *context = context_v;
(void)scene; /* unused */
@@ -122,33 +126,33 @@ int BKE_frameserver_start(struct Scene *scene, RenderData *UNUSED(rd), int rectx
return 0;
}
- if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ if ((context->sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
shutdown_socket_system();
BKE_report(reports, RPT_ERROR, "Cannot open socket");
return 0;
}
- setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *) &arg, sizeof(arg));
+ setsockopt(context->sock, SOL_SOCKET, SO_REUSEADDR, (char *) &arg, sizeof(arg));
addr.sin_family = AF_INET;
addr.sin_port = htons(U.frameserverport);
addr.sin_addr.s_addr = INADDR_ANY;
- if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ if (bind(context->sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
shutdown_socket_system();
BKE_report(reports, RPT_ERROR, "Cannot bind to socket");
return 0;
}
- if (listen(sock, SOMAXCONN) < 0) {
+ if (listen(context->sock, SOMAXCONN) < 0) {
shutdown_socket_system();
BKE_report(reports, RPT_ERROR, "Cannot establish listen backlog");
return 0;
}
- connsock = -1;
+ context->connsock = -1;
- render_width = rectx;
- render_height = recty;
+ context->render_width = rectx;
+ context->render_height = recty;
return 1;
}
@@ -177,7 +181,7 @@ static char good_bye[] =
"<body><pre>\n"
"Render stopped. Goodbye</pre></body></html>";
-static int safe_write(char *s, int tosend)
+static int safe_write(const int connsock, char *s, int tosend)
{
int total = tosend;
do {
@@ -192,12 +196,12 @@ static int safe_write(char *s, int tosend)
return total;
}
-static int safe_puts(char *s)
+static int safe_puts(const int connsock, char *s)
{
- return safe_write(s, strlen(s));
+ return safe_write(connsock, s, strlen(s));
}
-static int handle_request(RenderData *rd, char *req)
+static int handle_request(FrameserverContext *context, RenderData *rd, char *req)
{
char *p;
char *path;
@@ -214,16 +218,16 @@ static int handle_request(RenderData *rd, char *req)
*p = 0;
- if (STREQ(path, "/index.html") || STREQ(path, "/")) {
- safe_puts(index_page);
+ if (STREQ(path, "/index.html") || strcmp(path, "/")) {
+ safe_puts(context->connsock, index_page);
return -1;
}
- write_ppm = 0;
+ context->write_ppm = 0;
pathlen = strlen(path);
if (pathlen > 12 && memcmp(path, "/images/ppm/", 12) == 0) {
- write_ppm = 1;
+ context->write_ppm = 1;
return atoi(path + 12);
}
if (STREQ(path, "/info.txt")) {
@@ -241,24 +245,24 @@ static int handle_request(RenderData *rd, char *req)
"ratescale %d\n",
rd->sfra,
rd->efra,
- render_width,
- render_height,
+ context->render_width,
+ context->render_height,
rd->frs_sec,
1
);
- safe_puts(buf);
+ safe_puts(context->connsock, buf);
return -1;
}
if (STREQ(path, "/close.txt")) {
- safe_puts(good_bye);
+ safe_puts(context->connsock, good_bye);
G.is_break = true; /* Abort render */
return -1;
}
return -1;
}
-int BKE_frameserver_loop(RenderData *rd, ReportList *UNUSED(reports))
+int BKE_frameserver_loop(void *context_v, RenderData *rd, ReportList *UNUSED(reports))
{
fd_set readfds;
struct timeval tv;
@@ -271,18 +275,20 @@ int BKE_frameserver_loop(RenderData *rd, ReportList *UNUSED(reports))
#endif
char buf[4096];
- if (connsock != -1) {
- closesocket(connsock);
- connsock = -1;
+ FrameserverContext *context = context_v;
+
+ if (context->connsock != -1) {
+ closesocket(context->connsock);
+ context->connsock = -1;
}
tv.tv_sec = 1;
tv.tv_usec = 0;
FD_ZERO(&readfds);
- FD_SET(sock, &readfds);
+ FD_SET(context->sock, &readfds);
- rval = select(sock + 1, &readfds, NULL, NULL, &tv);
+ rval = select(context->sock + 1, &readfds, NULL, NULL, &tv);
if (rval < 0) {
return -1;
}
@@ -293,19 +299,19 @@ int BKE_frameserver_loop(RenderData *rd, ReportList *UNUSED(reports))
socklen = sizeof(addr);
- if ((connsock = accept(sock, (struct sockaddr *)&addr, &socklen)) < 0) {
+ if ((context->connsock = accept(context->sock, (struct sockaddr *)&addr, &socklen)) < 0) {
return -1;
}
FD_ZERO(&readfds);
- FD_SET(connsock, &readfds);
+ FD_SET(context->connsock, &readfds);
for (;;) {
/* give 10 seconds for telnet testing... */
tv.tv_sec = 10;
tv.tv_usec = 0;
- rval = select(connsock + 1, &readfds, NULL, NULL, &tv);
+ rval = select(context->connsock + 1, &readfds, NULL, NULL, &tv);
if (rval > 0) {
break;
}
@@ -319,7 +325,7 @@ int BKE_frameserver_loop(RenderData *rd, ReportList *UNUSED(reports))
}
}
- len = recv(connsock, buf, sizeof(buf) - 1, 0);
+ len = recv(context->connsock, buf, sizeof(buf) - 1, 0);
if (len < 0) {
return -1;
@@ -327,13 +333,13 @@ int BKE_frameserver_loop(RenderData *rd, ReportList *UNUSED(reports))
buf[len] = 0;
- return handle_request(rd, buf);
+ return handle_request(context, rd, buf);
}
-static void serve_ppm(int *pixels, int rectx, int recty)
+static void serve_ppm(FrameserverContext *context, int *pixels, int rectx, int recty)
{
unsigned char *rendered_frame;
- unsigned char *row = (unsigned char *) malloc(render_width * 3);
+ unsigned char *row = (unsigned char *) malloc(context->render_width * 3);
int y;
char header[1024];
@@ -348,7 +354,7 @@ static void serve_ppm(int *pixels, int rectx, int recty)
"255\n",
rectx, recty);
- safe_puts(header);
+ safe_puts(context->connsock, header);
rendered_frame = (unsigned char *)pixels;
@@ -364,36 +370,54 @@ static void serve_ppm(int *pixels, int rectx, int recty)
target += 3;
src += 4;
}
- safe_write((char *)row, 3 * rectx);
+ safe_write(context->connsock, (char *)row, 3 * rectx);
}
free(row);
- closesocket(connsock);
- connsock = -1;
+ closesocket(context->connsock);
+ context->connsock = -1;
}
-int BKE_frameserver_append(RenderData *UNUSED(rd), int UNUSED(start_frame), int frame, int *pixels,
- int rectx, int recty, ReportList *UNUSED(reports))
+int BKE_frameserver_append(void *context_v, RenderData *UNUSED(rd), int UNUSED(start_frame), int frame, int *pixels,
+ int rectx, int recty, const char *UNUSED(suffix), ReportList *UNUSED(reports))
{
+ FrameserverContext *context = context_v;
+
fprintf(stderr, "Serving frame: %d\n", frame);
- if (write_ppm) {
- serve_ppm(pixels, rectx, recty);
+ if (context->write_ppm) {
+ serve_ppm(context, pixels, rectx, recty);
}
- if (connsock != -1) {
- closesocket(connsock);
- connsock = -1;
+ if (context->connsock != -1) {
+ closesocket(context->connsock);
+ context->connsock = -1;
}
return 1;
}
-void BKE_frameserver_end(void)
+void BKE_frameserver_end(void *context_v)
{
- if (connsock != -1) {
- closesocket(connsock);
- connsock = -1;
+ FrameserverContext *context = context_v;
+
+ if (context->connsock != -1) {
+ closesocket(context->connsock);
+ context->connsock = -1;
}
- closesocket(sock);
+ closesocket(context->sock);
shutdown_socket_system();
}
+void *BKE_frameserver_context_create(void)
+{
+ FrameserverContext *context = MEM_mallocN(sizeof(FrameserverContext), "Frameserver Context");
+ return context;
+}
+
+void BKE_frameserver_context_free(void *context_v)
+{
+ FrameserverContext *context = context_v;
+ if (context) {
+ MEM_freeN(context);
+ }
+}
+
#endif /* WITH_FRAMESERVER */
diff --git a/source/blender/blenlib/BLI_threads.h b/source/blender/blenlib/BLI_threads.h
index 74291ca305e..18b8feaa99c 100644
--- a/source/blender/blenlib/BLI_threads.h
+++ b/source/blender/blenlib/BLI_threads.h
@@ -89,6 +89,7 @@ int BLI_system_num_threads_override_get(void);
#define LOCK_MOVIECLIP 7
#define LOCK_COLORMANAGE 8
#define LOCK_FFTW 9
+#define LOCK_VIEW3D 10
void BLI_lock_thread(int type);
void BLI_unlock_thread(int type);
diff --git a/source/blender/blenlib/intern/threads.c b/source/blender/blenlib/intern/threads.c
index ded2fd7e06d..da06500565c 100644
--- a/source/blender/blenlib/intern/threads.c
+++ b/source/blender/blenlib/intern/threads.c
@@ -122,6 +122,7 @@ static pthread_mutex_t _nodes_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t _movieclip_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t _colormanage_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t _fftw_lock = PTHREAD_MUTEX_INITIALIZER;
+static pthread_mutex_t _view3d_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_t mainid;
static int thread_levels = 0; /* threads can be invoked inside threads */
static int num_threads_override = 0;
@@ -402,6 +403,8 @@ void BLI_lock_thread(int type)
pthread_mutex_lock(&_colormanage_lock);
else if (type == LOCK_FFTW)
pthread_mutex_lock(&_fftw_lock);
+ else if (type == LOCK_VIEW3D)
+ pthread_mutex_lock(&_view3d_lock);
}
void BLI_unlock_thread(int type)
@@ -426,6 +429,8 @@ void BLI_unlock_thread(int type)
pthread_mutex_unlock(&_colormanage_lock);
else if (type == LOCK_FFTW)
pthread_mutex_unlock(&_fftw_lock);
+ else if (type == LOCK_VIEW3D)
+ pthread_mutex_unlock(&_view3d_lock);
}
/* Mutex Locks */
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 8c5959f20bf..4eb7413adc1 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -1533,9 +1533,15 @@ void blo_make_packed_pointer_map(FileData *fd, Main *oldmain)
fd->packedmap = oldnewmap_new();
- for (ima = oldmain->image.first; ima; ima = ima->id.next)
+ for (ima = oldmain->image.first; ima; ima = ima->id.next) {
+ ImagePackedFile *imapf;
if (ima->packedfile)
insert_packedmap(fd, ima->packedfile);
+
+ for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next)
+ if (imapf->packedfile)
+ insert_packedmap(fd, imapf->packedfile);
+ }
for (vfont = oldmain->vfont.first; vfont; vfont = vfont->id.next)
if (vfont->packedfile)
@@ -1568,8 +1574,13 @@ void blo_end_packed_pointer_map(FileData *fd, Main *oldmain)
entry->newp = NULL;
}
- for (ima = oldmain->image.first; ima; ima = ima->id.next)
+ for (ima = oldmain->image.first; ima; ima = ima->id.next) {
+ ImagePackedFile *imapf;
ima->packedfile = newpackedadr(fd, ima->packedfile);
+
+ for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next)
+ imapf->packedfile = newpackedadr(fd, imapf->packedfile);
+ }
for (vfont = oldmain->vfont.first; vfont; vfont = vfont->id.next)
vfont->packedfile = newpackedadr(fd, vfont->packedfile);
@@ -3391,6 +3402,8 @@ static void lib_link_image(FileData *fd, Main *main)
static void direct_link_image(FileData *fd, Image *ima)
{
+ ImagePackedFile *imapf;
+
/* for undo system, pointers could be restored */
if (fd->imamap)
ima->cache = newimaadr(fd, ima->cache);
@@ -3404,8 +3417,7 @@ static void direct_link_image(FileData *fd, Image *ima)
ima->gputexture = NULL;
ima->rr = NULL;
}
-
- ima->anim = NULL;
+
ima->repbind = NULL;
/* undo system, try to restore render buffers */
@@ -3419,9 +3431,18 @@ static void direct_link_image(FileData *fd, Image *ima)
memset(ima->renders, 0, sizeof(ima->renders));
ima->last_render_slot = ima->render_slot;
}
-
+
+ link_list(fd, &(ima->views));
+ link_list(fd, &(ima->packedfiles));
+
+ for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) {
+ imapf->packedfile = direct_link_packedfile(fd, imapf->packedfile);
+ }
+
+ ima->anims.first = ima->anims.last = NULL;
ima->packedfile = direct_link_packedfile(fd, ima->packedfile);
ima->preview = direct_link_preview_image(fd, ima->preview);
+ ima->stereo3d_format = newdataadr(fd, ima->stereo3d_format);
ima->ok = 1;
}
@@ -5378,7 +5399,7 @@ static void lib_link_scene(FileData *fd, Main *main)
seq->scene_sound = BKE_sound_add_scene_sound_defaults(sce, seq);
}
}
- seq->anim = NULL;
+ seq->anims.first = seq->anims.last = NULL;
lib_link_sequence_modifiers(fd, sce, &seq->modifiers);
}
@@ -5610,6 +5631,7 @@ static void direct_link_scene(FileData *fd, Scene *sce)
if (seq->seq3 == NULL) seq->seq3 = seq->seq2;
seq->effectdata = newdataadr(fd, seq->effectdata);
+ seq->stereo3d_format = newdataadr(fd, seq->stereo3d_format);
if (seq->type & SEQ_TYPE_EFFECT)
seq->flag |= SEQ_EFFECT_NOT_LOADED;
@@ -5722,6 +5744,7 @@ static void direct_link_scene(FileData *fd, Scene *sce)
link_list(fd, &(sce->markers));
link_list(fd, &(sce->transform_spaces));
link_list(fd, &(sce->r.layers));
+ link_list(fd, &(sce->r.views));
for (srl = sce->r.layers.first; srl; srl = srl->next) {
link_list(fd, &(srl->freestyleConfig.modules));
@@ -5785,8 +5808,8 @@ static void direct_link_windowmanager(FileData *fd, wmWindowManager *wm)
BLI_listbase_clear(&win->modalhandlers);
BLI_listbase_clear(&win->subwindows);
BLI_listbase_clear(&win->gesture);
+ BLI_listbase_clear(&win->drawdata);
- win->drawdata = NULL;
win->drawmethod = -1;
win->drawfail = 0;
win->active = 0;
@@ -5794,6 +5817,7 @@ static void direct_link_windowmanager(FileData *fd, wmWindowManager *wm)
win->cursor = 0;
win->lastcursor = 0;
win->modalcursor = 0;
+ win->stereo3d_format = newdataadr(fd, win->stereo3d_format);
}
BLI_listbase_clear(&wm->timers);
@@ -7765,7 +7789,6 @@ static void do_versions_userdef(FileData *fd, BlendFileData *bfd)
user->walk_navigation.jump_height = 0.4f; /* m */
user->walk_navigation.teleport_time = 0.2f; /* s */
}
-
}
static void do_versions(FileData *fd, Library *lib, Main *main)
@@ -7888,7 +7911,7 @@ static BHead *read_userdef(BlendFileData *bfd, FileData *fd, BHead *bhead)
link_list(fd, &user->user_keymaps);
link_list(fd, &user->addons);
link_list(fd, &user->autoexec_paths);
-
+
for (keymap=user->user_keymaps.first; keymap; keymap=keymap->next) {
keymap->modal_items= NULL;
keymap->poll = NULL;
diff --git a/source/blender/blenloader/intern/versioning_270.c b/source/blender/blenloader/intern/versioning_270.c
index 50cd39352a8..371ce495b07 100644
--- a/source/blender/blenloader/intern/versioning_270.c
+++ b/source/blender/blenloader/intern/versioning_270.c
@@ -27,6 +27,7 @@
#include "BLI_utildefines.h"
#include "BLI_compiler_attrs.h"
+#include "BLI_string.h"
/* for MinGW32 definition of NULL, could use BLI_blenlib.h instead too */
#include <stddef.h>
@@ -48,14 +49,18 @@
#include "DNA_particle_types.h"
#include "DNA_linestyle_types.h"
#include "DNA_actuator_types.h"
+#include "DNA_camera_types.h"
+#include "DNA_view3d_types.h"
#include "DNA_genfile.h"
#include "BKE_main.h"
#include "BKE_modifier.h"
#include "BKE_node.h"
+#include "BKE_scene.h"
#include "BKE_sequencer.h"
#include "BKE_screen.h"
+#include "BKE_sequencer.h"
#include "BLI_math.h"
#include "BLI_listbase.h"
@@ -460,10 +465,10 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
br->mtex.random_angle = 2.0 * M_PI;
br->mask_mtex.random_angle = 2.0 * M_PI;
}
+ }
#undef BRUSH_RAKE
#undef BRUSH_RANDOM_ROTATION
- }
/* Customizable Safe Areas */
if (!MAIN_VERSION_ATLEAST(main, 273, 2)) {
@@ -724,4 +729,85 @@ void blo_do_versions_270(FileData *fd, Library *UNUSED(lib), Main *main)
#undef SEQ_USE_PROXY_CUSTOM_DIR
#undef SEQ_USE_PROXY_CUSTOM_FILE
}
+
+ if (!MAIN_VERSION_ATLEAST(main, 274, 3))
+ {
+ SceneRenderView *srv;
+ wmWindowManager *wm;
+ bScreen *screen;
+ wmWindow *win;
+ Scene *scene;
+ Camera *cam;
+ Image *ima;
+
+ for (scene = main->scene.first; scene; scene = scene->id.next) {
+ Sequence *seq;
+
+ BKE_scene_add_render_view(scene, STEREO_LEFT_NAME);
+ srv = scene->r.views.first;
+ BLI_strncpy(srv->suffix, STEREO_LEFT_SUFFIX, sizeof(srv->suffix));
+
+ BKE_scene_add_render_view(scene, STEREO_RIGHT_NAME);
+ srv = scene->r.views.last;
+ BLI_strncpy(srv->suffix, STEREO_RIGHT_SUFFIX, sizeof(srv->suffix));
+
+ SEQ_BEGIN (scene->ed, seq)
+ {
+ seq->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Stereo Display 3d Format");
+ }
+ SEQ_END
+ }
+
+ for (screen = main->screen.first; screen; screen = screen->id.next) {
+ ScrArea *sa;
+ for (sa = screen->areabase.first; sa; sa = sa->next) {
+ SpaceLink *sl;
+
+ for (sl = sa->spacedata.first; sl; sl = sl->next) {
+ switch (sl->spacetype) {
+ case SPACE_VIEW3D:
+ {
+ View3D *v3d = (View3D *)sl;
+ v3d->stereo3d_camera = STEREO_3D_ID;
+ v3d->stereo3d_flag |= V3D_S3D_DISPPLANE;
+ v3d->stereo3d_convergence_alpha = 0.15f;
+ v3d->stereo3d_volume_alpha = 0.05f;
+ break;
+ }
+ case SPACE_IMAGE:
+ {
+ SpaceImage *sima = (SpaceImage *) sl;
+ sima->iuser.flag |= IMA_SHOW_STEREO;
+ sima->iuser.passtype = SCE_PASS_COMBINED;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ for (cam = main->camera.first; cam; cam = cam->id.next) {
+ cam->stereo.interocular_distance = 0.065;
+ cam->stereo.convergence_distance = 30.f * 0.065;
+ }
+
+ for (ima = main->image.first; ima; ima = ima->id.next) {
+ ima->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Image Stereo 3d Format");
+
+ if (ima->packedfile) {
+ ImagePackedFile *imapf = MEM_mallocN(sizeof(ImagePackedFile), "Image Packed File");
+ BLI_addtail(&ima->packedfiles, imapf);
+
+ imapf->packedfile = ima->packedfile;
+ BLI_strncpy(imapf->filepath, ima->name, FILE_MAX);
+ ima->packedfile = NULL;
+ }
+ }
+
+ for (wm = main->wm.first; wm; wm = wm->id.next) {
+ for (win = wm->windows.first; win; win = win->next) {
+ win->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Stereo Display 3d Format");
+ }
+ }
+ }
}
diff --git a/source/blender/blenloader/intern/writefile.c b/source/blender/blenloader/intern/writefile.c
index c616436d584..2580ba64622 100644
--- a/source/blender/blenloader/intern/writefile.c
+++ b/source/blender/blenloader/intern/writefile.c
@@ -2124,7 +2124,8 @@ static void write_images(WriteData *wd, ListBase *idbase)
{
Image *ima;
PackedFile * pf;
-
+ ImageView *iv;
+ ImagePackedFile *imapf;
ima= idbase->first;
while (ima) {
@@ -2133,13 +2134,20 @@ static void write_images(WriteData *wd, ListBase *idbase)
writestruct(wd, ID_IM, "Image", 1, ima);
if (ima->id.properties) IDP_WriteProperty(ima->id.properties, wd);
- if (ima->packedfile) {
- pf = ima->packedfile;
- writestruct(wd, DATA, "PackedFile", 1, pf);
- writedata(wd, DATA, pf->size, pf->data);
+ for (imapf = ima->packedfiles.first; imapf; imapf = imapf->next) {
+ writestruct(wd, DATA, "ImagePackedFile", 1, imapf);
+ if (imapf->packedfile) {
+ pf = imapf->packedfile;
+ writestruct(wd, DATA, "PackedFile", 1, pf);
+ writedata(wd, DATA, pf->size, pf->data);
+ }
}
write_previews(wd, ima->preview);
+
+ for (iv = ima->views.first; iv; iv = iv->next)
+ writestruct(wd, DATA, "ImageView", 1, iv);
+ writestruct(wd, DATA, "Stereo3dFormat", 1, ima->stereo3d_format);
}
ima= ima->id.next;
}
@@ -2340,6 +2348,7 @@ static void write_scenes(WriteData *wd, ListBase *scebase)
TimeMarker *marker;
TransformOrientation *ts;
SceneRenderLayer *srl;
+ SceneRenderView *srv;
ToolSettings *tos;
FreestyleModuleConfig *fmc;
FreestyleLineSet *fls;
@@ -2421,7 +2430,9 @@ static void write_scenes(WriteData *wd, ListBase *scebase)
break;
}
}
-
+
+ writestruct(wd, DATA, "Stereo3dFormat", 1, seq->stereo3d_format);
+
strip= seq->strip;
writestruct(wd, DATA, "Strip", 1, strip);
if (seq->flag & SEQ_USE_CROP && strip->crop) {
@@ -2486,6 +2497,10 @@ static void write_scenes(WriteData *wd, ListBase *scebase)
writestruct(wd, DATA, "FreestyleLineSet", 1, fls);
}
}
+
+ /* writing MultiView to the blend file */
+ for (srv = sce->r.views.first; srv; srv = srv->next)
+ writestruct(wd, DATA, "SceneRenderView", 1, srv);
if (sce->nodetree) {
writestruct(wd, DATA, "bNodeTree", 1, sce->nodetree);
@@ -2548,8 +2563,10 @@ static void write_windowmanagers(WriteData *wd, ListBase *lb)
for (wm= lb->first; wm; wm= wm->id.next) {
writestruct(wd, ID_WM, "wmWindowManager", 1, wm);
- for (win= wm->windows.first; win; win= win->next)
+ for (win= wm->windows.first; win; win= win->next) {
writestruct(wd, DATA, "wmWindow", 1, win);
+ writestruct(wd, DATA, "Stereo3dFormat", 1, win->stereo3d_format);
+ }
}
}
diff --git a/source/blender/collada/ImageExporter.cpp b/source/blender/collada/ImageExporter.cpp
index a5c1493208b..aac41e2e93c 100644
--- a/source/blender/collada/ImageExporter.cpp
+++ b/source/blender/collada/ImageExporter.cpp
@@ -74,7 +74,7 @@ void ImagesExporter::export_UV_Image(Image *image, bool use_copies)
short image_source = image->source;
bool is_generated = image_source == IMA_SRC_GENERATED;
- bool is_packed = image->packedfile != NULL;
+ bool is_packed = BKE_image_has_packedfile(image);
char export_path[FILE_MAX];
char source_path[FILE_MAX];
diff --git a/source/blender/compositor/CMakeLists.txt b/source/blender/compositor/CMakeLists.txt
index 048c974423f..c23aa4ec734 100644
--- a/source/blender/compositor/CMakeLists.txt
+++ b/source/blender/compositor/CMakeLists.txt
@@ -119,6 +119,8 @@ set(SRC
nodes/COM_TimeNode.h
nodes/COM_SwitchNode.cpp
nodes/COM_SwitchNode.h
+ nodes/COM_SwitchViewNode.cpp
+ nodes/COM_SwitchViewNode.h
nodes/COM_MovieClipNode.cpp
nodes/COM_MovieClipNode.h
nodes/COM_OutputFileNode.cpp
@@ -370,6 +372,8 @@ set(SRC
operations/COM_CompositorOperation.cpp
operations/COM_OutputFileOperation.h
operations/COM_OutputFileOperation.cpp
+ operations/COM_OutputFileMultiViewOperation.h
+ operations/COM_OutputFileMultiViewOperation.cpp
operations/COM_ViewerOperation.h
operations/COM_ViewerOperation.cpp
operations/COM_PreviewOperation.h
diff --git a/source/blender/compositor/COM_compositor.h b/source/blender/compositor/COM_compositor.h
index 9b22444cf7f..8b0a617f889 100644
--- a/source/blender/compositor/COM_compositor.h
+++ b/source/blender/compositor/COM_compositor.h
@@ -316,7 +316,8 @@ extern "C" {
* generation in display space
*/
void COM_execute(RenderData *rd, Scene *scene, bNodeTree *editingtree, int rendering,
- const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings);
+ const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings,
+ const char *viewName);
/**
* @brief Deinitialize the compositor caches and allocated memory.
diff --git a/source/blender/compositor/intern/COM_CompositorContext.h b/source/blender/compositor/intern/COM_CompositorContext.h
index 3fbafa0a029..d7ec653f480 100644
--- a/source/blender/compositor/intern/COM_CompositorContext.h
+++ b/source/blender/compositor/intern/COM_CompositorContext.h
@@ -87,6 +87,11 @@ private:
const ColorManagedViewSettings *m_viewSettings;
const ColorManagedDisplaySettings *m_displaySettings;
+ /**
+ * @brief active rendering view name
+ */
+ const char *m_viewName;
+
public:
/**
* @brief constructor initializes the context with default values.
@@ -180,7 +185,17 @@ public:
* @brief set has this system active openclDevices?
*/
void setHasActiveOpenCLDevices(bool hasAvtiveOpenCLDevices) { this->m_hasActiveOpenCLDevices = hasAvtiveOpenCLDevices; }
-
+
+ /**
+ * @brief get the active rendering view
+ */
+ const char *getViewName() const { return this->m_viewName; }
+
+ /**
+ * @brief set the active rendering view
+ */
+ void setViewName(const char *viewName) { this->m_viewName = viewName; }
+
int getChunksize() const { return this->getbNodeTree()->chunksize; }
void setFastCalculation(bool fastCalculation) {this->m_fastCalculation = fastCalculation;}
diff --git a/source/blender/compositor/intern/COM_Converter.cpp b/source/blender/compositor/intern/COM_Converter.cpp
index 99f66bcb5b4..9de22612bdc 100644
--- a/source/blender/compositor/intern/COM_Converter.cpp
+++ b/source/blender/compositor/intern/COM_Converter.cpp
@@ -101,6 +101,7 @@ extern "C" {
#include "COM_Stabilize2dNode.h"
#include "COM_SunBeamsNode.h"
#include "COM_SwitchNode.h"
+#include "COM_SwitchViewNode.h"
#include "COM_TextureNode.h"
#include "COM_TimeNode.h"
#include "COM_TonemapNode.h"
@@ -328,6 +329,9 @@ Node *Converter::convert(bNode *b_node)
case CMP_NODE_SWITCH:
node = new SwitchNode(b_node);
break;
+ case CMP_NODE_SWITCH_VIEW:
+ node = new SwitchViewNode(b_node);
+ break;
case CMP_NODE_GLARE:
node = new GlareNode(b_node);
break;
diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.cpp b/source/blender/compositor/intern/COM_ExecutionSystem.cpp
index 2ed9e6187ba..caeaa07d9f9 100644
--- a/source/blender/compositor/intern/COM_ExecutionSystem.cpp
+++ b/source/blender/compositor/intern/COM_ExecutionSystem.cpp
@@ -43,8 +43,10 @@ extern "C" {
#endif
ExecutionSystem::ExecutionSystem(RenderData *rd, Scene *scene, bNodeTree *editingtree, bool rendering, bool fastcalculation,
- const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings)
+ const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings,
+ const char *viewName)
{
+ this->m_context.setViewName(viewName);
this->m_context.setScene(scene);
this->m_context.setbNodeTree(editingtree);
this->m_context.setPreviewHash(editingtree->previews);
diff --git a/source/blender/compositor/intern/COM_ExecutionSystem.h b/source/blender/compositor/intern/COM_ExecutionSystem.h
index 555964f7b0c..41d63fb3a72 100644
--- a/source/blender/compositor/intern/COM_ExecutionSystem.h
+++ b/source/blender/compositor/intern/COM_ExecutionSystem.h
@@ -151,7 +151,8 @@ public:
* @param rendering [true false]
*/
ExecutionSystem(RenderData *rd, Scene *scene, bNodeTree *editingtree, bool rendering, bool fastcalculation,
- const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings);
+ const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings,
+ const char *viewName);
/**
* Destructor
diff --git a/source/blender/compositor/intern/COM_compositor.cpp b/source/blender/compositor/intern/COM_compositor.cpp
index ff601e6f58e..7f7fc141aca 100644
--- a/source/blender/compositor/intern/COM_compositor.cpp
+++ b/source/blender/compositor/intern/COM_compositor.cpp
@@ -45,7 +45,8 @@ static void intern_freeCompositorCaches()
void COM_execute(RenderData *rd, Scene *scene, bNodeTree *editingtree, int rendering,
const ColorManagedViewSettings *viewSettings,
- const ColorManagedDisplaySettings *displaySettings)
+ const ColorManagedDisplaySettings *displaySettings,
+ const char *viewName)
{
/* initialize mutex, TODO this mutex init is actually not thread safe and
* should be done somewhere as part of blender startup, all the other
@@ -82,7 +83,7 @@ void COM_execute(RenderData *rd, Scene *scene, bNodeTree *editingtree, int rende
bool twopass = (editingtree->flag & NTREE_TWO_PASS) > 0 && !rendering;
/* initialize execution system */
if (twopass) {
- ExecutionSystem *system = new ExecutionSystem(rd, scene, editingtree, rendering, twopass, viewSettings, displaySettings);
+ ExecutionSystem *system = new ExecutionSystem(rd, scene, editingtree, rendering, twopass, viewSettings, displaySettings, viewName);
system->execute();
delete system;
@@ -95,7 +96,7 @@ void COM_execute(RenderData *rd, Scene *scene, bNodeTree *editingtree, int rende
}
ExecutionSystem *system = new ExecutionSystem(rd, scene, editingtree, rendering, false,
- viewSettings, displaySettings);
+ viewSettings, displaySettings, viewName);
system->execute();
delete system;
diff --git a/source/blender/compositor/nodes/COM_CompositorNode.cpp b/source/blender/compositor/nodes/COM_CompositorNode.cpp
index 933b8d0282b..9e8b40d8af4 100644
--- a/source/blender/compositor/nodes/COM_CompositorNode.cpp
+++ b/source/blender/compositor/nodes/COM_CompositorNode.cpp
@@ -43,6 +43,7 @@ void CompositorNode::convertToOperations(NodeConverter &converter, const Composi
CompositorOperation *compositorOperation = new CompositorOperation();
compositorOperation->setSceneName(context.getScene()->id.name);
compositorOperation->setRenderData(context.getRenderData());
+ compositorOperation->setViewName(context.getViewName());
compositorOperation->setbNodeTree(context.getbNodeTree());
/* alpha socket gives either 1 or a custom alpha value if "use alpha" is enabled */
compositorOperation->setUseAlphaInput(ignore_alpha || alphaSocket->isLinked());
diff --git a/source/blender/compositor/nodes/COM_ImageNode.cpp b/source/blender/compositor/nodes/COM_ImageNode.cpp
index cb7ccfaedf9..93ab6dd765a 100644
--- a/source/blender/compositor/nodes/COM_ImageNode.cpp
+++ b/source/blender/compositor/nodes/COM_ImageNode.cpp
@@ -40,19 +40,19 @@ ImageNode::ImageNode(bNode *editorNode) : Node(editorNode)
}
NodeOperation *ImageNode::doMultilayerCheck(NodeConverter &converter, RenderLayer *rl, Image *image, ImageUser *user,
- int framenumber, int outputsocketIndex, int passindex, DataType datatype) const
+ int framenumber, int outputsocketIndex, int passtype, int view, DataType datatype) const
{
NodeOutput *outputSocket = this->getOutputSocket(outputsocketIndex);
MultilayerBaseOperation *operation = NULL;
switch (datatype) {
case COM_DT_VALUE:
- operation = new MultilayerValueOperation(passindex);
+ operation = new MultilayerValueOperation(passtype, view);
break;
case COM_DT_VECTOR:
- operation = new MultilayerVectorOperation(passindex);
+ operation = new MultilayerVectorOperation(passtype, view);
break;
case COM_DT_COLOR:
- operation = new MultilayerColorOperation(passindex);
+ operation = new MultilayerColorOperation(passtype, view);
break;
default:
break;
@@ -96,6 +96,10 @@ void ImageNode::convertToOperations(NodeConverter &converter, const CompositorCo
NodeOperation *operation = NULL;
socket = this->getOutputSocket(index);
bNodeSocket *bnodeSocket = socket->getbNodeSocket();
+ RenderPass *rpass = (RenderPass *)BLI_findstring(&rl->passes, bnodeSocket->identifier, offsetof(RenderPass, internal_name));
+
+ int view = (rpass ? rpass->view_id : 0);
+
/* Passes in the file can differ from passes stored in sockets (#36755).
* Look up the correct file pass using the socket identifier instead.
*/
@@ -104,8 +108,22 @@ void ImageNode::convertToOperations(NodeConverter &converter, const CompositorCo
int passindex = storage->pass_index;*/
RenderPass *rpass = (RenderPass *)BLI_findlink(&rl->passes, passindex);
#endif
- int passindex;
- RenderPass *rpass;
+
+ /* returns the image view to use for the current active view */
+ if (BLI_listbase_count_ex(&image->rr->views, 2) > 1) {
+ const int view_image = imageuser->view;
+ const bool is_allview = (view_image == 0); /* if view selected == All (0) */
+
+ if (is_allview) {
+ /* heuristic to match image name with scene names
+ * check if the view name exists in the image */
+ view = BLI_findstringindex(&image->rr->views, context.getViewName(), offsetof(RenderView, name));
+ }
+ else {
+ view = view_image - 1;
+ }
+ }
+
if (STREQ(bnodeSocket->identifier, "Alpha")) {
BLI_assert(combined_operation != NULL);
NodeOutput *outputSocket = this->getOutputSocket(index);
@@ -118,22 +136,21 @@ void ImageNode::convertToOperations(NodeConverter &converter, const CompositorCo
operation = separate_operation;
}
else {
- for (rpass = (RenderPass *)rl->passes.first, passindex = 0; rpass; rpass = rpass->next, ++passindex)
- if (STREQ(rpass->name, bnodeSocket->identifier))
- break;
if (rpass) {
- imageuser->pass = passindex;
switch (rpass->channels) {
case 1:
- operation = doMultilayerCheck(converter, rl, image, imageuser, framenumber, index, passindex, COM_DT_VALUE);
+ operation = doMultilayerCheck(converter, rl, image, imageuser, framenumber, index,
+ rpass->passtype, view, COM_DT_VALUE);
break;
/* using image operations for both 3 and 4 channels (RGB and RGBA respectively) */
/* XXX any way to detect actual vector images? */
case 3:
- operation = doMultilayerCheck(converter, rl, image, imageuser, framenumber, index, passindex, COM_DT_VECTOR);
+ operation = doMultilayerCheck(converter, rl, image, imageuser, framenumber, index,
+ rpass->passtype, view, COM_DT_VECTOR);
break;
case 4:
- operation = doMultilayerCheck(converter, rl, image, imageuser, framenumber, index, passindex, COM_DT_COLOR);
+ operation = doMultilayerCheck(converter, rl, image, imageuser, framenumber, index,
+ rpass->passtype, view, COM_DT_COLOR);
break;
default:
/* dummy operation is added below */
@@ -168,6 +185,8 @@ void ImageNode::convertToOperations(NodeConverter &converter, const CompositorCo
operation->setImage(image);
operation->setImageUser(imageuser);
operation->setFramenumber(framenumber);
+ operation->setRenderData(context.getRenderData());
+ operation->setViewName(context.getViewName());
converter.addOperation(operation);
if (outputStraightAlpha) {
@@ -190,6 +209,8 @@ void ImageNode::convertToOperations(NodeConverter &converter, const CompositorCo
alphaOperation->setImage(image);
alphaOperation->setImageUser(imageuser);
alphaOperation->setFramenumber(framenumber);
+ alphaOperation->setRenderData(context.getRenderData());
+ alphaOperation->setViewName(context.getViewName());
converter.addOperation(alphaOperation);
converter.mapOutputSocket(alphaImage, alphaOperation->getOutputSocket());
@@ -200,6 +221,8 @@ void ImageNode::convertToOperations(NodeConverter &converter, const CompositorCo
depthOperation->setImage(image);
depthOperation->setImageUser(imageuser);
depthOperation->setFramenumber(framenumber);
+ depthOperation->setRenderData(context.getRenderData());
+ depthOperation->setViewName(context.getViewName());
converter.addOperation(depthOperation);
converter.mapOutputSocket(depthImage, depthOperation->getOutputSocket());
@@ -239,6 +262,7 @@ void ImageNode::convertToOperations(NodeConverter &converter, const CompositorCo
}
if (operation) {
+ /* not supporting multiview for this generic case */
converter.addOperation(operation);
converter.mapOutputSocket(output, operation->getOutputSocket());
}
diff --git a/source/blender/compositor/nodes/COM_ImageNode.h b/source/blender/compositor/nodes/COM_ImageNode.h
index 1daa39a2a1f..267794510e1 100644
--- a/source/blender/compositor/nodes/COM_ImageNode.h
+++ b/source/blender/compositor/nodes/COM_ImageNode.h
@@ -36,7 +36,7 @@ extern "C" {
class ImageNode : public Node {
private:
NodeOperation *doMultilayerCheck(NodeConverter &converter, RenderLayer *rl, Image *image, ImageUser *user,
- int framenumber, int outputsocketIndex, int passindex, DataType datatype) const;
+ int framenumber, int outputsocketIndex, int passtype, int view, DataType datatype) const;
public:
ImageNode(bNode *editorNode);
void convertToOperations(NodeConverter &converter, const CompositorContext &context) const;
diff --git a/source/blender/compositor/nodes/COM_OutputFileNode.cpp b/source/blender/compositor/nodes/COM_OutputFileNode.cpp
index 92fa74b9a2e..acd2602e216 100644
--- a/source/blender/compositor/nodes/COM_OutputFileNode.cpp
+++ b/source/blender/compositor/nodes/COM_OutputFileNode.cpp
@@ -23,8 +23,11 @@
#include "COM_OutputFileNode.h"
#include "COM_OutputFileOperation.h"
+#include "COM_OutputFileMultiViewOperation.h"
#include "COM_ExecutionSystem.h"
+#include "BKE_scene.h"
+
#include "BLI_path_util.h"
OutputFileNode::OutputFileNode(bNode *editorNode) : Node(editorNode)
@@ -35,6 +38,7 @@ OutputFileNode::OutputFileNode(bNode *editorNode) : Node(editorNode)
void OutputFileNode::convertToOperations(NodeConverter &converter, const CompositorContext &context) const
{
NodeImageMultiFile *storage = (NodeImageMultiFile *)this->getbNode()->storage;
+ const bool is_multiview = (context.getRenderData()->scemode & R_MULTIVIEW) != 0;
if (!context.isRendering()) {
/* only output files when rendering a sequence -
@@ -46,10 +50,18 @@ void OutputFileNode::convertToOperations(NodeConverter &converter, const Composi
if (storage->format.imtype == R_IMF_IMTYPE_MULTILAYER) {
/* single output operation for the multilayer file */
- OutputOpenExrMultiLayerOperation *outputOperation = new OutputOpenExrMultiLayerOperation(
- context.getRenderData(), context.getbNodeTree(), storage->base_path, storage->format.exr_codec);
+ OutputOpenExrMultiLayerOperation *outputOperation;
+
+ if (is_multiview && storage->format.views_format == R_IMF_VIEWS_MULTIVIEW) {
+ outputOperation = new OutputOpenExrMultiLayerMultiViewOperation(
+ context.getRenderData(), context.getbNodeTree(), storage->base_path, storage->format.exr_codec, context.getViewName());
+ }
+ else {
+ outputOperation = new OutputOpenExrMultiLayerOperation(
+ context.getRenderData(), context.getbNodeTree(), storage->base_path, storage->format.exr_codec, context.getViewName());
+ }
converter.addOperation(outputOperation);
-
+
int num_inputs = getNumberOfInputSockets();
bool previewAdded = false;
for (int i = 0; i < num_inputs; ++i) {
@@ -76,17 +88,31 @@ void OutputFileNode::convertToOperations(NodeConverter &converter, const Composi
NodeImageMultiFileSocket *sockdata = (NodeImageMultiFileSocket *)input->getbNodeSocket()->storage;
ImageFormatData *format = (sockdata->use_node_format ? &storage->format : &sockdata->format);
char path[FILE_MAX];
-
+
/* combine file path for the input */
BLI_join_dirfile(path, FILE_MAX, storage->base_path, sockdata->path);
-
- OutputSingleLayerOperation *outputOperation = new OutputSingleLayerOperation(
- context.getRenderData(), context.getbNodeTree(), input->getDataType(), format, path,
- context.getViewSettings(), context.getDisplaySettings());
+
+ NodeOperation *outputOperation = NULL;
+
+ if (is_multiview && format->views_format == R_IMF_VIEWS_MULTIVIEW) {
+ outputOperation = new OutputOpenExrSingleLayerMultiViewOperation(
+ context.getRenderData(), context.getbNodeTree(), input->getDataType(), format, path,
+ context.getViewSettings(), context.getDisplaySettings(), context.getViewName());
+ }
+ else if ((!is_multiview) || (format->views_format == R_IMF_VIEWS_INDIVIDUAL)) {
+ outputOperation = new OutputSingleLayerOperation(
+ context.getRenderData(), context.getbNodeTree(), input->getDataType(), format, path,
+ context.getViewSettings(), context.getDisplaySettings(), context.getViewName());
+ }
+ else { /* R_IMF_VIEWS_STEREO_3D */
+ outputOperation = new OutputStereoOperation(
+ context.getRenderData(), context.getbNodeTree(), input->getDataType(), format, path,
+ sockdata->layer, context.getViewSettings(), context.getDisplaySettings(), context.getViewName());
+ }
+
converter.addOperation(outputOperation);
-
converter.mapInputSocket(input, outputOperation->getInputSocket(0));
-
+
if (!previewAdded) {
converter.addNodeInputPreview(input);
previewAdded = true;
diff --git a/source/blender/compositor/nodes/COM_RenderLayersNode.cpp b/source/blender/compositor/nodes/COM_RenderLayersNode.cpp
index cc66c688379..02bf1ec8cfb 100644
--- a/source/blender/compositor/nodes/COM_RenderLayersNode.cpp
+++ b/source/blender/compositor/nodes/COM_RenderLayersNode.cpp
@@ -42,7 +42,8 @@ void RenderLayersNode::testSocketLink(NodeConverter &converter, const Compositor
operation->setScene(scene);
operation->setLayerId(layerId);
operation->setRenderData(context.getRenderData());
-
+ operation->setViewName(context.getViewName());
+
converter.mapOutputSocket(outputSocket, operation->getOutputSocket());
converter.addOperation(operation);
diff --git a/source/blender/compositor/nodes/COM_SplitViewerNode.cpp b/source/blender/compositor/nodes/COM_SplitViewerNode.cpp
index 15eca0a97e5..0f917d317f9 100644
--- a/source/blender/compositor/nodes/COM_SplitViewerNode.cpp
+++ b/source/blender/compositor/nodes/COM_SplitViewerNode.cpp
@@ -22,6 +22,8 @@
#include "COM_SplitViewerNode.h"
#include "BKE_global.h"
+#include "BKE_image.h"
+#include "BKE_scene.h"
#include "COM_SplitOperation.h"
#include "COM_ViewerOperation.h"
@@ -55,6 +57,8 @@ void SplitViewerNode::convertToOperations(NodeConverter &converter, const Compos
viewerOperation->setImageUser(imageUser);
viewerOperation->setViewSettings(context.getViewSettings());
viewerOperation->setDisplaySettings(context.getDisplaySettings());
+ viewerOperation->setRenderData(context.getRenderData());
+ viewerOperation->setViewName(context.getViewName());
/* defaults - the viewer node has these options but not exposed for split view
* we could use the split to define an area of interest on one axis at least */
diff --git a/source/blender/compositor/nodes/COM_SwitchViewNode.cpp b/source/blender/compositor/nodes/COM_SwitchViewNode.cpp
new file mode 100644
index 00000000000..5a23b8b4d9e
--- /dev/null
+++ b/source/blender/compositor/nodes/COM_SwitchViewNode.cpp
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor:
+ * Dalai Felinto
+ */
+
+#include "COM_SwitchViewNode.h"
+#include "BLI_listbase.h"
+
+SwitchViewNode::SwitchViewNode(bNode *editorNode) : Node(editorNode)
+{
+ /* pass */
+}
+
+void SwitchViewNode::convertToOperations(NodeConverter &converter, const CompositorContext &context) const
+{
+ NodeOperationOutput *result;
+ const char *viewName = context.getViewName();
+ bNode *bnode = this->getbNode();
+
+ /* get the internal index of the socket with a matching name */
+ int nr = BLI_findstringindex(&bnode->inputs, viewName, offsetof(bNodeSocket, name));
+ nr = max(nr, 0);
+
+ result = converter.addInputProxy(getInputSocket(nr), false);
+ converter.mapOutputSocket(getOutputSocket(0), result);
+}
diff --git a/source/blender/compositor/nodes/COM_SwitchViewNode.h b/source/blender/compositor/nodes/COM_SwitchViewNode.h
new file mode 100644
index 00000000000..6ab5145bed5
--- /dev/null
+++ b/source/blender/compositor/nodes/COM_SwitchViewNode.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor:
+ * Dalai Felinto
+ */
+
+#ifndef _COM_SwitchViewNode_h_
+#define _COM_SwitchViewNode_h_
+
+#include "COM_Node.h"
+#include "COM_NodeOperation.h"
+#include "DNA_node_types.h"
+/**
+ * @brief SwitchViewNode
+ * @ingroup Node
+ */
+class SwitchViewNode : public Node {
+public:
+ SwitchViewNode(bNode *editorNode);
+ void convertToOperations(NodeConverter &converter, const CompositorContext &context) const;
+};
+#endif
diff --git a/source/blender/compositor/nodes/COM_ViewerNode.cpp b/source/blender/compositor/nodes/COM_ViewerNode.cpp
index ff0b8fb1706..ab819ce6e30 100644
--- a/source/blender/compositor/nodes/COM_ViewerNode.cpp
+++ b/source/blender/compositor/nodes/COM_ViewerNode.cpp
@@ -22,6 +22,9 @@
#include "COM_ViewerNode.h"
#include "BKE_global.h"
+#include "BKE_image.h"
+#include "BLI_listbase.h"
+#include "BKE_scene.h"
#include "COM_ViewerOperation.h"
#include "COM_ExecutionSystem.h"
@@ -51,6 +54,8 @@ void ViewerNode::convertToOperations(NodeConverter &converter, const CompositorC
viewerOperation->setCenterY(editorNode->custom4);
/* alpha socket gives either 1 or a custom alpha value if "use alpha" is enabled */
viewerOperation->setUseAlphaInput(ignore_alpha || alphaSocket->isLinked());
+ viewerOperation->setRenderData(context.getRenderData());
+ viewerOperation->setViewName(context.getViewName());
viewerOperation->setViewSettings(context.getViewSettings());
viewerOperation->setDisplaySettings(context.getDisplaySettings());
diff --git a/source/blender/compositor/operations/COM_CompositorOperation.cpp b/source/blender/compositor/operations/COM_CompositorOperation.cpp
index ffef81d0323..eacbdf91be2 100644
--- a/source/blender/compositor/operations/COM_CompositorOperation.cpp
+++ b/source/blender/compositor/operations/COM_CompositorOperation.cpp
@@ -52,6 +52,7 @@ CompositorOperation::CompositorOperation() : NodeOperation()
this->m_active = false;
this->m_sceneName[0] = '\0';
+ this->m_viewName = NULL;
}
void CompositorOperation::initExecution()
@@ -81,14 +82,16 @@ void CompositorOperation::deinitExecution()
RenderResult *rr = RE_AcquireResultWrite(re);
if (rr) {
- if (rr->rectf != NULL) {
- MEM_freeN(rr->rectf);
+ RenderView *rv = (RenderView *)BLI_findstring(&rr->views, this->m_viewName, offsetof(RenderView, name));
+
+ if (rv->rectf != NULL) {
+ MEM_freeN(rv->rectf);
}
- rr->rectf = this->m_outputBuffer;
- if (rr->rectz != NULL) {
- MEM_freeN(rr->rectz);
+ rv->rectf = this->m_outputBuffer;
+ if (rv->rectz != NULL) {
+ MEM_freeN(rv->rectz);
}
- rr->rectz = this->m_depthBuffer;
+ rv->rectz = this->m_depthBuffer;
}
else {
if (this->m_outputBuffer) {
diff --git a/source/blender/compositor/operations/COM_CompositorOperation.h b/source/blender/compositor/operations/COM_CompositorOperation.h
index 447d74131b3..e81ba520695 100644
--- a/source/blender/compositor/operations/COM_CompositorOperation.h
+++ b/source/blender/compositor/operations/COM_CompositorOperation.h
@@ -75,11 +75,17 @@ private:
* @brief operation is active for calculating final compo result
*/
bool m_active;
+
+ /**
+ * @brief View name, used for multiview
+ */
+ const char *m_viewName;
public:
CompositorOperation();
const bool isActiveCompositorOutput() const { return this->m_active; }
void executeRegion(rcti *rect, unsigned int tileNumber);
void setSceneName(const char *sceneName) { BLI_strncpy(this->m_sceneName, sceneName, sizeof(this->m_sceneName)); }
+ void setViewName(const char *viewName) { this->m_viewName = viewName; }
void setRenderData(const RenderData *rd) { this->m_rd = rd; }
bool isOutputOperation(bool /*rendering*/) const { return this->isActiveCompositorOutput(); }
void initExecution();
diff --git a/source/blender/compositor/operations/COM_ImageOperation.cpp b/source/blender/compositor/operations/COM_ImageOperation.cpp
index 624fdf8b626..c140b7afbd7 100644
--- a/source/blender/compositor/operations/COM_ImageOperation.cpp
+++ b/source/blender/compositor/operations/COM_ImageOperation.cpp
@@ -25,6 +25,7 @@
#include "BLI_listbase.h"
#include "DNA_image_types.h"
#include "BKE_image.h"
+#include "BKE_scene.h"
#include "BLI_math.h"
extern "C" {
@@ -48,6 +49,8 @@ BaseImageOperation::BaseImageOperation() : NodeOperation()
this->m_framenumber = 0;
this->m_depthBuffer = NULL;
this->m_numberOfChannels = 0;
+ this->m_rd = NULL;
+ this->m_viewName = NULL;
}
ImageOperation::ImageOperation() : BaseImageOperation()
{
@@ -65,8 +68,12 @@ ImageDepthOperation::ImageDepthOperation() : BaseImageOperation()
ImBuf *BaseImageOperation::getImBuf()
{
ImBuf *ibuf;
-
- ibuf = BKE_image_acquire_ibuf(this->m_image, this->m_imageUser, NULL);
+ ImageUser iuser = *this->m_imageUser;
+
+ /* local changes to the original ImageUser */
+ iuser.multi_index = BKE_scene_multiview_view_id_get(this->m_rd, this->m_viewName);
+
+ ibuf = BKE_image_acquire_ibuf(this->m_image, &iuser, NULL);
if (ibuf == NULL || (ibuf->rect == NULL && ibuf->rect_float == NULL)) {
BKE_image_release_ibuf(this->m_image, ibuf, NULL);
return NULL;
diff --git a/source/blender/compositor/operations/COM_ImageOperation.h b/source/blender/compositor/operations/COM_ImageOperation.h
index 206f1509f60..75222559810 100644
--- a/source/blender/compositor/operations/COM_ImageOperation.h
+++ b/source/blender/compositor/operations/COM_ImageOperation.h
@@ -49,7 +49,9 @@ protected:
int m_imagewidth;
int m_framenumber;
int m_numberOfChannels;
-
+ const RenderData *m_rd;
+ const char *m_viewName;
+
BaseImageOperation();
/**
* Determine the output resolution. The resolution is retrieved from the Renderer
@@ -64,7 +66,8 @@ public:
void deinitExecution();
void setImage(Image *image) { this->m_image = image; }
void setImageUser(ImageUser *imageuser) { this->m_imageUser = imageuser; }
-
+ void setRenderData(const RenderData *rd) { this->m_rd = rd; }
+ void setViewName(const char *viewName) { this->m_viewName = viewName; }
void setFramenumber(int framenumber) { this->m_framenumber = framenumber; }
};
class ImageOperation : public BaseImageOperation {
diff --git a/source/blender/compositor/operations/COM_MultilayerImageOperation.cpp b/source/blender/compositor/operations/COM_MultilayerImageOperation.cpp
index 513140e2f62..00be3b1cdde 100644
--- a/source/blender/compositor/operations/COM_MultilayerImageOperation.cpp
+++ b/source/blender/compositor/operations/COM_MultilayerImageOperation.cpp
@@ -27,18 +27,27 @@ extern "C" {
# include "IMB_imbuf_types.h"
}
-MultilayerBaseOperation::MultilayerBaseOperation(int passindex) : BaseImageOperation()
+MultilayerBaseOperation::MultilayerBaseOperation(int passtype, int view) : BaseImageOperation()
{
- this->m_passId = passindex;
+ this->m_passtype = passtype;
+ this->m_view = view;
}
+
ImBuf *MultilayerBaseOperation::getImBuf()
{
- RenderPass *rpass = (RenderPass *)BLI_findlink(&this->m_renderlayer->passes, this->m_passId);
- if (rpass) {
- this->m_imageUser->pass = m_passId;
- BKE_image_multilayer_index(this->m_image->rr, this->m_imageUser);
- return BaseImageOperation::getImBuf();
+ /* temporarily changes the view to get the right ImBuf */
+ int view = this->m_imageUser->view;
+
+ this->m_imageUser->view = this->m_view;
+ this->m_imageUser->passtype = this->m_passtype;
+
+ if (BKE_image_multilayer_index(this->m_image->rr, this->m_imageUser)) {
+ ImBuf *ibuf = BaseImageOperation::getImBuf();
+ this->m_imageUser->view = view;
+ return ibuf;
}
+
+ this->m_imageUser->view = view;
return NULL;
}
diff --git a/source/blender/compositor/operations/COM_MultilayerImageOperation.h b/source/blender/compositor/operations/COM_MultilayerImageOperation.h
index 37bee1b6a8c..2e140577d74 100644
--- a/source/blender/compositor/operations/COM_MultilayerImageOperation.h
+++ b/source/blender/compositor/operations/COM_MultilayerImageOperation.h
@@ -29,7 +29,8 @@
class MultilayerBaseOperation : public BaseImageOperation {
private:
- int m_passId;
+ int m_passtype;
+ int m_view;
RenderLayer *m_renderlayer;
protected:
ImBuf *getImBuf();
@@ -37,13 +38,13 @@ public:
/**
* Constructor
*/
- MultilayerBaseOperation(int passindex);
+ MultilayerBaseOperation(int passtype, int view);
void setRenderLayer(RenderLayer *renderlayer) { this->m_renderlayer = renderlayer; }
};
class MultilayerColorOperation : public MultilayerBaseOperation {
public:
- MultilayerColorOperation(int passindex) : MultilayerBaseOperation(passindex) {
+ MultilayerColorOperation(int passtype, int view) : MultilayerBaseOperation(passtype, view) {
this->addOutputSocket(COM_DT_COLOR);
}
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler);
@@ -51,7 +52,7 @@ public:
class MultilayerValueOperation : public MultilayerBaseOperation {
public:
- MultilayerValueOperation(int passindex) : MultilayerBaseOperation(passindex) {
+ MultilayerValueOperation(int passtype, int view) : MultilayerBaseOperation(passtype, view) {
this->addOutputSocket(COM_DT_VALUE);
}
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler);
@@ -59,7 +60,7 @@ public:
class MultilayerVectorOperation : public MultilayerBaseOperation {
public:
- MultilayerVectorOperation(int passindex) : MultilayerBaseOperation(passindex) {
+ MultilayerVectorOperation(int passtype, int view) : MultilayerBaseOperation(passtype, view) {
this->addOutputSocket(COM_DT_VECTOR);
}
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler);
diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cpp b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cpp
new file mode 100644
index 00000000000..c927dfa89a1
--- /dev/null
+++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.cpp
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor:
+ * Jeroen Bakker
+ * Monique Dewanchand
+ * Lukas Tönne
+ * Dalai Felinto
+ */
+
+#include "COM_OutputFileOperation.h"
+#include "COM_OutputFileMultiViewOperation.h"
+
+#include <string.h>
+#include "BLI_listbase.h"
+#include "BLI_path_util.h"
+#include "BLI_string.h"
+#include "BKE_image.h"
+#include "BKE_global.h"
+#include "BKE_main.h"
+#include "BKE_scene.h"
+
+#include "DNA_color_types.h"
+#include "MEM_guardedalloc.h"
+
+extern "C" {
+#include "IMB_imbuf.h"
+#include "IMB_colormanagement.h"
+#include "IMB_imbuf_types.h"
+}
+
+/************************************ OpenEXR Singlelayer Multiview *****************************************/
+
+OutputOpenExrSingleLayerMultiViewOperation::OutputOpenExrSingleLayerMultiViewOperation(
+ const RenderData *rd, const bNodeTree *tree, DataType datatype, ImageFormatData *format, const char *path,
+ const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings,
+ const char *viewName)
+ : OutputSingleLayerOperation(rd, tree, datatype, format, path, viewSettings, displaySettings, viewName)
+{
+}
+
+void *OutputOpenExrSingleLayerMultiViewOperation::get_handle(const char *filename)
+{
+ size_t width = this->getWidth();
+ size_t height = this->getHeight();
+ SceneRenderView *srv;
+
+ if (width != 0 && height != 0) {
+ void *exrhandle;
+
+ exrhandle = IMB_exr_get_handle_name(filename);
+
+ if (!BKE_scene_multiview_is_render_view_first(this->m_rd, this->m_viewName))
+ return exrhandle;
+
+ IMB_exr_clear_channels(exrhandle);
+
+ for (srv = (SceneRenderView *)this->m_rd->views.first; srv; srv = srv->next) {
+ if (BKE_scene_multiview_is_render_view_active(this->m_rd, srv) == false)
+ continue;
+
+ IMB_exr_add_view(exrhandle, srv->name);
+ add_exr_channels(exrhandle, NULL, this->m_datatype, srv->name, width, NULL);
+ }
+
+ BLI_make_existing_file(filename);
+
+ /* prepare the file with all the channels */
+
+ if (IMB_exr_begin_write(exrhandle, filename, width, height, this->m_format->exr_codec) == 0) {
+ printf("Error Writing Singlelayer Multiview Openexr\n");
+ IMB_exr_close(exrhandle);
+ }
+ else {
+ IMB_exr_clear_channels(exrhandle);
+ return exrhandle;
+ }
+ }
+ return NULL;
+}
+
+void OutputOpenExrSingleLayerMultiViewOperation::deinitExecution()
+{
+ unsigned int width = this->getWidth();
+ unsigned int height = this->getHeight();
+
+ if (width != 0 && height != 0) {
+ void *exrhandle;
+ Main *bmain = G.main; /* TODO, have this passed along */
+ char filename[FILE_MAX];
+
+ BKE_image_path_from_imtype(
+ filename, this->m_path, bmain->name, this->m_rd->cfra, R_IMF_IMTYPE_OPENEXR,
+ (this->m_rd->scemode & R_EXTENSION) != 0, true, NULL);
+
+ exrhandle = this->get_handle(filename);
+ add_exr_channels(exrhandle, NULL, this->m_datatype, this->m_viewName, width, this->m_outputBuffer);
+
+ /* memory can only be freed after we write all views to the file */
+ this->m_outputBuffer = NULL;
+ this->m_imageInput = NULL;
+
+ /* ready to close the file */
+ if (BKE_scene_multiview_is_render_view_last(this->m_rd, this->m_viewName)) {
+ IMB_exr_write_channels(exrhandle);
+
+ /* free buffer memory for all the views */
+ free_exr_channels(exrhandle, this->m_rd, NULL, this->m_datatype);
+
+ /* remove exr handle and data */
+ IMB_exr_close(exrhandle);
+ }
+ }
+}
+
+/************************************ OpenEXR Multilayer Multiview *****************************************/
+
+OutputOpenExrMultiLayerMultiViewOperation::OutputOpenExrMultiLayerMultiViewOperation(
+ const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec, const char *viewName)
+ : OutputOpenExrMultiLayerOperation(rd, tree, path, exr_codec, viewName)
+{
+}
+
+void *OutputOpenExrMultiLayerMultiViewOperation::get_handle(const char *filename)
+{
+ unsigned int width = this->getWidth();
+ unsigned int height = this->getHeight();
+
+ if (width != 0 && height != 0) {
+
+ void *exrhandle;
+ SceneRenderView *srv;
+
+ /* get a new global handle */
+ exrhandle = IMB_exr_get_handle_name(filename);
+
+ if (!BKE_scene_multiview_is_render_view_first(this->m_rd, this->m_viewName))
+ return exrhandle;
+
+ IMB_exr_clear_channels(exrhandle);
+
+ /* check renderdata for amount of views */
+ for (srv = (SceneRenderView *) this->m_rd->views.first; srv; srv = srv->next) {
+
+ if (BKE_scene_multiview_is_render_view_active(this->m_rd, srv) == false)
+ continue;
+
+ IMB_exr_add_view(exrhandle, srv->name);
+
+ for (unsigned int i = 0; i < this->m_layers.size(); ++i)
+ add_exr_channels(exrhandle, this->m_layers[i].name, this->m_layers[i].datatype, srv->name, width, NULL);
+ }
+
+ BLI_make_existing_file(filename);
+
+ /* prepare the file with all the channels for the header */
+ if (IMB_exr_begin_write(exrhandle, filename, width, height, this->m_exr_codec) == 0) {
+ printf("Error Writing Multilayer Multiview Openexr\n");
+ IMB_exr_close(exrhandle);
+ }
+ else {
+ IMB_exr_clear_channels(exrhandle);
+ return exrhandle;
+ }
+ }
+ return NULL;
+}
+
+void OutputOpenExrMultiLayerMultiViewOperation::deinitExecution()
+{
+ unsigned int width = this->getWidth();
+ unsigned int height = this->getHeight();
+
+ if (width != 0 && height != 0) {
+ void *exrhandle;
+ Main *bmain = G.main; /* TODO, have this passed along */
+ char filename[FILE_MAX];
+
+ BKE_image_path_from_imtype(
+ filename, this->m_path, bmain->name, this->m_rd->cfra, R_IMF_IMTYPE_MULTILAYER,
+ (this->m_rd->scemode & R_EXTENSION) != 0, true, NULL);
+
+ exrhandle = this->get_handle(filename);
+
+ for (unsigned int i = 0; i < this->m_layers.size(); ++i)
+ add_exr_channels(exrhandle, this->m_layers[i].name, this->m_layers[i].datatype, this->m_viewName, width, this->m_layers[i].outputBuffer);
+
+ for (unsigned int i = 0; i < this->m_layers.size(); ++i) {
+ /* memory can only be freed after we write all views to the file */
+ this->m_layers[i].outputBuffer = NULL;
+ this->m_layers[i].imageInput = NULL;
+ }
+
+ /* ready to close the file */
+ if (BKE_scene_multiview_is_render_view_last(this->m_rd, this->m_viewName)) {
+ IMB_exr_write_channels(exrhandle);
+
+ /* free buffer memory for all the views */
+ for (unsigned int i = 0; i < this->m_layers.size(); ++i) {
+ free_exr_channels(exrhandle, this->m_rd, this->m_layers[i].name, this->m_layers[i].datatype);
+ }
+
+ IMB_exr_close(exrhandle);
+ }
+ }
+}
+
+
+/******************************** Stereo3D ******************************/
+
+OutputStereoOperation::OutputStereoOperation(
+ const RenderData *rd, const bNodeTree *tree, DataType datatype, ImageFormatData *format, const char *path,
+ const char *name, const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings,
+ const char *viewName)
+ : OutputSingleLayerOperation(rd, tree, datatype, format, path, viewSettings, displaySettings, viewName)
+{
+ BLI_strncpy(this->m_name, name, sizeof(this->m_name));
+ this->m_channels = get_datatype_size(datatype);
+}
+
+void *OutputStereoOperation::get_handle(const char *filename)
+{
+ size_t width = this->getWidth();
+ size_t height = this->getHeight();
+ const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME};
+ size_t i;
+
+ if (width != 0 && height != 0) {
+ void *exrhandle;
+
+ exrhandle = IMB_exr_get_handle_name(filename);
+
+ if (!BKE_scene_multiview_is_render_view_first(this->m_rd, this->m_viewName))
+ return exrhandle;
+
+ IMB_exr_clear_channels(exrhandle);
+
+ for (i = 0; i < 2; i++)
+ IMB_exr_add_view(exrhandle, names[i]);
+
+ return exrhandle;
+ }
+ return NULL;
+}
+
+void OutputStereoOperation::deinitExecution()
+{
+ unsigned int width = this->getWidth();
+ unsigned int height = this->getHeight();
+
+ if (width != 0 && height != 0) {
+ void *exrhandle;
+
+ exrhandle = this->get_handle(this->m_path);
+ float *buf = this->m_outputBuffer;
+
+ /* populate single EXR channel with view data */
+ IMB_exr_add_channel(exrhandle, NULL, this->m_name, this->m_viewName, 1, this->m_channels * width * height, buf);
+
+ this->m_imageInput = NULL;
+ this->m_outputBuffer = NULL;
+
+ /* create stereo ibuf */
+ if (BKE_scene_multiview_is_render_view_last(this->m_rd, this->m_viewName)) {
+ ImBuf *ibuf[3] = {NULL};
+ const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME};
+ Main *bmain = G.main; /* TODO, have this passed along */
+ char filename[FILE_MAX];
+ int i;
+
+ /* get rectf from EXR */
+ for (i = 0; i < 2; i++) {
+ float *rectf = IMB_exr_channel_rect(exrhandle, NULL, this->m_name, names[i]);
+ ibuf[i] = IMB_allocImBuf(width, height, this->m_format->planes, 0);
+
+ ibuf[i]->channels = this->m_channels;
+ ibuf[i]->rect_float = rectf;
+ ibuf[i]->mall |= IB_rectfloat;
+ ibuf[i]->dither = this->m_rd->dither_intensity;
+
+ /* do colormanagement in the individual views, so it doesn't need to do in the stereo */
+ IMB_colormanagement_imbuf_for_write(ibuf[i], true, false, this->m_viewSettings,
+ this->m_displaySettings, this->m_format);
+ IMB_prepare_write_ImBuf(IMB_isfloat(ibuf[i]), ibuf[i]);
+ }
+
+ /* create stereo buffer */
+ ibuf[2] = IMB_stereo3d_ImBuf(this->m_format, ibuf[0], ibuf[1]);
+
+ BKE_image_path_from_imformat(
+ filename, this->m_path, bmain->name, this->m_rd->cfra, this->m_format,
+ (this->m_rd->scemode & R_EXTENSION) != 0, true, NULL);
+
+ BKE_imbuf_write(ibuf[2], filename, this->m_format);
+
+ /* imbuf knows which rects are not part of ibuf */
+ for (i = 0; i < 3; i++)
+ IMB_freeImBuf(ibuf[i]);
+
+ IMB_exr_close(exrhandle);
+ }
+ }
+}
diff --git a/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h
new file mode 100644
index 00000000000..25716fdb9e1
--- /dev/null
+++ b/source/blender/compositor/operations/COM_OutputFileMultiViewOperation.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2015, Blender Foundation.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * Contributor:
+ * Jeroen Bakker
+ * Monique Dewanchand
+ * Lukas Tönne
+ * Dalai Felinto
+ */
+
+#ifndef _COM_OutputFileMultiViewOperation_h
+#define _COM_OutputFileMultiViewOperation_h
+#include "COM_NodeOperation.h"
+
+#include "BLI_rect.h"
+#include "BLI_path_util.h"
+
+#include "DNA_color_types.h"
+#include "DNA_userdef_types.h"
+
+#include "intern/openexr/openexr_multi.h"
+
+class OutputOpenExrSingleLayerMultiViewOperation : public OutputSingleLayerOperation {
+private:
+public:
+ OutputOpenExrSingleLayerMultiViewOperation(const RenderData *rd, const bNodeTree *tree, DataType datatype,
+ ImageFormatData *format, const char *path,
+ const ColorManagedViewSettings *viewSettings,
+ const ColorManagedDisplaySettings *displaySettings,
+ const char *viewName);
+
+ void *get_handle(const char *filename);
+ void deinitExecution();
+};
+
+/* Writes inputs into OpenEXR multilayer channels. */
+class OutputOpenExrMultiLayerMultiViewOperation : public OutputOpenExrMultiLayerOperation {
+private:
+public:
+ OutputOpenExrMultiLayerMultiViewOperation(const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec, const char *viewName);
+
+ void *get_handle(const char *filename);
+ void deinitExecution();
+};
+
+/**/
+class OutputStereoOperation : public OutputSingleLayerOperation {
+private:
+ char m_name[FILE_MAX];
+ size_t m_channels;
+public:
+ OutputStereoOperation(const RenderData *rd, const bNodeTree *tree, DataType datatype,
+ struct ImageFormatData *format, const char *path, const char *name,
+ const ColorManagedViewSettings *viewSettings,
+ const ColorManagedDisplaySettings *displaySettings, const char *viewName);
+ void *get_handle(const char *filename);
+ void deinitExecution();
+};
+
+#endif
diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.cpp b/source/blender/compositor/operations/COM_OutputFileOperation.cpp
index b1c90a6355f..1a7a356f5b4 100644
--- a/source/blender/compositor/operations/COM_OutputFileOperation.cpp
+++ b/source/blender/compositor/operations/COM_OutputFileOperation.cpp
@@ -29,6 +29,7 @@
#include "BKE_image.h"
#include "BKE_global.h"
#include "BKE_main.h"
+#include "BKE_scene.h"
#include "DNA_color_types.h"
@@ -39,7 +40,60 @@ extern "C" {
# include "IMB_imbuf_types.h"
}
-static int get_datatype_size(DataType datatype)
+void add_exr_channels(void *exrhandle, const char *layerName, const DataType datatype, const char *viewName, const size_t width, float *buf)
+{
+ /* create channels */
+ switch (datatype) {
+ case COM_DT_VALUE:
+ IMB_exr_add_channel(exrhandle, layerName, "V", viewName, 1, width, buf ? buf : NULL);
+ break;
+ case COM_DT_VECTOR:
+ IMB_exr_add_channel(exrhandle, layerName, "X", viewName, 3, 3 * width, buf ? buf : NULL);
+ IMB_exr_add_channel(exrhandle, layerName, "Y", viewName, 3, 3 * width, buf ? buf + 1 : NULL);
+ IMB_exr_add_channel(exrhandle, layerName, "Z", viewName, 3, 3 * width, buf ? buf + 2 : NULL);
+ break;
+ case COM_DT_COLOR:
+ IMB_exr_add_channel(exrhandle, layerName, "R", viewName, 4, 4 * width, buf ? buf : NULL);
+ IMB_exr_add_channel(exrhandle, layerName, "G", viewName, 4, 4 * width, buf ? buf + 1 : NULL);
+ IMB_exr_add_channel(exrhandle, layerName, "B", viewName, 4, 4 * width, buf ? buf + 2 : NULL);
+ IMB_exr_add_channel(exrhandle, layerName, "A", viewName, 4, 4 * width, buf ? buf + 3 : NULL);
+ break;
+ default:
+ break;
+ }
+}
+
+void free_exr_channels(void *exrhandle, const RenderData *rd, const char *layerName, const DataType datatype)
+{
+ SceneRenderView *srv;
+
+ /* check renderdata for amount of views */
+ for (srv = (SceneRenderView *) rd->views.first; srv; srv = srv->next) {
+ float *rect = NULL;
+
+ if (BKE_scene_multiview_is_render_view_active(rd, srv) == false)
+ continue;
+
+ /* the pointer is stored in the first channel of each datatype */
+ switch (datatype) {
+ case COM_DT_VALUE:
+ rect = IMB_exr_channel_rect(exrhandle, layerName, "V", srv->name);
+ break;
+ case COM_DT_VECTOR:
+ rect = IMB_exr_channel_rect(exrhandle, layerName, "X", srv->name);
+ break;
+ case COM_DT_COLOR:
+ rect = IMB_exr_channel_rect(exrhandle, layerName, "R", srv->name);
+ break;
+ default:
+ break;
+ }
+ if (rect)
+ MEM_freeN(rect);
+ }
+}
+
+int get_datatype_size(DataType datatype)
{
switch (datatype) {
case COM_DT_VALUE: return 1;
@@ -94,7 +148,7 @@ static void write_buffer_rect(rcti *rect, const bNodeTree *tree,
OutputSingleLayerOperation::OutputSingleLayerOperation(
const RenderData *rd, const bNodeTree *tree, DataType datatype, ImageFormatData *format, const char *path,
- const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings)
+ const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings, const char *viewName)
{
this->m_rd = rd;
this->m_tree = tree;
@@ -110,6 +164,7 @@ OutputSingleLayerOperation::OutputSingleLayerOperation(
this->m_viewSettings = viewSettings;
this->m_displaySettings = displaySettings;
+ this->m_viewName = viewName;
}
void OutputSingleLayerOperation::initExecution()
@@ -131,6 +186,7 @@ void OutputSingleLayerOperation::deinitExecution()
ImBuf *ibuf = IMB_allocImBuf(this->getWidth(), this->getHeight(), this->m_format->planes, 0);
Main *bmain = G.main; /* TODO, have this passed along */
char filename[FILE_MAX];
+ const char *suffix;
ibuf->channels = size;
ibuf->rect_float = this->m_outputBuffer;
@@ -140,10 +196,12 @@ void OutputSingleLayerOperation::deinitExecution()
IMB_colormanagement_imbuf_for_write(ibuf, true, false, m_viewSettings, m_displaySettings,
this->m_format);
+ suffix = BKE_scene_multiview_view_suffix_get(this->m_rd, this->m_viewName);
+
BKE_image_path_from_imformat(
- filename, this->m_path, bmain->name, this->m_rd->cfra,
- this->m_format, (this->m_rd->scemode & R_EXTENSION) != 0, true);
-
+ filename, this->m_path, bmain->name, this->m_rd->cfra, this->m_format,
+ (this->m_rd->scemode & R_EXTENSION) != 0, true, suffix);
+
if (0 == BKE_imbuf_write(ibuf, filename, this->m_format))
printf("Cannot save Node File Output to %s\n", filename);
else
@@ -155,6 +213,7 @@ void OutputSingleLayerOperation::deinitExecution()
this->m_imageInput = NULL;
}
+/******************************* MultiLayer *******************************/
OutputOpenExrLayer::OutputOpenExrLayer(const char *name_, DataType datatype_, bool use_layer_)
{
@@ -168,13 +227,14 @@ OutputOpenExrLayer::OutputOpenExrLayer(const char *name_, DataType datatype_, bo
}
OutputOpenExrMultiLayerOperation::OutputOpenExrMultiLayerOperation(
- const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec)
+ const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec, const char *viewName)
{
this->m_rd = rd;
this->m_tree = tree;
BLI_strncpy(this->m_path, path, sizeof(this->m_path));
this->m_exr_codec = exr_codec;
+ this->m_viewName = viewName;
}
void OutputOpenExrMultiLayerOperation::add_layer(const char *name, DataType datatype, bool use_layer)
@@ -210,52 +270,21 @@ void OutputOpenExrMultiLayerOperation::deinitExecution()
if (width != 0 && height != 0) {
Main *bmain = G.main; /* TODO, have this passed along */
char filename[FILE_MAX];
+ const char *suffix;
void *exrhandle = IMB_exr_get_handle();
-
+
+ suffix = BKE_scene_multiview_view_suffix_get(this->m_rd, this->m_viewName);
BKE_image_path_from_imtype(
filename, this->m_path, bmain->name, this->m_rd->cfra, R_IMF_IMTYPE_MULTILAYER,
- (this->m_rd->scemode & R_EXTENSION) != 0, true);
+ (this->m_rd->scemode & R_EXTENSION) != 0, true, suffix);
BLI_make_existing_file(filename);
-
+
for (unsigned int i = 0; i < this->m_layers.size(); ++i) {
OutputOpenExrLayer &layer = this->m_layers[i];
if (!layer.imageInput)
continue; /* skip unconnected sockets */
- char channelname[EXR_TOT_MAXNAME];
- BLI_strncpy(channelname, this->m_layers[i].name, sizeof(channelname) - 2);
- char *channelname_ext = channelname + strlen(channelname);
-
- float *buf = this->m_layers[i].outputBuffer;
-
- /* create channels */
- switch (this->m_layers[i].datatype) {
- case COM_DT_VALUE:
- strcpy(channelname_ext, ".V");
- IMB_exr_add_channel(exrhandle, 0, channelname, 1, width, buf);
- break;
- case COM_DT_VECTOR:
- strcpy(channelname_ext, ".X");
- IMB_exr_add_channel(exrhandle, 0, channelname, 3, 3 * width, buf);
- strcpy(channelname_ext, ".Y");
- IMB_exr_add_channel(exrhandle, 0, channelname, 3, 3 * width, buf + 1);
- strcpy(channelname_ext, ".Z");
- IMB_exr_add_channel(exrhandle, 0, channelname, 3, 3 * width, buf + 2);
- break;
- case COM_DT_COLOR:
- strcpy(channelname_ext, ".R");
- IMB_exr_add_channel(exrhandle, 0, channelname, 4, 4 * width, buf);
- strcpy(channelname_ext, ".G");
- IMB_exr_add_channel(exrhandle, 0, channelname, 4, 4 * width, buf + 1);
- strcpy(channelname_ext, ".B");
- IMB_exr_add_channel(exrhandle, 0, channelname, 4, 4 * width, buf + 2);
- strcpy(channelname_ext, ".A");
- IMB_exr_add_channel(exrhandle, 0, channelname, 4, 4 * width, buf + 3);
- break;
- default:
- break;
- }
-
+ add_exr_channels(exrhandle, this->m_layers[i].name, this->m_layers[i].datatype, "", width, this->m_layers[i].outputBuffer);
}
/* when the filename has no permissions, this can fail */
@@ -279,3 +308,4 @@ void OutputOpenExrMultiLayerOperation::deinitExecution()
}
}
}
+
diff --git a/source/blender/compositor/operations/COM_OutputFileOperation.h b/source/blender/compositor/operations/COM_OutputFileOperation.h
index af16f186d9d..58ebf055583 100644
--- a/source/blender/compositor/operations/COM_OutputFileOperation.h
+++ b/source/blender/compositor/operations/COM_OutputFileOperation.h
@@ -34,7 +34,7 @@
/* Writes the image to a single-layer file. */
class OutputSingleLayerOperation : public NodeOperation {
-private:
+protected:
const RenderData *m_rd;
const bNodeTree *m_tree;
@@ -47,9 +47,11 @@ private:
const ColorManagedViewSettings *m_viewSettings;
const ColorManagedDisplaySettings *m_displaySettings;
+
+ const char *m_viewName;
public:
OutputSingleLayerOperation(const RenderData *rd, const bNodeTree *tree, DataType datatype, ImageFormatData *format, const char *path,
- const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings);
+ const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings, const char *viewName);
void executeRegion(rcti *rect, unsigned int tileNumber);
bool isOutputOperation(bool /*rendering*/) const { return true; }
@@ -75,7 +77,7 @@ struct OutputOpenExrLayer {
/* Writes inputs into OpenEXR multilayer channels. */
class OutputOpenExrMultiLayerOperation : public NodeOperation {
-private:
+protected:
typedef std::vector<OutputOpenExrLayer> LayerList;
const RenderData *m_rd;
@@ -84,9 +86,10 @@ private:
char m_path[FILE_MAX];
char m_exr_codec;
LayerList m_layers;
+ const char *m_viewName;
public:
- OutputOpenExrMultiLayerOperation(const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec);
+ OutputOpenExrMultiLayerOperation(const RenderData *rd, const bNodeTree *tree, const char *path, char exr_codec, const char *viewName);
void add_layer(const char *name, DataType datatype, bool use_layer);
@@ -99,4 +102,8 @@ public:
bool isFileOutputOperation() const { return true; }
};
+void add_exr_channels(void *exrhandle, const char *layerName, const DataType datatype, const char *viewName, const size_t width, float *buf);
+void free_exr_channels(void *exrhandle, const RenderData *rd, const char *layerName, const DataType datatype);
+int get_datatype_size(DataType datatype);
+
#endif
diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.cpp b/source/blender/compositor/operations/COM_RenderLayersProg.cpp
index defbc2ab57f..af176a766ff 100644
--- a/source/blender/compositor/operations/COM_RenderLayersProg.cpp
+++ b/source/blender/compositor/operations/COM_RenderLayersProg.cpp
@@ -23,6 +23,7 @@
#include "COM_RenderLayersProg.h"
#include "BLI_listbase.h"
+#include "BKE_scene.h"
#include "DNA_scene_types.h"
extern "C" {
@@ -57,11 +58,10 @@ void RenderLayersBaseProg::initExecution()
if (srl) {
RenderLayer *rl = RE_GetRenderLayer(rr, srl->name);
- if (rl && rl->rectf) {
- this->m_inputBuffer = RE_RenderLayerGetPass(rl, this->m_renderpass);
-
+ if (rl) {
+ this->m_inputBuffer = RE_RenderLayerGetPass(rl, this->m_renderpass, this->m_viewName);
if (this->m_inputBuffer == NULL && this->m_renderpass == SCE_PASS_COMBINED) {
- this->m_inputBuffer = rl->rectf;
+ this->m_inputBuffer = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, this->m_viewName);
}
}
}
@@ -195,7 +195,7 @@ void RenderLayersBaseProg::determineResolution(unsigned int resolution[2], unsig
SceneRenderLayer *srl = (SceneRenderLayer *)BLI_findlink(&sce->r.layers, getLayerId());
if (srl) {
RenderLayer *rl = RE_GetRenderLayer(rr, srl->name);
- if (rl && rl->rectf) {
+ if (rl) {
resolution[0] = rl->rectx;
resolution[1] = rl->recty;
}
diff --git a/source/blender/compositor/operations/COM_RenderLayersProg.h b/source/blender/compositor/operations/COM_RenderLayersProg.h
index f73d9de3e27..2ddbc968c01 100644
--- a/source/blender/compositor/operations/COM_RenderLayersProg.h
+++ b/source/blender/compositor/operations/COM_RenderLayersProg.h
@@ -51,7 +51,12 @@ private:
* layerId of the layer where this operation needs to get its data from
*/
short m_layerId;
-
+
+ /**
+ * viewName of the view to use (unless another view is specified by the node
+ */
+ const char *m_viewName;
+
/**
* cached instance to the float buffer inside the layer
*/
@@ -97,6 +102,8 @@ public:
void setRenderData(const RenderData *rd) { this->m_rd = rd; }
void setLayerId(short layerId) { this->m_layerId = layerId; }
short getLayerId() { return this->m_layerId; }
+ void setViewName(const char *viewName) { this->m_viewName = viewName; }
+ const char *getViewName() { return this->m_viewName; }
void initExecution();
void deinitExecution();
void executePixelSampled(float output[4], float x, float y, PixelSampler sampler);
diff --git a/source/blender/compositor/operations/COM_ViewerOperation.cpp b/source/blender/compositor/operations/COM_ViewerOperation.cpp
index da2706a2244..96ee5ecbb03 100644
--- a/source/blender/compositor/operations/COM_ViewerOperation.cpp
+++ b/source/blender/compositor/operations/COM_ViewerOperation.cpp
@@ -23,6 +23,7 @@
#include "COM_ViewerOperation.h"
#include "BLI_listbase.h"
#include "BKE_image.h"
+#include "BKE_scene.h"
#include "WM_api.h"
#include "WM_types.h"
#include "PIL_time.h"
@@ -57,6 +58,8 @@ ViewerOperation::ViewerOperation() : NodeOperation()
this->m_imageInput = NULL;
this->m_alphaInput = NULL;
this->m_depthInput = NULL;
+ this->m_rd = NULL;
+ this->m_viewName = NULL;
}
void ViewerOperation::initExecution()
@@ -123,8 +126,18 @@ void ViewerOperation::executeRegion(rcti *rect, unsigned int /*tileNumber*/)
void ViewerOperation::initImage()
{
Image *ima = this->m_image;
+ ImageUser iuser = *this->m_imageUser;
void *lock;
- ImBuf *ibuf = BKE_image_acquire_ibuf(ima, this->m_imageUser, &lock);
+ ImBuf *ibuf;
+
+ /* make sure the image has the correct number of views */
+ if (ima && BKE_scene_multiview_is_render_view_first(this->m_rd, this->m_viewName)) {
+ BKE_image_verify_viewer_views(this->m_rd, ima, this->m_imageUser);
+ }
+
+ /* local changes to the original ImageUser */
+ iuser.multi_index = BKE_scene_multiview_view_id_get(this->m_rd, this->m_viewName);
+ ibuf = BKE_image_acquire_ibuf(ima, &iuser, &lock);
if (!ibuf) return;
BLI_lock_thread(LOCK_DRAW_IMAGE);
diff --git a/source/blender/compositor/operations/COM_ViewerOperation.h b/source/blender/compositor/operations/COM_ViewerOperation.h
index a17375c2de9..107aee3a82f 100644
--- a/source/blender/compositor/operations/COM_ViewerOperation.h
+++ b/source/blender/compositor/operations/COM_ViewerOperation.h
@@ -40,7 +40,9 @@ private:
bool m_doDepthBuffer;
ImBuf *m_ibuf;
bool m_useAlphaInput;
-
+ const RenderData *m_rd;
+ const char *m_viewName;
+
const ColorManagedViewSettings *m_viewSettings;
const ColorManagedDisplaySettings *m_displaySettings;
@@ -67,6 +69,8 @@ public:
const CompositorPriority getRenderPriority() const;
bool isViewerOperation() const { return true; }
void setUseAlphaInput(bool value) { this->m_useAlphaInput = value; }
+ void setRenderData(const RenderData *rd) { this->m_rd = rd; }
+ void setViewName(const char *viewName) { this->m_viewName = viewName; }
void setViewSettings(const ColorManagedViewSettings *viewSettings) { this->m_viewSettings = viewSettings; }
void setDisplaySettings(const ColorManagedDisplaySettings *displaySettings) { this->m_displaySettings = displaySettings; }
diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h
index b4f7d806079..90b0b3510bc 100644
--- a/source/blender/editors/include/ED_screen.h
+++ b/source/blender/editors/include/ED_screen.h
@@ -112,6 +112,7 @@ void ED_screen_full_prevspace(struct bContext *C, ScrArea *sa);
void ED_screen_full_restore(struct bContext *C, ScrArea *sa);
struct ScrArea *ED_screen_state_toggle(struct bContext *C, struct wmWindow *win, struct ScrArea *sa, const short state);
void ED_screens_header_tools_menu_create(struct bContext *C, struct uiLayout *layout, void *arg);
+bool ED_screen_stereo3d_required(struct bScreen *screen);
/* anim */
void ED_update_for_newframe(struct Main *bmain, struct Scene *scene, int mute);
diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h
index 84ac3a7c938..ff4b8a5b631 100644
--- a/source/blender/editors/include/ED_view3d.h
+++ b/source/blender/editors/include/ED_view3d.h
@@ -323,12 +323,13 @@ void ED_view3d_draw_offscreen(
struct Scene *scene, struct View3D *v3d, struct ARegion *ar, int winx, int winy, float viewmat[4][4],
float winmat[4][4], bool do_bgpic, bool do_sky, bool is_persp,
struct GPUOffScreen *ofs,
- struct GPUFX *fx, struct GPUFXSettings *fx_settings);
+ struct GPUFX *fx, struct GPUFXSettings *fx_settings,
+ const char *viewname);
struct ImBuf *ED_view3d_draw_offscreen_imbuf(struct Scene *scene, struct View3D *v3d, struct ARegion *ar, int sizex, int sizey, unsigned int flag,
- bool draw_background, int alpha_mode, char err_out[256]);
+ bool draw_background, int alpha_mode, const char *viewname, char err_out[256]);
struct ImBuf *ED_view3d_draw_offscreen_imbuf_simple(struct Scene *scene, struct Object *camera, int width, int height, unsigned int flag, int drawtype,
- bool use_solid_tex, bool use_gpencil, bool draw_background, int alpha_mode, char err_out[256]);
+ bool use_solid_tex, bool use_gpencil, bool draw_background, int alpha_mode, const char *viewname, char err_out[256]);
struct Base *ED_view3d_give_base_under_cursor(struct bContext *C, const int mval[2]);
void ED_view3d_quadview_update(struct ScrArea *sa, struct ARegion *ar, bool do_clip);
diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h
index 814bbf94c79..4ca0be7c80d 100644
--- a/source/blender/editors/include/UI_interface.h
+++ b/source/blender/editors/include/UI_interface.h
@@ -889,6 +889,9 @@ void uiTemplateGameStates(uiLayout *layout, struct PointerRNA *ptr, const char *
PointerRNA *used_ptr, const char *used_propname, int active_state);
void uiTemplateImage(uiLayout *layout, struct bContext *C, struct PointerRNA *ptr, const char *propname, struct PointerRNA *userptr, int compact);
void uiTemplateImageSettings(uiLayout *layout, struct PointerRNA *imfptr, int color_management);
+void uiTemplateImageStereo3d(uiLayout *layout, struct PointerRNA *stereo3d_format_ptr);
+void uiTemplateImageViews(uiLayout *layout, struct PointerRNA *imaptr);
+void uiTemplateImageFormatViews(uiLayout *layout, PointerRNA *imfptr, PointerRNA *ptr);
void uiTemplateImageLayers(uiLayout *layout, struct bContext *C, struct Image *ima, struct ImageUser *iuser);
void uiTemplateImageInfo(uiLayout *layout, struct bContext *C, Image *ima, ImageUser *iuser);
void uiTemplateRunningJobs(uiLayout *layout, struct bContext *C);
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index 41d2a90e548..72abfcb9293 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -613,6 +613,7 @@ static int editsource_exec(bContext *C, wmOperator *op)
/* redraw and get active button python info */
ED_region_do_draw(C, ar);
+ ar->do_draw = false;
for (BLI_ghashIterator_init(&ghi, ui_editsource_info->hash);
BLI_ghashIterator_done(&ghi) == false;
diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c
index fe9ee71aa8e..d492a4b9dda 100644
--- a/source/blender/editors/object/object_bake_api.c
+++ b/source/blender/editors/object/object_bake_api.c
@@ -929,7 +929,7 @@ cage_cleanup:
BakeData *bake = &scene->r.bake;
char name[FILE_MAX];
- BKE_image_path_from_imtype(name, filepath, bmain->name, 0, bake->im_format.imtype, true, false);
+ BKE_image_path_from_imtype(name, filepath, bmain->name, 0, bake->im_format.imtype, true, false, NULL);
if (is_automatic_name) {
BLI_path_suffix(name, FILE_MAX, ob_low->id.name + 2, "_");
diff --git a/source/blender/editors/render/render_intern.h b/source/blender/editors/render/render_intern.h
index ee0046b49dc..5c6744b0248 100644
--- a/source/blender/editors/render/render_intern.h
+++ b/source/blender/editors/render/render_intern.h
@@ -55,6 +55,9 @@ void MATERIAL_OT_paste(struct wmOperatorType *ot);
void SCENE_OT_render_layer_add(struct wmOperatorType *ot);
void SCENE_OT_render_layer_remove(struct wmOperatorType *ot);
+void SCENE_OT_render_view_add(struct wmOperatorType *ot);
+void SCENE_OT_render_view_remove(struct wmOperatorType *ot);
+
#ifdef WITH_FREESTYLE
void SCENE_OT_freestyle_module_add(struct wmOperatorType *ot);
void SCENE_OT_freestyle_module_remove(struct wmOperatorType *ot);
diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c
index 1c9f32697d4..f76f7cad1ae 100644
--- a/source/blender/editors/render/render_internal.c
+++ b/source/blender/editors/render/render_internal.c
@@ -48,6 +48,7 @@
#include "DNA_userdef_types.h"
#include "BKE_blender.h"
+#include "BKE_camera.h"
#include "BKE_context.h"
#include "BKE_colortools.h"
#include "BKE_depsgraph.h"
@@ -116,7 +117,7 @@ typedef struct RenderJob {
} RenderJob;
/* called inside thread! */
-static void image_buffer_rect_update(RenderJob *rj, RenderResult *rr, ImBuf *ibuf, ImageUser *iuser, volatile rcti *renrect)
+static void image_buffer_rect_update(RenderJob *rj, RenderResult *rr, ImBuf *ibuf, ImageUser *iuser, volatile rcti *renrect, const char *viewname)
{
Scene *scene = rj->scene;
const float *rectf = NULL;
@@ -188,11 +189,11 @@ static void image_buffer_rect_update(RenderJob *rj, RenderResult *rr, ImBuf *ibu
*/
/* TODO(sergey): Need to check has_combined here? */
if (iuser->pass == 0) {
+ size_t view_id = BKE_scene_multiview_view_id_get(&scene->r, viewname);
/* find current float rect for display, first case is after composite... still weak */
- if (rr->rectf)
- rectf = rr->rectf;
- else {
- if (rr->rect32) {
+ rectf = RE_RenderViewGetRectf(rr, view_id);
+ if (rectf == NULL) {
+ if (RE_RenderViewGetRect32(rr, view_id)) {
/* special case, currently only happens with sequencer rendering,
* which updates the whole frame, so we can only mark display buffer
* as invalid here (sergey)
@@ -201,8 +202,8 @@ static void image_buffer_rect_update(RenderJob *rj, RenderResult *rr, ImBuf *ibu
return;
}
else {
- if (rr->renlay == NULL || rr->renlay->rectf == NULL) return;
- rectf = rr->renlay->rectf;
+ if (rr->renlay == NULL) return;
+ rectf = RE_RenderLayerGetPass(rr->renlay, SCE_PASS_COMBINED, viewname);
}
}
if (rectf == NULL) return;
@@ -531,6 +532,7 @@ static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrec
Image *ima = rj->image;
ImBuf *ibuf;
void *lock;
+ const char *viewname = RE_GetActiveRenderView(rj->re);
/* only update if we are displaying the slot being rendered */
if (ima->render_slot != ima->last_render_slot) {
@@ -563,7 +565,7 @@ static void image_rect_update(void *rjv, RenderResult *rr, volatile rcti *renrec
ibuf->channels == 1 ||
U.image_draw_method != IMAGE_DRAW_METHOD_GLSL)
{
- image_buffer_rect_update(rj, rr, ibuf, &rj->iuser, renrect);
+ image_buffer_rect_update(rj, rr, ibuf, &rj->iuser, renrect, viewname);
}
/* make jobs timer to send notifier */
@@ -1487,7 +1489,8 @@ void render_view3d_draw(RenderEngine *engine, const bContext *C)
if (re == NULL) return;
}
- RE_AcquireResultImage(re, &rres);
+ /* Viewport render preview doesn't support multiview, view hardcoded to 0 */
+ RE_AcquireResultImage(re, &rres, 0);
if (rres.rectf) {
RegionView3D *rv3d = CTX_wm_region_view3d(C);
diff --git a/source/blender/editors/render/render_opengl.c b/source/blender/editors/render/render_opengl.c
index 5c8bee07420..b59fd10870e 100644
--- a/source/blender/editors/render/render_opengl.c
+++ b/source/blender/editors/render/render_opengl.c
@@ -39,6 +39,7 @@
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
#include "BLI_jitter.h"
+#include "BLI_threads.h"
#include "DNA_scene_types.h"
#include "DNA_object_types.h"
@@ -103,11 +104,17 @@ typedef struct OGLRender {
bMovieHandle *mh;
int cfrao, nfra;
+ size_t totvideos;
+
+ /* quick lookup */
+ int view_id;
+
/* wm vars for timer and progress cursor */
wmWindowManager *wm;
wmWindow *win;
wmTimer *timer; /* use to check if running modal or not (invoke'd or exec'd)*/
+ void **movie_ctx_arr;
} OGLRender;
/* added because v3d is not always valid */
@@ -121,16 +128,125 @@ static unsigned int screen_opengl_layers(OGLRender *oglrender)
}
}
-static void screen_opengl_render_apply(OGLRender *oglrender)
+static bool screen_opengl_is_multiview(OGLRender *oglrender)
+{
+ View3D *v3d = oglrender->v3d;
+ RegionView3D *rv3d = oglrender->rv3d;
+ RenderData *rd = &oglrender->scene->r;
+
+ if ((rd == NULL) || ((!oglrender->is_sequencer) && ((rv3d == NULL) || (v3d == NULL))))
+ return false;
+
+ return (rd->scemode & R_MULTIVIEW) && ((oglrender->is_sequencer) || (rv3d->persp == RV3D_CAMOB && v3d->camera));
+}
+
+static void screen_opengl_views_setup(OGLRender *oglrender)
+{
+ RenderResult *rr;
+ RenderView *rv;
+ SceneRenderView *srv;
+ bool is_multiview;
+ View3D *v3d = oglrender->v3d;
+
+ RenderData *rd = &oglrender->scene->r;
+
+ rr = RE_AcquireResultWrite(oglrender->re);
+
+ is_multiview = screen_opengl_is_multiview(oglrender);
+
+ if (!is_multiview) {
+ /* we only have one view when multiview is off */
+ rv = rr->views.first;
+
+ if (rv == NULL) {
+ rv = MEM_callocN(sizeof(RenderView), "new opengl render view");
+ BLI_addtail(&rr->views, rv);
+ }
+
+ while (rv->next) {
+ RenderView *rv_del = rv->next;
+ BLI_remlink(&rr->views, rv_del);
+
+ if (rv_del->rectf)
+ MEM_freeN(rv_del->rectf);
+
+ if (rv_del->rectz)
+ MEM_freeN(rv_del->rectz);
+
+ MEM_freeN(rv_del);
+ }
+ }
+ else {
+ if (!oglrender->is_sequencer)
+ RE_SetOverrideCamera(oglrender->re, V3D_CAMERA_SCENE(oglrender->scene, v3d));
+
+ /* remove all the views that are not needed */
+ rv = rr->views.last;
+ while (rv) {
+ srv = BLI_findstring(&rd->views, rv->name, offsetof(SceneRenderView, name));
+ if (BKE_scene_multiview_is_render_view_active(rd, srv)) {
+ if (rv->rectf == NULL)
+ rv->rectf = MEM_callocN(sizeof(float) * 4 * oglrender->sizex * oglrender->sizey, "screen_opengl_render_init rect");
+ rv = rv->prev;
+ }
+ else {
+ RenderView *rv_del = rv;
+ rv = rv_del->prev;
+
+ BLI_remlink(&rr->views, rv_del);
+
+ if (rv_del->rectf)
+ MEM_freeN(rv_del->rectf);
+
+ if (rv_del->rectz)
+ MEM_freeN(rv_del->rectz);
+
+ MEM_freeN(rv_del);
+ }
+ }
+
+ /* create all the views that are needed */
+ for (srv = rd->views.first; srv; srv = srv->next) {
+ if (BKE_scene_multiview_is_render_view_active(rd, srv) == false)
+ continue;
+
+ rv = BLI_findstring(&rr->views, srv->name, offsetof(SceneRenderView, name));
+
+ if (rv == NULL) {
+ rv = MEM_callocN(sizeof(RenderView), "new opengl render view");
+ BLI_strncpy(rv->name, srv->name, sizeof(rv->name));
+ BLI_addtail(&rr->views, rv);
+ }
+ }
+ }
+
+ for (rv = rr->views.first; rv; rv = rv->next) {
+ if (rv->rectf == NULL) {
+ rv->rectf = MEM_callocN(sizeof(float) * 4 * oglrender->sizex * oglrender->sizey, "screen_opengl_render_init rect");
+ }
+ }
+
+ BLI_lock_thread(LOCK_DRAW_IMAGE);
+ if (is_multiview && BKE_scene_multiview_is_stereo3d(rd)) {
+ oglrender->ima->flag |= IMA_IS_STEREO;
+ }
+ else {
+ oglrender->ima->flag &= ~IMA_IS_STEREO;
+ oglrender->iuser.flag &= ~IMA_SHOW_STEREO;
+ }
+ BLI_unlock_thread(LOCK_DRAW_IMAGE);
+
+ RE_ReleaseResult(oglrender->re);
+}
+
+static void screen_opengl_render_doit(OGLRender *oglrender, RenderResult *rr)
{
Scene *scene = oglrender->scene;
ARegion *ar = oglrender->ar;
View3D *v3d = oglrender->v3d;
RegionView3D *rv3d = oglrender->rv3d;
- RenderResult *rr;
Object *camera = NULL;
ImBuf *ibuf;
- void *lock;
float winmat[4][4];
int sizex = oglrender->sizex;
int sizey = oglrender->sizey;
@@ -138,8 +254,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender)
bool draw_bgpic = true;
bool draw_sky = (scene->r.alphamode == R_ADDSKY);
unsigned char *rect = NULL;
-
- rr = RE_AcquireResultRead(oglrender->re);
+ const char *viewname = RE_GetActiveRenderView(oglrender->re);
if (oglrender->is_sequencer) {
SeqRenderData context;
@@ -152,9 +267,11 @@ static void screen_opengl_render_apply(OGLRender *oglrender)
oglrender->sizex, oglrender->sizey, 100.0f,
&context);
+ context.view_id = BKE_scene_multiview_view_id_get(&scene->r, viewname);
ibuf = BKE_sequencer_give_ibuf(&context, CFRA, chanshown);
if (ibuf) {
+ float *rectf;
ImBuf *linear_ibuf;
BLI_assert((oglrender->sizex == ibuf->x) && (oglrender->sizey == ibuf->y));
@@ -175,7 +292,8 @@ static void screen_opengl_render_apply(OGLRender *oglrender)
BKE_sequencer_imbuf_from_sequencer_space(scene, linear_ibuf);
}
- memcpy(rr->rectf, linear_ibuf->rect_float, sizeof(float) * 4 * oglrender->sizex * oglrender->sizey);
+ rectf = RE_RenderViewGetRectf(rr, oglrender->view_id);
+ memcpy(rectf, linear_ibuf->rect_float, sizeof(float) * 4 * oglrender->sizex * oglrender->sizey);
IMB_freeImBuf(linear_ibuf);
}
@@ -221,7 +339,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender)
/* render 3d view */
if (rv3d->persp == RV3D_CAMOB && v3d->camera) {
/*int is_ortho = scene->r.mode & R_ORTHO;*/
- camera = v3d->camera;
+ camera = BKE_camera_multiview_render(oglrender->scene, v3d->camera, viewname);
RE_GetCameraWindow(oglrender->re, camera, scene->r.cfra, winmat);
if (camera->type == OB_CAMERA) {
Camera *cam = camera->data;
@@ -248,7 +366,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender)
ED_view3d_draw_offscreen(
scene, v3d, ar, sizex, sizey, NULL, winmat,
draw_bgpic, draw_sky, is_persp,
- oglrender->ofs, oglrender->fx, &fx_settings);
+ oglrender->ofs, oglrender->fx, &fx_settings, viewname);
GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, rect);
}
else {
@@ -264,7 +382,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender)
ED_view3d_draw_offscreen(
scene, v3d, ar, sizex, sizey, NULL, winmat,
draw_bgpic, draw_sky, is_persp,
- oglrender->ofs, oglrender->fx, &fx_settings);
+ oglrender->ofs, oglrender->fx, &fx_settings, viewname);
GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, rect);
for (i = 0; i < sizex * sizey * 4; i++)
@@ -280,7 +398,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender)
ED_view3d_draw_offscreen(
scene, v3d, ar, sizex, sizey, NULL, winmat_jitter,
draw_bgpic, draw_sky, is_persp,
- oglrender->ofs, oglrender->fx, &fx_settings);
+ oglrender->ofs, oglrender->fx, &fx_settings, viewname);
GPU_offscreen_read_pixels(oglrender->ofs, GL_UNSIGNED_BYTE, rect);
for (i = 0; i < sizex * sizey * 4; i++)
@@ -300,7 +418,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender)
char err_out[256] = "unknown";
ImBuf *ibuf_view = ED_view3d_draw_offscreen_imbuf_simple(scene, scene->camera, oglrender->sizex, oglrender->sizey,
IB_rect, OB_SOLID, false, true, true,
- (draw_sky) ? R_ADDSKY : R_ALPHAPREMUL, err_out);
+ (draw_sky) ? R_ADDSKY : R_ALPHAPREMUL, viewname, err_out);
camera = scene->camera;
if (ibuf_view) {
@@ -325,6 +443,7 @@ static void screen_opengl_render_apply(OGLRender *oglrender)
if (rect) {
int profile_to;
+ float *rectf = RE_RenderViewGetRectf(rr, oglrender->view_id);
if (BKE_scene_check_color_management_enabled(scene))
profile_to = IB_PROFILE_LINEAR_RGB;
@@ -333,47 +452,60 @@ static void screen_opengl_render_apply(OGLRender *oglrender)
/* sequencer has got trickier conversion happened above
* also assume opengl's space matches byte buffer color space */
- IMB_buffer_float_from_byte(rr->rectf, rect,
+ IMB_buffer_float_from_byte(rectf, rect,
profile_to, IB_PROFILE_SRGB, true,
oglrender->sizex, oglrender->sizey, oglrender->sizex, oglrender->sizex);
- }
- /* rr->rectf is now filled with image data */
+ /* rr->rectf is now filled with image data */
- if ((scene->r.stamp & R_STAMP_ALL) && (scene->r.stamp & R_STAMP_DRAW)) {
- BKE_image_stamp_buf(scene, camera, rect, rr->rectf, rr->rectx, rr->recty, 4);
+ if ((scene->r.stamp & R_STAMP_ALL) && (scene->r.stamp & R_STAMP_DRAW))
+ BKE_image_stamp_buf(scene, camera, rect, rectf, rr->rectx, rr->recty, 4);
+
+ MEM_freeN(rect);
}
+}
- RE_ReleaseResult(oglrender->re);
+static void screen_opengl_render_write(OGLRender *oglrender)
+{
+ Scene *scene = oglrender->scene;
+ RenderResult *rr;
+ bool ok;
+ char name[FILE_MAX];
+ Object *camera = RE_GetCamera(oglrender->re);
+
+ rr = RE_AcquireResultRead(oglrender->re);
- /* update byte from float buffer */
- ibuf = BKE_image_acquire_ibuf(oglrender->ima, &oglrender->iuser, &lock);
+ BKE_image_path_from_imformat(
+ name, scene->r.pic, oglrender->bmain->name, scene->r.cfra,
+ &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, false, NULL);
- if (ibuf) {
- ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID;
+ /* write images as individual images or stereo */
+ ok = RE_WriteRenderViewsImage(oglrender->reports, rr, scene, camera, false, name);
- /* write file for animation */
- if (oglrender->write_still) {
- char name[FILE_MAX];
- int ok;
+ RE_ReleaseResultImage(oglrender->re);
- if (scene->r.im_format.planes == R_IMF_CHAN_DEPTH_8) {
- IMB_color_to_bw(ibuf);
- }
+ if (ok) printf("OpenGL Render written to '%s'\n", name);
+ else printf("OpenGL Render failed to write '%s'\n", name);
+}
- BKE_image_path_from_imformat(
- name, scene->r.pic, oglrender->bmain->name, scene->r.cfra,
- &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, false);
- ok = BKE_imbuf_write_as(ibuf, name, &scene->r.im_format, true); /* no need to stamp here */
- if (ok) printf("OpenGL Render written to '%s'\n", name);
- else printf("OpenGL Render failed to write '%s'\n", name);
- }
+static void screen_opengl_render_apply(OGLRender *oglrender)
+{
+ RenderResult *rr;
+ RenderView *rv;
+ int view_id;
+
+ rr = RE_AcquireResultRead(oglrender->re);
+ for (rv = rr->views.first, view_id = 0; rv; rv = rv->next, view_id++) {
+ RE_SetActiveRenderView(oglrender->re, rv->name);
+ oglrender->view_id = view_id;
+ screen_opengl_render_doit(oglrender, rr);
}
-
- BKE_image_release_ibuf(oglrender->ima, ibuf, lock);
- if (rect)
- MEM_freeN(rect);
+ RE_ReleaseResult(oglrender->re);
+
+ if (oglrender->write_still) {
+ screen_opengl_render_write(oglrender);
+ }
}
static bool screen_opengl_render_init(bContext *C, wmOperator *op)
@@ -385,7 +517,6 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
ScrArea *prevsa = CTX_wm_area(C);
ARegion *prevar = CTX_wm_region(C);
- RenderResult *rr;
GPUOffScreen *ofs;
OGLRender *oglrender;
int sizex, sizey;
@@ -458,7 +589,6 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op)
oglrender->sseq = CTX_wm_space_seq(C);
}
-
oglrender->prevsa = prevsa;
oglrender->prevar = prevar;
@@ -492,15 +622,17 @@ static bool screen_opengl_render_init(bContext *C, wmOperator *op)
/* create render result */
RE_InitState(oglrender->re, NULL, &scene->r, NULL, sizex, sizey, NULL);
- rr = RE_AcquireResultWrite(oglrender->re);
- if (rr->rectf == NULL)
- rr->rectf = MEM_callocN(sizeof(float) * 4 * sizex * sizey, "screen_opengl_render_init rect");
- RE_ReleaseResult(oglrender->re);
+ /* create render views */
+ screen_opengl_views_setup(oglrender);
/* wm vars */
oglrender->wm = wm;
oglrender->win = win;
+ oglrender->totvideos = 0;
+ oglrender->mh = NULL;
+ oglrender->movie_ctx_arr = NULL;
+
return true;
}
@@ -508,10 +640,19 @@ static void screen_opengl_render_end(bContext *C, OGLRender *oglrender)
{
Main *bmain = CTX_data_main(C);
Scene *scene = oglrender->scene;
+ size_t i;
if (oglrender->mh) {
- if (BKE_imtype_is_movie(scene->r.im_format.imtype))
- oglrender->mh->end_movie();
+ if (BKE_imtype_is_movie(scene->r.im_format.imtype)) {
+ for (i = 0; i < oglrender->totvideos; i++) {
+ oglrender->mh->end_movie(oglrender->movie_ctx_arr[i]);
+ oglrender->mh->context_free(oglrender->movie_ctx_arr[i]);
+ }
+ }
+
+ if (oglrender->movie_ctx_arr) {
+ MEM_freeN(oglrender->movie_ctx_arr);
+ }
}
if (oglrender->timer) { /* exec will not have a timer */
@@ -552,13 +693,27 @@ static int screen_opengl_render_anim_initialize(bContext *C, wmOperator *op)
oglrender = op->customdata;
scene = oglrender->scene;
+ oglrender->totvideos = BKE_scene_multiview_num_videos_get(&scene->r);
oglrender->reports = op->reports;
- oglrender->mh = BKE_movie_handle_get(scene->r.im_format.imtype);
+
if (BKE_imtype_is_movie(scene->r.im_format.imtype)) {
- if (!oglrender->mh->start_movie(scene, &scene->r, oglrender->sizex, oglrender->sizey, oglrender->reports, PRVRANGEON != 0)) {
- screen_opengl_render_end(C, oglrender);
- return 0;
+ size_t i, width, height;
+
+ BKE_scene_multiview_videos_dimensions_get(&scene->r, oglrender->sizex, oglrender->sizey, &width, &height);
+ oglrender->movie_ctx_arr = MEM_mallocN(sizeof(void *) * oglrender->totvideos, "Movies");
+ oglrender->mh = BKE_movie_handle_get(scene->r.im_format.imtype);
+
+ for (i = 0; i < oglrender->totvideos; i++) {
+ const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, i);
+
+ oglrender->movie_ctx_arr[i] = oglrender->mh->context_create();
+ if (!oglrender->mh->start_movie(oglrender->movie_ctx_arr[i], scene, &scene->r, oglrender->sizex,
+ oglrender->sizey, oglrender->reports, PRVRANGEON != 0, suffix))
+ {
+ screen_opengl_render_end(C, oglrender);
+ return 0;
+ }
}
}
@@ -568,18 +723,17 @@ static int screen_opengl_render_anim_initialize(bContext *C, wmOperator *op)
return 1;
}
+
static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
OGLRender *oglrender = op->customdata;
Scene *scene = oglrender->scene;
- ImBuf *ibuf, *ibuf_save = NULL;
- void *lock;
char name[FILE_MAX];
bool ok = false;
const bool view_context = (oglrender->v3d != NULL);
- Object *camera = NULL;
bool is_movie;
+ RenderResult *rr;
/* go to next frame */
if (CFRA < oglrender->nfra)
@@ -599,7 +753,7 @@ static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op)
if (!is_movie) {
BKE_image_path_from_imformat(
name, scene->r.pic, oglrender->bmain->name, scene->r.cfra,
- &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true);
+ &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true, NULL);
if ((scene->r.mode & R_NO_OVERWRITE) && BLI_exists(name)) {
BKE_reportf(op->reports, RPT_INFO, "Skipping existing frame \"%s\"", name);
@@ -619,89 +773,40 @@ static bool screen_opengl_render_anim_step(bContext *C, wmOperator *op)
if (BKE_scene_camera_switch_update(scene)) {
oglrender->v3d->camera = scene->camera;
}
-
- camera = oglrender->v3d->camera;
}
}
else {
BKE_scene_camera_switch_update(scene);
-
- camera = scene->camera;
}
/* render into offscreen buffer */
screen_opengl_render_apply(oglrender);
/* save to disk */
- ibuf = BKE_image_acquire_ibuf(oglrender->ima, &oglrender->iuser, &lock);
-
- if (ibuf) {
- bool needs_free = false;
-
- ibuf_save = ibuf;
-
- if (is_movie || !BKE_imtype_requires_linear_float(scene->r.im_format.imtype)) {
- ibuf_save = IMB_colormanagement_imbuf_for_write(ibuf, true, true, &scene->view_settings,
- &scene->display_settings, &scene->r.im_format);
-
- needs_free = true;
- }
-
- /* color -> grayscale */
- /* editing directly would alter the render view */
- if (scene->r.im_format.planes == R_IMF_PLANES_BW) {
- ImBuf *ibuf_bw = IMB_dupImBuf(ibuf_save);
- IMB_color_to_bw(ibuf_bw);
-
- if (needs_free)
- IMB_freeImBuf(ibuf_save);
-
- ibuf_save = ibuf_bw;
- }
- else {
- /* this is lightweight & doesnt re-alloc the buffers, only do this
- * to save the correct bit depth since the image is always RGBA */
- ImBuf *ibuf_cpy = IMB_allocImBuf(ibuf_save->x, ibuf_save->y, scene->r.im_format.planes, 0);
-
- ibuf_cpy->rect = ibuf_save->rect;
- ibuf_cpy->rect_float = ibuf_save->rect_float;
- ibuf_cpy->zbuf_float = ibuf_save->zbuf_float;
-
- if (needs_free) {
- ibuf_cpy->mall = ibuf_save->mall;
- ibuf_save->mall = 0;
- IMB_freeImBuf(ibuf_save);
- }
+ rr = RE_AcquireResultRead(oglrender->re);
- ibuf_save = ibuf_cpy;
+ if (is_movie) {
+ ok = RE_WriteRenderViewsMovie(oglrender->reports, rr, scene, &scene->r, oglrender->mh, oglrender->sizex,
+ oglrender->sizey, oglrender->movie_ctx_arr, oglrender->totvideos);
+ if (ok) {
+ printf("Append frame %d", scene->r.cfra);
+ BKE_reportf(op->reports, RPT_INFO, "Appended frame: %d", scene->r.cfra);
}
-
- if (is_movie) {
- ok = oglrender->mh->append_movie(&scene->r, PSFRA, CFRA, (int *)ibuf_save->rect,
- oglrender->sizex, oglrender->sizey, oglrender->reports);
- if (ok) {
- printf("Append frame %d", scene->r.cfra);
- BKE_reportf(op->reports, RPT_INFO, "Appended frame: %d", scene->r.cfra);
- }
+ }
+ else {
+ ok = RE_WriteRenderViewsImage(op->reports, rr, scene, scene->camera, true, name);
+ if (ok) {
+ printf("Saved: %s", name);
+ BKE_reportf(op->reports, RPT_INFO, "Saved file: %s", name);
}
else {
- ok = BKE_imbuf_write_stamp(scene, camera, ibuf_save, name, &scene->r.im_format);
-
- if (ok == 0) {
- printf("Write error: cannot save %s\n", name);
- BKE_reportf(op->reports, RPT_ERROR, "Write error: cannot save %s", name);
- }
- else {
- printf("Saved: %s", name);
- BKE_reportf(op->reports, RPT_INFO, "Saved file: %s", name);
- }
+ printf("Write error: cannot save %s\n", name);
+ BKE_reportf(op->reports, RPT_ERROR, "Write error: cannot save %s", name);
}
-
- if (needs_free)
- IMB_freeImBuf(ibuf_save);
}
- BKE_image_release_ibuf(oglrender->ima, ibuf, lock);
+ RE_ReleaseResult(oglrender->re);
+
/* movie stats prints have no line break */
printf("\n");
diff --git a/source/blender/editors/render/render_ops.c b/source/blender/editors/render/render_ops.c
index 0d334082a2b..6aaad98f946 100644
--- a/source/blender/editors/render/render_ops.c
+++ b/source/blender/editors/render/render_ops.c
@@ -58,6 +58,9 @@ void ED_operatortypes_render(void)
WM_operatortype_append(SCENE_OT_render_layer_add);
WM_operatortype_append(SCENE_OT_render_layer_remove);
+ WM_operatortype_append(SCENE_OT_render_view_add);
+ WM_operatortype_append(SCENE_OT_render_view_remove);
+
#ifdef WITH_FREESTYLE
WM_operatortype_append(SCENE_OT_freestyle_module_add);
WM_operatortype_append(SCENE_OT_freestyle_module_remove);
diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c
index 9dbd2e88563..e6fbfda3067 100644
--- a/source/blender/editors/render/render_preview.c
+++ b/source/blender/editors/render/render_preview.c
@@ -548,7 +548,9 @@ static bool ed_preview_draw_rect(ScrArea *sa, int split, int first, rcti *rect,
/* test if something rendered ok */
re = RE_GetRender(name);
- RE_AcquireResultImage(re, &rres);
+
+ /* material preview only needs monoscopy (view 0) */
+ RE_AcquireResultImage(re, &rres, 0);
if (rres.rectf) {
@@ -561,9 +563,11 @@ static bool ed_preview_draw_rect(ScrArea *sa, int split, int first, rcti *rect,
unsigned char *rect_byte = MEM_mallocN(rres.rectx * rres.recty * sizeof(int), "ed_preview_draw_rect");
float fx = rect->xmin + offx;
float fy = rect->ymin;
+
+ /* material preview only needs monoscopy (view 0) */
if (re)
- RE_AcquiredResultGet32(re, &rres, (unsigned int *)rect_byte);
-
+ RE_AcquiredResultGet32(re, &rres, (unsigned int *)rect_byte, 0);
+
glaDrawPixelsSafe(fx, fy, rres.rectx, rres.recty, rres.rectx, GL_RGBA, GL_UNSIGNED_BYTE, rect_byte);
MEM_freeN(rect_byte);
diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c
index cf712a653e6..3de21536148 100644
--- a/source/blender/editors/render/render_shading.c
+++ b/source/blender/editors/render/render_shading.c
@@ -605,6 +605,70 @@ void SCENE_OT_render_layer_remove(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL;
}
+/********************** render view operators *********************/
+
+static int render_view_remove_poll(bContext *C)
+{
+ Scene *scene = CTX_data_scene(C);
+
+ /* don't allow user to remove "left" and "right" views */
+ return scene->r.actview > 1;
+}
+
+static int render_view_add_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Scene *scene = CTX_data_scene(C);
+
+ BKE_scene_add_render_view(scene, NULL);
+ scene->r.actview = BLI_listbase_count(&scene->r.views) - 1;
+
+ WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene);
+
+ return OPERATOR_FINISHED;
+}
+
+void SCENE_OT_render_view_add(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Add Render View";
+ ot->idname = "SCENE_OT_render_view_add";
+ ot->description = "Add a render view";
+
+ /* api callbacks */
+ ot->exec = render_view_add_exec;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int render_view_remove_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Scene *scene = CTX_data_scene(C);
+ SceneRenderView *rv = BLI_findlink(&scene->r.views, scene->r.actview);
+
+ if (!BKE_scene_remove_render_view(scene, rv))
+ return OPERATOR_CANCELLED;
+
+ WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene);
+
+ return OPERATOR_FINISHED;
+}
+
+void SCENE_OT_render_view_remove(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Remove Render View";
+ ot->idname = "SCENE_OT_render_view_remove";
+ ot->description = "Remove the selected render view";
+
+ /* api callbacks */
+ ot->exec = render_view_remove_exec;
+ ot->poll = render_view_remove_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
#ifdef WITH_FREESTYLE
static bool freestyle_linestyle_check_report(FreestyleLineSet *lineset, ReportList *reports)
diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c
index be9336cc984..d786c0cfc36 100644
--- a/source/blender/editors/screen/area.c
+++ b/source/blender/editors/screen/area.c
@@ -493,7 +493,6 @@ void ED_region_do_draw(bContext *C, ARegion *ar)
glDisable(GL_BLEND);
#endif
- ar->do_draw = 0;
memset(&ar->drawrct, 0, sizeof(ar->drawrct));
UI_blocklist_free_inactive(C, &ar->uiblocks);
diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c
index e1c414b0031..3d76070f197 100644
--- a/source/blender/editors/screen/screen_edit.c
+++ b/source/blender/editors/screen/screen_edit.c
@@ -59,6 +59,7 @@
#include "ED_screen.h"
#include "ED_screen_types.h"
#include "ED_clip.h"
+#include "ED_node.h"
#include "ED_render.h"
#include "UI_interface.h"
@@ -2122,4 +2123,85 @@ void ED_update_for_newframe(Main *bmain, Scene *scene, int UNUSED(mute))
}
+/*
+ * return true if any active area requires to see in 3D
+ */
+bool ED_screen_stereo3d_required(bScreen *screen)
+{
+ ScrArea *sa;
+ Scene *sce = screen->scene;
+ const bool is_multiview = (sce->r.scemode & R_MULTIVIEW) != 0;
+
+ for (sa = screen->areabase.first; sa; sa = sa->next) {
+ switch (sa->spacetype) {
+ case SPACE_VIEW3D:
+ {
+ View3D *v3d;
+
+ if (!is_multiview)
+ continue;
+
+ v3d = sa->spacedata.first;
+ if (v3d->camera && v3d->stereo3d_camera == STEREO_3D_ID) {
+ ARegion *ar;
+ for (ar = sa->regionbase.first; ar; ar = ar->next) {
+ if (ar->regiondata && ar->regiontype == RGN_TYPE_WINDOW) {
+ RegionView3D *rv3d = ar->regiondata;
+ if (rv3d->persp == RV3D_CAMOB) {
+ return true;
+ }
+ }
+ }
+ }
+ break;
+ }
+ case SPACE_IMAGE:
+ {
+ SpaceImage *sima;
+
+ /* images should always show in stereo, even if
+ * the file doesn't have views enabled */
+ sima = sa->spacedata.first;
+ if (sima->image && (sima->image->flag & IMA_IS_STEREO) &&
+ (sima->iuser.flag & IMA_SHOW_STEREO))
+ {
+ return true;
+ }
+ break;
+ }
+ case SPACE_NODE:
+ {
+ SpaceNode *snode;
+
+ if (!is_multiview)
+ continue;
+ snode = sa->spacedata.first;
+ if ((snode->flag & SNODE_BACKDRAW) && ED_node_is_compositor(snode)) {
+ return true;
+ }
+ break;
+ }
+ case SPACE_SEQ:
+ {
+ SpaceSeq *sseq;
+
+ if (!is_multiview)
+ continue;
+
+ sseq = sa->spacedata.first;
+ if (ELEM(sseq->view, SEQ_VIEW_PREVIEW, SEQ_VIEW_SEQUENCE_PREVIEW)) {
+ return true;
+ }
+
+ if (sseq->draw_flag & SEQ_DRAW_BACKDROP) {
+ return true;
+ }
+
+ break;
+ }
+ }
+ }
+
+ return false;
+}
diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c
index 894a801b677..67c18ac287e 100644
--- a/source/blender/editors/screen/screen_ops.c
+++ b/source/blender/editors/screen/screen_ops.c
@@ -990,6 +990,7 @@ static int area_dupli_invoke(bContext *C, wmOperator *op, const wmEvent *event)
rect.ymax = rect.ymin + BLI_rcti_size_y(&rect) / U.pixelsize;
newwin = WM_window_open(C, &rect);
+ *newwin->stereo3d_format = *win->stereo3d_format;
/* allocs new screen and adds to newly created window, using window size */
newsc = ED_screen_add(newwin, CTX_data_scene(C), sc->id.name + 2);
diff --git a/source/blender/editors/screen/screendump.c b/source/blender/editors/screen/screendump.c
index 330bc7e3395..4644f0ae0b8 100644
--- a/source/blender/editors/screen/screendump.c
+++ b/source/blender/editors/screen/screendump.c
@@ -303,6 +303,7 @@ typedef struct ScreenshotJob {
const short *stop;
const short *do_update;
ReportList reports;
+ void *movie_ctx;
} ScreenshotJob;
@@ -312,7 +313,10 @@ static void screenshot_freejob(void *sjv)
if (sj->dumprect)
MEM_freeN(sj->dumprect);
-
+
+ if (sj->movie_ctx)
+ MEM_freeN(sj->movie_ctx);
+
MEM_freeN(sj);
}
@@ -337,20 +341,21 @@ static void screenshot_startjob(void *sjv, short *stop, short *do_update, float
{
ScreenshotJob *sj = sjv;
RenderData rd = sj->scene->r;
- bMovieHandle *mh = BKE_movie_handle_get(sj->scene->r.im_format.imtype);
-
+ bMovieHandle *mh = NULL;
+
/* we need this as local variables for renderdata */
rd.frs_sec = U.scrcastfps;
rd.frs_sec_base = 1.0f;
if (BKE_imtype_is_movie(rd.im_format.imtype)) {
- if (!mh->start_movie(sj->scene, &rd, sj->dumpsx, sj->dumpsy, &sj->reports, false)) {
+ mh = BKE_movie_handle_get(sj->scene->r.im_format.imtype);
+ sj->movie_ctx = mh->context_create();
+
+ if (!mh->start_movie(sj->movie_ctx, sj->scene, &rd, sj->dumpsx, sj->dumpsy, &sj->reports, false, "")) {
printf("screencast job stopped\n");
return;
}
}
- else
- mh = NULL;
sj->stop = stop;
sj->do_update = do_update;
@@ -362,8 +367,8 @@ static void screenshot_startjob(void *sjv, short *stop, short *do_update, float
if (sj->dumprect) {
if (mh) {
- if (mh->append_movie(&rd, rd.sfra, rd.cfra, (int *)sj->dumprect,
- sj->dumpsx, sj->dumpsy, &sj->reports))
+ if (mh->append_movie(sj->movie_ctx, &rd, rd.sfra, rd.cfra, (int *)sj->dumprect,
+ sj->dumpsx, sj->dumpsy, "", &sj->reports))
{
BKE_reportf(&sj->reports, RPT_INFO, "Appended frame: %d", rd.cfra);
printf("Appended frame %d\n", rd.cfra);
@@ -379,7 +384,7 @@ static void screenshot_startjob(void *sjv, short *stop, short *do_update, float
BKE_image_path_from_imformat(
name, rd.pic, sj->bmain->name, rd.cfra,
- &rd.im_format, (rd.scemode & R_EXTENSION) != 0, true);
+ &rd.im_format, (rd.scemode & R_EXTENSION) != 0, true, NULL);
ibuf->rect = sj->dumprect;
ok = BKE_imbuf_write(ibuf, name, &rd.im_format);
@@ -410,8 +415,10 @@ static void screenshot_startjob(void *sjv, short *stop, short *do_update, float
PIL_sleep_ms(U.scrcastwait);
}
- if (mh)
- mh->end_movie();
+ if (mh) {
+ mh->end_movie(sj->movie_ctx);
+ mh->context_free(sj->movie_ctx);
+ }
BKE_report(&sj->reports, RPT_INFO, "Screencast job stopped");
}
diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c
index 097ffe3523b..7992767072a 100644
--- a/source/blender/editors/sculpt_paint/paint_image.c
+++ b/source/blender/editors/sculpt_paint/paint_image.c
@@ -499,7 +499,7 @@ void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short te
int w = imapaintpartial.x2 - imapaintpartial.x1;
int h = imapaintpartial.y2 - imapaintpartial.y1;
/* Testing with partial update in uv editor too */
- GPU_paint_update_image(image, imapaintpartial.x1, imapaintpartial.y1, w, h); //!texpaint);
+ GPU_paint_update_image(image, (sima ? &sima->iuser : NULL), imapaintpartial.x1, imapaintpartial.y1, w, h); //!texpaint);
}
}
diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c
index 4f71bea7cd2..9d39eea7b96 100644
--- a/source/blender/editors/sculpt_paint/paint_image_2d.c
+++ b/source/blender/editors/sculpt_paint/paint_image_2d.c
@@ -1109,7 +1109,7 @@ static int paint_2d_canvas_set(ImagePaintState *s, Image *ima)
if (ima == NULL) {
return 0;
}
- else if (ima->packedfile && ima->rr) {
+ else if (BKE_image_has_packedfile(ima) && ima->rr) {
s->warnpackedfile = ima->id.name + 2;
return 0;
}
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index 9ee15f3a532..b01d315650f 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -5233,7 +5233,7 @@ static int texture_paint_image_from_view_exec(bContext *C, wmOperator *op)
if (w > maxsize) w = maxsize;
if (h > maxsize) h = maxsize;
- ibuf = ED_view3d_draw_offscreen_imbuf(scene, CTX_wm_view3d(C), CTX_wm_region(C), w, h, IB_rect, false, R_ALPHAPREMUL, err_out);
+ ibuf = ED_view3d_draw_offscreen_imbuf(scene, CTX_wm_view3d(C), CTX_wm_region(C), w, h, IB_rect, false, R_ALPHAPREMUL, NULL, err_out);
if (!ibuf) {
/* Mostly happens when OpenGL offscreen buffer was failed to create, */
/* but could be other reasons. Should be handled in the future. nazgul */
@@ -5444,7 +5444,7 @@ static Image *proj_paint_image_create(wmOperator *op, Main *bmain)
RNA_string_get(op->ptr, "name", imagename);
}
ima = BKE_image_add_generated(bmain, width, height, imagename, alpha ? 32 : 24, use_float,
- gen_type, color);
+ gen_type, color, false);
return ima;
}
diff --git a/source/blender/editors/space_image/image_buttons.c b/source/blender/editors/space_image/image_buttons.c
index fb3c140fddf..a02f64d68d8 100644
--- a/source/blender/editors/space_image/image_buttons.c
+++ b/source/blender/editors/space_image/image_buttons.c
@@ -44,6 +44,7 @@
#include "BKE_image.h"
#include "BKE_node.h"
#include "BKE_screen.h"
+#include "BKE_scene.h"
#include "RE_pipeline.h"
@@ -82,9 +83,9 @@ static void image_info(Scene *scene, ImageUser *iuser, Image *ima, ImBuf *ibuf,
else {
if (ima->source == IMA_SRC_MOVIE) {
ofs += BLI_strncpy_rlen(str + ofs, IFACE_("Movie"), len - ofs);
- if (ima->anim)
+ if (BKE_image_has_anim(ima))
ofs += BLI_snprintf(str + ofs, len - ofs, IFACE_(" %d frs"),
- IMB_anim_get_duration(ima->anim, IMB_TC_RECORD_RUN));
+ IMB_anim_get_duration(((ImageAnim *)ima->anims.first)->anim, IMB_TC_RECORD_RUN));
}
else {
ofs += BLI_strncpy_rlen(str, IFACE_("Image"), len - ofs);
@@ -311,10 +312,10 @@ static void ui_imageuser_slot_menu(bContext *UNUSED(C), uiLayout *layout, void *
static const char *ui_imageuser_layer_fake_name(RenderResult *rr)
{
- if (rr->rectf) {
+ if (RE_RenderViewGetRectf(rr, 0)) {
return IFACE_("Composite");
}
- else if (rr->rect32) {
+ else if (RE_RenderViewGetRect32(rr, 0)) {
return IFACE_("Sequence");
}
else {
@@ -375,7 +376,7 @@ final:
static const char *ui_imageuser_pass_fake_name(RenderLayer *rl)
{
- if (rl == NULL || rl->rectf) {
+ if (rl == NULL) {
return IFACE_("Combined");
}
else {
@@ -383,9 +384,9 @@ static const char *ui_imageuser_pass_fake_name(RenderLayer *rl)
}
}
-static void ui_imageuser_pass_menu(bContext *UNUSED(C), uiLayout *layout, void *ptrpair_p)
+static void ui_imageuser_pass_menu(bContext *UNUSED(C), uiLayout *layout, void *rnd_pt)
{
- void **rnd_data = ptrpair_p;
+ void **rnd_data = rnd_pt;
uiBlock *block = uiLayoutGetBlock(layout);
Image *image = rnd_data[0];
ImageUser *iuser = rnd_data[1];
@@ -398,6 +399,7 @@ static void ui_imageuser_pass_menu(bContext *UNUSED(C), uiLayout *layout, void *
RenderPass *rpass;
const char *fake_name;
int nr;
+ int passflag = 0;
/* may have been freed since drawing */
rr = BKE_image_acquire_renderresult(scene, image);
@@ -419,15 +421,22 @@ static void ui_imageuser_pass_menu(bContext *UNUSED(C), uiLayout *layout, void *
fake_name = ui_imageuser_pass_fake_name(rl);
if (fake_name) {
- BLI_strncpy(rpass_fake.name, fake_name, sizeof(rpass_fake.name));
+ BLI_strncpy(rpass_fake.internal_name, fake_name, sizeof(rpass_fake.internal_name));
nr += 1;
}
/* rendered results don't have a Combined pass */
for (rpass = rl ? rl->passes.last : NULL; rpass; rpass = rpass->prev, nr--) {
+
+ /* just show one pass of each kind */
+ if (passflag & rpass->passtype)
+ continue;
+
+ passflag |= rpass->passtype;
+
final:
- uiDefButS(block, UI_BTYPE_BUT_MENU, B_NOP, IFACE_(rpass->name), 0, 0,
- UI_UNIT_X * 5, UI_UNIT_X, &iuser->pass, (float) nr, 0.0, 0, -1, "");
+ uiDefButS(block, UI_BTYPE_BUT_MENU, B_NOP, IFACE_(rpass->internal_name), 0, 0,
+ UI_UNIT_X * 5, UI_UNIT_X, &iuser->passtype, (float) rpass->passtype, 0.0, 0, -1, "");
}
if (fake_name) {
@@ -441,21 +450,81 @@ final:
BKE_image_release_renderresult(scene, image);
}
+/**************************** view menus *****************************/
+static void ui_imageuser_view_menu_rr(bContext *UNUSED(C), uiLayout *layout, void *rnd_pt)
+{
+ void **rnd_data = rnd_pt;
+ uiBlock *block = uiLayoutGetBlock(layout);
+ Image *image = rnd_data[0];
+ ImageUser *iuser = rnd_data[1];
+ RenderResult *rr;
+ RenderView *rview;
+ int nr;
+ Scene *scene = iuser->scene;
+
+ /* may have been freed since drawing */
+ rr = BKE_image_acquire_renderresult(scene, image);
+ if (UNLIKELY(rr == NULL)) {
+ return;
+ }
+
+ UI_block_layout_set_current(block, layout);
+ uiLayoutColumn(layout, false);
+
+ uiDefBut(block, UI_BTYPE_LABEL, 0, IFACE_("View"),
+ 0, 0, UI_UNIT_X * 5, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
+
+ uiItemS(layout);
+
+ nr = (rr ? BLI_listbase_count(&rr->views) : 0) - 1;
+ for (rview = rr ? rr->views.last : NULL; rview; rview = rview->prev, nr--) {
+ uiDefButS(block, UI_BTYPE_BUT_MENU, B_NOP, IFACE_(rview->name), 0, 0,
+ UI_UNIT_X * 5, UI_UNIT_X, &iuser->view, (float) nr, 0.0, 0, -1, "");
+ }
+
+ BKE_image_release_renderresult(scene, image);
+}
+
+static void ui_imageuser_view_menu_multiview(bContext *UNUSED(C), uiLayout *layout, void *rnd_pt)
+{
+ void **rnd_data = rnd_pt;
+ uiBlock *block = uiLayoutGetBlock(layout);
+ Image *image = rnd_data[0];
+ ImageUser *iuser = rnd_data[1];
+ int nr;
+ ImageView *iv;
+
+ UI_block_layout_set_current(block, layout);
+ uiLayoutColumn(layout, false);
+
+ uiDefBut(block, UI_BTYPE_LABEL, 0, IFACE_("View"),
+ 0, 0, UI_UNIT_X * 5, UI_UNIT_Y, NULL, 0.0, 0.0, 0, 0, "");
+
+ uiItemS(layout);
+
+ nr = BLI_listbase_count(&image->views) - 1;
+ for (iv = image->views.last; iv; iv = iv->prev, nr--) {
+ uiDefButS(block, UI_BTYPE_BUT_MENU, B_NOP, IFACE_(iv->name), 0, 0,
+ UI_UNIT_X * 5, UI_UNIT_X, &iuser->view, (float) nr, 0.0, 0, -1, "");
+ }
+}
+
/* 5 layer button callbacks... */
static void image_multi_cb(bContext *C, void *rr_v, void *iuser_v)
{
ImageUser *iuser = iuser_v;
- BKE_image_multilayer_index(rr_v, iuser);
+ BKE_image_multilayer_index(rr_v, iuser);
WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL);
}
+
static void image_multi_inclay_cb(bContext *C, void *rr_v, void *iuser_v)
{
RenderResult *rr = rr_v;
ImageUser *iuser = iuser_v;
int tot = BLI_listbase_count(&rr->layers);
- if (rr->rectf || rr->rect32)
+ if (RE_HasFakeLayer(rr))
tot++; /* fake compo/sequencer layer */
if (iuser->layer < tot - 1) {
@@ -483,7 +552,7 @@ static void image_multi_incpass_cb(bContext *C, void *rr_v, void *iuser_v)
if (rl) {
int tot = BLI_listbase_count(&rl->passes);
- if (rr->rectf || rr->rect32)
+ if (RE_HasFakeLayer(rr))
tot++; /* fake compo/sequencer layer */
if (iuser->pass < tot - 1) {
@@ -504,6 +573,16 @@ static void image_multi_decpass_cb(bContext *C, void *rr_v, void *iuser_v)
}
}
+/* 5 view button callbacks... */
+static void image_multiview_cb(bContext *C, void *ima_v, void *iuser_v)
+{
+ Image *ima = ima_v;
+ ImageUser *iuser = iuser_v;
+
+ BKE_image_multiview_index(ima, iuser);
+ WM_event_add_notifier(C, NC_IMAGE | ND_DRAW, NULL);
+}
+
#if 0
static void image_freecache_cb(bContext *C, void *ima_v, void *unused)
{
@@ -523,13 +602,14 @@ static void image_user_change(bContext *C, void *iuser_v, void *unused)
static void uiblock_layer_pass_buttons(uiLayout *layout, Image *image, RenderResult *rr, ImageUser *iuser, int w, short *render_slot)
{
- static void *rnd_pt[3]; /* XXX, workaround */
+ static void *rnd_pt[4]; /* XXX, workaround */
uiBlock *block = uiLayoutGetBlock(layout);
uiBut *but;
RenderLayer *rl = NULL;
- int wmenu1, wmenu2, wmenu3;
+ int wmenu1, wmenu2, wmenu3, wmenu4;
const char *fake_name;
- const char *display_name;
+ const char *display_name = "";
+ const bool show_stereo = (iuser->flag & IMA_SHOW_STEREO);
uiLayoutRow(layout, true);
@@ -537,6 +617,7 @@ static void uiblock_layer_pass_buttons(uiLayout *layout, Image *image, RenderRes
wmenu1 = (2 * w) / 5;
wmenu2 = (3 * w) / 5;
wmenu3 = (3 * w) / 6;
+ wmenu4 = (3 * w) / 6;
rnd_pt[0] = image;
rnd_pt[1] = iuser;
@@ -558,6 +639,7 @@ static void uiblock_layer_pass_buttons(uiLayout *layout, Image *image, RenderRes
if (rr) {
RenderPass *rpass;
+ RenderView *rview;
int rpass_index;
/* layer */
@@ -566,20 +648,50 @@ static void uiblock_layer_pass_buttons(uiLayout *layout, Image *image, RenderRes
rl = BLI_findlink(&rr->layers, rpass_index);
rnd_pt[2] = SET_INT_IN_POINTER(rpass_index);
- display_name = rl ? rl->name : (fake_name ? fake_name : "");
- but = uiDefMenuBut(block, ui_imageuser_layer_menu, rnd_pt, display_name, 0, 0, wmenu2, UI_UNIT_Y, TIP_("Select Layer"));
- UI_but_func_set(but, image_multi_cb, rr, iuser);
- UI_but_type_set_menu_from_pulldown(but);
-
+ if (RE_layers_have_name(rr)) {
+ display_name = rl ? rl->name : (fake_name ? fake_name : "");
+ but = uiDefMenuBut(block, ui_imageuser_layer_menu, rnd_pt, display_name, 0, 0, wmenu2, UI_UNIT_Y, TIP_("Select Layer"));
+ UI_but_func_set(but, image_multi_cb, rr, iuser);
+ UI_but_type_set_menu_from_pulldown(but);
+ }
/* pass */
fake_name = ui_imageuser_pass_fake_name(rl);
rpass = (rl ? BLI_findlink(&rl->passes, iuser->pass - (fake_name ? 1 : 0)) : NULL);
- display_name = rpass ? rpass->name : (fake_name ? fake_name : "");
+ display_name = rpass ? rpass->internal_name : (fake_name ? fake_name : "");
but = uiDefMenuBut(block, ui_imageuser_pass_menu, rnd_pt, display_name, 0, 0, wmenu3, UI_UNIT_Y, TIP_("Select Pass"));
UI_but_func_set(but, image_multi_cb, rr, iuser);
UI_but_type_set_menu_from_pulldown(but);
+
+ /* view */
+ if (BLI_listbase_count_ex(&rr->views, 2) > 1 && !show_stereo) {
+ rview = BLI_findlink(&rr->views, iuser->view);
+ display_name = rview ? rview->name : "";
+
+ but = uiDefMenuBut(block, ui_imageuser_view_menu_rr, rnd_pt, display_name, 0, 0, wmenu4, UI_UNIT_Y, TIP_("Select View"));
+ UI_but_func_set(but, image_multi_cb, rr, iuser);
+ UI_but_type_set_menu_from_pulldown(but);
+ }
+ }
+
+ /* stereo image */
+ else if (((image->flag & IMA_IS_STEREO) && (!show_stereo)) ||
+ ((image->flag & IMA_IS_MULTIVIEW) && ((image->flag & IMA_IS_STEREO) == 0)))
+ {
+ ImageView *iv;
+ int nr = 0;
+
+ for (iv = image->views.first; iv; iv = iv->next) {
+ if (nr++ == iuser->view) {
+ display_name = iv->name;
+ break;
+ }
+ }
+
+ but = uiDefMenuBut(block, ui_imageuser_view_menu_multiview, rnd_pt, display_name, 0, 0, wmenu1, UI_UNIT_Y, TIP_("Select View"));
+ UI_but_func_set(but, image_multiview_cb, image, iuser);
+ UI_but_type_set_menu_from_pulldown(but);
}
}
@@ -734,13 +846,13 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char
if (ima->source != IMA_SRC_GENERATED) {
row = uiLayoutRow(layout, true);
- if (ima->packedfile)
+ if (BKE_image_has_packedfile(ima))
uiItemO(row, "", ICON_PACKAGE, "image.unpack");
else
uiItemO(row, "", ICON_UGLYPACKAGE, "image.pack");
row = uiLayoutRow(row, true);
- uiLayoutSetEnabled(row, ima->packedfile == NULL);
+ uiLayoutSetEnabled(row, BKE_image_has_packedfile(ima) == false);
uiItemR(row, &imaptr, "filepath", 0, "", ICON_NONE);
uiItemO(row, "", ICON_FILE_REFRESH, "image.reload");
}
@@ -792,6 +904,17 @@ void uiTemplateImage(uiLayout *layout, bContext *C, PointerRNA *ptr, const char
uiItemR(row, &imaptr, "alpha_mode", 0, IFACE_("Alpha"), ICON_NONE);
}
+ if ((scene->r.scemode & R_MULTIVIEW) != 0) {
+ uiItemS(layout);
+
+ col = uiLayoutColumn(layout, false);
+ uiItemR(col, &imaptr, "use_multiview", 0, NULL, ICON_NONE);
+
+ col = uiLayoutColumn(layout, false);
+ uiLayoutSetActive(col, RNA_boolean_get(&imaptr, "use_multiview"));
+ uiTemplateImageViews(col, &imaptr);
+ }
+
if (ima->source == IMA_SRC_MOVIE) {
col = uiLayoutColumn(layout, false);
uiItemR(col, &imaptr, "use_deinterlace", 0, IFACE_("Deinterlace"), ICON_NONE);
@@ -962,6 +1085,101 @@ void uiTemplateImageSettings(uiLayout *layout, PointerRNA *imfptr, int color_man
}
}
+void uiTemplateImageStereo3d(uiLayout *layout, PointerRNA *stereo3d_format_ptr)
+{
+ Stereo3dFormat *stereo3d_format = stereo3d_format_ptr->data;
+ uiLayout *col;
+
+ col = uiLayoutColumn(layout, false);
+ uiItemR(col, stereo3d_format_ptr, "display_mode", 0, NULL, ICON_NONE);
+
+ switch (stereo3d_format->display_mode) {
+ case S3D_DISPLAY_ANAGLYPH:
+ {
+ uiItemR(col, stereo3d_format_ptr, "anaglyph_type", 0, NULL, ICON_NONE);
+ break;
+ }
+ case S3D_DISPLAY_INTERLACE:
+ {
+ uiItemR(col, stereo3d_format_ptr, "interlace_type", 0, NULL, ICON_NONE);
+ uiItemR(col, stereo3d_format_ptr, "use_interlace_swap", 0, NULL, ICON_NONE);
+ break;
+ }
+ case S3D_DISPLAY_SIDEBYSIDE:
+ {
+ uiItemR(col, stereo3d_format_ptr, "use_sidebyside_crosseyed", 0, NULL, ICON_NONE);
+ /* fall-through */
+ }
+ case S3D_DISPLAY_TOPBOTTOM:
+ {
+ uiItemR(col, stereo3d_format_ptr, "use_squeezed_frame", 0, NULL, ICON_NONE);
+ break;
+ }
+ }
+}
+
+static void uiTemplateViewsFormat(uiLayout *layout, PointerRNA *ptr, PointerRNA *stereo3d_format_ptr)
+{
+ uiLayout *col, *box;
+
+ col = uiLayoutColumn(layout, false);
+
+ uiItemL(col, IFACE_("Views Format:"), ICON_NONE);
+ uiItemR(uiLayoutRow(col, false), ptr, "views_format", UI_ITEM_R_EXPAND, NULL, ICON_NONE);
+
+ if (stereo3d_format_ptr) {
+ box = uiLayoutBox(col);
+ uiLayoutSetActive(box, RNA_enum_get(ptr, "views_format") == R_IMF_VIEWS_STEREO_3D);
+ uiTemplateImageStereo3d(box, stereo3d_format_ptr);
+ }
+}
+
+void uiTemplateImageViews(uiLayout *layout, PointerRNA *imaptr)
+{
+ Image *ima = imaptr->data;
+
+ if (ima->type != IMA_TYPE_MULTILAYER) {
+ PropertyRNA *prop;
+ PointerRNA stereo3d_format_ptr;
+
+ prop = RNA_struct_find_property(imaptr, "stereo_3d_format");
+ stereo3d_format_ptr = RNA_property_pointer_get(imaptr, prop);
+
+ uiTemplateViewsFormat(layout, imaptr, &stereo3d_format_ptr);
+ }
+ else {
+ uiTemplateViewsFormat(layout, imaptr, NULL);
+ }
+}
+
+void uiTemplateImageFormatViews(uiLayout *layout, PointerRNA *imfptr, PointerRNA *ptr)
+{
+ ImageFormatData *imf = imfptr->data;
+ uiLayout *col;
+
+ if (ptr) {
+ uiItemR(layout, ptr, "use_multiview", 0, NULL, ICON_NONE);
+ col = uiLayoutColumn(layout, false);
+ uiLayoutSetActive(col, RNA_boolean_get(ptr, "use_multiview"));
+ }
+ else {
+ col = uiLayoutColumn(layout, false);
+ }
+
+ if (imf->imtype != R_IMF_IMTYPE_MULTILAYER) {
+ PropertyRNA *prop;
+ PointerRNA stereo3d_format_ptr;
+
+ prop = RNA_struct_find_property(imfptr, "stereo_3d_format");
+ stereo3d_format_ptr = RNA_property_pointer_get(imfptr, prop);
+
+ uiTemplateViewsFormat(col, imfptr, &stereo3d_format_ptr);
+ }
+ else {
+ uiTemplateViewsFormat(col, imfptr, NULL);
+ }
+}
+
void uiTemplateImageLayers(uiLayout *layout, bContext *C, Image *ima, ImageUser *iuser)
{
Scene *scene = CTX_data_scene(C);
diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c
index 517cf64214c..5a3c3e90618 100644
--- a/source/blender/editors/space_image/image_draw.c
+++ b/source/blender/editors/space_image/image_draw.c
@@ -783,7 +783,7 @@ void draw_image_main(const bContext *C, ARegion *ar)
Image *ima;
ImBuf *ibuf;
float zoomx, zoomy;
- bool show_viewer, show_render, show_paint;
+ bool show_viewer, show_render, show_paint, show_stereo3d, show_multilayer;
void *lock;
/* XXX can we do this in refresh? */
@@ -813,6 +813,8 @@ void draw_image_main(const bContext *C, ARegion *ar)
show_viewer = (ima && ima->source == IMA_SRC_VIEWER) != 0;
show_render = (show_viewer && ima->type == IMA_TYPE_R_RESULT) != 0;
show_paint = (ima && (sima->mode == SI_MODE_PAINT) && (show_viewer == false) && (show_render == false));
+ show_stereo3d = (ima && (ima->flag & IMA_IS_STEREO) && (sima->iuser.flag & IMA_SHOW_STEREO));
+ show_multilayer = ima && BKE_image_is_multilayer(ima);
if (show_viewer) {
/* use locked draw for drawing viewer image buffer since the compositor
@@ -823,6 +825,14 @@ void draw_image_main(const bContext *C, ARegion *ar)
BLI_lock_thread(LOCK_DRAW_IMAGE);
}
+ if (show_stereo3d) {
+ if (show_multilayer)
+ /* update multiindex and pass for the current eye */
+ BKE_image_multilayer_index(ima->rr, &sima->iuser);
+ else
+ BKE_image_multiview_index(ima, &sima->iuser);
+ }
+
ibuf = ED_space_image_acquire_buffer(sima, &lock);
/* draw the image or grid */
diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c
index 5890ed61118..6a5462bff45 100644
--- a/source/blender/editors/space_image/image_ops.c
+++ b/source/blender/editors/space_image/image_ops.c
@@ -30,14 +30,21 @@
#include <stddef.h>
#include <string.h>
+#include <fcntl.h>
#include <stdlib.h>
#include <errno.h>
+#ifndef WIN32
+# include <unistd.h>
+#else
+# include <io.h>
+#endif
#include "MEM_guardedalloc.h"
#include "BLI_math.h"
#include "BLI_blenlib.h"
#include "BLI_utildefines.h"
+#include "BLI_string_utf8.h"
#include "BLF_translation.h"
@@ -60,6 +67,7 @@
#include "BKE_report.h"
#include "BKE_screen.h"
#include "BKE_sound.h"
+#include "BKE_scene.h"
#include "GPU_draw.h"
#include "GPU_buffers.h"
@@ -68,6 +76,7 @@
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
#include "IMB_moviecache.h"
+#include "intern/openexr/openexr_multi.h"
#include "RE_pipeline.h"
@@ -936,6 +945,7 @@ static void image_filesel(bContext *C, wmOperator *op, const char *path)
typedef struct ImageOpenData {
PropertyPointerRNA pprop;
ImageUser *iuser;
+ ImageFormatData im_format;
} ImageOpenData;
typedef struct ImageFrame {
@@ -946,7 +956,6 @@ typedef struct ImageFrame {
static void image_open_init(bContext *C, wmOperator *op)
{
ImageOpenData *iod;
-
op->customdata = iod = MEM_callocN(sizeof(ImageOpenData), __func__);
iod->iuser = CTX_data_pointer_get_type(C, "image_user", &RNA_ImageUser).data;
UI_context_active_but_prop_get_templateID(C, &iod->pprop.ptr, &iod->pprop.prop);
@@ -1048,7 +1057,7 @@ static int image_open_exec(bContext *C, wmOperator *op)
Scene *scene = CTX_data_scene(C);
Object *obedit = CTX_data_edit_object(C);
ImageUser *iuser = NULL;
- ImageOpenData *iod;
+ ImageOpenData *iod = op->customdata;
PointerRNA idptr;
Image *ima = NULL;
char path[FILE_MAX];
@@ -1085,6 +1094,21 @@ static int image_open_exec(bContext *C, wmOperator *op)
if (!op->customdata)
image_open_init(C, op);
+ /* handle multiview images */
+ if (RNA_boolean_get(op->ptr, "use_multiview")) {
+ ImageFormatData *imf = &iod->im_format;
+
+ ima->flag |= IMA_USE_VIEWS;
+ ima->views_format = imf->views_format;
+ *ima->stereo3d_format = imf->stereo3d_format;
+ }
+ else {
+ ima->flag &= ~IMA_USE_VIEWS;
+ ima->flag &= ~IMA_IS_STEREO;
+ ima->flag &= ~IMA_IS_MULTIVIEW;
+ BKE_image_free_views(ima);
+ }
+
/* only image path after save, never ibuf */
if (is_relative_path) {
if (!exists) {
@@ -1128,6 +1152,8 @@ static int image_open_exec(bContext *C, wmOperator *op)
iuser->framenr = 1;
iuser->offset = frame_ofs - 1;
iuser->fie_ima = 2;
+ iuser->scene = scene;
+ BKE_image_init_imageuser(ima, iuser);
}
/* XXX unpackImage frees image buffers */
@@ -1146,7 +1172,8 @@ static int image_open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(
SpaceImage *sima = CTX_wm_space_image(C); /* XXX other space types can call */
const char *path = U.textudir;
Image *ima = NULL;
-
+ Scene *scene = CTX_data_scene(C);
+ PropertyRNA *prop;
if (sima) {
ima = sima->image;
}
@@ -1185,11 +1212,44 @@ static int image_open_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(
image_open_init(C, op);
+ /* show multiview save options only if scene has multiviews */
+ prop = RNA_struct_find_property(op->ptr, "show_multiview");
+ RNA_property_boolean_set(op->ptr, prop, (scene->r.scemode & R_MULTIVIEW) != 0);
+
image_filesel(C, op, path);
return OPERATOR_RUNNING_MODAL;
}
+static bool image_open_draw_check_prop(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
+{
+ const char *prop_id = RNA_property_identifier(prop);
+
+ return !(STREQ(prop_id, "filepath") ||
+ STREQ(prop_id, "directory") ||
+ STREQ(prop_id, "filename")
+ );
+}
+
+static void image_open_draw(bContext *UNUSED(C), wmOperator *op)
+{
+ uiLayout *layout = op->layout;
+ ImageOpenData *iod = op->customdata;
+ ImageFormatData *imf = &iod->im_format;
+ PointerRNA imf_ptr, ptr;
+
+ /* main draw call */
+ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
+ uiDefAutoButsRNA(layout, &ptr, image_open_draw_check_prop, '\0');
+
+ /* image template */
+ RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &imf_ptr);
+
+ /* multiview template */
+ if (RNA_boolean_get(op->ptr, "show_multiview"))
+ uiTemplateImageFormatViews(layout, &imf_ptr, op->ptr);
+}
+
/* called by other space types too */
void IMAGE_OT_open(wmOperatorType *ot)
{
@@ -1202,6 +1262,7 @@ void IMAGE_OT_open(wmOperatorType *ot)
ot->exec = image_open_exec;
ot->invoke = image_open_invoke;
ot->cancel = image_open_cancel;
+ ot->ui = image_open_draw;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -1233,10 +1294,10 @@ static int image_match_len_exec(bContext *C, wmOperator *UNUSED(op))
}
- if (!ima || !iuser || !ima->anim)
+ if (!ima || !iuser || !BKE_image_has_anim(ima))
return OPERATOR_CANCELLED;
- iuser->frames = IMB_anim_get_duration(ima->anim, IMB_TC_RECORD_RUN);
+ iuser->frames = IMB_anim_get_duration(((ImageAnim *) ima->anims.first)->anim, IMB_TC_RECORD_RUN);
BKE_image_user_frame_calc(iuser, scene->r.cfra, 0);
return OPERATOR_FINISHED;
@@ -1433,6 +1494,10 @@ static int save_image_options_init(SaveImageOptions *simopts, SpaceImage *sima,
}
}
+ /* use the multiview image settings as the default */
+ simopts->im_format.stereo3d_format = *ima->stereo3d_format;
+ simopts->im_format.views_format = ima->views_format;
+
/* color management */
BKE_color_managed_display_settings_copy(&simopts->im_format.display_settings, &scene->display_settings);
BKE_color_managed_view_settings_copy(&simopts->im_format.view_settings, &scene->view_settings);
@@ -1468,30 +1533,131 @@ static void save_image_options_to_op(SaveImageOptions *simopts, wmOperator *op)
RNA_string_set(op->ptr, "filepath", simopts->filepath);
}
+/* returns the pass index for the view_id */
+static int get_multiview_pass_id(RenderResult *rr, ImageUser *iuser, const int view_id)
+{
+ RenderLayer *rl;
+ RenderPass *rpass;
+ int passtype;
+ short rl_index = 0, rp_index;
+
+ if (rr == NULL || iuser == NULL)
+ return 0;
+
+ if (BLI_listbase_count_ex(&rr->views, 2) < 2)
+ return iuser->pass;
+
+ if (RE_HasFakeLayer(rr))
+ rl_index ++; /* fake compo/sequencer layer */
+
+ rl = BLI_findlink(&rr->layers, rl_index);
+ if (!rl) return iuser->pass;
+
+ rpass = BLI_findlink(&rl->passes, iuser->pass);
+ passtype = rpass->passtype;
+
+ rp_index = 0;
+ for (rpass = rl->passes.first; rpass; rpass = rpass->next, rp_index++) {
+ if (rpass->passtype == passtype &&
+ rpass->view_id == view_id)
+ {
+ return rp_index;
+ }
+ }
+
+ return iuser->pass;
+}
+
+static void save_image_post(wmOperator *op, ImBuf *ibuf, Image *ima, int ok, int save_copy, const char *relbase, int relative, int do_newpath, const char *filepath)
+{
+ if (ok) {
+ if (!save_copy) {
+ if (do_newpath) {
+ BLI_strncpy(ibuf->name, filepath, sizeof(ibuf->name));
+ BLI_strncpy(ima->name, filepath, sizeof(ima->name));
+ }
+
+ ibuf->userflags &= ~IB_BITMAPDIRTY;
+
+ /* change type? */
+ if (ima->type == IMA_TYPE_R_RESULT) {
+ ima->type = IMA_TYPE_IMAGE;
+
+ /* workaround to ensure the render result buffer is no longer used
+ * by this image, otherwise can crash when a new render result is
+ * created. */
+ if (ibuf->rect && !(ibuf->mall & IB_rect))
+ imb_freerectImBuf(ibuf);
+ if (ibuf->rect_float && !(ibuf->mall & IB_rectfloat))
+ imb_freerectfloatImBuf(ibuf);
+ if (ibuf->zbuf && !(ibuf->mall & IB_zbuf))
+ IMB_freezbufImBuf(ibuf);
+ if (ibuf->zbuf_float && !(ibuf->mall & IB_zbuffloat))
+ IMB_freezbuffloatImBuf(ibuf);
+ }
+ if (ELEM(ima->source, IMA_SRC_GENERATED, IMA_SRC_VIEWER)) {
+ ima->source = IMA_SRC_FILE;
+ ima->type = IMA_TYPE_IMAGE;
+ }
+
+ /* only image path, never ibuf */
+ if (relative) {
+ BLI_path_rel(ima->name, relbase); /* only after saving */
+ }
+
+ IMB_colormanagment_colorspace_from_ibuf_ftype(&ima->colorspace_settings, ibuf);
+
+ }
+ }
+ else {
+ BKE_reportf(op->reports, RPT_ERROR, "Could not write image %s", filepath);
+ }
+}
+
+static void save_imbuf_post(ImBuf *ibuf, ImBuf *colormanaged_ibuf)
+{
+ if (colormanaged_ibuf != ibuf) {
+ /* This guys might be modified by image buffer write functions,
+ * need to copy them back from color managed image buffer to an
+ * original one, so file type of image is being properly updated.
+ */
+ ibuf->ftype = colormanaged_ibuf->ftype;
+ ibuf->planes = colormanaged_ibuf->planes;
+
+ IMB_freeImBuf(colormanaged_ibuf);
+ }
+}
+
/**
* \return success.
* \note ``ima->name`` and ``ibuf->name`` should end up the same.
+ * \note for multiview the first ``ibuf`` is important to get the settings.
*/
static bool save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveImageOptions *simopts, bool do_newpath)
{
Image *ima = ED_space_image(sima);
void *lock;
ImBuf *ibuf = ED_space_image_acquire_buffer(sima, &lock);
+ Scene *scene;
+ RenderResult *rr = NULL;
bool ok = false;
+ WM_cursor_wait(1);
+
if (ibuf) {
- ImBuf *colormanaged_ibuf;
+ ImBuf *colormanaged_ibuf = NULL;
const char *relbase = ID_BLEND_PATH(CTX_data_main(C), &ima->id);
const bool relative = (RNA_struct_find_property(op->ptr, "relative_path") && RNA_boolean_get(op->ptr, "relative_path"));
const bool save_copy = (RNA_struct_find_property(op->ptr, "copy") && RNA_boolean_get(op->ptr, "copy"));
const bool save_as_render = (RNA_struct_find_property(op->ptr, "save_as_render") && RNA_boolean_get(op->ptr, "save_as_render"));
ImageFormatData *imf = &simopts->im_format;
+ const bool is_multilayer = imf->imtype == R_IMF_IMTYPE_MULTILAYER;
+ bool is_mono;
+
/* old global to ensure a 2nd save goes to same dir */
BLI_strncpy(G.ima, simopts->filepath, sizeof(G.ima));
- WM_cursor_wait(1);
-
if (ima->type == IMA_TYPE_R_RESULT) {
/* enforce user setting for RGB or RGBA, but skip BW */
if (simopts->im_format.planes == R_IMF_PLANES_RGBA) {
@@ -1512,83 +1678,197 @@ static bool save_image_doit(bContext *C, SpaceImage *sima, wmOperator *op, SaveI
}
}
- colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, true, &imf->view_settings, &imf->display_settings, imf);
+ /* we need renderresult for exr and rendered multiview */
+ scene = CTX_data_scene(C);
+ rr = BKE_image_acquire_renderresult(scene, ima);
+ is_mono = rr ? BLI_listbase_count_ex(&rr->views, 2) < 2 : (ima->flag & IMA_IS_MULTIVIEW) == 0;
- if (simopts->im_format.imtype == R_IMF_IMTYPE_MULTILAYER) {
- Scene *scene = CTX_data_scene(C);
- RenderResult *rr = BKE_image_acquire_renderresult(scene, ima);
- if (rr) {
- ok = RE_WriteRenderResult(op->reports, rr, simopts->filepath, simopts->im_format.exr_codec);
- }
- else {
+ /* error handling */
+ if (!rr) {
+ if (imf->imtype == R_IMF_IMTYPE_MULTILAYER) {
BKE_report(op->reports, RPT_ERROR, "Did not write, no Multilayer Image");
+ goto cleanup;
}
- BKE_image_release_renderresult(scene, ima);
}
else {
- ok = BKE_imbuf_write_as(colormanaged_ibuf, simopts->filepath, &simopts->im_format, save_copy);
- }
-
- if (ok) {
- if (!save_copy) {
- if (do_newpath) {
- BLI_strncpy(ibuf->name, simopts->filepath, sizeof(ibuf->name));
- BLI_strncpy(ima->name, simopts->filepath, sizeof(ima->name));
+ if (imf->views_format == R_IMF_VIEWS_STEREO_3D) {
+ if ((ima->flag & IMA_IS_STEREO) == 0) {
+ BKE_reportf(op->reports, RPT_ERROR, "Did not write, the image doesn't have a \"%s\" and \"%s\" views",
+ STEREO_LEFT_NAME, STEREO_RIGHT_NAME);
+ goto cleanup;
}
- ibuf->userflags &= ~IB_BITMAPDIRTY;
-
- /* change type? */
- if (ima->type == IMA_TYPE_R_RESULT) {
- ima->type = IMA_TYPE_IMAGE;
-
- /* workaround to ensure the render result buffer is no longer used
- * by this image, otherwise can crash when a new render result is
- * created. */
- if (ibuf->rect && !(ibuf->mall & IB_rect))
- imb_freerectImBuf(ibuf);
- if (ibuf->rect_float && !(ibuf->mall & IB_rectfloat))
- imb_freerectfloatImBuf(ibuf);
- if (ibuf->zbuf && !(ibuf->mall & IB_zbuf))
- IMB_freezbufImBuf(ibuf);
- if (ibuf->zbuf_float && !(ibuf->mall & IB_zbuffloat))
- IMB_freezbuffloatImBuf(ibuf);
+ /* it shouldn't ever happen*/
+ if ((BLI_findstring(&rr->views, STEREO_LEFT_NAME, offsetof(RenderView, name)) == NULL) ||
+ (BLI_findstring(&rr->views, STEREO_RIGHT_NAME, offsetof(RenderView, name)) == NULL))
+ {
+ BKE_reportf(op->reports, RPT_ERROR, "Did not write, the image doesn't have a \"%s\" and \"%s\" views",
+ STEREO_LEFT_NAME, STEREO_RIGHT_NAME);
+ goto cleanup;
}
- if (ELEM(ima->source, IMA_SRC_GENERATED, IMA_SRC_VIEWER)) {
- ima->source = IMA_SRC_FILE;
- ima->type = IMA_TYPE_IMAGE;
+ }
+ }
+
+ /* fancy multiview OpenEXR */
+ if ((imf->imtype == R_IMF_IMTYPE_MULTILAYER) && (imf->views_format == R_IMF_VIEWS_MULTIVIEW)) {
+ ok = RE_WriteRenderResult(op->reports, rr, simopts->filepath, imf, true, NULL);
+ save_image_post(op, ibuf, ima, ok, true, relbase, relative, do_newpath, simopts->filepath);
+ ED_space_image_release_buffer(sima, ibuf, lock);
+ }
+ else if ((imf->imtype == R_IMF_IMTYPE_OPENEXR) && (imf->views_format == R_IMF_VIEWS_MULTIVIEW)) {
+ /* treat special Openexr case separetely (this is the singlelayer multiview OpenEXR */
+ BKE_imbuf_write_prepare(ibuf, imf);
+ ok = BKE_image_save_openexr_multiview(ima, ibuf, simopts->filepath, (IB_rect | IB_zbuf | IB_zbuffloat | IB_multiview));
+ ED_space_image_release_buffer(sima, ibuf, lock);
+ }
+ /* regular mono pipeline */
+ else if (is_mono) {
+ if (is_multilayer) {
+ ok = RE_WriteRenderResult(op->reports, rr, simopts->filepath, imf, false, NULL);
+ }
+ else {
+ colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, true, &imf->view_settings, &imf->display_settings, imf);
+ ok = BKE_imbuf_write_as(colormanaged_ibuf, simopts->filepath, imf, save_copy);
+ save_imbuf_post(ibuf, colormanaged_ibuf);
+ }
+ save_image_post(op, ibuf, ima, ok, (is_multilayer ? true : save_copy), relbase, relative, do_newpath, simopts->filepath);
+ ED_space_image_release_buffer(sima, ibuf, lock);
+ }
+ /* individual multiview images */
+ else if (imf->views_format == R_IMF_VIEWS_INDIVIDUAL) {
+ size_t i;
+ unsigned char planes = ibuf->planes;
+ const size_t totviews = (rr ? BLI_listbase_count(&rr->views) : BLI_listbase_count(&ima->views));
+
+ if (!is_multilayer) {
+ ED_space_image_release_buffer(sima, ibuf, lock);
+ }
+
+ for (i = 0; i < totviews; i++) {
+ char filepath[FILE_MAX];
+ bool ok_view = false;
+ const char *view = rr ? ((RenderView *) BLI_findlink(&rr->views, i))->name :
+ ((ImageView *) BLI_findlink(&ima->views, i))->name;
+
+ if (is_multilayer) {
+ BKE_scene_multiview_view_filepath_get(&scene->r, simopts->filepath, view, filepath);
+ ok_view = RE_WriteRenderResult(op->reports, rr, filepath, imf, false, view);
+ save_image_post(op, ibuf, ima, ok_view, true, relbase, relative, do_newpath, filepath);
}
+ else {
+ /* copy iuser to get the correct ibuf for this view */
+ ImageUser iuser = sima->iuser;
+ iuser.view = i;
+ iuser.flag &= ~IMA_SHOW_STEREO;
+
+ if (rr) {
+ iuser.pass = get_multiview_pass_id(rr, &sima->iuser, i);
+ BKE_image_multilayer_index(rr, &iuser);
+ }
+ else {
+ BKE_image_multiview_index(ima, &iuser);
+ }
+
+ ibuf = BKE_image_acquire_ibuf(sima->image, &iuser, &lock);
+ ibuf->planes = planes;
- /* only image path, never ibuf */
- if (relative) {
- BLI_path_rel(ima->name, relbase); /* only after saving */
+ BKE_scene_multiview_view_filepath_get(&scene->r, simopts->filepath, view, filepath);
+
+ colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, true, &imf->view_settings, &imf->display_settings, imf);
+ ok_view = BKE_imbuf_write_as(colormanaged_ibuf, filepath, &simopts->im_format, save_copy);
+ save_imbuf_post(ibuf, colormanaged_ibuf);
+ save_image_post(op, ibuf, ima, ok_view, true, relbase, relative, do_newpath, filepath);
+ BKE_image_release_ibuf(sima->image, ibuf, lock);
}
+ ok &= ok_view;
+ }
- IMB_colormanagment_colorspace_from_ibuf_ftype(&ima->colorspace_settings, ibuf);
+ if (is_multilayer) {
+ ED_space_image_release_buffer(sima, ibuf, lock);
}
}
- else {
- BKE_reportf(op->reports, RPT_ERROR, "Could not write image %s", simopts->filepath);
- }
+ /* stereo (multiview) images */
+ else if (simopts->im_format.views_format == R_IMF_VIEWS_STEREO_3D) {
+ if (imf->imtype == R_IMF_IMTYPE_MULTILAYER) {
+ ok = RE_WriteRenderResult(op->reports, rr, simopts->filepath, imf, false, NULL);
+ save_image_post(op, ibuf, ima, ok, true, relbase, relative, do_newpath, simopts->filepath);
+ ED_space_image_release_buffer(sima, ibuf, lock);
+ }
+ else {
+ ImBuf *ibuf_stereo[2] = {NULL};
+ unsigned char planes = ibuf->planes;
+ const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME};
+ int i;
- WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, sima->image);
+ /* we need to get the specific per-view buffers */
+ ED_space_image_release_buffer(sima, ibuf, lock);
+
+ for (i = 0; i < 2; i ++) {
+ ImageUser iuser = sima->iuser;
+ iuser.flag &= ~IMA_SHOW_STEREO;
+
+ if (rr) {
+ int id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name));
+ iuser.pass = get_multiview_pass_id(rr, &sima->iuser, id);
+ iuser.view = id;
+
+ BKE_image_multilayer_index(rr, &iuser);
+ }
+ else {
+ iuser.view = i;
+ BKE_image_multiview_index(ima, &iuser);
+ }
+
+ ibuf = BKE_image_acquire_ibuf(sima->image, &iuser, &lock);
+
+ if (ibuf == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "Did not write, unexpected error when saving stereo image");
+ goto cleanup;
+ }
- WM_cursor_wait(0);
+ ibuf->planes = planes;
- if (colormanaged_ibuf != ibuf) {
- /* This guys might be modified by image buffer write functions,
- * need to copy them back from color managed image buffer to an
- * original one, so file type of image is being properly updated.
- */
- ibuf->ftype = colormanaged_ibuf->ftype;
- ibuf->planes = colormanaged_ibuf->planes;
+ /* color manage the ImBuf leaving it ready for saving */
+ colormanaged_ibuf = IMB_colormanagement_imbuf_for_write(ibuf, save_as_render, true,
+ &imf->view_settings, &imf->display_settings, imf);
- IMB_freeImBuf(colormanaged_ibuf);
+ BKE_imbuf_write_prepare(colormanaged_ibuf, imf);
+ IMB_prepare_write_ImBuf(IMB_isfloat(colormanaged_ibuf), colormanaged_ibuf);
+
+ /* duplicate buffer to prevent locker issue when using render result */
+ ibuf_stereo[i] = IMB_dupImBuf(colormanaged_ibuf);
+
+ save_imbuf_post(ibuf, colormanaged_ibuf);
+ BKE_image_release_ibuf(sima->image, ibuf, lock);
+ }
+
+ ibuf = IMB_stereo3d_ImBuf(imf, ibuf_stereo[0], ibuf_stereo[1]);
+
+ /* save via traditional path */
+ ok = BKE_imbuf_write_as(ibuf, simopts->filepath, imf, save_copy);
+
+ IMB_freeImBuf(ibuf);
+
+ for (i = 0; i < 2; i ++) {
+ IMB_freeImBuf(ibuf_stereo[i]);
+ }
+ }
}
+
+ WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, sima->image);
+
+ }
+ else {
+cleanup:
+ ED_space_image_release_buffer(sima, ibuf, lock);
}
- ED_space_image_release_buffer(sima, ibuf, lock);
+ if (rr) {
+ BKE_image_release_renderresult(scene, ima);
+ }
+
+ WM_cursor_wait(0);
return ok;
}
@@ -1636,6 +1916,7 @@ static int image_save_as_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS
Image *ima = ED_space_image(sima);
Scene *scene = CTX_data_scene(C);
SaveImageOptions simopts;
+ PropertyRNA *prop;
const bool save_as_render = ((ima->source == IMA_SRC_VIEWER) || (ima->flag & IMA_VIEW_AS_RENDER));
if (RNA_struct_property_is_set(op->ptr, "filepath"))
@@ -1657,6 +1938,12 @@ static int image_save_as_invoke(bContext *C, wmOperator *op, const wmEvent *UNUS
op->customdata = MEM_mallocN(sizeof(simopts.im_format), __func__);
memcpy(op->customdata, &simopts.im_format, sizeof(simopts.im_format));
+ /* show multiview save options only if image has multiviews */
+ prop = RNA_struct_find_property(op->ptr, "show_multiview");
+ RNA_property_boolean_set(op->ptr, prop, (ima->flag & IMA_IS_MULTIVIEW) != 0);
+ prop = RNA_struct_find_property(op->ptr, "use_multiview");
+ RNA_property_boolean_set(op->ptr, prop, (ima->flag & IMA_IS_MULTIVIEW) != 0);
+
image_filesel(C, op, simopts.filepath);
return OPERATOR_RUNNING_MODAL;
@@ -1683,15 +1970,20 @@ static void image_save_as_draw(bContext *UNUSED(C), wmOperator *op)
{
uiLayout *layout = op->layout;
ImageFormatData *imf = op->customdata;
- PointerRNA ptr;
+ PointerRNA imf_ptr, ptr;
+ const bool is_multiview = RNA_boolean_get(op->ptr, "use_multiview");
/* image template */
- RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &ptr);
- uiTemplateImageSettings(layout, &ptr, false);
+ RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &imf_ptr);
+ uiTemplateImageSettings(layout, &imf_ptr, false);
/* main draw call */
RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
uiDefAutoButsRNA(layout, &ptr, image_save_as_draw_check_prop, '\0');
+
+ /* multiview template */
+ if (is_multiview)
+ uiTemplateImageFormatViews(layout, &imf_ptr, NULL);
}
static int image_save_as_poll(bContext *C)
@@ -1714,8 +2006,6 @@ static int image_save_as_poll(bContext *C)
void IMAGE_OT_save_as(wmOperatorType *ot)
{
-// PropertyRNA *prop;
-
/* identifiers */
ot->name = "Save As Image";
ot->idname = "IMAGE_OT_save_as";
@@ -1932,6 +2222,7 @@ static int image_new_exec(bContext *C, wmOperator *op)
float color[4];
int width, height, floatbuf, gen_type, alpha;
int gen_context;
+ int stereo3d;
/* retrieve state */
sima = CTX_wm_space_image(C);
@@ -1952,11 +2243,12 @@ static int image_new_exec(bContext *C, wmOperator *op)
RNA_float_get_array(op->ptr, "color", color);
alpha = RNA_boolean_get(op->ptr, "alpha");
gen_context = RNA_enum_get(op->ptr, "gen_context");
+ stereo3d = RNA_boolean_get(op->ptr, "use_stereo_3d");
if (!alpha)
color[3] = 1.0f;
- ima = BKE_image_add_generated(bmain, width, height, name, alpha ? 32 : 24, floatbuf, gen_type, color);
+ ima = BKE_image_add_generated(bmain, width, height, name, alpha ? 32 : 24, floatbuf, gen_type, color, stereo3d);
if (!ima)
return OPERATOR_CANCELLED;
@@ -2036,6 +2328,53 @@ static int image_new_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e
return WM_operator_props_dialog_popup(C, op, 15 * UI_UNIT_X, 5 * UI_UNIT_Y);
}
+static void image_new_draw(bContext *UNUSED(C), wmOperator *op)
+{
+ uiLayout *split, *col[2];
+ uiLayout *layout = op->layout;
+ PointerRNA ptr;
+#if 0
+ Scene *scene = CTX_data_scene(C);
+ const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0;
+#endif
+
+ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
+
+ /* copy of WM_operator_props_dialog_popup() layout */
+
+ split = uiLayoutSplit(layout, 0.5f, false);
+ col[0] = uiLayoutColumn(split, false);
+ col[1] = uiLayoutColumn(split, false);
+
+ uiItemL(col[0], IFACE_("Name"), ICON_NONE);
+ uiItemR(col[1], &ptr, "name", 0, "", ICON_NONE);
+
+ uiItemL(col[0], IFACE_("Width"), ICON_NONE);
+ uiItemR(col[1], &ptr, "width", 0, "", ICON_NONE);
+
+ uiItemL(col[0], IFACE_("Height"), ICON_NONE);
+ uiItemR(col[1], &ptr, "height", 0, "", ICON_NONE);
+
+ uiItemL(col[0], IFACE_("Color"), ICON_NONE);
+ uiItemR(col[1], &ptr, "color", 0, "", ICON_NONE);
+
+ uiItemL(col[0], "", ICON_NONE);
+ uiItemR(col[1], &ptr, "alpha", 0, NULL, ICON_NONE);
+
+ uiItemL(col[0], IFACE_("Generated Type"), ICON_NONE);
+ uiItemR(col[1], &ptr, "generated_type", 0, "", ICON_NONE);
+
+ uiItemL(col[0], "", ICON_NONE);
+ uiItemR(col[1], &ptr, "float", 0, NULL, ICON_NONE);
+
+#if 0
+ if (is_multiview) {
+ uiItemL(col[0], "", ICON_NONE);
+ uiItemR(col[1], &ptr, "use_stereo_3d", 0, NULL, ICON_NONE);
+ }
+#endif
+}
+
void IMAGE_OT_new(wmOperatorType *ot)
{
PropertyRNA *prop;
@@ -2056,6 +2395,7 @@ void IMAGE_OT_new(wmOperatorType *ot)
/* api callbacks */
ot->exec = image_new_exec;
ot->invoke = image_new_invoke;
+ ot->ui = image_new_draw;
/* flags */
ot->flag = OPTYPE_UNDO;
@@ -2075,7 +2415,8 @@ void IMAGE_OT_new(wmOperatorType *ot)
RNA_def_boolean(ot->srna, "float", 0, "32 bit Float", "Create image with 32 bit floating point bit depth");
prop = RNA_def_enum(ot->srna, "gen_context", gen_context_items, 0, "Gen Context", "Generation context");
RNA_def_property_flag(prop, PROP_HIDDEN);
-
+ prop = RNA_def_boolean(ot->srna, "use_stereo_3d", 0, "Stereo 3D", "Create an image with left and right views");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE | PROP_HIDDEN);
}
#undef IMA_DEF_NAME
@@ -2200,7 +2541,7 @@ static bool image_pack_test(bContext *C, wmOperator *op)
if (!ima)
return 0;
- if (!as_png && ima->packedfile)
+ if (!as_png && BKE_image_has_packedfile(ima))
return 0;
if (ima->source == IMA_SRC_SEQUENCE || ima->source == IMA_SRC_MOVIE) {
@@ -2229,7 +2570,7 @@ static int image_pack_exec(bContext *C, wmOperator *op)
if (as_png)
BKE_image_memorypack(ima);
else
- ima->packedfile = newPackedFile(op->reports, ima->name, ID_BLEND_PATH(bmain, &ima->id));
+ BKE_image_packfiles(op->reports, ima, ID_BLEND_PATH(bmain, &ima->id));
WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
@@ -2301,7 +2642,7 @@ static int image_unpack_exec(bContext *C, wmOperator *op)
if (!ima) ima = CTX_data_edit_image(C);
}
- if (!ima || !ima->packedfile)
+ if (!ima || !BKE_image_has_packedfile(ima))
return OPERATOR_CANCELLED;
if (ima->source == IMA_SRC_SEQUENCE || ima->source == IMA_SRC_MOVIE) {
@@ -2329,7 +2670,7 @@ static int image_unpack_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE
if (RNA_struct_property_is_set(op->ptr, "id"))
return image_unpack_exec(C, op);
- if (!ima || !ima->packedfile)
+ if (!ima || !BKE_image_has_packedfile(ima))
return OPERATOR_CANCELLED;
if (ima->source == IMA_SRC_SEQUENCE || ima->source == IMA_SRC_MOVIE) {
@@ -2340,7 +2681,7 @@ static int image_unpack_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE
if (G.fileflags & G_AUTOPACK)
BKE_report(op->reports, RPT_WARNING, "AutoPack is enabled, so image will be packed again on file save");
- unpack_menu(C, "IMAGE_OT_unpack", ima->id.name + 2, ima->name, "textures", ima->packedfile);
+ unpack_menu(C, "IMAGE_OT_unpack", ima->id.name + 2, ima->name, "textures", BKE_image_has_packedfile(ima) ? ((ImagePackedFile *)ima->packedfiles.first)->packedfile : NULL);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c
index bd3d76bbaba..1023f9641b3 100644
--- a/source/blender/editors/space_image/space_image.c
+++ b/source/blender/editors/space_image/space_image.c
@@ -156,7 +156,8 @@ static SpaceLink *image_new(const bContext *UNUSED(C))
simage->iuser.ok = true;
simage->iuser.fie_ima = 2;
simage->iuser.frames = 100;
-
+ simage->iuser.flag = IMA_SHOW_STEREO;
+
scopes_new(&simage->scopes);
simage->sample_line_hist.height = 100;
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c
index 6fa164a483f..d08a8bacd29 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -703,7 +703,9 @@ static void node_buts_image_user(uiLayout *layout, bContext *C, PointerRNA *ptr,
uiItemR(col, ptr, "use_auto_refresh", 0, NULL, ICON_NONE);
}
- if (RNA_enum_get(imaptr, "type") == IMA_TYPE_MULTILAYER) {
+ if (RNA_enum_get(imaptr, "type") == IMA_TYPE_MULTILAYER &&
+ RNA_boolean_get(ptr, "has_layers"))
+ {
col = uiLayoutColumn(layout, false);
uiItemR(col, ptr, "layer", 0, NULL, ICON_NONE);
}
@@ -1214,6 +1216,24 @@ static void node_shader_set_butfunc(bNodeType *ntype)
/* ****************** BUTTON CALLBACKS FOR COMPOSITE NODES ***************** */
+static void node_buts_image_views(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr,
+ PointerRNA *imaptr)
+{
+ uiLayout *col;
+
+ if (!imaptr->data)
+ return;
+
+ col = uiLayoutColumn(layout, false);
+
+ if (RNA_boolean_get(ptr, "has_views")) {
+ if (RNA_enum_get(ptr, "view") == 0)
+ uiItemR(col, ptr, "view", 0, NULL, ICON_CAMERA_STEREO);
+ else
+ uiItemR(col, ptr, "view", 0, NULL, ICON_SCENE);
+ }
+}
+
static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
bNode *node = ptr->data;
@@ -1227,6 +1247,8 @@ static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA *
imaptr = RNA_pointer_get(ptr, "image");
node_buts_image_user(layout, C, ptr, &imaptr, &iuserptr);
+
+ node_buts_image_views(layout, C, ptr, &imaptr);
}
static void node_composit_buts_image_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
@@ -1717,8 +1739,8 @@ static void node_composit_buts_id_mask(uiLayout *layout, bContext *UNUSED(C), Po
static void node_composit_buts_file_output(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
PointerRNA imfptr = RNA_pointer_get(ptr, "format");
- int multilayer = (RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER);
-
+ const bool multilayer = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER;
+
if (multilayer)
uiItemL(layout, IFACE_("Path:"), ICON_NONE);
else
@@ -1727,15 +1749,22 @@ static void node_composit_buts_file_output(uiLayout *layout, bContext *UNUSED(C)
}
static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, PointerRNA *ptr)
{
+ Scene *scene = CTX_data_scene(C);
PointerRNA imfptr = RNA_pointer_get(ptr, "format");
PointerRNA active_input_ptr, op_ptr;
uiLayout *row, *col;
int active_index;
- int multilayer = (RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER);
+ const bool multilayer = RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER;
+ const bool is_multiview = (scene->r.scemode & R_MULTIVIEW) != 0;
node_composit_buts_file_output(layout, C, ptr);
uiTemplateImageSettings(layout, &imfptr, false);
+ /* disable stereo output for multilayer, too much work for something that no one will use */
+ /* if someone asks for that we can implement it */
+ if (is_multiview)
+ uiTemplateImageFormatViews(layout, &imfptr, NULL);
+
uiItemS(layout);
uiItemO(layout, IFACE_("Add Input"), ICON_ZOOMIN, "NODE_OT_output_file_add_socket");
@@ -1797,6 +1826,9 @@ static void node_composit_buts_file_output_ex(uiLayout *layout, bContext *C, Poi
col = uiLayoutColumn(layout, false);
uiLayoutSetActive(col, RNA_boolean_get(&active_input_ptr, "use_node_format") == false);
uiTemplateImageSettings(col, &imfptr, false);
+
+ if (is_multiview)
+ uiTemplateImageFormatViews(layout, &imfptr, NULL);
}
}
}
@@ -2090,6 +2122,18 @@ static void node_composit_buts_switch(uiLayout *layout, bContext *UNUSED(C), Poi
uiItemR(layout, ptr, "check", 0, NULL, ICON_NONE);
}
+static void node_composit_buts_switch_view_ex(uiLayout *layout, bContext *UNUSED(C), PointerRNA *UNUSED(ptr))
+{
+ PointerRNA op_ptr;
+ wmOperatorType *ot = WM_operatortype_find("NODE_OT_switch_view_update", 1);
+
+ BLI_assert(ot != 0);
+
+ WM_operator_properties_create_ptr(&op_ptr, ot);
+
+ uiItemFullO_ptr(layout, ot, "Update Views", ICON_FILE_REFRESH, op_ptr.data, WM_OP_INVOKE_DEFAULT, 0);
+}
+
static void node_composit_buts_boxmask(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
{
uiLayout *row;
@@ -2587,6 +2631,9 @@ static void node_composit_set_butfunc(bNodeType *ntype)
case CMP_NODE_SWITCH:
ntype->draw_buttons = node_composit_buts_switch;
break;
+ case CMP_NODE_SWITCH_VIEW:
+ ntype->draw_buttons_ex = node_composit_buts_switch_view_ex;
+ break;
case CMP_NODE_MASK_BOX:
ntype->draw_buttons = node_composit_buts_boxmask;
ntype->draw_backdrop = node_composit_backdrop_boxmask;
@@ -2959,6 +3006,7 @@ static void node_file_output_socket_draw(bContext *C, uiLayout *layout, PointerR
imfptr = RNA_pointer_get(node_ptr, "format");
imtype = RNA_enum_get(&imfptr, "file_format");
+
if (imtype == R_IMF_IMTYPE_MULTILAYER) {
NodeImageMultiFileSocket *input = sock->storage;
RNA_pointer_create(&ntree->id, &RNA_NodeOutputFileSlotLayer, input, &inputptr);
diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c
index 27af4e7490b..80ecab09442 100644
--- a/source/blender/editors/space_node/node_edit.c
+++ b/source/blender/editors/space_node/node_edit.c
@@ -212,13 +212,13 @@ static void compo_progressjob(void *cjv, float progress)
*(cj->progress) = progress;
}
-
/* only this runs inside thread */
static void compo_startjob(void *cjv, short *stop, short *do_update, float *progress)
{
CompoJob *cj = cjv;
bNodeTree *ntree = cj->localtree;
Scene *scene = cj->scene;
+ SceneRenderView *srv;
if (scene->use_nodes == false)
return;
@@ -238,7 +238,11 @@ static void compo_startjob(void *cjv, short *stop, short *do_update, float *prog
// XXX BIF_store_spare();
/* 1 is do_previews */
- ntreeCompositExecTree(cj->scene, ntree, &cj->scene->r, false, true, &scene->view_settings, &scene->display_settings);
+
+ for (srv = scene->r.views.first; srv; srv = srv->next) {
+ if (BKE_scene_multiview_is_render_view_active(&scene->r, srv) == false) continue;
+ ntreeCompositExecTree(cj->scene, ntree, &cj->scene->r, false, true, &scene->view_settings, &scene->display_settings, srv->name);
+ }
ntree->test_break = NULL;
ntree->stats_draw = NULL;
@@ -1660,6 +1664,54 @@ void NODE_OT_delete(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
+/* ****************** Switch View ******************* */
+
+static int node_switch_view_poll(bContext *C)
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+
+ if (snode && snode->edittree)
+ return true;
+
+ return false;
+}
+
+static int node_switch_view_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ bNode *node, *next;
+
+ for (node = snode->edittree->nodes.first; node; node = next) {
+ next = node->next;
+ if (node->flag & SELECT) {
+ /* call the update function from the Switch View node */
+ node->update = NODE_UPDATE_OPERATOR;
+ }
+ }
+
+ ntreeUpdateTree(CTX_data_main(C), snode->edittree);
+
+ snode_notify(C, snode);
+ snode_dag_update(C, snode);
+
+ return OPERATOR_FINISHED;
+}
+
+void NODE_OT_switch_view_update(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Update Views";
+ ot->description = "Update views of selected node";
+ ot->idname = "NODE_OT_switch_view_update";
+
+ /* api callbacks */
+ ot->exec = node_switch_view_exec;
+ ot->poll = node_switch_view_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
/* ****************** Delete with reconnect ******************* */
static int node_delete_reconnect_exec(bContext *C, wmOperator *UNUSED(op))
{
diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h
index d49d37c651e..b15e9025a82 100644
--- a/source/blender/editors/space_node/node_intern.h
+++ b/source/blender/editors/space_node/node_intern.h
@@ -200,6 +200,8 @@ void NODE_OT_output_file_add_socket(struct wmOperatorType *ot);
void NODE_OT_output_file_remove_active_socket(struct wmOperatorType *ot);
void NODE_OT_output_file_move_active_socket(struct wmOperatorType *ot);
+void NODE_OT_switch_view_update (struct wmOperatorType *ot);
+
/* Note: clipboard_cut is a simple macro of copy + delete */
void NODE_OT_clipboard_copy(struct wmOperatorType *ot);
void NODE_OT_clipboard_paste(struct wmOperatorType *ot);
diff --git a/source/blender/editors/space_node/node_ops.c b/source/blender/editors/space_node/node_ops.c
index e2d83c243a2..474ad4db4af 100644
--- a/source/blender/editors/space_node/node_ops.c
+++ b/source/blender/editors/space_node/node_ops.c
@@ -123,6 +123,8 @@ void node_operatortypes(void)
WM_operatortype_append(NODE_OT_viewer_border);
WM_operatortype_append(NODE_OT_clear_viewer_border);
+ WM_operatortype_append(NODE_OT_switch_view_update);
+
WM_operatortype_append(NODE_OT_tree_socket_add);
WM_operatortype_append(NODE_OT_tree_socket_remove);
WM_operatortype_append(NODE_OT_tree_socket_move);
diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c
index 329e6db4cd0..373095d68f9 100644
--- a/source/blender/editors/space_sequencer/sequencer_add.c
+++ b/source/blender/editors/space_sequencer/sequencer_add.c
@@ -60,6 +60,7 @@
#include "ED_screen.h"
#include "ED_sequencer.h"
+#include "UI_interface.h"
#include "BKE_sound.h"
@@ -70,6 +71,10 @@
/* own include */
#include "sequencer_intern.h"
+typedef struct SequencerAddData {
+ ImageFormatData im_format;
+} SequencerAddData;
+
/* Generic functions, reused by add strip operators */
/* avoid passing multiple args and be more verbose */
@@ -222,6 +227,19 @@ static void seq_load_operator_info(SeqLoadInfo *seq_load, wmOperator *op)
}
RNA_PROP_END;
}
+
+ if ((prop = RNA_struct_find_property(op->ptr, "use_multiview")) && RNA_property_boolean_get(op->ptr, prop)) {
+ if (op->customdata) {
+ SequencerAddData *sad = op->customdata;
+ ImageFormatData *imf = &sad->im_format;
+
+ seq_load->views_format = imf->views_format;
+ seq_load->flag |= SEQ_USE_VIEWS;
+
+ /* operator custom data is always released after the SeqLoadInfo, no need to handle the memory here */
+ seq_load->stereo3d_format = &imf->stereo3d_format;
+ }
+ }
}
/**
@@ -573,6 +591,9 @@ static int sequencer_add_generic_strip_exec(bContext *C, wmOperator *op, SeqLoad
return OPERATOR_CANCELLED;
}
+ if (op->customdata)
+ MEM_freeN(op->customdata);
+
BKE_sequencer_sort(scene);
BKE_sequencer_update_muting(ed);
@@ -581,15 +602,40 @@ static int sequencer_add_generic_strip_exec(bContext *C, wmOperator *op, SeqLoad
return OPERATOR_FINISHED;
}
+/* add sequencer operators */
+static void sequencer_add_init(bContext *UNUSED(C), wmOperator *op)
+{
+ op->customdata = MEM_callocN(sizeof(SequencerAddData), __func__);
+}
+
+static void sequencer_add_cancel(bContext *UNUSED(C), wmOperator *op)
+{
+ if (op->customdata)
+ MEM_freeN(op->customdata);
+ op->customdata = NULL;
+}
+
+static bool sequencer_add_draw_check_prop(PointerRNA *UNUSED(ptr), PropertyRNA *prop)
+{
+ const char *prop_id = RNA_property_identifier(prop);
+
+ return !(STREQ(prop_id, "filepath") ||
+ STREQ(prop_id, "directory") ||
+ STREQ(prop_id, "filename")
+ );
+}
+
/* add movie operator */
static int sequencer_add_movie_strip_exec(bContext *C, wmOperator *op)
{
return sequencer_add_generic_strip_exec(C, op, BKE_sequencer_add_movie_strip);
}
-
static int sequencer_add_movie_strip_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
+ PropertyRNA *prop;
+ Scene *scene = CTX_data_scene(C);
+
/* This is for drag and drop */
if ((RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) ||
RNA_struct_property_is_set(op->ptr, "filepath"))
@@ -599,13 +645,37 @@ static int sequencer_add_movie_strip_invoke(bContext *C, wmOperator *op, const w
}
sequencer_generic_invoke_xy__internal(C, op, 0, SEQ_TYPE_MOVIE);
-
+
+ sequencer_add_init(C, op);
+
+ /* show multiview save options only if scene has multiviews */
+ prop = RNA_struct_find_property(op->ptr, "show_multiview");
+ RNA_property_boolean_set(op->ptr, prop, (scene->r.scemode & R_MULTIVIEW) != 0);
+
WM_event_add_fileselect(C, op);
return OPERATOR_RUNNING_MODAL;
//return sequencer_add_movie_strip_exec(C, op);
}
+static void sequencer_add_draw(bContext *UNUSED(C), wmOperator *op)
+{
+ uiLayout *layout = op->layout;
+ SequencerAddData *sad = op->customdata;
+ ImageFormatData *imf = &sad->im_format;
+ PointerRNA imf_ptr, ptr;
+
+ /* main draw call */
+ RNA_pointer_create(NULL, op->type->srna, op->properties, &ptr);
+ uiDefAutoButsRNA(layout, &ptr, sequencer_add_draw_check_prop, '\0');
+
+ /* image template */
+ RNA_pointer_create(NULL, &RNA_ImageFormatSettings, imf, &imf_ptr);
+
+ /* multiview template */
+ if (RNA_boolean_get(op->ptr, "show_multiview"))
+ uiTemplateImageFormatViews(layout, &imf_ptr, op->ptr);
+}
void SEQUENCER_OT_movie_strip_add(struct wmOperatorType *ot)
{
@@ -618,9 +688,11 @@ void SEQUENCER_OT_movie_strip_add(struct wmOperatorType *ot)
/* api callbacks */
ot->invoke = sequencer_add_movie_strip_invoke;
ot->exec = sequencer_add_movie_strip_exec;
+ ot->cancel = sequencer_add_cancel;
+ ot->ui = sequencer_add_draw;
ot->poll = ED_operator_sequencer_active_editable;
-
+
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
@@ -735,6 +807,9 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op)
sequencer_add_apply_overlap(C, op, seq);
+ if (op->customdata)
+ MEM_freeN(op->customdata);
+
WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene);
return OPERATOR_FINISHED;
@@ -742,6 +817,9 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op)
static int sequencer_add_image_strip_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
+ PropertyRNA *prop;
+ Scene *scene = CTX_data_scene(C);
+
/* drag drop has set the names */
if (RNA_struct_property_is_set(op->ptr, "files") && RNA_collection_length(op->ptr, "files")) {
sequencer_generic_invoke_xy__internal(C, op, SEQPROP_ENDFRAME | SEQPROP_NOPATHS, SEQ_TYPE_IMAGE);
@@ -750,6 +828,12 @@ static int sequencer_add_image_strip_invoke(bContext *C, wmOperator *op, const w
sequencer_generic_invoke_xy__internal(C, op, SEQPROP_ENDFRAME, SEQ_TYPE_IMAGE);
+ sequencer_add_init(C, op);
+
+ /* show multiview save options only if scene has multiviews */
+ prop = RNA_struct_find_property(op->ptr, "show_multiview");
+ RNA_property_boolean_set(op->ptr, prop, (scene->r.scemode & R_MULTIVIEW) != 0);
+
WM_event_add_fileselect(C, op);
return OPERATOR_RUNNING_MODAL;
}
@@ -766,6 +850,8 @@ void SEQUENCER_OT_image_strip_add(struct wmOperatorType *ot)
/* api callbacks */
ot->invoke = sequencer_add_image_strip_invoke;
ot->exec = sequencer_add_image_strip_exec;
+ ot->cancel = sequencer_add_cancel;
+ ot->ui = sequencer_add_draw;
ot->poll = ED_operator_sequencer_active_editable;
diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c
index ec4ae5a3a62..baadc28cd65 100644
--- a/source/blender/editors/space_sequencer/sequencer_draw.c
+++ b/source/blender/editors/space_sequencer/sequencer_draw.c
@@ -50,6 +50,7 @@
#include "BKE_main.h"
#include "BKE_sequencer.h"
#include "BKE_sound.h"
+#include "BKE_scene.h"
#include "IMB_colormanagement.h"
#include "IMB_imbuf.h"
@@ -860,7 +861,7 @@ void ED_sequencer_special_preview_clear(void)
sequencer_special_update_set(NULL);
}
-ImBuf *sequencer_ibuf_get(struct Main *bmain, Scene *scene, SpaceSeq *sseq, int cfra, int frame_ofs)
+ImBuf *sequencer_ibuf_get(struct Main *bmain, Scene *scene, SpaceSeq *sseq, int cfra, int frame_ofs, const char *viewname)
{
SeqRenderData context;
ImBuf *ibuf;
@@ -888,6 +889,7 @@ ImBuf *sequencer_ibuf_get(struct Main *bmain, Scene *scene, SpaceSeq *sseq, int
bmain->eval_ctx, bmain, scene,
rectx, recty, proxy_size,
&context);
+ context.view_id = BKE_scene_multiview_view_id_get(&scene->r, viewname);
/* sequencer could start rendering, in this case we need to be sure it wouldn't be canceled
* by Esc pressed somewhere in the past
@@ -994,6 +996,7 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq
const bool is_imbuf = ED_space_sequencer_check_show_imbuf(sseq);
int format, type;
bool glsl_used = false;
+ const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME};
if (G.is_rendering == false && (scene->r.seq_flag & R_SEQ_GL_PREV) == 0) {
/* stop all running jobs, except screen one. currently previews frustrate Render
@@ -1023,8 +1026,9 @@ void draw_image_seq(const bContext *C, Scene *scene, ARegion *ar, SpaceSeq *sseq
return;
}
- ibuf = sequencer_ibuf_get(bmain, scene, sseq, cfra, frame_ofs);
-
+ /* for now we only support Left/Right */
+ ibuf = sequencer_ibuf_get(bmain, scene, sseq, cfra, frame_ofs, names[sseq->multiview_eye]);
+
if (ibuf == NULL)
return;
diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c
index ad38cb66946..138d9b943fc 100644
--- a/source/blender/editors/space_sequencer/sequencer_edit.c
+++ b/source/blender/editors/space_sequencer/sequencer_edit.c
@@ -180,8 +180,6 @@ static void seq_proxy_build_job(const bContext *C)
Scene *scene = CTX_data_scene(C);
Editing *ed = BKE_sequencer_editing_get(scene, false);
ScrArea *sa = CTX_wm_area(C);
- struct SeqIndexBuildContext *context;
- LinkData *link;
Sequence *seq;
GSet *file_list;
@@ -209,9 +207,7 @@ static void seq_proxy_build_job(const bContext *C)
SEQP_BEGIN (ed, seq)
{
if ((seq->flag & SELECT)) {
- context = BKE_sequencer_proxy_rebuild_context(pj->main, pj->scene, seq, file_list);
- link = BLI_genericNodeN(context);
- BLI_addtail(&pj->queue, link);
+ BKE_sequencer_proxy_rebuild_context(pj->main, pj->scene, seq, file_list, &pj->queue);
}
}
SEQ_END
@@ -3442,12 +3438,18 @@ static int sequencer_rebuild_proxy_exec(bContext *C, wmOperator *UNUSED(op))
SEQP_BEGIN(ed, seq)
{
if ((seq->flag & SELECT)) {
- struct SeqIndexBuildContext *context;
+ ListBase queue = {NULL, NULL};
+ LinkData *link;
short stop = 0, do_update;
float progress;
- context = BKE_sequencer_proxy_rebuild_context(bmain, scene, seq, file_list);
- BKE_sequencer_proxy_rebuild(context, &stop, &do_update, &progress);
- BKE_sequencer_proxy_rebuild_finish(context, 0);
+
+ BKE_sequencer_proxy_rebuild_context(bmain, scene, seq, file_list, &queue);
+
+ for (link = queue.first; link; link = link->next) {
+ struct SeqIndexBuildContext *context = link->data;
+ BKE_sequencer_proxy_rebuild(context, &stop, &do_update, &progress);
+ BKE_sequencer_proxy_rebuild_finish(context, 0);
+ }
BKE_sequencer_free_imbuf(scene, &ed->seqbase, false);
}
}
diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h
index b02e254370a..78255507321 100644
--- a/source/blender/editors/space_sequencer/sequencer_intern.h
+++ b/source/blender/editors/space_sequencer/sequencer_intern.h
@@ -62,7 +62,7 @@ void sequencer_special_update_set(Sequence *seq);
/* UNUSED */
// void seq_reset_imageofs(struct SpaceSeq *sseq);
-struct ImBuf *sequencer_ibuf_get(struct Main *bmain, struct Scene *scene, struct SpaceSeq *sseq, int cfra, int frame_ofs);
+struct ImBuf *sequencer_ibuf_get(struct Main *bmain, struct Scene *scene, struct SpaceSeq *sseq, int cfra, int frame_ofs, const char *viewname);
/* sequencer_edit.c */
struct View2D;
diff --git a/source/blender/editors/space_sequencer/sequencer_view.c b/source/blender/editors/space_sequencer/sequencer_view.c
index 7fdbc9cc7de..4eafd2f86a8 100644
--- a/source/blender/editors/space_sequencer/sequencer_view.c
+++ b/source/blender/editors/space_sequencer/sequencer_view.c
@@ -94,7 +94,7 @@ static void sample_apply(bContext *C, wmOperator *op, const wmEvent *event)
Scene *scene = CTX_data_scene(C);
SpaceSeq *sseq = (SpaceSeq *) CTX_wm_space_data(C);
ARegion *ar = CTX_wm_region(C);
- ImBuf *ibuf = sequencer_ibuf_get(bmain, scene, sseq, CFRA, 0);
+ ImBuf *ibuf = sequencer_ibuf_get(bmain, scene, sseq, CFRA, 0, NULL);
ImageSampleInfo *info = op->customdata;
float fx, fy;
diff --git a/source/blender/editors/space_view3d/drawobject.c b/source/blender/editors/space_view3d/drawobject.c
index f14c19dc07e..f0b78649307 100644
--- a/source/blender/editors/space_view3d/drawobject.c
+++ b/source/blender/editors/space_view3d/drawobject.c
@@ -1741,6 +1741,238 @@ static void draw_viewport_reconstruction(Scene *scene, Base *base, View3D *v3d,
GPU_select_load_id(base->selcol);
}
+static void drawcamera_volume(float near_plane[4][3], float far_plane[4][3], const GLenum mode)
+{
+ glBegin(mode);
+ glVertex3fv(near_plane[0]);
+ glVertex3fv(far_plane[0]);
+ glVertex3fv(far_plane[1]);
+ glVertex3fv(near_plane[1]);
+ glEnd();
+
+ glBegin(mode);
+ glVertex3fv(near_plane[1]);
+ glVertex3fv(far_plane[1]);
+ glVertex3fv(far_plane[2]);
+ glVertex3fv(near_plane[2]);
+ glEnd();
+
+ glBegin(mode);
+ glVertex3fv(near_plane[2]);
+ glVertex3fv(near_plane[1]);
+ glVertex3fv(far_plane[1]);
+ glVertex3fv(far_plane[2]);
+ glEnd();
+
+ glBegin(mode);
+ glVertex3fv(far_plane[0]);
+ glVertex3fv(near_plane[0]);
+ glVertex3fv(near_plane[3]);
+ glVertex3fv(far_plane[3]);
+ glEnd();
+}
+
+/* camera frame */
+static void drawcamera_frame(float vec[4][3], const GLenum mode)
+{
+ glBegin(mode);
+ glVertex3fv(vec[0]);
+ glVertex3fv(vec[1]);
+ glVertex3fv(vec[2]);
+ glVertex3fv(vec[3]);
+ glEnd();
+}
+
+/* center point to camera frame */
+static void drawcamera_framelines(float vec[4][3], float origin[3])
+{
+ glBegin(GL_LINE_STRIP);
+ glVertex3fv(vec[1]);
+ glVertex3fv(origin);
+ glVertex3fv(vec[0]);
+ glVertex3fv(vec[3]);
+ glVertex3fv(origin);
+ glVertex3fv(vec[2]);
+ glEnd();
+}
+
+static bool drawcamera_is_stereo3d(Scene *scene, View3D *v3d, Object *ob)
+{
+ return (ob == v3d->camera) &&
+ (scene->r.scemode & R_MULTIVIEW) != 0 &&
+ (v3d->stereo3d_flag);
+}
+
+static void drawcamera_stereo3d(
+ Scene *scene, View3D *v3d, RegionView3D *rv3d, Object *ob, const Camera *cam,
+ float vec[4][3], float drawsize, const float scale[3])
+{
+ int i, j;
+ float obmat[4][4];
+ float vec_lr[2][4][3];
+ const float fac = (cam->stereo.pivot == CAM_S3D_PIVOT_CENTER) ? 2.0f : 1.0f;
+ float origin[2][3] = {{0}};
+ float tvec[3];
+ const Camera *cam_lr[2];
+ const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME};
+
+ const bool is_stereo3d_cameras = (v3d->stereo3d_flag & V3D_S3D_DISPCAMERAS) && (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D);
+ const bool is_stereo3d_plane = (v3d->stereo3d_flag & V3D_S3D_DISPPLANE) && (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D);
+ const bool is_stereo3d_volume = (v3d->stereo3d_flag & V3D_S3D_DISPVOLUME);
+
+ zero_v3(tvec);
+
+ glPushMatrix();
+
+ for (i = 0; i < 2; i++) {
+ ob = BKE_camera_multiview_render(scene, ob, names[i]);
+ cam_lr[i] = ob->data;
+
+ glLoadMatrixf(rv3d->viewmat);
+ BKE_camera_multiview_model_matrix(&scene->r, ob, names[i], obmat);
+ glMultMatrixf(obmat);
+
+ copy_m3_m3(vec_lr[i], vec);
+ copy_v3_v3(vec_lr[i][3], vec[3]);
+
+ if (cam->stereo.convergence_mode == CAM_S3D_OFFAXIS) {
+ const float shift_x =
+ ((BKE_camera_multiview_shift_x(&scene->r, ob, names[i]) - cam->shiftx) *
+ (drawsize * scale[0] * fac));
+
+ for (j = 0; j < 4; j++) {
+ vec_lr[i][j][0] += shift_x;
+ }
+ }
+
+ if (is_stereo3d_cameras) {
+ /* camera frame */
+ drawcamera_frame(vec_lr[i], GL_LINE_LOOP);
+
+ /* center point to camera frame */
+ drawcamera_framelines(vec_lr[i], tvec);
+ }
+
+ /* connecting line */
+ mul_m4_v3(obmat, origin[i]);
+
+ /* convergence plane */
+ if (is_stereo3d_plane || is_stereo3d_volume) {
+ for (j = 0; j < 4; j++) {
+ mul_m4_v3(obmat, vec_lr[i][j]);
+ }
+ }
+ }
+
+
+ /* the remaining drawing takes place in the view space */
+ glLoadMatrixf(rv3d->viewmat);
+
+ if (is_stereo3d_cameras) {
+ /* draw connecting lines */
+ glPushAttrib(GL_ENABLE_BIT);
+
+ glLineStipple(2, 0xAAAA);
+ glEnable(GL_LINE_STIPPLE);
+
+ glBegin(GL_LINES);
+ glVertex3fv(origin[0]);
+ glVertex3fv(origin[1]);
+ glEnd();
+ glPopAttrib();
+ }
+
+ /* draw convergence plane*/
+ if (is_stereo3d_plane) {
+ float axis_center[3], screen_center[3];
+ float world_plane[4][3];
+ float local_plane[4][3];
+ float offset;
+
+ mid_v3_v3v3(axis_center, origin[0], origin[1]);
+
+ for (i = 0; i < 4; i++) {
+ mid_v3_v3v3(world_plane[i], vec_lr[0][i], vec_lr[1][i]);
+ sub_v3_v3v3(local_plane[i], world_plane[i], axis_center);
+ }
+
+ mid_v3_v3v3(screen_center, world_plane[0], world_plane[2]);
+ offset = cam->stereo.convergence_distance / len_v3v3(screen_center, axis_center);
+
+ for (i = 0; i < 4; i++) {
+ mul_v3_fl(local_plane[i], offset);
+ add_v3_v3(local_plane[i], axis_center);
+ }
+
+ glColor3f(0.0f, 0.0f, 0.0f);
+
+ /* camera frame */
+ drawcamera_frame(local_plane, GL_LINE_LOOP);
+
+ if (v3d->stereo3d_convergence_alpha > 0.0f) {
+ glEnable(GL_BLEND);
+ glDepthMask(0); /* disable write in zbuffer, needed for nice transp */
+
+ glColor4f(0.0f, 0.0f, 0.0f, v3d->stereo3d_convergence_alpha);
+
+ drawcamera_frame(local_plane, GL_QUADS);
+
+ glDisable(GL_BLEND);
+ glDepthMask(1); /* restore write in zbuffer */
+ }
+ }
+
+ /* draw convergence plane*/
+ if (is_stereo3d_volume) {
+ float screen_center[3];
+ float near_plane[4][3], far_plane[4][3];
+ float offset;
+ int j;
+
+ for (i = 0; i < 2; i++) {
+ mid_v3_v3v3(screen_center, vec_lr[i][0], vec_lr[i][2]);
+
+ offset = len_v3v3(screen_center, origin[i]);
+
+ for (j = 0; j < 4; j++) {
+ sub_v3_v3v3(near_plane[j], vec_lr[i][j], origin[i]);
+ mul_v3_fl(near_plane[j], cam_lr[i]->clipsta / offset);
+ add_v3_v3(near_plane[j], origin[i]);
+
+ sub_v3_v3v3(far_plane[j], vec_lr[i][j], origin[i]);
+ mul_v3_fl(far_plane[j], cam_lr[i]->clipend / offset);
+ add_v3_v3(far_plane[j], origin[i]);
+ }
+
+ /* camera frame */
+ glColor3f(0.0f, 0.0f, 0.0f);
+
+ drawcamera_frame(near_plane, GL_LINE_LOOP);
+ drawcamera_frame(far_plane, GL_LINE_LOOP);
+ drawcamera_volume(near_plane, far_plane, GL_LINE_LOOP);
+
+ if (v3d->stereo3d_volume_alpha > 0.0f) {
+ glEnable(GL_BLEND);
+ glDepthMask(0); /* disable write in zbuffer, needed for nice transp */
+
+ if (i == 0)
+ glColor4f(0.0f, 1.0f, 1.0f, v3d->stereo3d_volume_alpha);
+ else
+ glColor4f(1.0f, 0.0f, 0.0f, v3d->stereo3d_volume_alpha);
+
+ drawcamera_frame(near_plane, GL_QUADS);
+ drawcamera_frame(far_plane, GL_QUADS);
+ drawcamera_volume(near_plane, far_plane, GL_QUADS);
+
+ glDisable(GL_BLEND);
+ glDepthMask(1); /* restore write in zbuffer */
+ }
+ }
+ }
+
+ glPopMatrix();
+}
+
/* flag similar to draw_object() */
static void drawcamera(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base,
const short dflag, const unsigned char ob_wire_col[4])
@@ -1755,6 +1987,12 @@ static void drawcamera(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base
const bool is_view = (rv3d->persp == RV3D_CAMOB && ob == v3d->camera);
MovieClip *clip = BKE_object_movieclip_get(scene, base->object, false);
+ const bool is_stereo3d = drawcamera_is_stereo3d(scene, v3d, ob);
+ const bool is_stereo3d_cameras = (ob == scene->camera) &&
+ (scene->r.scemode & R_MULTIVIEW) &&
+ (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D) &&
+ (v3d->stereo3d_flag & V3D_S3D_DISPCAMERAS);
+
/* draw data for movie clip set as active for scene */
if (clip) {
draw_viewport_reconstruction(scene, base, v3d, clip, dflag, ob_wire_col, false);
@@ -1791,12 +2029,8 @@ static void drawcamera(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base
glDisable(GL_CULL_FACE);
/* camera frame */
- glBegin(GL_LINE_LOOP);
- glVertex3fv(vec[0]);
- glVertex3fv(vec[1]);
- glVertex3fv(vec[2]);
- glVertex3fv(vec[3]);
- glEnd();
+ if (!is_stereo3d_cameras)
+ drawcamera_frame(vec, GL_LINE_LOOP);
if (is_view)
return;
@@ -1804,20 +2038,12 @@ static void drawcamera(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base
zero_v3(tvec);
/* center point to camera frame */
- glBegin(GL_LINE_STRIP);
- glVertex3fv(vec[1]);
- glVertex3fv(tvec);
- glVertex3fv(vec[0]);
- glVertex3fv(vec[3]);
- glVertex3fv(tvec);
- glVertex3fv(vec[2]);
- glEnd();
-
+ if (!is_stereo3d_cameras)
+ drawcamera_framelines(vec, tvec);
/* arrow on top */
tvec[2] = vec[1][2]; /* copy the depth */
-
/* draw an outline arrow for inactive cameras and filled
* for active cameras. We actually draw both outline+filled
* for active cameras so the wire can be seen side-on */
@@ -1867,6 +2093,11 @@ static void drawcamera(Scene *scene, View3D *v3d, RegionView3D *rv3d, Base *base
glPopMatrix();
}
}
+
+ /* stereo cameras drawing */
+ if (is_stereo3d) {
+ drawcamera_stereo3d(scene, v3d, rv3d, ob, cam, vec, drawsize, scale);
+ }
}
/* flag similar to draw_object() */
diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c
index ddb3e2f20c5..f8345e91afd 100644
--- a/source/blender/editors/space_view3d/space_view3d.c
+++ b/source/blender/editors/space_view3d/space_view3d.c
@@ -344,7 +344,13 @@ static SpaceLink *view3d_new(const bContext *C)
v3d->bundle_size = 0.2f;
v3d->bundle_drawtype = OB_PLAINAXES;
-
+
+ /* stereo */
+ v3d->stereo3d_camera = STEREO_3D_ID;
+ v3d->stereo3d_flag |= V3D_S3D_DISPPLANE;
+ v3d->stereo3d_convergence_alpha = 0.15f;
+ v3d->stereo3d_volume_alpha = 0.05f;
+
/* header */
ar = MEM_callocN(sizeof(ARegion), "header for view3d");
diff --git a/source/blender/editors/space_view3d/view3d_draw.c b/source/blender/editors/space_view3d/view3d_draw.c
index e480593b1e5..95a1ab87348 100644
--- a/source/blender/editors/space_view3d/view3d_draw.c
+++ b/source/blender/editors/space_view3d/view3d_draw.c
@@ -50,6 +50,7 @@
#include "BLI_math.h"
#include "BLI_utildefines.h"
#include "BLI_endian_switch.h"
+#include "BLI_threads.h"
#include "BKE_anim.h"
#include "BKE_camera.h"
@@ -100,6 +101,11 @@
#include "view3d_intern.h" /* own include */
+/* prototypes */
+static void view3d_stereo3d_setup(Scene *scene, View3D *v3d, ARegion *ar);
+static void view3d_stereo3d_setup_offscreen(Scene *scene, View3D *v3d, ARegion *ar,
+ float winmat[4][4], const char *viewname);
+
/* handy utility for drawing shapes in the viewport for arbitrary code.
* could add lines and points too */
// #define DEBUG_DRAW
@@ -1603,6 +1609,24 @@ exit:
/* ************************************************************* */
+static void view3d_stereo_bgpic_setup(Scene *scene, View3D *v3d, Image *ima, ImageUser *iuser)
+{
+ if ((ima->flag & IMA_IS_STEREO)) {
+ iuser->flag |= IMA_SHOW_STEREO;
+
+ if ((scene->r.scemode & R_MULTIVIEW) == 0)
+ iuser->multiview_eye = STEREO_LEFT_ID;
+
+ /* show only left or right camera */
+ else if (v3d->stereo3d_camera != STEREO_3D_ID)
+ iuser->multiview_eye = v3d->stereo3d_camera;
+
+ BKE_image_multiview_index(ima, iuser);
+ }
+ else
+ iuser->flag &= ~IMA_SHOW_STEREO;
+}
+
static void view3d_draw_bgpic(Scene *scene, ARegion *ar, View3D *v3d,
const bool do_foreground, const bool do_camera_frame)
{
@@ -1646,6 +1670,7 @@ static void view3d_draw_bgpic(Scene *scene, ARegion *ar, View3D *v3d,
ibuf = NULL; /* frame is out of range, dont show */
}
else {
+ view3d_stereo_bgpic_setup(scene, v3d, ima, &bgpic->iuser);
ibuf = BKE_image_acquire_ibuf(ima, &bgpic->iuser, &lock);
releaseibuf = ibuf;
}
@@ -2551,7 +2576,7 @@ static void gpu_update_lamps_shadows_world(Scene *scene, View3D *v3d)
ED_view3d_draw_offscreen(
scene, v3d, &ar, winsize, winsize, viewmat, winmat,
false, false, true,
- NULL, NULL, NULL);
+ NULL, NULL, NULL, NULL);
GPU_lamp_shadow_buffer_unbind(shadow->lamp);
v3d->drawtype = drawtype;
@@ -3097,7 +3122,8 @@ void ED_view3d_draw_offscreen(
float viewmat[4][4], float winmat[4][4],
bool do_bgpic, bool do_sky, bool is_persp,
GPUOffScreen *ofs,
- GPUFX *fx, GPUFXSettings *fx_settings)
+ GPUFX *fx, GPUFXSettings *fx_settings,
+ const char *viewname)
{
struct bThemeState theme_state;
int bwinx, bwiny;
@@ -3132,7 +3158,10 @@ void ED_view3d_draw_offscreen(
}
/* setup view matrices before fx or unbinding the offscreen buffers will cause issues */
- view3d_main_area_setup_view(scene, v3d, ar, viewmat, winmat);
+ if ((viewname != NULL && viewname[0] != '\0') && (viewmat == NULL))
+ view3d_stereo3d_setup_offscreen(scene, v3d, ar, winmat, viewname);
+ else
+ view3d_main_area_setup_view(scene, v3d, ar, viewmat, winmat);
/* framebuffer fx needed, we need to draw offscreen first */
if (v3d->fx_settings.fx_flag && fx) {
@@ -3196,7 +3225,7 @@ void ED_view3d_draw_offscreen(
/* utility func for ED_view3d_draw_offscreen */
ImBuf *ED_view3d_draw_offscreen_imbuf(Scene *scene, View3D *v3d, ARegion *ar, int sizex, int sizey, unsigned int flag,
- bool draw_background, int alpha_mode, char err_out[256])
+ bool draw_background, int alpha_mode, const char *viewname, char err_out[256])
{
RegionView3D *rv3d = ar->regiondata;
ImBuf *ibuf;
@@ -3221,28 +3250,29 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Scene *scene, View3D *v3d, ARegion *ar, in
if (rv3d->persp == RV3D_CAMOB && v3d->camera) {
CameraParams params;
GPUFXSettings fx_settings = {NULL};
- Object *camera = v3d->camera;
+ Object *camera = BKE_camera_multiview_render(scene, v3d->camera, viewname);
BKE_camera_params_init(&params);
/* fallback for non camera objects */
params.clipsta = v3d->near;
params.clipend = v3d->far;
- BKE_camera_params_from_object(&params, v3d->camera);
+ BKE_camera_params_from_object(&params, camera);
+ BKE_camera_multiview_params(&scene->r, &params, camera, viewname);
BKE_camera_params_compute_viewplane(&params, sizex, sizey, scene->r.xasp, scene->r.yasp);
BKE_camera_params_compute_matrix(&params);
BKE_camera_to_gpu_dof(camera, &fx_settings);
ED_view3d_draw_offscreen(
- scene, v3d, ar, sizex, sizey, NULL, params.winmat,
+ scene, v3d, ar, sizex, sizey, NULL, params.winmat,
draw_background, draw_sky, !params.is_ortho,
- ofs, NULL, &fx_settings);
+ ofs, NULL, &fx_settings, viewname);
}
else {
ED_view3d_draw_offscreen(
scene, v3d, ar, sizex, sizey, NULL, NULL,
draw_background, draw_sky, true,
- ofs, NULL, NULL);
+ ofs, NULL, NULL, viewname);
}
/* read in pixels & stamp */
@@ -3267,7 +3297,8 @@ ImBuf *ED_view3d_draw_offscreen_imbuf(Scene *scene, View3D *v3d, ARegion *ar, in
/* creates own 3d views, used by the sequencer */
ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Scene *scene, Object *camera, int width, int height, unsigned int flag, int drawtype,
- bool use_solid_tex, bool use_gpencil, bool draw_background, int alpha_mode, char err_out[256])
+ bool use_solid_tex, bool use_gpencil, bool draw_background, int alpha_mode,
+ const char *viewname, char err_out[256])
{
View3D v3d = {NULL};
ARegion ar = {NULL};
@@ -3297,9 +3328,11 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Scene *scene, Object *camera, int w
{
CameraParams params;
+ Object *camera = BKE_camera_multiview_render(scene, v3d.camera, viewname);
BKE_camera_params_init(&params);
- BKE_camera_params_from_object(&params, v3d.camera);
+ BKE_camera_params_from_object(&params, camera);
+ BKE_camera_multiview_params(&scene->r, &params, camera, viewname);
BKE_camera_params_compute_viewplane(&params, width, height, scene->r.xasp, scene->r.yasp);
BKE_camera_params_compute_matrix(&params);
@@ -3313,7 +3346,7 @@ ImBuf *ED_view3d_draw_offscreen_imbuf_simple(Scene *scene, Object *camera, int w
invert_m4_m4(rv3d.persinv, rv3d.viewinv);
return ED_view3d_draw_offscreen_imbuf(scene, &v3d, &ar, width, height, flag,
- draw_background, alpha_mode, err_out);
+ draw_background, alpha_mode, viewname, err_out);
// seq_view3d_cb(scene, cfra, render_size, seqrectx, seqrecty);
}
@@ -3518,6 +3551,104 @@ static void view3d_main_area_draw_engine_info(View3D *v3d, RegionView3D *rv3d, A
ED_region_info_draw(ar, rv3d->render_engine->text, 1, fill_color);
}
+static bool view3d_stereo3d_active(const bContext *C, Scene *scene, View3D *v3d, RegionView3D *rv3d)
+{
+ wmWindow *win = CTX_wm_window(C);
+
+ if ((scene->r.scemode & R_MULTIVIEW) == 0)
+ return false;
+
+ if (WM_stereo3d_enabled(win, true) == false)
+ return false;
+
+ if ((v3d->camera == NULL) || rv3d->persp != RV3D_CAMOB)
+ return false;
+
+ if (scene->r.views_format & SCE_VIEWS_FORMAT_MULTIVIEW) {
+ if (v3d->stereo3d_camera == STEREO_MONO_ID)
+ return false;
+
+ return BKE_scene_multiview_is_stereo3d(&scene->r);
+ }
+
+ return true;
+}
+
+/* setup the view and win matrices for the multiview cameras
+ *
+ * unlike view3d_stereo3d_setup_offscreen, when view3d_stereo3d_setup is called
+ * we have no winmatrix (i.e., projection matrix) defined at that time.
+ * Since the camera and the camera shift are needed for the winmat calculation
+ * we do a small hack to replace it temporarily so we don't need to change the
+ * view3d)main_area_setup_view() code to account for that.
+ */
+static void view3d_stereo3d_setup(Scene *scene, View3D *v3d, ARegion *ar)
+{
+ bool is_left;
+ const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME};
+ const char *viewname;
+
+ /* show only left or right camera */
+ if (v3d->stereo3d_camera != STEREO_3D_ID)
+ v3d->multiview_eye = v3d->stereo3d_camera;
+
+ is_left = v3d->multiview_eye == STEREO_LEFT_ID;
+ viewname = names[is_left ? STEREO_LEFT_ID : STEREO_RIGHT_ID];
+
+ /* update the viewport matrices with the new camera */
+ if (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D) {
+ Camera *data;
+ float viewmat[4][4];
+ float shiftx;
+
+ data = (Camera *)v3d->camera->data;
+ shiftx = data->shiftx;
+
+ BLI_lock_thread(LOCK_VIEW3D);
+ data->shiftx = BKE_camera_multiview_shift_x(&scene->r, v3d->camera, viewname);
+
+ BKE_camera_multiview_view_matrix(&scene->r, v3d->camera, is_left, viewmat);
+ view3d_main_area_setup_view(scene, v3d, ar, viewmat, NULL);
+
+ data->shiftx = shiftx;
+ BLI_unlock_thread(LOCK_VIEW3D);
+ }
+ else { /* SCE_VIEWS_FORMAT_MULTIVIEW */
+ float viewmat[4][4];
+ Object *view_ob = v3d->camera;
+ Object *camera = BKE_camera_multiview_render(scene, v3d->camera, viewname);
+
+ BLI_lock_thread(LOCK_VIEW3D);
+ v3d->camera = camera;
+
+ BKE_camera_multiview_view_matrix(&scene->r, camera, false, viewmat);
+ view3d_main_area_setup_view(scene, v3d, ar, viewmat, NULL);
+
+ v3d->camera = view_ob;
+ BLI_unlock_thread(LOCK_VIEW3D);
+ }
+}
+
+static void view3d_stereo3d_setup_offscreen(Scene *scene, View3D *v3d, ARegion *ar,
+ float winmat[4][4], const char *viewname)
+{
+ /* update the viewport matrices with the new camera */
+ if (scene->r.views_format == SCE_VIEWS_FORMAT_STEREO_3D) {
+ float viewmat[4][4];
+ const bool is_left = STREQ(viewname, STEREO_LEFT_NAME);
+
+ BKE_camera_multiview_view_matrix(&scene->r, v3d->camera, is_left, viewmat);
+ view3d_main_area_setup_view(scene, v3d, ar, viewmat, winmat);
+ }
+ else { /* SCE_VIEWS_FORMAT_MULTIVIEW */
+ float viewmat[4][4];
+ Object *camera = BKE_camera_multiview_render(scene, v3d->camera, viewname);
+
+ BKE_camera_multiview_view_matrix(&scene->r, camera, false, viewmat);
+ view3d_main_area_setup_view(scene, v3d, ar, viewmat, winmat);
+ }
+}
+
#ifdef WITH_GAMEENGINE
static void update_lods(Scene *scene, float camera_pos[3])
{
@@ -3551,8 +3682,11 @@ static void view3d_main_area_draw_objects(const bContext *C, Scene *scene, View3
GPU_default_lights();
}
- /* setup view matrices */
- view3d_main_area_setup_view(scene, v3d, ar, NULL, NULL);
+ /* setup the view matrix */
+ if (view3d_stereo3d_active(C, scene, v3d, rv3d))
+ view3d_stereo3d_setup(scene, v3d, ar);
+ else
+ view3d_main_area_setup_view(scene, v3d, ar, NULL, NULL);
rv3d->rflag &= ~RV3D_IS_GAME_ENGINE;
#ifdef WITH_GAMEENGINE
diff --git a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp
index d1285cf2c18..6b102410aa6 100644
--- a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp
+++ b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp
@@ -494,13 +494,14 @@ void FRS_composite_result(Render *re, SceneRenderLayer *srl, Render *freestyle_r
return;
rl = render_get_active_layer( freestyle_render, freestyle_render->result );
- if (!rl || rl->rectf == NULL) {
+ if (!rl) {
if (G.debug & G_DEBUG_FREESTYLE) {
cout << "No Freestyle result image to composite" << endl;
}
return;
}
- src = rl->rectf;
+
+ src = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, re->viewname);
#if 0
if (G.debug & G_DEBUG_FREESTYLE) {
cout << "src: " << rl->rectx << " x " << rl->recty << endl;
@@ -508,13 +509,13 @@ void FRS_composite_result(Render *re, SceneRenderLayer *srl, Render *freestyle_r
#endif
rl = RE_GetRenderLayer(re->result, srl->name);
- if (!rl || rl->rectf == NULL) {
+ if (!rl || src == NULL) {
if (G.debug & G_DEBUG_FREESTYLE) {
cout << "No layer to composite to" << endl;
}
return;
}
- dest = rl->rectf;
+ dest = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, re->viewname);
#if 0
if (G.debug & G_DEBUG_FREESTYLE) {
cout << "dest: " << rl->rectx << " x " << rl->recty << endl;
diff --git a/source/blender/gpu/GPU_draw.h b/source/blender/gpu/GPU_draw.h
index cc1a63d6256..20aa4919416 100644
--- a/source/blender/gpu/GPU_draw.h
+++ b/source/blender/gpu/GPU_draw.h
@@ -121,7 +121,7 @@ void GPU_set_gpu_mipmapping(int gpu_mipmap);
/* Image updates and free
* - these deal with images bound as opengl textures */
-void GPU_paint_update_image(struct Image *ima, int x, int y, int w, int h);
+void GPU_paint_update_image(struct Image *ima, ImageUser *iuser, int x, int y, int w, int h);
void GPU_update_images_framechange(void);
int GPU_update_image_time(struct Image *ima, double time);
int GPU_verify_image(struct Image *ima, struct ImageUser *iuser, int tftile, bool compare, bool mipmap, bool is_data);
diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c
index 7f3a9981a5c..daf6b90f604 100644
--- a/source/blender/gpu/intern/gpu_draw.c
+++ b/source/blender/gpu/intern/gpu_draw.c
@@ -1040,11 +1040,11 @@ static bool GPU_check_scaled_image(ImBuf *ibuf, Image *ima, float *frect, int x,
return false;
}
-void GPU_paint_update_image(Image *ima, int x, int y, int w, int h)
+void GPU_paint_update_image(Image *ima, ImageUser *iuser, int x, int y, int w, int h)
{
ImBuf *ibuf;
- ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL);
+ ibuf = BKE_image_acquire_ibuf(ima, iuser, NULL);
if (ima->repbind || (GPU_get_mipmap() && !GTS.gpu_mipmap) || !ima->bindcode || !ibuf ||
(w == 0) || (h == 0))
diff --git a/source/blender/imbuf/CMakeLists.txt b/source/blender/imbuf/CMakeLists.txt
index e8977913948..eadedbd5e00 100644
--- a/source/blender/imbuf/CMakeLists.txt
+++ b/source/blender/imbuf/CMakeLists.txt
@@ -62,6 +62,7 @@ set(SRC
intern/rectop.c
intern/rotate.c
intern/scaling.c
+ intern/stereoimbuf.c
intern/targa.c
intern/thumbs.c
intern/thumbs_blend.c
diff --git a/source/blender/imbuf/IMB_imbuf.h b/source/blender/imbuf/IMB_imbuf.h
index 18682302b3a..a39832dd1cb 100644
--- a/source/blender/imbuf/IMB_imbuf.h
+++ b/source/blender/imbuf/IMB_imbuf.h
@@ -89,6 +89,13 @@ struct ColorManagedDisplay;
struct GSet;
/**
*
+ * \attention defined in DNA_scene_types.h
+ */
+struct ImageFormatData;
+struct Stereo3dFormat;
+
+/**
+ *
* \attention Defined in allocimbuf.c
*/
void IMB_init(void);
@@ -279,10 +286,10 @@ bool IMB_anim_get_fps(struct anim *anim,
* \attention Defined in anim_movie.c
*/
struct anim *IMB_open_anim(const char *name, int ib_flags, int streamindex, char colorspace[IM_MAX_SPACE]);
+void IMB_suffix_anim(struct anim *anim, const char *suffix);
void IMB_close_anim(struct anim *anim);
void IMB_close_anim_proxies(struct anim *anim);
-
/**
*
* \attention Defined in anim_movie.c
@@ -377,6 +384,7 @@ void IMB_scaleImBuf_threaded(struct ImBuf *ibuf, unsigned int newx, unsigned int
* \attention Defined in writeimage.c
*/
short IMB_saveiff(struct ImBuf *ibuf, const char *filepath, int flags);
+struct ImBuf *IMB_prepare_write_ImBuf(const bool isfloat, struct ImBuf *ibuf);
/**
*
@@ -399,6 +407,12 @@ int imb_get_anim_type(const char *name);
/**
*
+ * \attention Defined in util.c
+ */
+bool IMB_isfloat(struct ImBuf *ibuf);
+
+/**
+ *
* \attention Defined in divers.c
*/
void IMB_de_interlace(struct ImBuf *ibuf);
@@ -557,5 +571,27 @@ void IMB_processor_apply_threaded(int buffer_lines, int handle_size, void *init_
void IMB_ffmpeg_init(void);
const char *IMB_ffmpeg_last_error(void);
-#endif
+/**
+ *
+ * \attention defined in stereoimbuf.c
+ */
+void IMB_stereo3d_write_dimensions(
+ const char mode, const bool is_squeezed, const size_t width, const size_t height,
+ size_t *r_width, size_t *r_height);
+void IMB_stereo3d_read_dimensions(
+ const char mode, const bool is_squeezed, const size_t width, const size_t height,
+ size_t *r_width, size_t *r_height);
+int *IMB_stereo3d_from_rect(
+ struct ImageFormatData *im_format, const size_t x, const size_t y, const size_t channels,
+ int *rect_left, int *rect_right);
+float *IMB_stereo3d_from_rectf(
+ struct ImageFormatData *im_format, const size_t x, const size_t y, const size_t channels,
+ float *rectf_left, float *rectf_right);
+struct ImBuf *IMB_stereo3d_ImBuf(
+ struct ImageFormatData *im_format,
+ struct ImBuf *ibuf_left, struct ImBuf *ibuf_right);
+void IMB_ImBufFromStereo3d(
+ struct Stereo3dFormat *s3d, struct ImBuf *ibuf_stereo,
+ struct ImBuf **r_ibuf_left, struct ImBuf **r_ibuf_right);
+#endif
diff --git a/source/blender/imbuf/IMB_imbuf_types.h b/source/blender/imbuf/IMB_imbuf_types.h
index 9e7201f53b4..862c587f75b 100644
--- a/source/blender/imbuf/IMB_imbuf_types.h
+++ b/source/blender/imbuf/IMB_imbuf_types.h
@@ -110,7 +110,7 @@ typedef struct ImBuf {
int index; /* reference index for ImBuf lists */
int userflags; /* used to set imbuf to dirty and other stuff */
struct IDProperty *metadata; /* image metadata */
- void *userdata; /* temporary storage, only used by baking at the moment */
+ void *userdata; /* temporary storage */
/* file information */
int ftype; /* file type we are going to save as */
@@ -172,6 +172,8 @@ typedef struct ImBuf {
#define IB_alphamode_premul (1 << 12) /* indicates whether image on disk have premul alpha */
#define IB_alphamode_detect (1 << 13) /* if this flag is set, alpha mode would be guessed from file */
#define IB_ignore_alpha (1 << 14) /* ignore alpha on load and substitude it with 1.0f */
+#define IB_thumbnail (1 << 15)
+#define IB_multiview (1 << 16)
/*
* The bit flag is stored in the ImBuf.ftype variable.
diff --git a/source/blender/imbuf/intern/IMB_anim.h b/source/blender/imbuf/intern/IMB_anim.h
index ed349e8f7eb..1fc43e22f68 100644
--- a/source/blender/imbuf/intern/IMB_anim.h
+++ b/source/blender/imbuf/intern/IMB_anim.h
@@ -194,6 +194,7 @@ struct anim {
struct anim_index *curr_idx[IMB_TC_MAX_SLOT];
char colorspace[64];
+ char suffix[64]; /* MAX_NAME - multiview */
};
#endif
diff --git a/source/blender/imbuf/intern/anim_movie.c b/source/blender/imbuf/intern/anim_movie.c
index ffdecb793aa..0bb9f0fce51 100644
--- a/source/blender/imbuf/intern/anim_movie.c
+++ b/source/blender/imbuf/intern/anim_movie.c
@@ -288,6 +288,11 @@ struct anim *IMB_open_anim(const char *name, int ib_flags, int streamindex, char
return(anim);
}
+void IMB_suffix_anim(struct anim *anim, const char *suffix)
+{
+ BLI_strncpy(anim->suffix, suffix, sizeof(anim->suffix));
+}
+
#ifdef WITH_AVI
static int startavi(struct anim *anim)
{
diff --git a/source/blender/imbuf/intern/indexer.c b/source/blender/imbuf/intern/indexer.c
index 183159f7d6c..150ea0995ac 100644
--- a/source/blender/imbuf/intern/indexer.c
+++ b/source/blender/imbuf/intern/indexer.c
@@ -398,7 +398,7 @@ static void get_proxy_filename(struct anim *anim, IMB_Proxy_Size preview_size,
}
BLI_snprintf(proxy_name, sizeof(proxy_name), name,
- (int) (proxy_fac[i] * 100), stream_suffix);
+ (int) (proxy_fac[i] * 100), stream_suffix, anim->suffix);
get_index_dir(anim, index_dir, sizeof(index_dir));
@@ -411,10 +411,10 @@ static void get_tc_filename(struct anim *anim, IMB_Timecode_Type tc,
char index_dir[FILE_MAXDIR];
int i = IMB_timecode_to_array_index(tc);
const char *index_names[] = {
- "record_run%s.blen_tc",
- "free_run%s.blen_tc",
- "interp_free_run%s.blen_tc",
- "record_run_no_gaps%s.blen_tc"
+ "record_run%s%s.blen_tc",
+ "free_run%s%s.blen_tc",
+ "interp_free_run%s%s.blen_tc",
+ "record_run_no_gaps%s%s.blen_tc"
};
char stream_suffix[20];
@@ -426,7 +426,7 @@ static void get_tc_filename(struct anim *anim, IMB_Timecode_Type tc,
BLI_snprintf(stream_suffix, 20, "_st%d", anim->streamindex);
}
- BLI_snprintf(index_name, 256, index_names[i], stream_suffix);
+ BLI_snprintf(index_name, 256, index_names[i], stream_suffix, anim->suffix);
get_index_dir(anim, index_dir, sizeof(index_dir));
diff --git a/source/blender/imbuf/intern/openexr/openexr_api.cpp b/source/blender/imbuf/intern/openexr/openexr_api.cpp
index 45eae89ad9d..1262f0af95d 100644
--- a/source/blender/imbuf/intern/openexr/openexr_api.cpp
+++ b/source/blender/imbuf/intern/openexr/openexr_api.cpp
@@ -92,9 +92,32 @@ _CRTIMP void __cdecl _invalid_parameter_noinfo(void)
#include <ImfStringAttribute.h>
#include <ImfStandardAttributes.h>
+/* multiview/multipart */
+#include <ImfMultiView.h>
+#include <ImfMultiPartInputFile.h>
+#include <ImfInputPart.h>
+#include <ImfOutputPart.h>
+#include <ImfMultiPartOutputFile.h>
+#include <ImfTiledOutputPart.h>
+#include <ImfPartType.h>
+#include <ImfPartHelper.h>
+
using namespace Imf;
using namespace Imath;
+extern "C"
+{
+/* prototype */
+static struct ExrPass *imb_exr_get_pass(ListBase *lb, char *passname);
+static bool exr_has_multiview(MultiPartInputFile& file);
+static bool exr_has_multipart_file(MultiPartInputFile& file);
+static int exr_has_alpha(MultiPartInputFile& file);
+static int exr_has_zbuffer(MultiPartInputFile& file);
+static void exr_printf(const char *__restrict format, ...);
+static void imb_exr_type_by_channels(ChannelList& channels, StringVector& views,
+ bool *r_singlelayer, bool *r_multilayer, bool *r_multiview);
+}
+
/* Memory Input Stream */
class Mem_IStream : public Imf::IStream
@@ -340,13 +363,21 @@ static void openexr_header_metadata(Header *header, struct ImBuf *ibuf)
addXDensity(*header, ibuf->ppm[0] / 39.3700787); /* 1 meter = 39.3700787 inches */
}
-static int imb_save_openexr_half(struct ImBuf *ibuf, const char *name, int flags)
+static bool imb_save_openexr_half(ImBuf *ibuf, const char *name, const int flags, const size_t totviews,
+ const char * (*getview)(void *base, size_t view_id),
+ ImBuf * (*getbuffer)(void *base, const size_t view_id))
{
const int channels = ibuf->channels;
const int is_alpha = (channels >= 4) && (ibuf->planes == 32);
const int is_zbuf = (flags & IB_zbuffloat) && ibuf->zbuf_float != NULL; /* summarize */
const int width = ibuf->x;
const int height = ibuf->y;
+ const bool is_multiview = (flags & IB_multiview) && ibuf->userdata;
+
+ BLI_assert((!is_multiview) || (getview && getbuffer));
+
+ std::vector <string> views;
+ size_t view_id;
try
{
@@ -355,13 +386,22 @@ static int imb_save_openexr_half(struct ImBuf *ibuf, const char *name, int flags
openexr_header_compression(&header, ibuf->ftype & OPENEXR_COMPRESS);
openexr_header_metadata(&header, ibuf);
- header.channels().insert("R", Channel(HALF));
- header.channels().insert("G", Channel(HALF));
- header.channels().insert("B", Channel(HALF));
- if (is_alpha)
- header.channels().insert("A", Channel(HALF));
- if (is_zbuf) // z we do as float always
- header.channels().insert("Z", Channel(Imf::FLOAT));
+ /* create views when possible */
+ for (view_id = 0; view_id < totviews; view_id ++)
+ views.push_back(is_multiview ? getview(ibuf->userdata, view_id) : "");
+
+ if (is_multiview)
+ addMultiView(header, views);
+
+ for (view_id = 0; view_id < totviews; view_id ++) {
+ header.channels().insert(insertViewName("R", views, view_id), Channel(HALF));
+ header.channels().insert(insertViewName("G", views, view_id), Channel(HALF));
+ header.channels().insert(insertViewName("B", views, view_id), Channel(HALF));
+ if (is_alpha)
+ header.channels().insert(insertViewName("A", views, view_id), Channel(HALF));
+ if (is_zbuf) // z we do as float always
+ header.channels().insert(insertViewName("Z", views, view_id), Channel(Imf::FLOAT));
+ }
FrameBuffer frameBuffer;
@@ -370,75 +410,91 @@ static int imb_save_openexr_half(struct ImBuf *ibuf, const char *name, int flags
OutputFile file(file_stream, header);
/* we store first everything in half array */
- RGBAZ *pixels = new RGBAZ[height * width];
- RGBAZ *to = pixels;
+ RGBAZ *pixels = new RGBAZ[height * width * totviews];
int xstride = sizeof(RGBAZ);
int ystride = xstride * width;
- /* indicate used buffers */
- frameBuffer.insert("R", Slice(HALF, (char *) &pixels[0].r, xstride, ystride));
- frameBuffer.insert("G", Slice(HALF, (char *) &pixels[0].g, xstride, ystride));
- frameBuffer.insert("B", Slice(HALF, (char *) &pixels[0].b, xstride, ystride));
- if (is_alpha)
- frameBuffer.insert("A", Slice(HALF, (char *) &pixels[0].a, xstride, ystride));
- if (is_zbuf)
- frameBuffer.insert("Z", Slice(Imf::FLOAT, (char *)(ibuf->zbuf_float + (height - 1) * width),
- sizeof(float), sizeof(float) * -width));
- if (ibuf->rect_float) {
- float *from;
-
- for (int i = ibuf->y - 1; i >= 0; i--) {
- from = ibuf->rect_float + channels * i * width;
-
- for (int j = ibuf->x; j > 0; j--) {
- to->r = from[0];
- to->g = (channels >= 2) ? from[1] : from[0];
- to->b = (channels >= 3) ? from[2] : from[0];
- to->a = (channels >= 4) ? from[3] : 1.0f;
- to++; from += channels;
+ for (view_id = 0; view_id < totviews; view_id ++) {
+ ImBuf *view_ibuf = is_multiview ? getbuffer(ibuf->userdata, view_id) : ibuf;
+ const size_t offset = view_id * width * height;
+ RGBAZ *to = pixels + offset;
+
+ /* indicate used buffers */
+ frameBuffer.insert(insertViewName("R", views, view_id), Slice(HALF, (char *) &pixels[offset].r, xstride, ystride));
+ frameBuffer.insert(insertViewName("G", views, view_id), Slice(HALF, (char *) &pixels[offset].g, xstride, ystride));
+ frameBuffer.insert(insertViewName("B", views, view_id), Slice(HALF, (char *) &pixels[offset].b, xstride, ystride));
+ if (is_alpha)
+ frameBuffer.insert(insertViewName("A", views, view_id), Slice(HALF, (char *) &pixels[offset].a, xstride, ystride));
+ if (is_zbuf)
+ frameBuffer.insert(insertViewName("Z", views, view_id), Slice(Imf::FLOAT, (char *)(view_ibuf->zbuf_float + (height - 1) * width),
+ sizeof(float), sizeof(float) * -width));
+ if (view_ibuf->rect_float) {
+ float *from;
+
+ for (int i = view_ibuf->y - 1; i >= 0; i--) {
+ from = view_ibuf->rect_float + channels * i * width;
+
+ for (int j = view_ibuf->x; j > 0; j--) {
+ to->r = from[0];
+ to->g = (channels >= 2) ? from[1] : from[0];
+ to->b = (channels >= 3) ? from[2] : from[0];
+ to->a = (channels >= 4) ? from[3] : 1.0f;
+ to++; from += channels;
+ }
}
}
- }
- else {
- unsigned char *from;
-
- for (int i = ibuf->y - 1; i >= 0; i--) {
- from = (unsigned char *)ibuf->rect + 4 * i * width;
-
- for (int j = ibuf->x; j > 0; j--) {
- to->r = srgb_to_linearrgb((float)from[0] / 255.0f);
- to->g = srgb_to_linearrgb((float)from[1] / 255.0f);
- to->b = srgb_to_linearrgb((float)from[2] / 255.0f);
- to->a = channels >= 4 ? (float)from[3] / 255.0f : 1.0f;
- to++; from += 4;
+ else {
+ unsigned char *from;
+
+ for (int i = view_ibuf->y - 1; i >= 0; i--) {
+ from = (unsigned char *)view_ibuf->rect + 4 * i * width;
+
+ for (int j = view_ibuf->x; j > 0; j--) {
+ to->r = srgb_to_linearrgb((float)from[0] / 255.0f);
+ to->g = srgb_to_linearrgb((float)from[1] / 255.0f);
+ to->b = srgb_to_linearrgb((float)from[2] / 255.0f);
+ to->a = channels >= 4 ? (float)from[3] / 255.0f : 1.0f;
+ to++; from += 4;
+ }
}
}
+
+ if (is_multiview)
+ IMB_freeImBuf(view_ibuf);
}
-// printf("OpenEXR-save: Writing OpenEXR file of height %d.\n", height);
+ exr_printf("OpenEXR-save: Writing OpenEXR file of height %d.\n", height);
file.setFrameBuffer(frameBuffer);
file.writePixels(height);
delete[] pixels;
}
- catch (const std::exception &exc)
+ catch (const std::exception& exc)
{
printf("OpenEXR-save: ERROR: %s\n", exc.what());
- return (0);
+ return false;
}
- return (1);
+ return true;
}
-static int imb_save_openexr_float(struct ImBuf *ibuf, const char *name, int flags)
+static bool imb_save_openexr_float(ImBuf *ibuf, const char *name, const int flags, const size_t totviews,
+ const char * (*getview)(void *base, const size_t view_id),
+ ImBuf * (*getbuffer)(void *base, const size_t view_id))
{
const int channels = ibuf->channels;
const int is_alpha = (channels >= 4) && (ibuf->planes == 32);
const int is_zbuf = (flags & IB_zbuffloat) && ibuf->zbuf_float != NULL; /* summarize */
const int width = ibuf->x;
const int height = ibuf->y;
+ const bool is_multiview = (flags & IB_multiview) && ibuf->userdata;
+
+ BLI_assert((!is_multiview) || (getview && getbuffer));
+
+ std::vector <string> views;
+ size_t view_id;
try
{
@@ -447,13 +503,22 @@ static int imb_save_openexr_float(struct ImBuf *ibuf, const char *name, int flag
openexr_header_compression(&header, ibuf->ftype & OPENEXR_COMPRESS);
openexr_header_metadata(&header, ibuf);
- header.channels().insert("R", Channel(Imf::FLOAT));
- header.channels().insert("G", Channel(Imf::FLOAT));
- header.channels().insert("B", Channel(Imf::FLOAT));
- if (is_alpha)
- header.channels().insert("A", Channel(Imf::FLOAT));
- if (is_zbuf)
- header.channels().insert("Z", Channel(Imf::FLOAT));
+ /* create views when possible */
+ for (view_id = 0; view_id < totviews; view_id ++)
+ views.push_back(is_multiview ? getview(ibuf->userdata, view_id) : "");
+
+ if (is_multiview)
+ addMultiView(header, views);
+
+ for (view_id = 0; view_id < totviews; view_id ++) {
+ header.channels().insert(insertViewName("R", views, view_id), Channel(Imf::FLOAT));
+ header.channels().insert(insertViewName("G", views, view_id), Channel(Imf::FLOAT));
+ header.channels().insert(insertViewName("B", views, view_id), Channel(Imf::FLOAT));
+ if (is_alpha)
+ header.channels().insert(insertViewName("A", views, view_id), Channel(Imf::FLOAT));
+ if (is_zbuf)
+ header.channels().insert(insertViewName("Z", views, view_id), Channel(Imf::FLOAT));
+ }
FrameBuffer frameBuffer;
@@ -463,37 +528,41 @@ static int imb_save_openexr_float(struct ImBuf *ibuf, const char *name, int flag
int xstride = sizeof(float) * channels;
int ystride = -xstride * width;
- float *rect[4] = {NULL, NULL, NULL, NULL};
- /* last scanline, stride negative */
- rect[0] = ibuf->rect_float + channels * (height - 1) * width;
- rect[1] = (channels >= 2) ? rect[0] + 1 : rect[0];
- rect[2] = (channels >= 3) ? rect[0] + 2 : rect[0];
- rect[3] = (channels >= 4) ? rect[0] + 3 : rect[0]; /* red as alpha, is this needed since alpha isn't written? */
-
- frameBuffer.insert("R", Slice(Imf::FLOAT, (char *)rect[0], xstride, ystride));
- frameBuffer.insert("G", Slice(Imf::FLOAT, (char *)rect[1], xstride, ystride));
- frameBuffer.insert("B", Slice(Imf::FLOAT, (char *)rect[2], xstride, ystride));
- if (is_alpha)
- frameBuffer.insert("A", Slice(Imf::FLOAT, (char *)rect[3], xstride, ystride));
- if (is_zbuf)
- frameBuffer.insert("Z", Slice(Imf::FLOAT, (char *) (ibuf->zbuf_float + (height - 1) * width),
- sizeof(float), sizeof(float) * -width));
+ for (view_id = 0; view_id < totviews; view_id ++) {
+ float *rect[4] = {NULL, NULL, NULL, NULL};
+ ImBuf *view_ibuf = is_multiview ? getbuffer(ibuf->userdata, view_id) : ibuf;
+
+ /* last scanline, stride negative */
+ rect[0] = view_ibuf->rect_float + channels * (height - 1) * width;
+ rect[1] = (channels >= 2) ? rect[0] + 1 : rect[0];
+ rect[2] = (channels >= 3) ? rect[0] + 2 : rect[0];
+ rect[3] = (channels >= 4) ? rect[0] + 3 : rect[0]; /* red as alpha, is this needed since alpha isn't written? */
+
+ frameBuffer.insert(insertViewName("R", views, view_id), Slice(Imf::FLOAT, (char *)rect[0], xstride, ystride));
+ frameBuffer.insert(insertViewName("G", views, view_id), Slice(Imf::FLOAT, (char *)rect[1], xstride, ystride));
+ frameBuffer.insert(insertViewName("B", views, view_id), Slice(Imf::FLOAT, (char *)rect[2], xstride, ystride));
+ if (is_alpha)
+ frameBuffer.insert(insertViewName("A", views, view_id), Slice(Imf::FLOAT, (char *)rect[3], xstride, ystride));
+ if (is_zbuf)
+ frameBuffer.insert(insertViewName("Z", views, view_id), Slice(Imf::FLOAT, (char *) (view_ibuf->zbuf_float + (height - 1) * width),
+ sizeof(float), sizeof(float) * -width));
+
+ if (is_multiview)
+ IMB_freeImBuf(view_ibuf);
+ }
file.setFrameBuffer(frameBuffer);
file.writePixels(height);
}
- catch (const std::exception &exc)
+ catch (const std::exception& exc)
{
printf("OpenEXR-save: ERROR: %s\n", exc.what());
-
- return (0);
+ return false;
}
- return (1);
- // printf("OpenEXR-save: Done.\n");
+ return true;
}
-
int imb_save_openexr(struct ImBuf *ibuf, const char *name, int flags)
{
if (flags & IB_mem) {
@@ -504,16 +573,48 @@ int imb_save_openexr(struct ImBuf *ibuf, const char *name, int flags)
}
if (ibuf->ftype & OPENEXR_HALF)
- return imb_save_openexr_half(ibuf, name, flags);
+ return (int) imb_save_openexr_half(ibuf, name, flags, 1, NULL, NULL);
+ else {
+ /* when no float rect, we save as half (16 bits is sufficient) */
+ if (ibuf->rect_float == NULL)
+ return (int) imb_save_openexr_half(ibuf, name, flags, 1, NULL, NULL);
+ else
+ return (int) imb_save_openexr_float(ibuf, name, flags, 1, NULL, NULL);
+ }
+}
+
+static bool imb_save_openexr_multiview(ImBuf *ibuf, const char *name, const int flags, const size_t totviews,
+ const char * (*getview)(void *base, const size_t view_id),
+ ImBuf * (*getbuffer)(void *base, const size_t view_id))
+{
+ if (flags & IB_mem) {
+ printf("OpenEXR-save: Create multiview EXR in memory CURRENTLY NOT SUPPORTED !\n");
+ imb_addencodedbufferImBuf(ibuf);
+ ibuf->encodedsize = 0;
+ return false;
+ }
+
+ if (ibuf->ftype & OPENEXR_HALF)
+ return imb_save_openexr_half(ibuf, name, flags, totviews, getview, getbuffer);
else {
/* when no float rect, we save as half (16 bits is sufficient) */
if (ibuf->rect_float == NULL)
- return imb_save_openexr_half(ibuf, name, flags);
+ return imb_save_openexr_half(ibuf, name, flags, totviews, getview, getbuffer);
else
- return imb_save_openexr_float(ibuf, name, flags);
+ return imb_save_openexr_float(ibuf, name, flags, totviews, getview, getbuffer);
}
}
+/* Save single-layer multiview OpenEXR
+ * If we have more multiview formats in the future, the function below could be incorporated
+ * in our ImBuf write functions, meanwhile this is an OpenEXR special case only */
+bool IMB_exr_multiview_save(ImBuf *ibuf, const char *name, const int flags, const size_t totviews,
+ const char * (*getview)(void *base, size_t view_id),
+ ImBuf * (*getbuffer)(void *base, const size_t view_id))
+{
+ return imb_save_openexr_multiview(ibuf, name, flags, totviews, getview, getbuffer);
+}
+
/* ********************* Nicer API, MultiLayer and with Tile file support ************************************ */
/* naming rules:
@@ -527,18 +628,22 @@ static ListBase exrhandles = {NULL, NULL};
typedef struct ExrHandle {
struct ExrHandle *next, *prev;
+ char name[FILE_MAX];
IFileStream *ifile_stream;
- InputFile *ifile;
+ MultiPartInputFile *ifile;
OFileStream *ofile_stream;
- TiledOutputFile *tofile;
+ MultiPartOutputFile *mpofile;
OutputFile *ofile;
int tilex, tiley;
int width, height;
int mipmap;
+ StringVector *multiView; /* it needs to be a pointer due to Windows release builds of EXR2.0 segfault when opening EXR bug */
+ int parts;
+
ListBase channels; /* flattened out, ExrChannel */
ListBase layers; /* hierarchical, pointing in end to ExrChannel */
} ExrHandle;
@@ -547,10 +652,12 @@ typedef struct ExrHandle {
typedef struct ExrChannel {
struct ExrChannel *next, *prev;
- char name[EXR_TOT_MAXNAME + 1]; /* full name of layer+pass */
+ char name[EXR_TOT_MAXNAME + 1]; /* full name with everything */
+ struct MultiViewChannelName *m; /* struct to store all multipart channel info */
int xstride, ystride; /* step to next pixel, to next scanline */
float *rect; /* first pointer to write in */
char chan_id; /* quick lookup of channel char */
+ int view_id; /* quick lookup of channel view */
} ExrChannel;
@@ -562,6 +669,10 @@ typedef struct ExrPass {
float *rect;
struct ExrChannel *chan[EXR_PASS_MAXCHAN];
char chan_id[EXR_PASS_MAXCHAN];
+
+ char internal_name[EXR_PASS_MAXNAME]; /* name with no view */
+ char view[EXR_VIEW_MAXNAME];
+ int view_id;
} ExrPass;
typedef struct ExrLayer {
@@ -575,39 +686,141 @@ typedef struct ExrLayer {
void *IMB_exr_get_handle(void)
{
ExrHandle *data = (ExrHandle *)MEM_callocN(sizeof(ExrHandle), "exr handle");
+ data->multiView = new StringVector();
+
BLI_addtail(&exrhandles, data);
return data;
}
+void *IMB_exr_get_handle_name(const char *name)
+{
+ ExrHandle *data = (ExrHandle *) BLI_rfindstring(&exrhandles, name, offsetof(ExrHandle, name));
+
+ if (data == NULL) {
+ data = (ExrHandle *)IMB_exr_get_handle();
+ BLI_strncpy(data->name, name, strlen(name) + 1);
+ }
+ return data;
+}
+
+/* multiview functions */
+} // extern "C"
+
+extern "C"
+{
+
+void IMB_exr_add_view(void *handle, const char *name)
+{
+ ExrHandle *data = (ExrHandle *)handle;
+ data->multiView->push_back(name);
+}
+
+static int imb_exr_get_multiView_id(StringVector& views, const std::string& name)
+{
+ int count = 0;
+ for (StringVector::const_iterator i = views.begin(); count < views.size(); ++i) {
+ if (name == *i)
+ return count;
+ else
+ count ++;
+ }
+
+ /* no views or wrong name */
+ return -1;
+}
+
+static void imb_exr_get_views(MultiPartInputFile& file, StringVector& views)
+{
+ if (exr_has_multipart_file(file) == false) {
+ if (exr_has_multiview(file)) {
+ StringVector sv = multiView(file.header(0));
+ for (StringVector::const_iterator i = sv.begin(); i != sv.end(); ++i)
+ views.push_back(*i);
+ }
+ }
+
+ else {
+ for (int p = 0; p < file.parts(); p++) {
+ std::string view = "";
+ if (file.header(p).hasView())
+ view = file.header(p).view();
+
+ if (imb_exr_get_multiView_id(views, view) == -1)
+ views.push_back(view);
+ }
+ }
+}
+
+/* Multilayer Blender files have the view name in all the passes (even the default view one) */
+static const char *imb_exr_insert_view_name(const char *passname, const char *viewname)
+{
+ if (viewname == NULL || viewname[0] == '\0')
+ return passname;
+
+ static char retstr[EXR_PASS_MAXNAME];
+ const char *end = passname + strlen(passname);
+ const char *token;
+
+ int len = IMB_exr_split_token(passname, end, &token);
+
+ if (len == 0)
+ BLI_snprintf(retstr, sizeof(retstr), "%s.%s", passname, viewname);
+ else
+ BLI_snprintf(retstr, sizeof(retstr), "%.*s%s.%s",
+ (int)(end - passname) - len, passname, viewname, token);
+
+ return retstr;
+}
+
/* adds flattened ExrChannels */
/* xstride, ystride and rect can be done in set_channel too, for tile writing */
-void IMB_exr_add_channel(void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect)
+/* passname does not include view */
+void IMB_exr_add_channel(void *handle, const char *layname, const char *passname, const char *viewname, int xstride, int ystride, float *rect)
{
ExrHandle *data = (ExrHandle *)handle;
ExrChannel *echan;
- echan = (ExrChannel *)MEM_callocN(sizeof(ExrChannel), "exr tile channel");
+ echan = (ExrChannel *)MEM_callocN(sizeof(ExrChannel), "exr channel");
+ echan->m = new MultiViewChannelName ();
- if (layname) {
- char lay[EXR_LAY_MAXNAME + 1], pass[EXR_PASS_MAXNAME + 1];
- BLI_strncpy(lay, layname, EXR_LAY_MAXNAME);
- BLI_strncpy(pass, passname, EXR_PASS_MAXNAME);
+ if (layname && layname[0] != '\0') {
+ echan->m->name = layname;
+ echan->m->name.append(".");
+ echan->m->name.append(passname);
+ }
+ else {
+ echan->m->name.assign(passname);
+ }
- BLI_snprintf(echan->name, sizeof(echan->name), "%s.%s", lay, pass);
+ echan->m->internal_name = echan->m->name;
+
+ echan->m->view.assign(viewname ? viewname : "");
+
+ /* quick look up */
+ echan->view_id = std::max(0, imb_exr_get_multiView_id(*data->multiView, echan->m->view));
+
+ /* name has to be unique, thus it's a combination of layer, pass, view, and channel */
+ if (layname && layname[0] != '\0') {
+ std::string raw_name = imb_exr_insert_view_name(echan->m->name.c_str(), echan->m->view.c_str());
+ BLI_strncpy(echan->name, raw_name.c_str(), sizeof(echan->name));
+ }
+ else if (data->multiView->size() > 1) {
+ std::string raw_name = insertViewName(echan->m->name, *data->multiView, echan->view_id);
+ BLI_strncpy(echan->name, raw_name.c_str(), sizeof(echan->name));
}
else {
- BLI_strncpy(echan->name, passname, EXR_TOT_MAXNAME - 1);
+ BLI_strncpy(echan->name, echan->m->name.c_str(), sizeof(echan->name));
}
echan->xstride = xstride;
echan->ystride = ystride;
echan->rect = rect;
- // printf("added channel %s\n", echan->name);
+ exr_printf("added channel %s\n", echan->name);
BLI_addtail(&data->channels, echan);
}
-/* only used for writing temp. render results (not image files) */
+/* used for output files (from RenderResult) (single and multilayer, single and multiview) */
int IMB_exr_begin_write(void *handle, const char *filename, int width, int height, int compress)
{
ExrHandle *data = (ExrHandle *)handle;
@@ -617,6 +830,8 @@ int IMB_exr_begin_write(void *handle, const char *filename, int width, int heigh
data->width = width;
data->height = height;
+ bool is_singlelayer, is_multilayer, is_multiview;
+
for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next)
header.channels().insert(echan->name, Channel(Imf::FLOAT));
@@ -624,7 +839,13 @@ int IMB_exr_begin_write(void *handle, const char *filename, int width, int heigh
// openexr_header_metadata(&header, ibuf); // no imbuf. cant write
/* header.lineOrder() = DECREASING_Y; this crashes in windows for file read! */
- header.insert("BlenderMultiChannel", StringAttribute("Blender V2.55.1 and newer"));
+ imb_exr_type_by_channels(header.channels(), *data->multiView, &is_singlelayer, &is_multilayer, &is_multiview);
+
+ if (is_multilayer)
+ header.insert("BlenderMultiChannel", StringAttribute("Blender V2.55.1 and newer"));
+
+ if (is_multiview)
+ addMultiView(header, *data->multiView);
/* avoid crash/abort when we don't have permission to write here */
/* manually create ofstream, so we can handle utf-8 filepaths on windows */
@@ -632,7 +853,7 @@ int IMB_exr_begin_write(void *handle, const char *filename, int width, int heigh
data->ofile_stream = new OFileStream(filename);
data->ofile = new OutputFile(*(data->ofile_stream), header);
}
- catch (const std::exception &exc) {
+ catch (const std::exception& exc) {
std::cerr << "IMB_exr_begin_write: ERROR: " << exc.what() << std::endl;
delete data->ofile;
@@ -645,10 +866,13 @@ int IMB_exr_begin_write(void *handle, const char *filename, int width, int heigh
return (data->ofile != NULL);
}
+/* only used for writing temp. render results (not image files)
+ * (FSA and Save Buffers) */
void IMB_exrtile_begin_write(void *handle, const char *filename, int mipmap, int width, int height, int tilex, int tiley)
{
ExrHandle *data = (ExrHandle *)handle;
Header header(width, height);
+ std::vector<Header> headers;
ExrChannel *echan;
data->tilex = tilex;
@@ -657,26 +881,47 @@ void IMB_exrtile_begin_write(void *handle, const char *filename, int mipmap, int
data->height = height;
data->mipmap = mipmap;
- for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next)
- header.channels().insert(echan->name, Channel(Imf::FLOAT));
-
header.setTileDescription(TileDescription(tilex, tiley, (mipmap) ? MIPMAP_LEVELS : ONE_LEVEL));
- header.lineOrder() = RANDOM_Y;
header.compression() = RLE_COMPRESSION;
+ header.setType(TILEDIMAGE);
header.insert("BlenderMultiChannel", StringAttribute("Blender V2.43"));
+ int numparts = data->multiView->size();
+
+ /* copy header from all parts of input to our header array
+ * those temporary files have one part per view */
+ for (int i = 0; i < numparts; i++) {
+ headers.push_back (header);
+ headers[headers.size() - 1].setView((*(data->multiView))[i]);
+ headers[headers.size() - 1].setName((*(data->multiView))[i]);
+ }
+
+ exr_printf("\nIMB_exrtile_begin_write\n");
+ exr_printf("%s %-6s %-22s \"%s\"\n", "p", "view", "name", "internal_name");
+ exr_printf("---------------------------------------------------------------\n");
+
+ /* assign channels */
+ for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
+
+ echan->m->internal_name = echan->m->name;
+ echan->m->part_number = echan->view_id;
+
+ headers[echan->view_id].channels().insert(echan->m->internal_name, Channel(Imf::FLOAT));
+ exr_printf("%d %-6s %-22s \"%s\"\n", echan->m->part_number, echan->m->view.c_str(), echan->m->name.c_str(), echan->m->internal_name.c_str());
+ }
+
/* avoid crash/abort when we don't have permission to write here */
/* manually create ofstream, so we can handle utf-8 filepaths on windows */
try {
data->ofile_stream = new OFileStream(filename);
- data->tofile = new TiledOutputFile(*(data->ofile_stream), header);
+ data->mpofile = new MultiPartOutputFile(*(data->ofile_stream), &headers[0], headers.size());
}
catch (const std::exception &) {
- delete data->tofile;
+ delete data->mpofile;
delete data->ofile_stream;
- data->tofile = NULL;
+ data->mpofile = NULL;
data->ofile_stream = NULL;
}
}
@@ -685,12 +930,13 @@ void IMB_exrtile_begin_write(void *handle, const char *filename, int mipmap, int
int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *height)
{
ExrHandle *data = (ExrHandle *)handle;
+ ExrChannel *echan;
if (BLI_exists(filename) && BLI_file_size(filename) > 32) { /* 32 is arbitrary, but zero length files crashes exr */
/* avoid crash/abort when we don't have permission to write here */
try {
data->ifile_stream = new IFileStream(filename);
- data->ifile = new InputFile(*(data->ifile_stream));
+ data->ifile = new MultiPartInputFile(*(data->ifile_stream));
}
catch (const std::exception &) {
delete data->ifile;
@@ -701,14 +947,24 @@ int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *heig
}
if (data->ifile) {
- Box2i dw = data->ifile->header().dataWindow();
+ Box2i dw = data->ifile->header(0).dataWindow();
data->width = *width = dw.max.x - dw.min.x + 1;
data->height = *height = dw.max.y - dw.min.y + 1;
- const ChannelList &channels = data->ifile->header().channels();
+ imb_exr_get_views(*data->ifile, *data->multiView);
+
+ std::vector<MultiViewChannelName> channels;
+ GetChannelsInMultiPartFile(*data->ifile, channels);
- for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
- IMB_exr_add_channel(data, NULL, i.name(), 0, 0, NULL);
+ for (size_t i = 0; i < channels.size(); i++) {
+ IMB_exr_add_channel(data, NULL, channels[i].name.c_str(), channels[i].view.c_str(), 0, 0, NULL);
+
+ echan = (ExrChannel *)data->channels.last;
+ echan->m->name = channels[i].name;
+ echan->m->view = channels[i].view;
+ echan->m->part_number = channels[i].part_number;
+ echan->m->internal_name = channels[i].internal_name;
+ }
return 1;
}
@@ -717,6 +973,7 @@ int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *heig
}
/* still clumsy name handling, layers/channels can be ordered as list in list later */
+/* passname here is the raw channel name without the layer */
void IMB_exr_set_channel(void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect)
{
ExrHandle *data = (ExrHandle *)handle;
@@ -741,37 +998,53 @@ void IMB_exr_set_channel(void *handle, const char *layname, const char *passname
echan->rect = rect;
}
else
- printf("IMB_exrtile_set_channel error %s\n", name);
-}
-
-void IMB_exrtile_clear_channels(void *handle)
-{
- ExrHandle *data = (ExrHandle *)handle;
- BLI_freelistN(&data->channels);
+ printf("IMB_exr_set_channel error %s\n", name);
}
-void IMB_exrtile_write_channels(void *handle, int partx, int party, int level)
+float *IMB_exr_channel_rect(void *handle, const char *layname, const char *passname, const char *viewname)
{
ExrHandle *data = (ExrHandle *)handle;
- FrameBuffer frameBuffer;
ExrChannel *echan;
+ char name[EXR_TOT_MAXNAME + 1];
- for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
- float *rect = echan->rect - echan->xstride * partx - echan->ystride * party;
+ if (layname) {
+ char lay[EXR_LAY_MAXNAME + 1], pass[EXR_PASS_MAXNAME + 1];
+ BLI_strncpy(lay, layname, EXR_LAY_MAXNAME);
+ BLI_strncpy(pass, passname, EXR_PASS_MAXNAME);
- frameBuffer.insert(echan->name, Slice(Imf::FLOAT, (char *)rect,
- echan->xstride * sizeof(float), echan->ystride * sizeof(float)));
+ BLI_snprintf(name, sizeof(name), "%s.%s", lay, pass);
}
+ else
+ BLI_strncpy(name, passname, EXR_TOT_MAXNAME - 1);
- data->tofile->setFrameBuffer(frameBuffer);
-
- try {
- // printf("write tile %d %d\n", partx/data->tilex, party/data->tiley);
- data->tofile->writeTile(partx / data->tilex, party / data->tiley, level);
+ /* name has to be unique, thus it's a combination of layer, pass, view, and channel */
+ if (layname && layname[0] != '\0') {
+ std::string raw_name = imb_exr_insert_view_name(name, viewname);
+ BLI_strncpy(name, raw_name.c_str(), sizeof(name));
}
- catch (const std::exception &exc) {
- std::cerr << "OpenEXR-writeTile: ERROR: " << exc.what() << std::endl;
+ else if (data->multiView->size() > 1) {
+ size_t view_id = std::max(0, imb_exr_get_multiView_id(*data->multiView, viewname));
+ std::string raw_name = insertViewName(name, *data->multiView, view_id);
+ BLI_strncpy(name, raw_name.c_str(), sizeof(name));
}
+
+ echan = (ExrChannel *)BLI_findstring(&data->channels, name, offsetof(ExrChannel, name));
+
+ if (echan)
+ return echan->rect;
+
+ return NULL;
+}
+
+void IMB_exr_clear_channels(void *handle)
+{
+ ExrHandle *data = (ExrHandle *)handle;
+ ExrChannel *chan;
+
+ for (chan = (ExrChannel *)data->channels.first; chan; chan = chan->next)
+ delete chan->m;
+
+ BLI_freelistN(&data->channels);
}
void IMB_exr_write_channels(void *handle)
@@ -793,7 +1066,7 @@ void IMB_exr_write_channels(void *handle)
try {
data->ofile->writePixels(data->height);
}
- catch (const std::exception &exc) {
+ catch (const std::exception& exc) {
std::cerr << "OpenEXR-writePixels: ERROR: " << exc.what() << std::endl;
}
}
@@ -802,49 +1075,171 @@ void IMB_exr_write_channels(void *handle)
}
}
-void IMB_exr_read_channels(void *handle)
+/* temporary function, used for FSA and Save Buffers */
+/* called once per tile * view */
+void IMB_exrtile_write_channels(void *handle, int partx, int party, int level, const char *viewname)
{
ExrHandle *data = (ExrHandle *)handle;
FrameBuffer frameBuffer;
ExrChannel *echan;
+ std::string view(viewname);
+ const size_t view_id = imb_exr_get_multiView_id(*data->multiView, view);
+
+ exr_printf("\nIMB_exrtile_write_channels(view: %s)\n", viewname);
+ exr_printf("%s %-6s %-22s \"%s\"\n", "p", "view", "name", "internal_name");
+ exr_printf("---------------------------------------------------------------------\n");
+
+ for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
+
+ /* eventually we can make the parts' channels to include
+ only the current view TODO */
+ if (strcmp(viewname, echan->m->view.c_str()) != 0)
+ continue;
+
+ exr_printf("%d %-6s %-22s \"%s\"\n",
+ echan->m->part_number,
+ echan->m->view.c_str(),
+ echan->m->name.c_str(),
+ echan->m->internal_name.c_str()
+ );
+
+ float *rect = echan->rect - echan->xstride * partx - echan->ystride * party;
+ frameBuffer.insert(echan->m->internal_name,
+ Slice(Imf::FLOAT,
+ (char *)rect,
+ echan->xstride * sizeof(float),
+ echan->ystride * sizeof(float)
+ )
+ );
+ }
+
+ TiledOutputPart out (*data->mpofile, view_id);
+ out.setFrameBuffer(frameBuffer);
+
+ try {
+ // printf("write tile %d %d\n", partx/data->tilex, party/data->tiley);
+ out.writeTile(partx / data->tilex, party / data->tiley, level);
+ }
+ catch (const std::exception& exc) {
+ std::cerr << "OpenEXR-writeTile: ERROR: " << exc.what() << std::endl;
+ }
+}
+
+/* called only when handle has all views */
+void IMB_exrmultiview_write_channels(void *handle, const char *viewname)
+{
+ ExrHandle *data = (ExrHandle *)handle;
+ const size_t view_id = viewname ? imb_exr_get_multiView_id(*data->multiView, viewname) : -1;
+ int numparts = (view_id == -1 ? data->parts : view_id + 1);
+ std::vector <FrameBuffer> frameBuffers(numparts);
+ std::vector <OutputPart> outputParts;
+ ExrChannel *echan;
+ int i, part;
+
+ if (data->channels.first == NULL)
+ return;
+
+ exr_printf("\nIMB_exrmultiview_write_channels()\n");
+
+ for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
+ if (view_id != -1 && echan->view_id != view_id)
+ continue;
+
+ part = (view_id == -1 ? echan->m->part_number : echan->view_id);
+
+ /* last scanline, stride negative */
+ float *rect = echan->rect + echan->xstride * (data->height - 1) * data->width;
+ frameBuffers[part].insert(echan->m->internal_name,
+ Slice(Imf::FLOAT,
+ (char *)rect,
+ echan->xstride * sizeof(float),
+ -echan->ystride * sizeof(float))
+ );
+ }
+
+ for (i = 0; i < numparts; i++) {
+ OutputPart out(*data->mpofile, i);
+ out.setFrameBuffer(frameBuffers[i]);
+ outputParts.push_back(out);
+ }
+
+ try {
+ for (i = 0; i < numparts; i++) {
+ if (view_id != -1 && i != view_id)
+ continue;
+
+ outputParts[i].writePixels(data->height);
+ }
+ }
+ catch (const std::exception& exc) {
+ std::cerr << "OpenEXR-write Multi Part: ERROR: " << exc.what() << std::endl;
+ }
+}
+
+void IMB_exr_read_channels(void *handle)
+{
+ ExrHandle *data = (ExrHandle *)handle;
+ ExrChannel *echan;
+ int numparts = data->ifile->parts();
+ std::vector<FrameBuffer> frameBuffers(numparts);
+ std::vector<InputPart> inputParts;
/* check if exr was saved with previous versions of blender which flipped images */
- const StringAttribute *ta = data->ifile->header().findTypedAttribute <StringAttribute> ("BlenderMultiChannel");
+ const StringAttribute *ta = data->ifile->header(0).findTypedAttribute <StringAttribute> ("BlenderMultiChannel");
short flip = (ta && STREQLEN(ta->value().c_str(), "Blender V2.43", 13)); /* 'previous multilayer attribute, flipped */
+ exr_printf("\nIMB_exr_read_channels\n%s %-6s %-22s \"%s\"\n---------------------------------------------------------------------\n", "p", "view", "name", "internal_name");
+
for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
+ exr_printf("%d %-6s %-22s \"%s\"\n", echan->m->part_number, echan->m->view.c_str(), echan->m->name.c_str(), echan->m->internal_name.c_str());
if (echan->rect) {
if (flip)
- frameBuffer.insert(echan->name, Slice(Imf::FLOAT, (char *)echan->rect,
+ frameBuffers[echan->m->part_number].insert(echan->m->internal_name, Slice(Imf::FLOAT, (char *)echan->rect,
echan->xstride * sizeof(float), echan->ystride * sizeof(float)));
else
- frameBuffer.insert(echan->name, Slice(Imf::FLOAT, (char *)(echan->rect + echan->xstride * (data->height - 1) * data->width),
+ frameBuffers[echan->m->part_number].insert(echan->m->internal_name, Slice(Imf::FLOAT, (char *)(echan->rect + echan->xstride * (data->height - 1) * data->width),
echan->xstride * sizeof(float), -echan->ystride * sizeof(float)));
}
else
- printf("warning, channel with no rect set %s\n", echan->name);
+ printf("warning, channel with no rect set %s\n", echan->m->internal_name.c_str());
}
- data->ifile->setFrameBuffer(frameBuffer);
+ for (int i = 0; i < numparts; i++) {
+ InputPart in (*data->ifile, i);
+ in.setFrameBuffer(frameBuffers[i]);
+ inputParts.push_back(in);
+ }
try {
- data->ifile->readPixels(0, data->height - 1);
+ for (int i = 0; i < numparts; i++) {
+ Header header = inputParts[i].header();
+ exr_printf("readPixels:readPixels[%d]: min.y: %d, max.y: %d\n", i, header.dataWindow().min.y, header.dataWindow().max.y);
+ inputParts[i].readPixels(header.dataWindow().min.y, header.dataWindow().max.y);
+ inputParts[i].readPixels(0, data->height - 1);
+ }
}
- catch (const std::exception &exc) {
+ catch (const std::exception& exc) {
std::cerr << "OpenEXR-readPixels: ERROR: " << exc.what() << std::endl;
}
}
void IMB_exr_multilayer_convert(void *handle, void *base,
+ void * (*addview)(void *base, const char *str),
void * (*addlayer)(void *base, const char *str),
void (*addpass)(void *base, void *lay, const char *str,
- float *rect, int totchan, const char *chan_id))
+ float *rect, int totchan, const char *chan_id,
+ const char *view))
{
ExrHandle *data = (ExrHandle *)handle;
ExrLayer *lay;
ExrPass *pass;
+ /* add views to RenderResult */
+ for (StringVector::const_iterator i = data->multiView->begin(); i != data->multiView->end(); ++i) {
+ addview(base, (*i).c_str());
+ }
+
if (BLI_listbase_is_empty(&data->layers)) {
printf("cannot convert multilayer, no layers in handle\n");
return;
@@ -854,32 +1249,98 @@ void IMB_exr_multilayer_convert(void *handle, void *base,
void *laybase = addlayer(base, lay->name);
if (laybase) {
for (pass = (ExrPass *)lay->passes.first; pass; pass = pass->next) {
- addpass(base, laybase, pass->name, pass->rect, pass->totchan, pass->chan_id);
+ addpass(base, laybase, pass->internal_name, pass->rect, pass->totchan, pass->chan_id, pass->view);
pass->rect = NULL;
}
}
}
}
+void IMB_exr_multiview_convert(void *handle, void *base,
+ void (*addview)(void *base, const char *str),
+ void (*addbuffer)(void *base, const char *str, ImBuf *ibuf, const int frame),
+ const int frame)
+{
+ ExrHandle *data = (ExrHandle *)handle;
+ MultiPartInputFile *file = data->ifile;
+ ExrLayer *lay;
+ ExrPass *pass;
+ ImBuf *ibuf = NULL;
+ const int is_alpha = exr_has_alpha(*file);
+ Box2i dw = file->header(0).dataWindow();
+ const size_t width = dw.max.x - dw.min.x + 1;
+ const size_t height = dw.max.y - dw.min.y + 1;
+ const bool is_depth = exr_has_zbuffer(*file);
+
+ /* add views to RenderResult */
+ for (StringVector::const_iterator i = data->multiView->begin(); i != data->multiView->end(); ++i) {
+ addview(base, (*i).c_str());
+ }
+
+ if (BLI_listbase_is_empty(&data->layers)) {
+ printf("cannot convert multiviews, no views in handle\n");
+ return;
+ }
+
+ /* there is one float/pass per layer (layer here is a view) */
+ BLI_assert(BLI_listbase_count_ex(&data->layers, 2) == 1);
+ lay = (ExrLayer *)data->layers.first;
+ for (pass = (ExrPass *)lay->passes.first; pass; pass = pass->next) {
+ if (STREQ(pass->chan_id, "RGB") || STREQ(pass->chan_id, "RGBA")) {
+ ibuf = IMB_allocImBuf(width, height, is_alpha ? 32 : 24, IB_rectfloat);
+
+ if (!ibuf) {
+ printf("error creating multiview buffer\n");
+ return;
+ }
+
+ IMB_buffer_float_from_float(
+ ibuf->rect_float, pass->rect, pass->totchan,
+ IB_PROFILE_LINEAR_RGB, IB_PROFILE_LINEAR_RGB, false,
+ ibuf->x, ibuf->y, ibuf->x, ibuf->x);
+
+ if (hasXDensity(file->header(0))) {
+ ibuf->ppm[0] = xDensity(file->header(0)) * 39.3700787f;
+ ibuf->ppm[1] = ibuf->ppm[0] * (double)file->header(0).pixelAspectRatio();
+ }
+
+ if (is_depth) {
+ ExrPass *zpass;
+ for (zpass = (ExrPass *)lay->passes.first; zpass; zpass = zpass->next) {
+ if (STREQ(zpass->chan_id, "Z") && STREQ(zpass->view, pass->view)) {
+ addzbuffloatImBuf(ibuf);
+ memcpy(ibuf->zbuf_float, zpass->rect, sizeof(float) * ibuf->x * ibuf->y);
+ }
+ }
+ }
+
+ addbuffer(base, pass->view, ibuf, frame);
+ }
+ }
+}
void IMB_exr_close(void *handle)
{
ExrHandle *data = (ExrHandle *)handle;
ExrLayer *lay;
ExrPass *pass;
+ ExrChannel *chan;
delete data->ifile;
delete data->ifile_stream;
delete data->ofile;
- delete data->tofile;
+ delete data->mpofile;
delete data->ofile_stream;
data->ifile = NULL;
data->ifile_stream = NULL;
data->ofile = NULL;
- data->tofile = NULL;
+ data->mpofile = NULL;
data->ofile_stream = NULL;
+ for (chan = (ExrChannel *)data->channels.first; chan; chan = chan->next) {
+ delete chan->m;
+ }
BLI_freelistN(&data->channels);
for (lay = (ExrLayer *)data->layers.first; lay; lay = lay->next) {
@@ -897,7 +1358,7 @@ void IMB_exr_close(void *handle)
/* ********* */
/* get a substring from the end of the name, separated by '.' */
-static int imb_exr_split_token(const char *str, const char *end, const char **token)
+int IMB_exr_split_token(const char *str, const char *end, const char **token)
{
ptrdiff_t maxlen = end - str;
int len = 0;
@@ -911,7 +1372,7 @@ static int imb_exr_split_token(const char *str, const char *end, const char **to
static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *passname)
{
- const char *name = echan->name;
+ const char *name = echan->m->name.c_str();
const char *end = name + strlen(name);
const char *token;
char tokenbuf[EXR_TOT_MAXNAME];
@@ -933,7 +1394,7 @@ static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *pa
}
/* last token is single character channel identifier */
- len = imb_exr_split_token(name, end, &token);
+ len = IMB_exr_split_token(name, end, &token);
if (len == 0) {
printf("multilayer read: bad channel name: %s\n", name);
return 0;
@@ -971,7 +1432,7 @@ static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *pa
end -= len + 1; /* +1 to skip '.' separator */
/* second token is pass name */
- len = imb_exr_split_token(name, end, &token);
+ len = IMB_exr_split_token(name, end, &token);
if (len == 0) {
printf("multilayer read: bad channel name: %s\n", name);
return 0;
@@ -1020,7 +1481,7 @@ static ExrPass *imb_exr_get_pass(ListBase *lb, char *passname)
}
/* creates channels, makes a hierarchy and assigns memory to channels */
-static ExrHandle *imb_exr_begin_read_mem(InputFile *file, int width, int height)
+static ExrHandle *imb_exr_begin_read_mem(MultiPartInputFile& file, int width, int height)
{
ExrLayer *lay;
ExrPass *pass;
@@ -1029,30 +1490,57 @@ static ExrHandle *imb_exr_begin_read_mem(InputFile *file, int width, int height)
int a;
char layname[EXR_TOT_MAXNAME], passname[EXR_TOT_MAXNAME];
- data->ifile = file;
+ data->ifile = &file;
data->width = width;
data->height = height;
- const ChannelList &channels = data->ifile->header().channels();
+ std::vector<MultiViewChannelName> channels;
+ GetChannelsInMultiPartFile(*data->ifile, channels);
- for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i)
- IMB_exr_add_channel(data, NULL, i.name(), 0, 0, NULL);
+ data->multiView = new StringVector();
+ imb_exr_get_views(*data->ifile, *data->multiView);
+
+ for (size_t i = 0; i < channels.size(); i++) {
+ IMB_exr_add_channel(data, NULL, channels[i].name.c_str(), channels[i].view.c_str(), 0, 0, NULL);
+
+ echan = (ExrChannel *)data->channels.last;
+ echan->m->name = channels[i].name;
+ echan->m->view = channels[i].view;
+ echan->m->part_number = channels[i].part_number;
+ echan->m->internal_name = channels[i].internal_name;
+ }
/* now try to sort out how to assign memory to the channels */
/* first build hierarchical layer list */
for (echan = (ExrChannel *)data->channels.first; echan; echan = echan->next) {
- if (imb_exr_split_channel_name(echan, layname, passname) ) {
+ if (imb_exr_split_channel_name(echan, layname, passname)) {
+
+ const char *view = echan->m->view.c_str();
+ char internal_name[EXR_PASS_MAXNAME];
+
+ BLI_strncpy(internal_name, passname, EXR_PASS_MAXNAME);
+
+ if (view[0] != '\0') {
+ char tmp_pass[EXR_PASS_MAXNAME];
+ BLI_snprintf(tmp_pass, sizeof(tmp_pass), "%s.%s", passname, view);
+ BLI_strncpy(passname, tmp_pass, sizeof(passname));
+ }
+
ExrLayer *lay = imb_exr_get_layer(&data->layers, layname);
ExrPass *pass = imb_exr_get_pass(&lay->passes, passname);
pass->chan[pass->totchan] = echan;
pass->totchan++;
+ pass->view_id = echan->view_id;
+ BLI_strncpy(pass->view, view, sizeof(pass->view));
+ BLI_strncpy(pass->internal_name, internal_name, EXR_PASS_MAXNAME);
+
if (pass->totchan >= EXR_PASS_MAXCHAN)
break;
}
}
if (echan) {
- printf("error, too many channels in one pass: %s\n", echan->name);
+ printf("error, too many channels in one pass: %s\n", echan->m->name.c_str());
IMB_exr_close(data);
return NULL;
}
@@ -1122,20 +1610,52 @@ static ExrHandle *imb_exr_begin_read_mem(InputFile *file, int width, int height)
/* ********************************************************* */
/* debug only */
-static void exr_print_filecontents(InputFile *file)
+static void exr_printf(const char *fmt, ...)
{
- const ChannelList &channels = file->header().channels();
+#if 0
+ char output[1024];
+ va_list args;
+ va_start(args, fmt);
+ std::vsprintf(output, fmt, args);
+ va_end(args);
+ printf("%s", output);
+#else
+ (void)fmt;
+#endif
+}
- for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) {
- const Channel &channel = i.channel();
- printf("OpenEXR-load: Found channel %s of type %d\n", i.name(), channel.type);
+static void exr_print_filecontents(MultiPartInputFile& file)
+{
+ int numparts = file.parts();
+ if (numparts == 1 && hasMultiView(file.header(0))) {
+ const StringVector views = multiView(file.header(0));
+ printf("OpenEXR-load: MultiView file\n");
+ printf("OpenEXR-load: Default view: %s\n", defaultViewName(views).c_str());
+ for (StringVector::const_iterator i = views.begin(); i != views.end(); ++i) {
+ printf("OpenEXR-load: Found view %s\n", (*i).c_str());
+ }
+ }
+ else if (numparts > 1) {
+ printf("OpenEXR-load: MultiPart file\n");
+ for (int i = 0; i < numparts; i++) {
+ if (file.header(i).hasView())
+ printf("OpenEXR-load: Part %d: view = \"%s\"\n", i, file.header(i).view().c_str());
+ }
+ }
+
+ for (int j = 0; j < numparts; j++) {
+ const ChannelList& channels = file.header(j).channels();
+ for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) {
+ const Channel& channel = i.channel();
+ printf("OpenEXR-load: Found channel %s of type %d\n", i.name(), channel.type);
+ }
}
}
/* for non-multilayer, map R G B A channel names to something that's in this file */
-static const char *exr_rgba_channelname(InputFile *file, const char *chan)
+static const char *exr_rgba_channelname(MultiPartInputFile& file, const char *chan)
{
- const ChannelList &channels = file->header().channels();
+ const ChannelList& channels = file.header(0).channels();
for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); ++i) {
/* const Channel &channel = i.channel(); */ /* Not used yet */
@@ -1150,48 +1670,48 @@ static const char *exr_rgba_channelname(InputFile *file, const char *chan)
return chan;
}
-static bool exr_has_rgb(InputFile *file)
+static bool exr_has_rgb(MultiPartInputFile& file)
{
- return file->header().channels().findChannel("R") != NULL &&
- file->header().channels().findChannel("G") != NULL &&
- file->header().channels().findChannel("B") != NULL;
+ return file.header(0).channels().findChannel("R") != NULL &&
+ file.header(0).channels().findChannel("G") != NULL &&
+ file.header(0).channels().findChannel("B") != NULL;
}
-static bool exr_has_luma(InputFile *file)
+static bool exr_has_luma(MultiPartInputFile& file)
{
/* Y channel is the luma and should always present fir luma space images,
* optionally it could be also channels for chromas called BY and RY.
*/
- return file->header().channels().findChannel("Y") != NULL;
+ return file.header(0).channels().findChannel("Y") != NULL;
}
-static bool exr_has_chroma(InputFile *file)
+static bool exr_has_chroma(MultiPartInputFile& file)
{
- return file->header().channels().findChannel("BY") != NULL &&
- file->header().channels().findChannel("RY") != NULL;
+ return file.header(0).channels().findChannel("BY") != NULL &&
+ file.header(0).channels().findChannel("RY") != NULL;
}
-static int exr_has_zbuffer(InputFile *file)
+static int exr_has_zbuffer(MultiPartInputFile& file)
{
- return !(file->header().channels().findChannel("Z") == NULL);
+ return !(file.header(0).channels().findChannel("Z") == NULL);
}
-static int exr_has_alpha(InputFile *file)
+static int exr_has_alpha(MultiPartInputFile& file)
{
- return !(file->header().channels().findChannel("A") == NULL);
+ return !(file.header(0).channels().findChannel("A") == NULL);
}
-static bool exr_is_multilayer(InputFile *file)
+static bool imb_exr_is_multilayer_file(MultiPartInputFile& file)
{
- const StringAttribute *comments = file->header().findTypedAttribute<StringAttribute>("BlenderMultiChannel");
- const ChannelList &channels = file->header().channels();
+ const StringAttribute *comments = file.header(0).findTypedAttribute<StringAttribute>("BlenderMultiChannel");
+ const ChannelList& channels = file.header(0).channels();
std::set <std::string> layerNames;
/* will not include empty layer names */
channels.layers(layerNames);
if (comments || layerNames.size() > 1)
- return 1;
+ return true;
if (layerNames.size()) {
/* if layerNames is not empty, it means at least one layer is non-empty,
@@ -1206,17 +1726,122 @@ static bool exr_is_multilayer(InputFile *file)
size_t pos = layerName.rfind ('.');
if (pos == std::string::npos)
- return 1;
+ return true;
}
}
- return 0;
+ return false;
+}
+
+static void imb_exr_type_by_channels(ChannelList& channels, StringVector& views,
+ bool *r_singlelayer, bool *r_multilayer, bool *r_multiview)
+{
+ std::set <std::string> layerNames;
+
+ *r_singlelayer = true;
+ *r_multilayer = *r_multiview = false;
+
+ /* will not include empty layer names */
+ channels.layers(layerNames);
+
+ if (views.size() && views[0] != "")
+ *r_multiview = true;
+
+ if (layerNames.size()) {
+ /* if layerNames is not empty, it means at least one layer is non-empty,
+ * but it also could be layers without names in the file and such case
+ * shall be considered a multilayer exr
+ *
+ * that's what we do here: test whether there're empty layer names together
+ * with non-empty ones in the file
+ */
+ for (ChannelList::ConstIterator i = channels.begin(); i != channels.end(); i++)
+ for (std::set<string>::iterator i = layerNames.begin(); i != layerNames.end(); i++)
+ /* see if any layername differs from a viewname */
+ if (imb_exr_get_multiView_id(views, *i) == -1) {
+ std::string layerName = *i;
+ size_t pos = layerName.rfind ('.');
+
+ if (pos != std::string::npos) {
+ *r_multilayer = true;
+ *r_singlelayer = false;
+ return;
+ }
+ }
+ }
+ else {
+ *r_singlelayer = true;
+ *r_multilayer = false;
+ *r_multiview = false;
+ }
+
+ BLI_assert(r_singlelayer != r_multilayer);
+}
+
+bool IMB_exr_has_singlelayer_multiview(void *handle)
+{
+ ExrHandle *data = (ExrHandle *)handle;
+ MultiPartInputFile *file = data->ifile;
+ std::set <std::string> layerNames;
+ const ChannelList& channels = file->header(0).channels();
+ const StringAttribute *comments;
+
+ if (exr_has_multiview(*file) == false)
+ return false;
+
+ comments = file->header(0).findTypedAttribute<StringAttribute>("BlenderMultiChannel");
+
+ if (comments)
+ return false;
+
+ /* will not include empty layer names */
+ channels.layers(layerNames);
+
+ /* returns false if any layer differs from views list */
+ if (layerNames.size())
+ for (std::set<string>::iterator i = layerNames.begin(); i != layerNames.end(); i++)
+ if (imb_exr_get_multiView_id(*data->multiView, *i) == -1)
+ return false;
+
+ return true;
+}
+
+bool IMB_exr_has_multilayer(void *handle)
+{
+ ExrHandle *data = (ExrHandle *)handle;
+ return imb_exr_is_multilayer_file(*data->ifile);
+}
+
+static bool exr_has_multiview(MultiPartInputFile& file)
+{
+ return hasMultiView(file.header(0));
+}
+
+static bool exr_has_multipart_file(MultiPartInputFile& file)
+{
+ return file.parts() > 1;
+}
+
+/* it returns true if the file is multilayer or multiview */
+static bool imb_exr_is_multi(MultiPartInputFile& file)
+{
+ /* multipart files are treated as multilayer in blender - even if they are single layer openexr with multiview */
+ if (exr_has_multipart_file(file))
+ return true;
+
+ if (exr_has_multiview(file))
+ return true;
+
+ if (imb_exr_is_multilayer_file(file))
+ return true;
+
+ return false;
}
struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE])
{
struct ImBuf *ibuf = NULL;
- InputFile *file = NULL;
+ MultiPartInputFile *file = NULL;
if (imb_is_a_openexr(mem) == 0) return(NULL);
@@ -1226,9 +1851,9 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char
{
Mem_IStream *membuf = new Mem_IStream(mem, size);
bool is_multi;
- file = new InputFile(*membuf);
+ file = new MultiPartInputFile(*membuf);
- Box2i dw = file->header().dataWindow();
+ Box2i dw = file->header(0).dataWindow();
const int width = dw.max.x - dw.min.x + 1;
const int height = dw.max.y - dw.min.y + 1;
@@ -1236,38 +1861,38 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char
// dw.min.x, dw.min.y, dw.max.x, dw.max.y);
if (0) // debug
- exr_print_filecontents(file);
+ exr_print_filecontents(*file);
- is_multi = exr_is_multilayer(file);
+ is_multi = imb_exr_is_multi(*file);
/* do not make an ibuf when */
if (is_multi && !(flags & IB_test) && !(flags & IB_multilayer)) {
printf("Error: can't process EXR multilayer file\n");
}
else {
- const int is_alpha = exr_has_alpha(file);
+ const int is_alpha = exr_has_alpha(*file);
ibuf = IMB_allocImBuf(width, height, is_alpha ? 32 : 24, 0);
- if (hasXDensity(file->header())) {
- ibuf->ppm[0] = xDensity(file->header()) * 39.3700787f;
- ibuf->ppm[1] = ibuf->ppm[0] * (double)file->header().pixelAspectRatio();
+ if (hasXDensity(file->header(0))) {
+ ibuf->ppm[0] = xDensity(file->header(0)) * 39.3700787f;
+ ibuf->ppm[1] = ibuf->ppm[0] * (double)file->header(0).pixelAspectRatio();
}
ibuf->ftype = OPENEXR;
if (!(flags & IB_test)) {
- if (is_multi) { /* only enters with IB_multilayer flag set */
+ if (is_multi && ((flags & IB_thumbnail) == 0)) { /* only enters with IB_multilayer flag set */
/* constructs channels for reading, allocates memory in channels */
- ExrHandle *handle = imb_exr_begin_read_mem(file, width, height);
+ ExrHandle *handle = imb_exr_begin_read_mem(*file, width, height);
if (handle) {
IMB_exr_read_channels(handle);
ibuf->userdata = handle; /* potential danger, the caller has to check for this! */
}
}
else {
- const bool has_rgb = exr_has_rgb(file);
- const bool has_luma = exr_has_luma(file);
+ const bool has_rgb = exr_has_rgb(*file);
+ const bool has_luma = exr_has_luma(*file);
FrameBuffer frameBuffer;
float *first;
int xstride = sizeof(float) * 4;
@@ -1281,27 +1906,27 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char
first += 4 * (height - 1) * width;
if (has_rgb) {
- frameBuffer.insert(exr_rgba_channelname(file, "R"),
+ frameBuffer.insert(exr_rgba_channelname(*file, "R"),
Slice(Imf::FLOAT, (char *) first, xstride, ystride));
- frameBuffer.insert(exr_rgba_channelname(file, "G"),
+ frameBuffer.insert(exr_rgba_channelname(*file, "G"),
Slice(Imf::FLOAT, (char *) (first + 1), xstride, ystride));
- frameBuffer.insert(exr_rgba_channelname(file, "B"),
+ frameBuffer.insert(exr_rgba_channelname(*file, "B"),
Slice(Imf::FLOAT, (char *) (first + 2), xstride, ystride));
}
else if (has_luma) {
- frameBuffer.insert(exr_rgba_channelname(file, "Y"),
+ frameBuffer.insert(exr_rgba_channelname(*file, "Y"),
Slice(Imf::FLOAT, (char *) first, xstride, ystride));
- frameBuffer.insert(exr_rgba_channelname(file, "BY"),
+ frameBuffer.insert(exr_rgba_channelname(*file, "BY"),
Slice(Imf::FLOAT, (char *) (first + 1), xstride, ystride, 1, 1, 0.5f));
- frameBuffer.insert(exr_rgba_channelname(file, "RY"),
+ frameBuffer.insert(exr_rgba_channelname(*file, "RY"),
Slice(Imf::FLOAT, (char *) (first + 2), xstride, ystride, 1, 1, 0.5f));
}
/* 1.0 is fill value, this still needs to be assigned even when (is_alpha == 0) */
- frameBuffer.insert(exr_rgba_channelname(file, "A"),
+ frameBuffer.insert(exr_rgba_channelname(*file, "A"),
Slice(Imf::FLOAT, (char *) (first + 3), xstride, ystride, 1, 1, 1.0f));
- if (exr_has_zbuffer(file)) {
+ if (exr_has_zbuffer(*file)) {
float *firstz;
addzbuffloatImBuf(ibuf);
@@ -1310,8 +1935,9 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char
frameBuffer.insert("Z", Slice(Imf::FLOAT, (char *)firstz, sizeof(float), -width * sizeof(float)));
}
- file->setFrameBuffer(frameBuffer);
- file->readPixels(dw.min.y, dw.max.y);
+ InputPart in (*file, 0);
+ in.setFrameBuffer(frameBuffer);
+ in.readPixels(dw.min.y, dw.max.y);
// XXX, ImBuf has no nice way to deal with this.
// ideally IM_rect would be used when the caller wants a rect BUT
@@ -1325,7 +1951,7 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char
if (!has_rgb && has_luma) {
size_t a;
- if (exr_has_chroma(file)) {
+ if (exr_has_chroma(*file)) {
for (a = 0; a < (size_t) ibuf->x * ibuf->y; ++a) {
float *color = ibuf->rect_float + a * 4;
ycc_to_rgb(color[0] * 255.0f, color[1] * 255.0f, color[2] * 255.0f,
@@ -1351,7 +1977,7 @@ struct ImBuf *imb_load_openexr(unsigned char *mem, size_t size, int flags, char
}
return(ibuf);
}
- catch (const std::exception &exc)
+ catch (const std::exception& exc)
{
std::cerr << exc.what() << std::endl;
if (ibuf) IMB_freeImBuf(ibuf);
diff --git a/source/blender/imbuf/intern/openexr/openexr_multi.h b/source/blender/imbuf/intern/openexr/openexr_multi.h
index 376d2401b1c..45ab0ed37c7 100644
--- a/source/blender/imbuf/intern/openexr/openexr_multi.h
+++ b/source/blender/imbuf/intern/openexr/openexr_multi.h
@@ -39,8 +39,9 @@
/* This api also supports max 8 channels per pass now. easy to fix! */
#define EXR_LAY_MAXNAME 64
#define EXR_PASS_MAXNAME 64
+#define EXR_VIEW_MAXNAME 64
#define EXR_TOT_MAXNAME 64
-#define EXR_PASS_MAXCHAN 8
+#define EXR_PASS_MAXCHAN 24
#ifdef __cplusplus
@@ -48,25 +49,49 @@ extern "C" {
#endif
void *IMB_exr_get_handle(void);
-void IMB_exr_add_channel(void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect);
+void *IMB_exr_get_handle_name(const char *name);
+void IMB_exr_add_channel(void *handle, const char *layname, const char *passname, const char *view, int xstride, int ystride, float *rect);
int IMB_exr_begin_read(void *handle, const char *filename, int *width, int *height);
int IMB_exr_begin_write(void *handle, const char *filename, int width, int height, int compress);
void IMB_exrtile_begin_write(void *handle, const char *filename, int mipmap, int width, int height, int tilex, int tiley);
void IMB_exr_set_channel(void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect);
+float *IMB_exr_channel_rect(void *handle, const char *layname, const char *passname, const char *view);
void IMB_exr_read_channels(void *handle);
void IMB_exr_write_channels(void *handle);
-void IMB_exrtile_write_channels(void *handle, int partx, int party, int level);
-void IMB_exrtile_clear_channels(void *handle);
+void IMB_exrtile_write_channels(void *handle, int partx, int party, int level, const char *viewname);
+void IMB_exrmultiview_write_channels(void *handle, const char *viewname);
+void IMB_exr_clear_channels(void *handle);
-void IMB_exr_multilayer_convert(void *handle, void *base,
- void * (*addlayer)(void *base, const char *str),
- void (*addpass)(void *base, void *lay, const char *str, float *rect, int totchan, const char *chan_id));
+void IMB_exr_multilayer_convert(
+ void *handle, void *base,
+ void * (*addview)(void *base, const char *str),
+ void * (*addlayer)(void *base, const char *str),
+ void (*addpass)(void *base, void *lay, const char *str, float *rect, int totchan,
+ const char *chan_id, const char *view));
+
+void IMB_exr_multiview_convert(
+ void *handle, void *base,
+ void (*addview)(void *base, const char *str),
+ void (*addbuffer)(void *base, const char *str, struct ImBuf *ibuf, const int frame),
+ const int frame);
+
+bool IMB_exr_multiview_save(
+ struct ImBuf *ibuf, const char *name, const int flags, const size_t totviews,
+ const char * (*getview)(void *base, size_t view_id),
+ struct ImBuf * (*getbuffer)(void *base, const size_t view_id));
void IMB_exr_close(void *handle);
+void IMB_exr_add_view(void *handle, const char *name);
+
+int IMB_exr_split_token(const char *str, const char *end, const char **token);
+
+bool IMB_exr_has_multilayer(void *handle);
+bool IMB_exr_has_singlelayer_multiview(void *handle);
+
#ifdef __cplusplus
} // extern "C"
#endif
diff --git a/source/blender/imbuf/intern/openexr/openexr_stub.cpp b/source/blender/imbuf/intern/openexr/openexr_stub.cpp
index 21fa878c08a..c326efd2427 100644
--- a/source/blender/imbuf/intern/openexr/openexr_stub.cpp
+++ b/source/blender/imbuf/intern/openexr/openexr_stub.cpp
@@ -31,27 +31,55 @@
#include "openexr_api.h"
#include "openexr_multi.h"
-
+#include "BLI_utildefines.h" /* UNUSED_VARS */
void *IMB_exr_get_handle (void) {return NULL;}
-void IMB_exr_add_channel (void *handle, const char *layname, const char *channame, int xstride, int ystride, float *rect) { (void)handle; (void)layname; (void)channame; (void)xstride; (void)ystride; (void)rect; }
+void *IMB_exr_get_handle_name (const char *name) {(void)name; return NULL;}
+void IMB_exr_add_channel (void *handle, const char *layname, const char *passname, const char *view, int xstride, int ystride, float *rect) { (void)handle; (void)layname; (void)passname; (void)xstride; (void)ystride; (void)rect; }
int IMB_exr_begin_read (void *handle, const char *filename, int *width, int *height) { (void)handle; (void)filename; (void)width; (void)height; return 0;}
int IMB_exr_begin_write (void *handle, const char *filename, int width, int height, int compress) { (void)handle; (void)filename; (void)width; (void)height; (void)compress; return 0;}
void IMB_exrtile_begin_write (void *handle, const char *filename, int mipmap, int width, int height, int tilex, int tiley) { (void)handle; (void)filename; (void)mipmap; (void)width; (void)height; (void)tilex; (void)tiley; }
-void IMB_exr_set_channel (void *handle, const char *layname, const char *channame, int xstride, int ystride, float *rect) { (void)handle; (void)layname; (void)channame; (void)xstride; (void)ystride; (void)rect; }
+void IMB_exr_set_channel (void *handle, const char *layname, const char *passname, int xstride, int ystride, float *rect) { (void)handle; (void)layname; (void)passname; (void)xstride; (void)ystride; (void)rect; }
+float *IMB_exr_channel_rect (void *handle, const char *layname, const char *passname, const char *view) { (void)handle; (void)layname; (void)passname; (void)view; return NULL; }
void IMB_exr_read_channels (void *handle) { (void)handle; }
void IMB_exr_write_channels (void *handle) { (void)handle; }
-void IMB_exrtile_write_channels (void *handle, int partx, int party, int level) { (void)handle; (void)partx; (void)party; (void)level; }
-void IMB_exrtile_clear_channels (void *handle) { (void)handle; }
+void IMB_exrtile_write_channels (void *handle, int partx, int party, int level, const char *viewname) { UNUSED_VARS(handle, partx, party, level, viewname); }
+void IMB_exrmultiview_write_channels(void *handle, const char *viewname) { UNUSED_VARS(handle, viewname); }
+void IMB_exr_clear_channels (void *handle) { (void)handle; }
+
+void IMB_exr_multilayer_convert(
+ void *handle, void *base,
+ void * (*addview)(void *base, const char *str),
+ void * (*addlayer)(void *base, const char *str),
+ void (*addpass)(void *base, void *lay, const char *str, float *rect, int totchan,
+ const char *chan_id, const char *view))
+{
+ UNUSED_VARS(handle, base, addview, addlayer, addpass);
+}
-void IMB_exr_multilayer_convert (void *handle, void *base,
- void * (*addlayer)(void *base, const char *str),
- void (*addpass)(void *base, void *lay, const char *str, float *rect, int totchan, const char *chan_id))
+void IMB_exr_multiview_convert(
+ void *handle, void *base,
+ void (*addview)(void *base, const char *str),
+ void (*addbuffer)(void *base, const char *str, struct ImBuf *ibuf, const int frame), const int frame)
{
- (void)handle; (void)base; (void)addlayer; (void)addpass;
+ UNUSED_VARS(handle, base, addview, addbuffer, frame);
+}
+
+bool IMB_exr_multiview_save(
+ struct ImBuf *ibuf, const char *name, const int flags, const size_t totviews,
+ const char * (*getview)(void *base, size_t view_id),
+ struct ImBuf * (*getbuffer)(void *base, const size_t view_id))
+{
+ UNUSED_VARS(ibuf, name, flags, totviews, getview, getbuffer);
+ return false;
}
void IMB_exr_close (void *handle) { (void)handle; }
+
+void IMB_exr_add_view(void *handle, const char *name) { UNUSED_VARS(handle, name); }
+int IMB_exr_split_token(const char *str, const char *end, const char **token) { UNUSED_VARS(str, end, token); return 1; }
+bool IMB_exr_has_multilayer(void *handle) { UNUSED_VARS(handle); return false; }
+bool IMB_exr_has_singlelayer_multiview(void *handle) { UNUSED_VARS(handle); return false; }
diff --git a/source/blender/imbuf/intern/stereoimbuf.c b/source/blender/imbuf/intern/stereoimbuf.c
new file mode 100644
index 00000000000..cf9c6b388ff
--- /dev/null
+++ b/source/blender/imbuf/intern/stereoimbuf.c
@@ -0,0 +1,1292 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2015 by Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Dalai Felinto
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/imbuf/intern/stereoimbuf.c
+ * \ingroup imbuf
+ */
+
+#include <stddef.h>
+
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
+
+#include "IMB_allocimbuf.h"
+#include "IMB_filetype.h"
+#include "IMB_metadata.h"
+#include "IMB_colormanagement_intern.h"
+
+#include "imbuf.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_threads.h"
+#include "BLI_utildefines.h"
+
+#include "BLI_math.h"
+
+#include "DNA_userdef_types.h"
+#include "DNA_scene_types.h"
+
+/* prototypes */
+struct Stereo3DData;
+static void imb_stereo3d_write_doit(struct Stereo3DData *s3d_data, struct Stereo3dFormat *s3d);
+static void imb_stereo3d_read_doit(struct Stereo3DData *s3d_data, struct Stereo3dFormat *s3d);
+
+typedef struct Stereo3DData {
+ struct { float *left, *right, *stereo; } rectf;
+ struct { uchar *left, *right, *stereo; } rect;
+ size_t x, y, channels;
+ bool is_float;
+} Stereo3DData;
+
+static void imb_stereo3d_write_anaglyph(Stereo3DData *s3d, enum eStereo3dAnaglyphType mode)
+{
+ int x, y;
+ size_t width = s3d->x;
+ size_t height = s3d->y;
+ const size_t channels = s3d->channels;
+
+ const int stride_from = width;
+ const int stride_to = width;
+
+ int anaglyph_encoding[3][3] = {
+ {0, 1, 1},
+ {1, 0, 1},
+ {0, 0, 1},
+ };
+
+ int r, g, b;
+
+ r = anaglyph_encoding[mode][0];
+ g = anaglyph_encoding[mode][1];
+ b = anaglyph_encoding[mode][2];
+
+ if (s3d->is_float) {
+ float *rect_left = s3d->rectf.left;
+ float *rect_right = s3d->rectf.right;
+ float *rect_to = s3d->rectf.stereo;
+
+ if (channels == 3) {
+ for (y = 0; y < height; y++) {
+ float *to = rect_to + stride_to * y * 3;
+ float *from[2] = {
+ rect_left + stride_from * y * 3,
+ rect_right + stride_from * y * 3,
+ };
+
+ for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) {
+ to[0] = from[r][0];
+ to[1] = from[g][1];
+ to[2] = from[b][2];
+ }
+ }
+ }
+ else if (channels == 4) {
+ for (y = 0; y < height; y++) {
+ float *to = rect_to + stride_to * y * 4;
+ float *from[2] = {
+ rect_left + stride_from * y * 4,
+ rect_right + stride_from * y * 4,
+ };
+
+ for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) {
+ to[0] = from[r][0];
+ to[1] = from[g][1];
+ to[2] = from[b][2];
+ to[3] = MAX2(from[0][2], from[0][2]);
+ }
+ }
+ }
+ }
+ else {
+ uchar *rect_left = s3d->rect.left;
+ uchar *rect_right = s3d->rect.right;
+ uchar *rect_to = s3d->rect.stereo;
+
+ if (channels == 3) {
+ for (y = 0; y < height; y++) {
+ uchar *to = rect_to + stride_to * y * 3;
+ uchar *from[2] = {
+ rect_left + stride_from * y * 3,
+ rect_right + stride_from * y * 3,
+ };
+
+ for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) {
+ to[0] = from[r][0];
+ to[1] = from[g][1];
+ to[2] = from[b][2];
+ }
+ }
+ }
+ else if (channels == 4) {
+ for (y = 0; y < height; y++) {
+ uchar *to = rect_to + stride_to * y * 4;
+ uchar *from[2] = {
+ rect_left + stride_from * y * 4,
+ rect_right + stride_from * y * 4,
+ };
+
+ for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) {
+ to[0] = from[r][0];
+ to[1] = from[g][1];
+ to[2] = from[b][2];
+ to[3] = MAX2(from[0][2], from[0][2]);
+ }
+ }
+ }
+ }
+}
+
+static void imb_stereo3d_write_interlace(Stereo3DData *s3d, enum eStereo3dInterlaceType mode, const bool swap)
+{
+ int x, y;
+ size_t width = s3d->x;
+ size_t height = s3d->y;
+ const size_t channels = s3d->channels;
+
+ const int stride_from = width;
+ const int stride_to = width;
+
+ if (s3d->is_float) {
+ const float *rect_left = s3d->rectf.left;
+ const float *rect_right = s3d->rectf.right;
+ float *rect_to = s3d->rectf.stereo;
+
+ switch (mode) {
+ case S3D_INTERLACE_ROW:
+ {
+ char i = (char) swap;
+ for (y = 0; y < height; y++) {
+ float *to = rect_to + stride_to * y * channels;
+ const float *from[2] = {
+ rect_left + stride_from * y * channels,
+ rect_right + stride_from * y * channels,
+ };
+ memcpy(to, from[i], sizeof(float) * channels * stride_from);
+ i = !i;
+ }
+ break;
+ }
+ case S3D_INTERLACE_COLUMN:
+ {
+ if (channels == 1) {
+ for (y = 0; y < height; y++) {
+ float *to = rect_to + stride_to * y;
+ const float *from[2] = {
+ rect_left + stride_from * y,
+ rect_right + stride_from * y,
+ };
+
+ char i = (char) swap;
+ for (x = 0; x < width; x++, from[0] += 1, from[1] += 1, to += 1) {
+ to[0] = from[i][0];
+ i = !i;
+ }
+ }
+ }
+ else if (channels == 3) {
+ for (y = 0; y < height; y++) {
+ float *to = rect_to + stride_to * y * 3;
+ const float *from[2] = {
+ rect_left + stride_from * y * 3,
+ rect_right + stride_from * y * 3,
+ };
+
+ char i = (char) swap;
+ for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) {
+ copy_v3_v3(to, from[i]);
+ i = !i;
+ }
+ }
+ }
+ else if (channels == 4) {
+ for (y = 0; y < height; y++) {
+ float *to = rect_to + stride_to * y * channels;
+ const float *from[2] = {
+ rect_left + stride_from * y * channels,
+ rect_right + stride_from * y * channels,
+ };
+
+ char i = (char) swap;
+ for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) {
+ copy_v4_v4(to, from[i]);
+ i = !i;
+ }
+ }
+ }
+ break;
+ }
+ case S3D_INTERLACE_CHECKERBOARD:
+ {
+ if (channels == 1) {
+ char i = (char) swap;
+ for (y = 0; y < height; y++) {
+ float *to = rect_to + stride_to * y;
+ const float *from[2] = {
+ rect_left + stride_from * y,
+ rect_right + stride_from * y,
+ };
+ char j = i;
+ for (x = 0; x < width; x++, from[0] += 1, from[1] += 1, to += 1) {
+ to[0] = from[j][0];
+ j = !j;
+ }
+ i = !i;
+ }
+ }
+ else if (channels == 3) {
+ char i = (char) swap;
+ for (y = 0; y < height; y++) {
+ float *to = rect_to + stride_to * y * 3;
+ const float *from[2] = {
+ rect_left + stride_from * y * 3,
+ rect_right + stride_from * y * 3,
+ };
+ char j = i;
+ for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) {
+ copy_v3_v3(to, from[j]);
+ j = !j;
+ }
+ i = !i;
+ }
+ }
+ else if (channels == 4) {
+ char i = (char) swap;
+ for (y = 0; y < height; y++) {
+ float *to = rect_to + stride_to * y * 4;
+ const float *from[2] = {
+ rect_left + stride_from * y * 4,
+ rect_right + stride_from * y * 4,
+ };
+ char j = i;
+ for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) {
+ copy_v4_v4(to, from[j]);
+ j = !j;
+ }
+ i = !i;
+ }
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+ else {
+ const uchar *rect_left = s3d->rect.left;
+ const uchar *rect_right = s3d->rect.right;
+ uchar *rect_to = s3d->rect.stereo;
+
+ switch (mode) {
+ case S3D_INTERLACE_ROW:
+ {
+ char i = (char) swap;
+ for (y = 0; y < height; y++) {
+ uchar *to = rect_to + stride_to * y * channels;
+ const uchar *from[2] = {
+ rect_left + stride_from * y * channels,
+ rect_right + stride_from * y * channels,
+ };
+ memcpy(to, from[i], sizeof(uchar) * channels * stride_from);
+ i = !i;
+ }
+ break;
+ }
+ case S3D_INTERLACE_COLUMN:
+ {
+ if (channels == 1) {
+ for (y = 0; y < height; y++) {
+ uchar *to = rect_to + stride_to * y;
+ const uchar *from[2] = {
+ rect_left + stride_from * y,
+ rect_right + stride_from * y,
+ };
+ char i = (char) swap;
+ for (x = 0; x < width; x++, from[0] += 1, from[1] += 1, to += 1) {
+ to[0] = from[i][0];
+ i = !i;
+ }
+ }
+ }
+ else if (channels == 3) {
+ for (y = 0; y < height; y++) {
+ uchar *to = rect_to + stride_to * y * 3;
+ const uchar *from[2] = {
+ rect_left + stride_from * y * 3,
+ rect_right + stride_from * y * 3,
+ };
+ char i = (char) swap;
+ for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) {
+ copy_v3_v3_char((char *)to, (char *)from[i]);
+ i = !i;
+ }
+ }
+ }
+ else if (channels == 4) {
+ for (y = 0; y < height; y++) {
+ uchar *to = rect_to + stride_to * y * 4;
+ const uchar *from[2] = {
+ rect_left + stride_from * y * 4,
+ rect_right + stride_from * y * 4,
+ };
+ char i = (char) swap;
+ for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) {
+ copy_v4_v4_char((char *)to, (char *)from[i]);
+ i = !i;
+ }
+ }
+ }
+ break;
+ }
+ case S3D_INTERLACE_CHECKERBOARD:
+ {
+ if (channels == 1) {
+ char i = (char) swap;
+ for (y = 0; y < height; y++) {
+ uchar *to = rect_to + stride_to * y;
+ const uchar *from[2] = {
+ rect_left + stride_from * y,
+ rect_right + stride_from * y,
+ };
+ char j = i;
+ for (x = 0; x < width; x++, from[0] += 1, from[1] += 1, to += 1) {
+ to[0] = from[j][0];
+ j = !j;
+ }
+ i = !i;
+ }
+ }
+ else if (channels == 3) {
+ char i = (char) swap;
+ for (y = 0; y < height; y++) {
+ uchar *to = rect_to + stride_to * y * 3;
+ const uchar *from[2] = {
+ rect_left + stride_from * y * 3,
+ rect_right + stride_from * y * 3,
+ };
+ char j = i;
+ for (x = 0; x < width; x++, from[0] += 3, from[1] += 3, to += 3) {
+ copy_v3_v3_char((char *)to, (char *)from[j]);
+ j = !j;
+ }
+ i = !i;
+ }
+ }
+ else if (channels == 4) {
+ char i = (char) swap;
+ for (y = 0; y < height; y++) {
+ uchar *to = rect_to + stride_to * y * 4;
+ const uchar *from[2] = {
+ rect_left + stride_from * y * 4,
+ rect_right + stride_from * y * 4,
+ };
+ char j = i;
+ for (x = 0; x < width; x++, from[0] += 4, from[1] += 4, to += 4) {
+ copy_v4_v4_char((char *)to, (char *)from[j]);
+ j = !j;
+ }
+ i = !i;
+ }
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+}
+
+/* stereo3d output (s3d->rectf.stereo) is always unsqueezed */
+static void imb_stereo3d_write_sidebyside(Stereo3DData *s3d, const bool crosseyed)
+{
+ int y;
+ size_t width = s3d->x;
+ size_t height = s3d->y;
+ const size_t channels = s3d->channels;
+
+ const int stride_from = width;
+ const int stride_to = width * 2;
+
+ const int l = (int) crosseyed;
+ const int r = !l;
+
+ if (s3d->is_float) {
+ const float *rect_left = s3d->rectf.left;
+ const float *rect_right = s3d->rectf.right;
+ float *rect_to = s3d->rectf.stereo;
+
+ for (y = 0; y < height; y++) {
+ float *to = rect_to + stride_to * y * channels;
+ const float *from[2] = {
+ rect_left + stride_from * y * channels,
+ rect_right + stride_from * y * channels,
+ };
+
+ memcpy(to, from[l], sizeof(float) * channels * stride_from);
+ memcpy(to + channels * stride_from, from[r], sizeof(float) * channels * stride_from);
+ }
+ }
+ else {
+ const uchar *rect_left = s3d->rect.left;
+ const uchar *rect_right = s3d->rect.right;
+ uchar *rect_to = s3d->rect.stereo;
+
+ for (y = 0; y < height; y++) {
+ uchar *to = rect_to + stride_to * y * channels;
+ const uchar *from[2] = {
+ rect_left + stride_from * y * channels,
+ rect_right + stride_from * y * channels,
+ };
+
+ memcpy(to, from[l], sizeof(uchar) * channels * stride_from);
+ memcpy(to + channels * stride_from, from[r], sizeof(uchar) * channels * stride_from);
+ }
+ }
+}
+
+/* stereo3d output (s3d->rectf.stereo) is always unsqueezed */
+static void imb_stereo3d_write_topbottom(Stereo3DData *s3d)
+{
+ int y;
+ size_t width = s3d->x;
+ size_t height = s3d->y;
+ const size_t channels = s3d->channels;
+
+ const int stride_from = width;
+ const int stride_to = width;
+
+ if (s3d->is_float) {
+ const float *rect_left = s3d->rectf.left;
+ const float *rect_right = s3d->rectf.right;
+ float *rect_to = s3d->rectf.stereo;
+
+ for (y = 0; y < height; y++) {
+ float *to = rect_to + stride_to * y * channels;
+ const float *from[2] = {
+ rect_left + stride_from * y * channels,
+ rect_right + stride_from * y * channels,
+ };
+
+ memcpy(to, from[1], sizeof(float) * channels * stride_from);
+ memcpy(to + channels * height * stride_from, from[0], sizeof(float) * channels * stride_from);
+ }
+ }
+ else {
+ const uchar *rect_left = s3d->rect.left;
+ const uchar *rect_right = s3d->rect.right;
+ uchar *rect_to = s3d->rect.stereo;
+
+ for (y = 0; y < height; y++) {
+ uchar *to = rect_to + stride_to * y * channels;
+ const uchar *from[2] = {
+ rect_left + stride_from * y * channels,
+ rect_right + stride_from * y * channels,
+ };
+
+ memcpy(to, from[1], sizeof(uchar) * channels * stride_from);
+ memcpy(to + channels * height * stride_from, from[0], sizeof(uchar) * channels * stride_from);
+ }
+ }
+}
+
+/**************************** dimension utils ****************************************/
+
+void IMB_stereo3d_write_dimensions(
+ const char mode, const bool is_squeezed, const size_t width, const size_t height,
+ size_t *r_width, size_t *r_height)
+{
+ switch (mode) {
+ case S3D_DISPLAY_SIDEBYSIDE:
+ {
+ *r_width = is_squeezed ? width : width * 2;
+ *r_height = height;
+ break;
+ }
+ case S3D_DISPLAY_TOPBOTTOM:
+ {
+ *r_width = width;
+ *r_height = is_squeezed ? height : height * 2;
+ break;
+ }
+ case S3D_DISPLAY_ANAGLYPH:
+ case S3D_DISPLAY_INTERLACE:
+ default:
+ {
+ *r_width = width;
+ *r_height = height;
+ break;
+ }
+ }
+}
+
+void IMB_stereo3d_read_dimensions(
+ const char mode, const bool is_squeezed, const size_t width, const size_t height,
+ size_t *r_width, size_t *r_height)
+{
+ switch (mode) {
+ case S3D_DISPLAY_SIDEBYSIDE:
+ {
+ *r_width = is_squeezed ? width / 2 : width;
+ *r_height = height;
+ break;
+ }
+ case S3D_DISPLAY_TOPBOTTOM:
+ {
+ *r_width = width;
+ *r_height = is_squeezed ? height / 2 : height;
+ break;
+ }
+ case S3D_DISPLAY_ANAGLYPH:
+ case S3D_DISPLAY_INTERLACE:
+ default:
+ {
+ *r_width = width;
+ *r_height = height;
+ break;
+ }
+ }
+}
+
+/**************************** un/squeeze frame ****************************************/
+
+static void imb_stereo3d_squeeze_ImBuf(ImBuf *ibuf, Stereo3dFormat *s3d, const size_t x, const size_t y)
+{
+ if (ELEM(s3d->display_mode, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM) == false)
+ return;
+
+ if ((s3d->flag & S3D_SQUEEZED_FRAME) == 0)
+ return;
+
+ IMB_scaleImBuf_threaded(ibuf, x, y);
+}
+
+static void imb_stereo3d_unsqueeze_ImBuf(ImBuf *ibuf, Stereo3dFormat *s3d, const size_t x, const size_t y)
+{
+ if (ELEM(s3d->display_mode, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM) == false)
+ return;
+
+ if ((s3d->flag & S3D_SQUEEZED_FRAME) == 0)
+ return;
+
+ IMB_scaleImBuf_threaded(ibuf, x, y);
+}
+
+static void imb_stereo3d_squeeze_rectf(float *rectf, Stereo3dFormat *s3d, const size_t x, const size_t y, const size_t channels)
+{
+ ImBuf *ibuf;
+ size_t width, height;
+
+ if (ELEM(s3d->display_mode, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM) == false)
+ return;
+
+ if ((s3d->flag & S3D_SQUEEZED_FRAME) == 0)
+ return;
+
+ /* creates temporary imbuf to store the rectf */
+ IMB_stereo3d_write_dimensions(s3d->display_mode, false, x, y, &width, &height);
+ ibuf = IMB_allocImBuf(width, height, channels, IB_rectfloat);
+
+ IMB_buffer_float_from_float(
+ ibuf->rect_float, rectf, channels,
+ IB_PROFILE_LINEAR_RGB, IB_PROFILE_LINEAR_RGB, false,
+ width, height, width, width);
+
+ IMB_scaleImBuf_threaded(ibuf, x, y);
+ rectf = MEM_dupallocN(ibuf->rect_float);
+ IMB_freeImBuf(ibuf);
+}
+
+static void imb_stereo3d_squeeze_rect(int *rect, Stereo3dFormat *s3d, const size_t x, const size_t y, const size_t channels)
+{
+ ImBuf *ibuf;
+ size_t width, height;
+
+ if (ELEM(s3d->display_mode, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM) == false)
+ return;
+
+ if ((s3d->flag & S3D_SQUEEZED_FRAME) == 0)
+ return;
+
+ /* creates temporary imbuf to store the rectf */
+ IMB_stereo3d_write_dimensions(s3d->display_mode, false, x, y, &width, &height);
+ ibuf = IMB_allocImBuf(width, height, channels, IB_rect);
+
+ IMB_buffer_byte_from_byte(
+ (unsigned char *)ibuf->rect, (unsigned char *)rect,
+ IB_PROFILE_SRGB, IB_PROFILE_SRGB, false,
+ width, height, width, width);
+
+ IMB_scaleImBuf_threaded(ibuf, x, y);
+ rect = MEM_dupallocN(ibuf->rect);
+ IMB_freeImBuf(ibuf);
+}
+
+
+/*************************** preparing to call the write functions **************************/
+
+static void imb_stereo3d_data_initialize(
+ Stereo3DData *s3d_data, const bool is_float,
+ const size_t x, const size_t y, const size_t channels,
+ int *rect_left, int *rect_right, int *rect_stereo,
+ float *rectf_left, float *rectf_right, float *rectf_stereo)
+{
+ s3d_data->is_float = is_float;
+ s3d_data->x = x;
+ s3d_data->y = y;
+ s3d_data->channels = channels;
+ s3d_data->rect.left = (uchar *)rect_left;
+ s3d_data->rect.right = (uchar *)rect_right;
+ s3d_data->rect.stereo = (uchar *)rect_stereo;
+ s3d_data->rectf.left = rectf_left;
+ s3d_data->rectf.right = rectf_right;
+ s3d_data->rectf.stereo = rectf_stereo;
+}
+
+int *IMB_stereo3d_from_rect(
+ ImageFormatData *im_format, const size_t x, const size_t y, const size_t channels,
+ int *rect_left, int *rect_right)
+{
+ int *r_rect;
+ Stereo3DData s3d_data = {{NULL}};
+ size_t width, height;
+ const bool is_float = im_format->depth > 8;
+
+ IMB_stereo3d_write_dimensions(im_format->stereo3d_format.display_mode, false, x, y, &width, &height);
+ r_rect = MEM_mallocN(channels * sizeof(int) * width * height, __func__);
+
+ imb_stereo3d_data_initialize(&s3d_data, is_float, x, y, channels, rect_left, rect_right, r_rect, NULL, NULL, NULL);
+ imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format);
+ imb_stereo3d_squeeze_rect(r_rect, &im_format->stereo3d_format, x, y, channels);
+
+ return r_rect;
+}
+
+float *IMB_stereo3d_from_rectf(
+ ImageFormatData *im_format, const size_t x, const size_t y, const size_t channels,
+ float *rectf_left, float *rectf_right)
+{
+ float *r_rectf;
+ Stereo3DData s3d_data = {{NULL}};
+ size_t width, height;
+ const bool is_float = im_format->depth > 8;
+
+ IMB_stereo3d_write_dimensions(im_format->stereo3d_format.display_mode, false, x, y, &width, &height);
+ r_rectf = MEM_mallocN(channels * sizeof(float) * width * height, __func__);
+
+ imb_stereo3d_data_initialize(&s3d_data, is_float, x, y, channels, NULL, NULL, NULL, rectf_left, rectf_right, r_rectf);
+ imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format);
+ imb_stereo3d_squeeze_rectf(r_rectf, &im_format->stereo3d_format, x, y, channels);
+
+ return r_rectf;
+}
+
+/* left/right are always float */
+ImBuf *IMB_stereo3d_ImBuf(ImageFormatData *im_format, ImBuf *ibuf_left, ImBuf *ibuf_right)
+{
+ ImBuf *ibuf_stereo = NULL;
+ Stereo3DData s3d_data = {{NULL}};
+ size_t width, height;
+ const bool is_float = im_format->depth > 8;
+
+ IMB_stereo3d_write_dimensions(im_format->stereo3d_format.display_mode, false, ibuf_left->x, ibuf_left->y, &width, &height);
+ ibuf_stereo = IMB_allocImBuf(width, height, ibuf_left->planes, (is_float ? IB_rectfloat : IB_rect));
+
+ /* copy flags for IB_fields and other settings */
+ ibuf_stereo->flags = ibuf_left->flags;
+
+ imb_stereo3d_data_initialize(
+ &s3d_data, is_float, ibuf_left->x, ibuf_left->y, 4,
+ (int *)ibuf_left->rect, (int *)ibuf_right->rect, (int *)ibuf_stereo->rect,
+ ibuf_left->rect_float, ibuf_right->rect_float, ibuf_stereo->rect_float);
+
+ imb_stereo3d_write_doit(&s3d_data, &im_format->stereo3d_format);
+ imb_stereo3d_squeeze_ImBuf(ibuf_stereo, &im_format->stereo3d_format, ibuf_left->x, ibuf_left->y);
+
+ return ibuf_stereo;
+}
+
+static void imb_stereo3d_write_doit(Stereo3DData *s3d_data, Stereo3dFormat *s3d)
+{
+ switch (s3d->display_mode) {
+ case S3D_DISPLAY_ANAGLYPH:
+ imb_stereo3d_write_anaglyph(s3d_data, s3d->anaglyph_type);
+ break;
+ case S3D_DISPLAY_INTERLACE:
+ imb_stereo3d_write_interlace(s3d_data, s3d->interlace_type, (s3d->flag & S3D_INTERLACE_SWAP) != 0);
+ break;
+ case S3D_DISPLAY_SIDEBYSIDE:
+ imb_stereo3d_write_sidebyside(s3d_data, (s3d->flag & S3D_SIDEBYSIDE_CROSSEYED) != 0);
+ break;
+ case S3D_DISPLAY_TOPBOTTOM:
+ imb_stereo3d_write_topbottom(s3d_data);
+ break;
+ default:
+ break;
+ }
+}
+
+/******************************** reading stereo imbufs **********************/
+
+static void imb_stereo3d_read_anaglyph(Stereo3DData *s3d, enum eStereo3dAnaglyphType mode)
+{
+ int x, y;
+ size_t width = s3d->x;
+ size_t height = s3d->y;
+ const size_t channels = s3d->channels;
+
+ const int stride_from = width;
+ const int stride_to = width;
+
+ int anaglyph_encoding[3][3] = {
+ {0, 1, 1},
+ {1, 0, 1},
+ {0, 0, 1},
+ };
+
+ int r, g, b;
+
+ r = anaglyph_encoding[mode][0];
+ g = anaglyph_encoding[mode][1];
+ b = anaglyph_encoding[mode][2];
+
+ if (s3d->is_float) {
+ float *rect_left = s3d->rectf.left;
+ float *rect_right = s3d->rectf.right;
+ float *rect_from = s3d->rectf.stereo;
+
+ if (channels == 3) {
+ for (y = 0; y < height; y++) {
+ float *from = rect_from + stride_from * y * 3;
+ float *to[2] = {
+ rect_left + stride_to * y * 3,
+ rect_right + stride_to * y * 3,
+ };
+
+ for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) {
+ to[r][0] = from[0];
+ to[g][1] = from[1];
+ to[b][2] = from[2];
+ }
+ }
+ }
+ else if (channels == 4) {
+ for (y = 0; y < height; y++) {
+ float *from = rect_from + stride_from * y * 4;
+ float *to[2] = {
+ rect_left + stride_to * y * 4,
+ rect_right + stride_to * y * 4,
+ };
+
+ for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) {
+ to[r][0] = from[0];
+ to[g][1] = from[1];
+ to[b][2] = from[2];
+ to[0][3] = to[1][3] = from[3];
+ }
+ }
+ }
+ }
+ else {
+ uchar *rect_left = s3d->rect.left;
+ uchar *rect_right = s3d->rect.right;
+ uchar *rect_from = s3d->rect.stereo;
+
+ if (channels == 3) {
+ for (y = 0; y < height; y++) {
+ uchar *from = rect_from + stride_from * y * 3;
+ uchar *to[2] = {
+ rect_left + stride_to * y * 3,
+ rect_right + stride_to * y * 3,
+ };
+
+ for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) {
+ to[r][0] = from[0];
+ to[g][1] = from[1];
+ to[b][2] = from[2];
+ }
+ }
+ }
+ else if (channels == 4) {
+ for (y = 0; y < height; y++) {
+ uchar *from = rect_from + stride_from * y * 4;
+ uchar *to[2] = {
+ rect_left + stride_to * y * 4,
+ rect_right + stride_to * y * 4,
+ };
+
+ for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) {
+ to[r][0] = from[0];
+ to[g][1] = from[1];
+ to[b][2] = from[2];
+ to[0][3] = to[1][3] = from[3];
+ }
+ }
+ }
+ }
+}
+
+static void imb_stereo3d_read_interlace(Stereo3DData *s3d, enum eStereo3dInterlaceType mode, const bool swap)
+{
+ int x, y;
+ size_t width = s3d->x;
+ size_t height = s3d->y;
+ const size_t channels = s3d->channels;
+
+ const int stride_from = width;
+ const int stride_to = width;
+
+ if (s3d->is_float) {
+ float *rect_left = s3d->rectf.left;
+ float *rect_right = s3d->rectf.right;
+ const float *rect_from = s3d->rectf.stereo;
+
+ switch (mode) {
+ case S3D_INTERLACE_ROW:
+ {
+ char i = (char) swap;
+ for (y = 0; y < height; y++) {
+ const float *from = rect_from + stride_from * y * channels;
+ float *to[2] = {
+ rect_left + stride_to * y * channels,
+ rect_right + stride_to * y * channels,
+ };
+ memcpy(to[i], from, sizeof(float) * channels * stride_to);
+ i = !i;
+ }
+ break;
+ }
+ case S3D_INTERLACE_COLUMN:
+ {
+ if (channels == 1) {
+ for (y = 0; y < height; y++) {
+ const float *from = rect_from + stride_from * y;
+ float *to[2] = {
+ rect_left + stride_to * y,
+ rect_right + stride_to * y,
+ };
+
+ char i = (char) swap;
+ for (x = 0; x < width; x++, from += 1, to[0] += 1, to[1] += 1) {
+ to[i][0] = from[0];
+ i = !i;
+ }
+ }
+ }
+ else if (channels == 3) {
+ for (y = 0; y < height; y++) {
+ const float *from = rect_from + stride_from * y * 3;
+ float *to[2] = {
+ rect_left + stride_to * y * 3,
+ rect_right + stride_to * y * 3,
+ };
+
+ char i = (char) swap;
+ for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) {
+ copy_v3_v3(to[i], from);
+ i = !i;
+ }
+ }
+ }
+ else if (channels == 4) {
+ for (y = 0; y < height; y++) {
+ const float *from = rect_from + stride_from * y * channels;
+ float *to[2] = {
+ rect_left + stride_to * y * channels,
+ rect_right + stride_to * y * channels,
+ };
+
+ char i = (char) swap;
+ for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) {
+ copy_v4_v4(to[i], from);
+ i = !i;
+ }
+ }
+ }
+ break;
+ }
+ case S3D_INTERLACE_CHECKERBOARD:
+ {
+ if (channels == 1) {
+ char i = (char) swap;
+ for (y = 0; y < height; y++) {
+ const float *from = rect_from + stride_from * y;
+ float *to[2] = {
+ rect_left + stride_to * y,
+ rect_right + stride_to * y,
+ };
+ char j = i;
+ for (x = 0; x < width; x++, from += 1, to[0] += 1, to[1] += 1) {
+ to[j][0] = from[0];
+ j = !j;
+ }
+ i = !i;
+ }
+ }
+ else if (channels == 3) {
+ char i = (char) swap;
+ for (y = 0; y < height; y++) {
+ const float *from = rect_from + stride_from * y * 3;
+ float *to[2] = {
+ rect_left + stride_to * y * 3,
+ rect_right + stride_to * y * 3,
+ };
+ char j = i;
+ for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) {
+ copy_v3_v3(to[j], from);
+ j = !j;
+ }
+ i = !i;
+ }
+ }
+ else if (channels == 4) {
+ char i = (char) swap;
+ for (y = 0; y < height; y++) {
+ const float *from = rect_from + stride_from * y * 4;
+ float *to[2] = {
+ rect_left + stride_to * y * 4,
+ rect_right + stride_to * y * 4,
+ };
+ char j = i;
+ for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) {
+ copy_v4_v4(to[j], from);
+ j = !j;
+ }
+ i = !i;
+ }
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+ else {
+ uchar *rect_left = s3d->rect.right;
+ uchar *rect_right = s3d->rect.left;
+ const uchar *rect_from = s3d->rect.stereo;
+
+ switch (mode) {
+ case S3D_INTERLACE_ROW:
+ {
+ char i = (char) swap;
+ for (y = 0; y < height; y++) {
+ const uchar *from = rect_from + stride_from * y * channels;
+ uchar *to[2] = {
+ rect_left + stride_to * y * channels,
+ rect_right + stride_to * y * channels,
+ };
+ memcpy(to[i], from, sizeof(uchar) * channels * stride_to);
+ i = !i;
+ }
+ break;
+ }
+ case S3D_INTERLACE_COLUMN:
+ {
+ if (channels == 1) {
+ for (y = 0; y < height; y++) {
+ const uchar *from = rect_from + stride_from * y;
+ uchar *to[2] = {
+ rect_left + stride_to * y,
+ rect_right + stride_to * y,
+ };
+ char i = (char) swap;
+ for (x = 0; x < width; x++, from += 1, to[0] += 1, to[1] += 1) {
+ to[i][0] = from[0];
+ i = !i;
+ }
+ }
+ }
+ else if (channels == 3) {
+ for (y = 0; y < height; y++) {
+ const uchar *from = rect_from + stride_from * y * 3;
+ uchar *to[2] = {
+ rect_left + stride_to * y * 3,
+ rect_right + stride_to * y * 3,
+ };
+ char i = (char) swap;
+ for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) {
+ copy_v3_v3_char((char *)to[i], (char *)from);
+ i = !i;
+ }
+ }
+ }
+ else if (channels == 4) {
+ for (y = 0; y < height; y++) {
+ const uchar *from = rect_from + stride_from * y * 4;
+ uchar *to[2] = {
+ rect_left + stride_to * y * 4,
+ rect_right + stride_to * y * 4,
+ };
+ char i = (char) swap;
+ for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) {
+ copy_v4_v4_char((char *)to[i], (char *)from);
+ i = !i;
+ }
+ }
+ }
+ break;
+ }
+ case S3D_INTERLACE_CHECKERBOARD:
+ {
+ if (channels == 1) {
+ char i = (char) swap;
+ for (y = 0; y < height; y++) {
+ const uchar *from = rect_from + stride_from * y;
+ uchar *to[2] = {
+ rect_left + stride_to * y,
+ rect_right + stride_to * y,
+ };
+ char j = i;
+ for (x = 0; x < width; x++, from += 1, to[0] += 1, to[1] += 1) {
+ to[j][0] = from[0];
+ j = !j;
+ }
+ i = !i;
+ }
+ }
+ else if (channels == 3) {
+ char i = (char) swap;
+ for (y = 0; y < height; y++) {
+ const uchar *from = rect_from + stride_from * y * 3;
+ uchar *to[2] = {
+ rect_left + stride_to * y * 3,
+ rect_right + stride_to * y * 3,
+ };
+ char j = i;
+ for (x = 0; x < width; x++, from += 3, to[0] += 3, to[1] += 3) {
+ copy_v3_v3_char((char *)to[j], (char *)from);
+ j = !j;
+ }
+ i = !i;
+ }
+ }
+ else if (channels == 4) {
+ char i = (char) swap;
+ for (y = 0; y < height; y++) {
+ const uchar *from = rect_from + stride_from * y * 4;
+ uchar *to[2] = {
+ rect_left + stride_to * y * 4,
+ rect_right + stride_to * y * 4,
+ };
+ char j = i;
+ for (x = 0; x < width; x++, from += 4, to[0] += 4, to[1] += 4) {
+ copy_v4_v4_char((char *)to[j], (char *)from);
+ j = !j;
+ }
+ i = !i;
+ }
+ }
+ break;
+ }
+ default:
+ {
+ break;
+ }
+ }
+ }
+}
+
+/* stereo input (s3d->rectf.stereo) is always unsqueezed */
+static void imb_stereo3d_read_sidebyside(Stereo3DData *s3d, const bool crosseyed)
+{
+ int y;
+ size_t width = s3d->x;
+ size_t height = s3d->y;
+ const size_t channels = s3d->channels;
+
+ const int stride_from = width * 2;
+ const int stride_to = width;
+
+ const int l = (int) crosseyed;
+ const int r = !l;
+
+ if (s3d->is_float) {
+ float *rect_left = s3d->rectf.left;
+ float *rect_right = s3d->rectf.right;
+ const float *rect_from = s3d->rectf.stereo;
+
+ for (y = 0; y < height; y++) {
+ const float *from = rect_from + stride_from * y * channels;
+ float *to[2] = {
+ rect_left + stride_to * y * channels,
+ rect_right + stride_to * y * channels,
+ };
+
+ memcpy(to[l], from, sizeof(float) * channels * stride_to);
+ memcpy(to[r], from + channels * stride_to, sizeof(float) * channels * stride_to);
+ }
+ }
+ else {
+ uchar *rect_left = s3d->rect.left;
+ uchar *rect_right = s3d->rect.right;
+ const uchar *rect_from = s3d->rect.stereo;
+
+ /* always RGBA input/output */
+ for (y = 0; y < height; y++) {
+ const uchar *from = rect_from + stride_from * y * channels;
+ uchar *to[2] = {
+ rect_left + stride_to * y * channels,
+ rect_right + stride_to * y * channels,
+ };
+
+ memcpy(to[l], from, sizeof(uchar) * channels * stride_to);
+ memcpy(to[r], from + channels * stride_to, sizeof(uchar) * channels * stride_to);
+ }
+ }
+}
+
+/* stereo input (s3d->rectf.stereo) is always unsqueezed */
+static void imb_stereo3d_read_topbottom(Stereo3DData *s3d)
+{
+ int y;
+ size_t width = s3d->x;
+ size_t height = s3d->y;
+ const size_t channels = s3d->channels;
+
+ const int stride_from = width;
+ const int stride_to = width;
+
+ if (s3d->is_float) {
+ float *rect_left = s3d->rectf.left;
+ float *rect_right = s3d->rectf.right;
+ const float *rect_from = s3d->rectf.stereo;
+
+ for (y = 0; y < height; y++) {
+ const float *from = rect_from + stride_from * y * channels;
+ float *to[2] = {
+ rect_left + stride_to * y * channels,
+ rect_right + stride_to * y * channels,
+ };
+
+ memcpy(to[1], from, sizeof(float) * channels * stride_to);
+ memcpy(to[0], from + channels * height * stride_to, sizeof(float) * channels * stride_to);
+ }
+ }
+ else {
+ uchar *rect_left = s3d->rect.left;
+ uchar *rect_right = s3d->rect.right;
+ const uchar *rect_from = s3d->rect.stereo;
+
+ for (y = 0; y < height; y++) {
+ const uchar *from = rect_from + stride_from * y * channels;
+ uchar *to[2] = {
+ rect_left + stride_to * y * channels,
+ rect_right + stride_to * y * channels,
+ };
+
+ memcpy(to[1], from, sizeof(uchar) * channels * stride_to);
+ memcpy(to[0], from + channels * height * stride_to, sizeof(uchar) * channels * stride_to);
+ }
+ }
+}
+
+
+/*************************** preparing to call the read functions **************************/
+
+/* reading a stereo encoded ibuf (*left) and generating two ibufs from it (*left and *right) */
+void IMB_ImBufFromStereo3d(
+ Stereo3dFormat *s3d, ImBuf *ibuf_stereo3d,
+ ImBuf **r_ibuf_left, ImBuf **r_ibuf_right)
+{
+ Stereo3DData s3d_data = {{NULL}};
+ ImBuf *ibuf_left, *ibuf_right;
+ size_t width, height;
+ const bool is_float = (ibuf_stereo3d->rect_float != NULL);
+
+ IMB_stereo3d_read_dimensions(
+ s3d->display_mode, ((s3d->flag & S3D_SQUEEZED_FRAME) == 0), ibuf_stereo3d->x, ibuf_stereo3d->y,
+ &width, &height);
+
+ ibuf_left = IMB_allocImBuf(width, height, ibuf_stereo3d->planes, (is_float ? IB_rectfloat : IB_rect));
+ ibuf_right = IMB_allocImBuf(width, height, ibuf_stereo3d->planes, (is_float ? IB_rectfloat : IB_rect));
+
+ /* copy flags for IB_fields and other settings */
+ ibuf_left->flags = ibuf_stereo3d->flags;
+ ibuf_right->flags = ibuf_stereo3d->flags;
+
+ /* we always work with unsqueezed formats */
+ IMB_stereo3d_write_dimensions(
+ s3d->display_mode, ((s3d->flag & S3D_SQUEEZED_FRAME) == 0), ibuf_stereo3d->x, ibuf_stereo3d->y,
+ &width, &height);
+ imb_stereo3d_unsqueeze_ImBuf(ibuf_stereo3d, s3d, width, height);
+
+ imb_stereo3d_data_initialize(
+ &s3d_data, is_float, ibuf_left->x, ibuf_left->y, 4,
+ (int *)ibuf_left->rect, (int *)ibuf_right->rect, (int *)ibuf_stereo3d->rect,
+ ibuf_left->rect_float, ibuf_right->rect_float, ibuf_stereo3d->rect_float);
+
+ imb_stereo3d_read_doit(&s3d_data, s3d);
+
+ if (ibuf_stereo3d->flags & (IB_zbuf | IB_zbuffloat)) {
+ if (is_float) {
+ addzbuffloatImBuf(ibuf_left);
+ addzbuffloatImBuf(ibuf_right);
+ }
+ else {
+ addzbufImBuf(ibuf_left);
+ addzbufImBuf(ibuf_right);
+ }
+
+ imb_stereo3d_data_initialize(
+ &s3d_data, is_float, ibuf_left->x, ibuf_left->y, 1,
+ (int *)ibuf_left->zbuf, (int *)ibuf_right->zbuf, (int *)ibuf_stereo3d->zbuf,
+ ibuf_left->zbuf_float, ibuf_right->zbuf_float, ibuf_stereo3d->zbuf_float);
+
+ imb_stereo3d_read_doit(&s3d_data, s3d);
+ }
+
+ IMB_freeImBuf(ibuf_stereo3d);
+
+ *r_ibuf_left = ibuf_left;
+ *r_ibuf_right = ibuf_right;
+}
+
+static void imb_stereo3d_read_doit(Stereo3DData *s3d_data, Stereo3dFormat *s3d)
+{
+ switch (s3d->display_mode) {
+ case S3D_DISPLAY_ANAGLYPH:
+ imb_stereo3d_read_anaglyph(s3d_data, s3d->anaglyph_type);
+ break;
+ case S3D_DISPLAY_INTERLACE:
+ imb_stereo3d_read_interlace(s3d_data, s3d->interlace_type, (s3d->flag & S3D_INTERLACE_SWAP) != 0);
+ break;
+ case S3D_DISPLAY_SIDEBYSIDE:
+ imb_stereo3d_read_sidebyside(s3d_data, (s3d->flag & S3D_SIDEBYSIDE_CROSSEYED) != 0);
+ break;
+ case S3D_DISPLAY_TOPBOTTOM:
+ imb_stereo3d_read_topbottom(s3d_data);
+ break;
+ default:
+ break;
+ }
+}
diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c
index 118f0405303..21f440a8232 100644
--- a/source/blender/imbuf/intern/thumbs.c
+++ b/source/blender/imbuf/intern/thumbs.c
@@ -338,7 +338,7 @@ ImBuf *IMB_thumb_create(const char *path, ThumbSize size, ThumbSource source, Im
img = IMB_loadblend_thumb(path);
}
else {
- img = IMB_loadiffname(path, IB_rect | IB_metadata, NULL);
+ img = IMB_loadiffname(path, IB_rect | IB_metadata | IB_thumbnail, NULL);
}
}
diff --git a/source/blender/imbuf/intern/util.c b/source/blender/imbuf/intern/util.c
index 32100aa2288..3fee6688014 100644
--- a/source/blender/imbuf/intern/util.c
+++ b/source/blender/imbuf/intern/util.c
@@ -439,3 +439,15 @@ bool IMB_isanim(const char *filename)
return (type && type != ANIM_SEQUENCE);
}
+
+bool IMB_isfloat(ImBuf *ibuf)
+{
+ ImFileType *type;
+
+ for (type = IMB_FILE_TYPES; type < IMB_FILE_TYPES_LAST; type++) {
+ if (type->ftype(type, ibuf)) {
+ return (type->flag & IM_FTYPE_FLOAT) != 0;
+ }
+ }
+ return false;
+}
diff --git a/source/blender/imbuf/intern/writeimage.c b/source/blender/imbuf/intern/writeimage.c
index 087330d10d2..5e9ccf4437e 100644
--- a/source/blender/imbuf/intern/writeimage.c
+++ b/source/blender/imbuf/intern/writeimage.c
@@ -43,19 +43,7 @@
static ImBuf *prepare_write_imbuf(ImFileType *type, ImBuf *ibuf)
{
- ImBuf *write_ibuf = ibuf;
-
- if (type->flag & IM_FTYPE_FLOAT) {
- /* pass */
- }
- else {
- if (ibuf->rect == NULL && ibuf->rect_float) {
- ibuf->rect_colorspace = colormanage_colorspace_get_roled(COLOR_ROLE_DEFAULT_BYTE);
- IMB_rect_from_float(ibuf);
- }
- }
-
- return write_ibuf;
+ return IMB_prepare_write_ImBuf((type->flag & IM_FTYPE_FLOAT), ibuf);
}
short IMB_saveiff(struct ImBuf *ibuf, const char *name, int flags)
@@ -86,3 +74,19 @@ short IMB_saveiff(struct ImBuf *ibuf, const char *name, int flags)
return false;
}
+ImBuf *IMB_prepare_write_ImBuf(const bool isfloat, ImBuf *ibuf)
+{
+ ImBuf *write_ibuf = ibuf;
+
+ if (isfloat) {
+ /* pass */
+ }
+ else {
+ if (ibuf->rect == NULL && ibuf->rect_float) {
+ ibuf->rect_colorspace = colormanage_colorspace_get_roled(COLOR_ROLE_DEFAULT_BYTE);
+ IMB_rect_from_float(ibuf);
+ }
+ }
+
+ return write_ibuf;
+}
diff --git a/source/blender/makesdna/DNA_camera_types.h b/source/blender/makesdna/DNA_camera_types.h
index c67a356a708..6a9942aea66 100644
--- a/source/blender/makesdna/DNA_camera_types.h
+++ b/source/blender/makesdna/DNA_camera_types.h
@@ -44,6 +44,16 @@ struct Object;
struct AnimData;
struct Ipo;
+/* ------------------------------------------- */
+/* Stereo Settings */
+typedef struct CameraStereoSettings {
+ float interocular_distance;
+ float convergence_distance;
+ short convergence_mode;
+ short pivot;
+ short pad, pad2;
+} CameraStereoSettings;
+
typedef struct Camera {
ID id;
struct AnimData *adt; /* animation data (must be immediately after id for utilities to use it) */
@@ -69,6 +79,9 @@ typedef struct Camera {
char sensor_fit;
char pad[7];
+
+ /* Stereo settings */
+ struct CameraStereoSettings stereo;
} Camera;
/* **************** CAMERA ********************* */
@@ -123,6 +136,20 @@ enum {
#define DEFAULT_SENSOR_WIDTH 32.0f
#define DEFAULT_SENSOR_HEIGHT 18.0f
+/* stereo->convergence_mode */
+enum {
+ CAM_S3D_OFFAXIS = 0,
+ CAM_S3D_PARALLEL = 1,
+ CAM_S3D_TOE = 2,
+};
+
+/* stereo->pivot */
+enum {
+ CAM_S3D_PIVOT_LEFT = 0,
+ CAM_S3D_PIVOT_RIGHT = 1,
+ CAM_S3D_PIVOT_CENTER = 2,
+};
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h
index 7444abb6d11..79980c6d2d5 100644
--- a/source/blender/makesdna/DNA_image_types.h
+++ b/source/blender/makesdna/DNA_image_types.h
@@ -43,7 +43,6 @@ struct MovieCache;
struct RenderResult;
struct GPUTexture;
-
/* ImageUser is in Texture, in Nodes, Background Image, Image Window, .... */
/* should be used in conjunction with an ID * to Image. */
typedef struct ImageUser {
@@ -53,16 +52,34 @@ typedef struct ImageUser {
int frames; /* total amount of frames to use */
int offset, sfra; /* offset within movie, start frame in global time */
char fie_ima, cycl; /* fields/image in movie, cyclic flag */
- char ok, pad;
+ char ok;
+
+ char multiview_eye; /* multiview current eye - for internal use of drawing routines */
- short multi_index, layer, pass; /* listbase indices, for menu browsing or retrieve buffer */
+ short multi_index, view, layer, pass; /* listbase indices, for menu browsing or retrieve buffer */
short flag;
-
- int pad2;
+ short passtype;
} ImageUser;
+typedef struct ImageAnim {
+ struct ImageAnim *next, *prev;
+ struct anim *anim;
+} ImageAnim;
+
+typedef struct ImageView {
+ struct ImageView *next, *prev;
+ char name[64]; /* MAX_NAME */
+ char filepath[1024]; /* 1024 = FILE_MAX */
+} ImageView;
+
+typedef struct ImagePackedFile {
+ struct ImagePackedFile *next, *prev;
+ struct PackedFile *packedfile;
+ char filepath[1024]; /* 1024 = FILE_MAX */
+} ImagePackedFile;
+
typedef struct RenderSlot {
char name[64]; /* 64 = MAX_NAME */
} RenderSlot;
@@ -72,6 +89,7 @@ typedef struct RenderSlot {
#define IMA_ANIM_REFRESHED 2
/* #define IMA_DO_PREMUL 4 */
#define IMA_NEED_FRAME_RECALC 8
+#define IMA_SHOW_STEREO 16
typedef struct Image {
ID id;
@@ -82,13 +100,13 @@ typedef struct Image {
struct GPUTexture *gputexture; /* not written in file */
/* sources from: */
- struct anim *anim;
+ ListBase anims;
struct RenderResult *rr;
struct RenderResult *renders[8]; /* IMA_MAX_RENDER_SLOT */
short render_slot, last_render_slot;
-
- short ok, flag;
+
+ int flag;
short source, type;
int lastframe;
@@ -99,14 +117,16 @@ typedef struct Image {
unsigned int bindcode; /* only for current image... */
unsigned int *repbind; /* for repeat of parts of images */
- struct PackedFile *packedfile;
+ struct PackedFile *packedfile; /* deprecated */
+ struct ListBase packedfiles;
struct PreviewImage *preview;
/* game engine tile animation */
float lastupdate;
int lastused;
short animspeed;
- short pad2;
+
+ short ok;
/* for generated images */
int gen_x, gen_y;
@@ -121,7 +141,14 @@ typedef struct Image {
ColorManagedColorspaceSettings colorspace_settings;
char alpha_mode;
- char pad[7];
+ char pad[5];
+
+ /* Multiview */
+ char eye; /* for viewer node stereoscopy */
+ char views_format;
+ ListBase views;
+ struct Stereo3dFormat *stereo3d_format;
+
RenderSlot render_slots[8]; /* 8 = IMA_MAX_RENDER_SLOT */
} Image;
@@ -143,6 +170,9 @@ enum {
IMA_VIEW_AS_RENDER = (1 << 11),
IMA_IGNORE_ALPHA = (1 << 12),
IMA_DEINTERLACE = (1 << 13),
+ IMA_USE_VIEWS = (1 << 14),
+ IMA_IS_STEREO = (1 << 15),
+ IMA_IS_MULTIVIEW = (1 << 16), /* similar to stereo, but a more general case */
};
#if (DNA_DEPRECATED_GCC_POISON == 1)
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index 9ab6527daee..2a1a35d158a 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -263,6 +263,7 @@ typedef struct bNode {
*/
#define NODE_UPDATE 0xFFFF /* generic update flag (includes all others) */
#define NODE_UPDATE_ID 1 /* associated id data block has changed */
+#define NODE_UPDATE_OPERATOR 2 /* node update triggered from update operator */
/* Unique hash key for identifying node instances
* Defined as a struct because DNA does not support other typedefs.
diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h
index ac0803d494c..250bfe85ec2 100644
--- a/source/blender/makesdna/DNA_scene_types.h
+++ b/source/blender/makesdna/DNA_scene_types.h
@@ -47,6 +47,7 @@ extern "C" {
#include "DNA_ID.h"
#include "DNA_freestyle_types.h"
#include "DNA_gpu_types.h"
+#include "DNA_userdef_types.h"
struct CurveMapping;
struct Object;
@@ -249,6 +250,73 @@ typedef enum ScenePassType {
/* note, srl->passflag is treestore element 'nr' in outliner, short still... */
+/* View - MultiView */
+typedef struct SceneRenderView {
+ struct SceneRenderView *next, *prev;
+
+ char name[64]; /* MAX_NAME */
+ char suffix[64]; /* MAX_NAME */
+
+ int viewflag;
+ int pad[2];
+ char pad2[4];
+
+} SceneRenderView;
+
+/* srv->viewflag */
+#define SCE_VIEW_DISABLE (1<<0)
+
+/* scene.render.views_format */
+enum {
+ SCE_VIEWS_FORMAT_STEREO_3D = 0,
+ SCE_VIEWS_FORMAT_MULTIVIEW = 1,
+};
+
+/* ImageFormatData.views_output */
+enum {
+ R_IMF_VIEWS_INDIVIDUAL = 0,
+ R_IMF_VIEWS_STEREO_3D = 1,
+ R_IMF_VIEWS_MULTIVIEW = 2,
+};
+
+typedef struct Stereo3dFormat {
+ short flag;
+ char display_mode; /* encoding mode */
+ char anaglyph_type; /* anaglyph scheme for the user display */
+ char interlace_type; /* interlace type for the user display */
+ char pad[3];
+} Stereo3dFormat;
+
+/* Stereo3dFormat.display_mode */
+typedef enum eStereoDisplayMode {
+ S3D_DISPLAY_ANAGLYPH = 0,
+ S3D_DISPLAY_INTERLACE = 1,
+ S3D_DISPLAY_PAGEFLIP = 2,
+ S3D_DISPLAY_SIDEBYSIDE = 3,
+ S3D_DISPLAY_TOPBOTTOM = 4,
+} eStereoDisplayMode;
+
+/* Stereo3dFormat.flag */
+typedef enum eStereo3dFlag {
+ S3D_INTERLACE_SWAP = (1 << 0),
+ S3D_SIDEBYSIDE_CROSSEYED = (1 << 1),
+ S3D_SQUEEZED_FRAME = (1 << 2),
+} eStereo3dFlag;
+
+/* Stereo3dFormat.anaglyph_type */
+typedef enum eStereo3dAnaglyphType {
+ S3D_ANAGLYPH_REDCYAN = 0,
+ S3D_ANAGLYPH_GREENMAGENTA = 1,
+ S3D_ANAGLYPH_YELLOWBLUE = 2,
+} eStereo3dAnaglyphType;
+
+/* Stereo3dFormat.interlace_type */
+typedef enum eStereo3dInterlaceType {
+ S3D_INTERLACE_ROW = 0,
+ S3D_INTERLACE_COLUMN = 1,
+ S3D_INTERLACE_CHECKERBOARD = 2,
+} eStereo3dInterlaceType;
+
/* *************************************************************** */
/* Generic image format settings,
@@ -286,7 +354,11 @@ typedef struct ImageFormatData {
char jp2_flag;
char jp2_codec;
- char pad[6];
+ char pad[5];
+
+ /* Multiview */
+ char views_format;
+ Stereo3dFormat stereo3d_format;
/* color management */
ColorManagedViewSettings view_settings;
@@ -616,6 +688,12 @@ typedef struct RenderData {
int preview_start_resolution;
int pad;
+
+ /* MultiView */
+ ListBase views;
+ short actview;
+ short views_format;
+ short pad8[2];
} RenderData;
/* *************************************************************** */
@@ -810,6 +888,19 @@ enum {
#define UV_SCULPT_TOOL_RELAX_LAPLACIAN 1
#define UV_SCULPT_TOOL_RELAX_HC 2
+/* Stereo Flags */
+#define STEREO_RIGHT_NAME "right"
+#define STEREO_LEFT_NAME "left"
+#define STEREO_RIGHT_SUFFIX "_R"
+#define STEREO_LEFT_SUFFIX "_L"
+
+typedef enum StereoViews {
+ STEREO_LEFT_ID = 0,
+ STEREO_RIGHT_ID = 1,
+ STEREO_3D_ID = 2,
+ STEREO_MONO_ID = 3,
+} StereoViews;
+
/* Markers */
typedef struct TimeMarker {
@@ -1440,6 +1531,7 @@ typedef struct Scene {
#define R_TEXNODE_PREVIEW 0x40000
#define R_VIEWPORT_PREVIEW 0x80000
#define R_EXR_CACHE_FILE 0x100000
+#define R_MULTIVIEW 0x200000
/* r->stamp */
#define R_STAMP_TIME 0x0001
diff --git a/source/blender/makesdna/DNA_sequence_types.h b/source/blender/makesdna/DNA_sequence_types.h
index 46a91cf6ceb..76c27fd3547 100644
--- a/source/blender/makesdna/DNA_sequence_types.h
+++ b/source/blender/makesdna/DNA_sequence_types.h
@@ -53,6 +53,11 @@ struct MovieClip;
/* strlens; 256= FILE_MAXFILE, 768= FILE_MAXDIR */
+typedef struct StripAnim {
+ struct StripAnim *next, *prev;
+ struct anim *anim;
+} StripAnim;
+
typedef struct StripElem {
char name[256];
int orig_width, orig_height;
@@ -156,8 +161,7 @@ typedef struct Sequence {
struct Object *scene_camera; /* override scene camera */
struct MovieClip *clip; /* for MOVIECLIP strips */
struct Mask *mask; /* for MASK strips */
-
- struct anim *anim; /* for MOVIE strips */
+ ListBase anims; /* for MOVIE strips */
float effect_fader;
float speed_fader;
@@ -187,7 +191,11 @@ typedef struct Sequence {
int sfra; /* starting frame according to the timeline of the scene. */
char alpha_mode;
- char pad[3];
+ char pad[2];
+
+ /* Multiview */
+ char views_format;
+ struct Stereo3dFormat *stereo3d_format;
struct IDProperty *prop;
@@ -376,6 +384,7 @@ enum {
/* don't include Grease Pencil in OpenGL previews of Scene strips */
SEQ_SCENE_NO_GPENCIL = (1 << 28),
+ SEQ_USE_VIEWS = (1 << 29),
SEQ_INVALID_EFFECT = (1 << 31),
};
diff --git a/source/blender/makesdna/DNA_space_types.h b/source/blender/makesdna/DNA_space_types.h
index dff6d3cacb5..05127f176b4 100644
--- a/source/blender/makesdna/DNA_space_types.h
+++ b/source/blender/makesdna/DNA_space_types.h
@@ -500,6 +500,9 @@ typedef struct SpaceSeq {
struct bGPdata *gpd; /* grease-pencil data */
struct SequencerScopes scopes; /* different scoped displayed in space */
+
+ char multiview_eye; /* multiview current eye - for internal use */
+ char pad2[7];
} SpaceSeq;
diff --git a/source/blender/makesdna/DNA_view3d_types.h b/source/blender/makesdna/DNA_view3d_types.h
index 93e61487000..66ee1fe4242 100644
--- a/source/blender/makesdna/DNA_view3d_types.h
+++ b/source/blender/makesdna/DNA_view3d_types.h
@@ -215,8 +215,10 @@ typedef struct View3D {
/* drawflags, denoting state */
char zbuf, transp, xray;
+ char multiview_eye; /* multiview current eye - for internal use */
+
/* built-in shader effects (eGPUFXFlags) */
- char pad3[5];
+ char pad3[4];
/* note, 'fx_settings.dof' is currently _not_ allocated,
* instead set (temporarily) from camera */
@@ -228,9 +230,21 @@ typedef struct View3D {
/* XXX deprecated? */
struct bGPdata *gpd DNA_DEPRECATED; /* Grease-Pencil Data (annotation layers) */
+ /* multiview - stereo 3d */
+ short stereo3d_flag;
+ char stereo3d_camera;
+ char pad4;
+ float stereo3d_convergence_factor;
+ float stereo3d_volume_alpha;
+ float stereo3d_convergence_alpha;
} View3D;
+/* View3D->stereo_flag (short) */
+#define V3D_S3D_DISPCAMERAS (1 << 0)
+#define V3D_S3D_DISPPLANE (1 << 1)
+#define V3D_S3D_DISPVOLUME (1 << 2)
+
/* View3D->flag (short) */
/*#define V3D_DISPIMAGE 1*/ /*UNUSED*/
#define V3D_DISPBGPICS 2
diff --git a/source/blender/makesdna/DNA_windowmanager_types.h b/source/blender/makesdna/DNA_windowmanager_types.h
index ebbafb19aa5..dca2faf0d54 100644
--- a/source/blender/makesdna/DNA_windowmanager_types.h
+++ b/source/blender/makesdna/DNA_windowmanager_types.h
@@ -33,6 +33,7 @@
#include "DNA_listBase.h"
#include "DNA_vec_types.h"
+#include "DNA_userdef_types.h"
#include "DNA_ID.h"
@@ -56,6 +57,7 @@ struct PointerRNA;
struct ReportList;
struct Report;
struct uiLayout;
+struct Stereo3dFormat;
#define OP_MAX_TYPENAME 64
#define KMAP_MAX_NAME 64
@@ -206,7 +208,7 @@ typedef struct wmWindow {
struct wmIMEData *ime_data;
int drawmethod, drawfail; /* internal for wm_draw.c only */
- void *drawdata; /* internal for wm_draw.c only */
+ ListBase drawdata; /* internal for wm_draw.c only */
ListBase queue; /* all events (ghost level events were handled) */
ListBase handlers; /* window+screen handlers, handled last */
@@ -214,6 +216,8 @@ typedef struct wmWindow {
ListBase subwindows; /* opengl stuff for sub windows, see notes in wm_subwindow.c */
ListBase gesture; /* gesture stuff */
+
+ struct Stereo3dFormat *stereo3d_format; /* properties for stereoscopic displays */
} wmWindow;
#ifdef ime_data
diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h
index 8aa1bc59b43..cff9e424f31 100644
--- a/source/blender/makesrna/RNA_access.h
+++ b/source/blender/makesrna/RNA_access.h
@@ -172,6 +172,7 @@ extern StructRNA RNA_CompositorNodeSepYCCA;
extern StructRNA RNA_CompositorNodeSepYUVA;
extern StructRNA RNA_CompositorNodeSetAlpha;
extern StructRNA RNA_CompositorNodeSplitViewer;
+extern StructRNA RNA_CompositorNodeSwitchView;
extern StructRNA RNA_CompositorNodeTexture;
extern StructRNA RNA_CompositorNodeTime;
extern StructRNA RNA_CompositorNodeTonemap;
@@ -566,6 +567,7 @@ extern StructRNA RNA_SpeedControlSequence;
extern StructRNA RNA_Spline;
extern StructRNA RNA_SplineIKConstraint;
extern StructRNA RNA_SpotLamp;
+extern StructRNA RNA_Stereo3dDisplay;
extern StructRNA RNA_StretchToConstraint;
extern StructRNA RNA_StringProperty;
extern StructRNA RNA_Struct;
diff --git a/source/blender/makesrna/RNA_enum_types.h b/source/blender/makesrna/RNA_enum_types.h
index 4d836601ac4..d3d3578e8b0 100644
--- a/source/blender/makesrna/RNA_enum_types.h
+++ b/source/blender/makesrna/RNA_enum_types.h
@@ -72,6 +72,13 @@ extern EnumPropertyItem normal_space_items[];
extern EnumPropertyItem normal_swizzle_items[];
extern EnumPropertyItem bake_save_mode_items[];
+extern EnumPropertyItem views_format_items[];
+extern EnumPropertyItem views_format_multilayer_items[];
+extern EnumPropertyItem views_format_multiview_items[];
+extern EnumPropertyItem stereo3d_display_items[];
+extern EnumPropertyItem stereo3d_anaglyph_type_items[];
+extern EnumPropertyItem stereo3d_interlace_type_items[];
+
extern EnumPropertyItem exr_codec_items[];
extern EnumPropertyItem color_sets_items[];
diff --git a/source/blender/makesrna/intern/rna_camera.c b/source/blender/makesrna/intern/rna_camera.c
index 31e991dd2b6..b63e2f97da8 100644
--- a/source/blender/makesrna/intern/rna_camera.c
+++ b/source/blender/makesrna/intern/rna_camera.c
@@ -89,6 +89,60 @@ static void rna_Camera_update(Main *UNUSED(bmain), Scene *UNUSED(scene), Pointer
#else
+static void rna_def_camera_stereo_data(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ static EnumPropertyItem convergence_mode_items[] = {
+ {CAM_S3D_OFFAXIS, "OFFAXIS", 0, "Off-Axis", "Off-axis frustrums converging in a plane"},
+ {CAM_S3D_PARALLEL, "PARALLEL", 0, "Parallel", "Parallel cameras with no convergence"},
+ {CAM_S3D_TOE, "TOE", 0, "Toe-in", "Rotated cameras, looking at the convergence distance"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ static EnumPropertyItem pivot_items[] = {
+ {CAM_S3D_PIVOT_LEFT, "LEFT", 0, "Left", ""},
+ {CAM_S3D_PIVOT_RIGHT, "RIGHT", 0, "Right", ""},
+ {CAM_S3D_PIVOT_CENTER, "CENTER", 0, "Center", ""},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ srna = RNA_def_struct(brna, "CameraStereoData", NULL);
+ RNA_def_struct_sdna(srna, "CameraStereoSettings");
+ RNA_def_struct_nested(brna, srna, "Camera");
+ RNA_def_struct_ui_text(srna, "Stereo", "Stereoscopy settings for a Camera datablock");
+
+ prop = RNA_def_property(srna, "convergence_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, convergence_mode_items);
+ RNA_def_property_ui_text(prop, "Mode", "");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
+
+ prop = RNA_def_property(srna, "pivot", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, pivot_items);
+ RNA_def_property_ui_text(prop, "Pivot", "");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
+
+ prop = RNA_def_property(srna, "interocular_distance", PROP_FLOAT, PROP_DISTANCE);
+ RNA_def_property_range(prop, 0.0f, 100.0f);
+ RNA_def_property_ui_range(prop, 0.0f, 1.f, 1, 2);
+ RNA_def_property_ui_text(prop, "Interocular Distance", "Set the distance between the eyes - the stereo plane distance / 30 should be fine");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
+
+ prop = RNA_def_property(srna, "convergence_distance", PROP_FLOAT, PROP_DISTANCE);
+ RNA_def_property_range(prop, 0.00001f, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0f, 15.f, 1, 2);
+ RNA_def_property_ui_text(prop, "Convergence Plane Distance", "The converge point for the stereo cameras (often the distance between a projector and the projection screen)");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
+
+ prop = RNA_def_property(srna, "viewport_convergence", PROP_FLOAT, PROP_DISTANCE);
+ RNA_def_property_float_sdna(prop, NULL, "convergence_distance");
+ RNA_def_property_range(prop, 0.00001f, FLT_MAX);
+ RNA_def_property_ui_range(prop, 0.0f, 15.f, 1, 2);
+ RNA_def_property_ui_text(prop, "Viewport Convergence", "Preview convergence distance for the stereo effect in the viewport. It doesn't affect the render!");
+ RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
+}
+
void RNA_def_camera(BlenderRNA *brna)
{
StructRNA *srna;
@@ -241,6 +295,13 @@ void RNA_def_camera(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "DOF Distance", "Distance to the focus point for depth of field");
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, NULL);
+ /* Stereo Settings */
+ prop = RNA_def_property(srna, "stereo", PROP_POINTER, PROP_NONE);
+ RNA_def_property_flag(prop, PROP_NEVER_NULL);
+ RNA_def_property_pointer_sdna(prop, NULL, "stereo");
+ RNA_def_property_struct_type(prop, "CameraStereoData");
+ RNA_def_property_ui_text(prop, "Stereo", "");
+
/* flag */
prop = RNA_def_property(srna, "show_limits", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", CAM_SHOWLIMITS);
@@ -298,6 +359,12 @@ void RNA_def_camera(BlenderRNA *brna)
rna_def_animdata_common(srna);
+ /* Nested Data */
+ RNA_define_animate_sdna(true);
+
+ /* *** Animated *** */
+ rna_def_camera_stereo_data(brna);
+
/* Camera API */
RNA_api_camera(srna);
}
diff --git a/source/blender/makesrna/intern/rna_color.c b/source/blender/makesrna/intern/rna_color.c
index e6193b31260..0c871317d04 100644
--- a/source/blender/makesrna/intern/rna_color.c
+++ b/source/blender/makesrna/intern/rna_color.c
@@ -620,10 +620,8 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *UNUSED(bmain)
}
if (seq_found) {
- if (seq->anim) {
- IMB_free_anim(seq->anim);
- seq->anim = NULL;
- }
+ BKE_sequence_free_anim(seq);
+
if (seq->strip->proxy && seq->strip->proxy->anim) {
IMB_free_anim(seq->strip->proxy->anim);
seq->strip->proxy->anim = NULL;
@@ -635,10 +633,7 @@ static void rna_ColorManagedColorspaceSettings_reload_update(Main *UNUSED(bmain)
else {
SEQ_BEGIN(scene->ed, seq);
{
- if (seq->anim) {
- IMB_free_anim(seq->anim);
- seq->anim = NULL;
- }
+ BKE_sequence_free_anim(seq);
}
SEQ_END;
diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c
index cf2973c49a7..bac5c4aadba 100644
--- a/source/blender/makesrna/intern/rna_image.c
+++ b/source/blender/makesrna/intern/rna_image.c
@@ -35,6 +35,7 @@
#include "BKE_depsgraph.h"
#include "BKE_image.h"
+#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_enum_types.h"
@@ -137,6 +138,23 @@ static void rna_Image_colormanage_update(Main *UNUSED(bmain), Scene *UNUSED(scen
WM_main_add_notifier(NC_IMAGE | NA_EDITED, &ima->id);
}
+static void rna_Image_views_format_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr)
+{
+ Image *ima = ptr->id.data;
+ ImBuf *ibuf;
+ void *lock;
+
+ ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
+
+ if (ibuf) {
+ ImageUser iuser = {NULL};
+ iuser.scene = scene;
+ BKE_image_signal(ima, &iuser, IMA_SIGNAL_FREE);
+ }
+
+ BKE_image_release_ibuf(ima, ibuf, lock);
+}
+
static void rna_ImageUser_update(Main *UNUSED(bmain), Scene *scene, PointerRNA *ptr)
{
ImageUser *iuser = ptr->data;
@@ -295,17 +313,16 @@ static int rna_Image_frame_duration_get(PointerRNA *ptr)
Image *ima = ptr->id.data;
int duration = 1;
- if (!ima->anim) {
+ if (BKE_image_has_anim(ima)) {
+ duration = IMB_anim_get_duration(((ImageAnim *)ima->anims.first)->anim, IMB_TC_RECORD_RUN);
+ }
+ else {
/* acquire ensures ima->anim is set, if possible! */
void *lock;
ImBuf *ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
BKE_image_release_ibuf(ima, ibuf, lock);
}
- if (ima->anim) {
- duration = IMB_anim_get_duration(ima->anim, IMB_TC_RECORD_RUN);
- }
-
return duration;
}
@@ -409,6 +426,19 @@ static int rna_Image_is_float_get(PointerRNA *ptr)
return is_float;
}
+static PointerRNA rna_Image_packed_file_get(PointerRNA *ptr)
+{
+ Image *ima = (Image *)ptr->id.data;
+
+ if (BKE_image_has_packedfile(ima)) {
+ ImagePackedFile *imapf = ima->packedfiles.first;
+ return rna_pointer_inherit_refine(ptr, &RNA_PackedFile, imapf->packedfile);
+ }
+ else {
+ return PointerRNA_NULL;
+ }
+}
+
static void rna_Image_render_slots_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
{
Image *image = (Image *)ptr->id.data;
@@ -512,6 +542,32 @@ static void rna_def_imageuser(BlenderRNA *brna)
RNA_def_property_int_sdna(prop, NULL, "pass");
RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* image_multi_cb */
RNA_def_property_ui_text(prop, "Pass", "Pass in multilayer image");
+
+ prop = RNA_def_property(srna, "multilayer_view", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_sdna(prop, NULL, "view");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE); /* image_multi_cb */
+ RNA_def_property_ui_text(prop, "View", "View in multilayer image");
+}
+
+/* image.packed_files */
+static void rna_def_image_packed_files(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "ImagePackedFile", NULL);
+ RNA_def_struct_sdna(srna, "ImagePackedFile");
+
+ prop = RNA_def_property(srna, "packed_file", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "packedfile");
+ RNA_def_property_ui_text(prop, "Packed File", "");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+
+ prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH);
+ RNA_def_property_string_sdna(prop, NULL, "filepath");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_struct_name_property(srna, prop);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
}
static void rna_def_render_slot(BlenderRNA *brna)
@@ -613,15 +669,23 @@ static void rna_def_image(BlenderRNA *brna)
RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, NULL);
prop = RNA_def_property(srna, "packed_file", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "PackedFile");
RNA_def_property_pointer_sdna(prop, NULL, "packedfile");
- RNA_def_property_ui_text(prop, "Packed File", "");
-
+ RNA_def_property_pointer_funcs(prop, "rna_Image_packed_file_get", NULL, NULL, NULL);
+ RNA_def_property_ui_text(prop, "Packed File", "First packed file of the image");
+
+ prop = RNA_def_property(srna, "packed_files", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "packedfiles", NULL);
+ RNA_def_property_struct_type(prop, "ImagePackedFile");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Packed Files", "Collection of packed images");
+
prop = RNA_def_property(srna, "field_order", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_bitflag_sdna(prop, NULL, "flag");
RNA_def_property_enum_items(prop, prop_field_order_items);
RNA_def_property_ui_text(prop, "Field Order", "Order of video fields (select which lines are displayed first)");
RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, NULL);
-
+
/* booleans */
prop = RNA_def_property(srna, "use_fields", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", IMA_FIELDS);
@@ -645,6 +709,21 @@ static void rna_def_image(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Deinterlace", "Deinterlace movie file on load");
RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_reload_update");
+ prop = RNA_def_property(srna, "use_multiview", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", IMA_USE_VIEWS);
+ RNA_def_property_ui_text(prop, "Use Multi-View", "Use Multiple Views (when available)");
+ RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_views_format_update");
+
+ prop = RNA_def_property(srna, "is_stereo_3d", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", IMA_IS_STEREO);
+ RNA_def_property_ui_text(prop, "Stereo 3D", "Image has left and right views");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+
+ prop = RNA_def_property(srna, "is_multiview", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", IMA_IS_MULTIVIEW);
+ RNA_def_property_ui_text(prop, "Multiple Views", "Image has more than one view");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+
prop = RNA_def_property(srna, "is_dirty", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_funcs(prop, "rna_Image_dirty_get", NULL);
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
@@ -825,6 +904,19 @@ static void rna_def_image(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Alpha Mode", "Representation of alpha information in the RGBA pixels");
RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_colormanage_update");
+ /* multiview */
+ prop = RNA_def_property(srna, "views_format", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "views_format");
+ RNA_def_property_enum_items(prop, views_format_items);
+ RNA_def_property_ui_text(prop, "Views Format", "Mode to load image views");
+ RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Image_views_format_update");
+
+ prop = RNA_def_property(srna, "stereo_3d_format", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "stereo3d_format");
+ RNA_def_property_flag(prop, PROP_NEVER_NULL);
+ RNA_def_property_struct_type(prop, "Stereo3dFormat");
+ RNA_def_property_ui_text(prop, "Stereo 3D Format", "Settings for stereo 3d");
+
RNA_api_image(srna);
}
@@ -834,6 +926,7 @@ void RNA_def_image(BlenderRNA *brna)
rna_def_render_slots(brna);
rna_def_image(brna);
rna_def_imageuser(brna);
+ rna_def_image_packed_files(brna);
}
#endif
diff --git a/source/blender/makesrna/intern/rna_image_api.c b/source/blender/makesrna/intern/rna_image_api.c
index 7fb5885f709..47be7bec432 100644
--- a/source/blender/makesrna/intern/rna_image_api.c
+++ b/source/blender/makesrna/intern/rna_image_api.c
@@ -167,7 +167,7 @@ static void rna_Image_pack(
image->packedfile = newPackedFileMemory(data_dup, data_len);
}
else {
- image->packedfile = newPackedFile(reports, image->name, ID_BLEND_PATH(bmain, &image->id));
+ BKE_image_packfiles(reports, image, ID_BLEND_PATH(bmain, &image->id));
}
}
diff --git a/source/blender/makesrna/intern/rna_main_api.c b/source/blender/makesrna/intern/rna_main_api.c
index 1d232720fc5..59c3befad9e 100644
--- a/source/blender/makesrna/intern/rna_main_api.c
+++ b/source/blender/makesrna/intern/rna_main_api.c
@@ -341,10 +341,10 @@ static void rna_Main_lamps_remove(Main *bmain, ReportList *reports, PointerRNA *
}
}
-static Image *rna_Main_images_new(Main *bmain, const char *name, int width, int height, int alpha, int float_buffer)
+static Image *rna_Main_images_new(Main *bmain, const char *name, int width, int height, int alpha, int float_buffer, int stereo3d)
{
float color[4] = {0.0, 0.0, 0.0, 1.0};
- Image *image = BKE_image_add_generated(bmain, width, height, name, alpha ? 32 : 24, float_buffer, 0, color);
+ Image *image = BKE_image_add_generated(bmain, width, height, name, alpha ? 32 : 24, float_buffer, 0, color, stereo3d);
id_us_min(&image->id);
return image;
}
@@ -1174,6 +1174,7 @@ void RNA_def_main_images(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_property_flag(parm, PROP_REQUIRED);
RNA_def_boolean(func, "alpha", 0, "Alpha", "Use alpha channel");
RNA_def_boolean(func, "float_buffer", 0, "Float Buffer", "Create an image with floating point color");
+ RNA_def_boolean(func, "stereo3d", 0, "Stereo 3D", "Create left and right views");
/* return type */
parm = RNA_def_pointer(func, "image", "Image", "", "New image datablock");
RNA_def_function_return(func, parm);
diff --git a/source/blender/makesrna/intern/rna_nodetree.c b/source/blender/makesrna/intern/rna_nodetree.c
index e577f7635f1..075b139c6c4 100644
--- a/source/blender/makesrna/intern/rna_nodetree.c
+++ b/source/blender/makesrna/intern/rna_nodetree.c
@@ -2616,6 +2616,70 @@ static EnumPropertyItem *rna_Node_image_layer_itemf(bContext *UNUSED(C), Pointer
return item;
}
+static int rna_Node_image_has_layers_get(PointerRNA *ptr)
+{
+ bNode *node = (bNode *)ptr->data;
+ Image *ima = (Image *)node->id;
+
+ if (!ima || !(ima->rr)) return 0;
+
+ return RE_layers_have_name(ima->rr);
+}
+
+static int rna_Node_image_has_views_get(PointerRNA *ptr)
+{
+ bNode *node = (bNode *)ptr->data;
+ Image *ima = (Image *)node->id;
+
+ if (!ima || !(ima->rr)) return 0;
+
+ return BLI_listbase_count_ex(&ima->rr->views, 2) > 1;
+}
+
+static EnumPropertyItem *renderresult_views_add_enum(RenderView *rv)
+{
+ EnumPropertyItem *item = NULL;
+ EnumPropertyItem tmp = {0, "ALL", 0, "All", ""};
+ int i = 1, totitem = 0;
+
+ /* option to use all views */
+ RNA_enum_item_add(&item, &totitem, &tmp);
+
+ while (rv) {
+ tmp.identifier = rv->name;
+ /* little trick: using space char instead empty string makes the item selectable in the dropdown */
+ if (rv->name[0] == '\0')
+ tmp.name = " ";
+ else
+ tmp.name = rv->name;
+ tmp.value = i++;
+ RNA_enum_item_add(&item, &totitem, &tmp);
+ rv = rv->next;
+ }
+
+ RNA_enum_item_end(&item, &totitem);
+
+ return item;
+}
+
+static EnumPropertyItem *rna_Node_image_view_itemf(bContext *UNUSED(C), PointerRNA *ptr,
+ PropertyRNA *UNUSED(prop), bool *free)
+{
+ bNode *node = (bNode *)ptr->data;
+ Image *ima = (Image *)node->id;
+ EnumPropertyItem *item = NULL;
+ RenderView *rv;
+
+ if (!ima || !(ima->rr)) return NULL;
+
+ rv = ima->rr->views.first;
+ item = renderresult_views_add_enum(rv);
+
+ *free = true;
+
+ return item;
+}
+
static EnumPropertyItem *rna_Node_scene_layer_itemf(bContext *UNUSED(C), PointerRNA *ptr,
PropertyRNA *UNUSED(prop), bool *r_free)
{
@@ -2907,6 +2971,11 @@ static EnumPropertyItem prop_image_layer_items[] = {
{0, NULL, 0, NULL, NULL}
};
+static EnumPropertyItem prop_image_view_items[] = {
+ { 0, "ALL", 0, "All", ""},
+ {0, NULL, 0, NULL, NULL}
+};
+
static EnumPropertyItem prop_scene_layer_items[] = {
{ 0, "PLACEHOLDER", 0, "Placeholder", ""},
{0, NULL, 0, NULL, NULL}
@@ -4263,6 +4332,24 @@ static void def_node_image_user(StructRNA *srna)
RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
RNA_def_property_ui_text(prop, "Layer", "");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_image_layer_update");
+
+ prop = RNA_def_property(srna, "has_layers", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(prop, "rna_Node_image_has_layers_get", NULL);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Has Layers", "True if this image has any named layer");
+
+ prop = RNA_def_property(srna, "view", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "view");
+ RNA_def_property_enum_items(prop, prop_image_view_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_Node_image_view_itemf");
+ RNA_def_property_flag(prop, PROP_ENUM_NO_TRANSLATE);
+ RNA_def_property_ui_text(prop, "View", "");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+
+ prop = RNA_def_property(srna, "has_views", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(prop, "rna_Node_image_has_views_get", NULL);
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_property_ui_text(prop, "Has View", "True if this image has multiple views");
}
static void def_cmp_image(StructRNA *srna)
@@ -5811,6 +5898,16 @@ static void def_cmp_switch(StructRNA *srna)
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
}
+static void def_cmp_switch_view(StructRNA *srna)
+{
+ PropertyRNA *prop;
+
+ prop = RNA_def_property(srna, "check", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "custom1", 0);
+ RNA_def_property_ui_text(prop, "Switch", "Off: first socket, On: second socket");
+ RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_update");
+}
+
static void def_cmp_colorcorrection(StructRNA *srna)
{
PropertyRNA *prop;
diff --git a/source/blender/makesrna/intern/rna_render.c b/source/blender/makesrna/intern/rna_render.c
index 124a83a5f30..94fabb857e3 100644
--- a/source/blender/makesrna/intern/rna_render.c
+++ b/source/blender/makesrna/intern/rna_render.c
@@ -27,6 +27,7 @@
#include <stdlib.h>
#include "DNA_scene_types.h"
+#include "DNA_object_types.h"
#include "BLI_utildefines.h"
#include "BLI_path_util.h"
@@ -322,38 +323,35 @@ static PointerRNA rna_RenderEngine_render_get(PointerRNA *ptr)
}
}
-static void rna_RenderResult_layers_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+static PointerRNA rna_RenderEngine_camera_override_get(PointerRNA *ptr)
{
- RenderResult *rr = (RenderResult *)ptr->data;
- rna_iterator_listbase_begin(iter, &rr->layers, NULL);
-}
+ RenderEngine *engine = (RenderEngine *)ptr->data;
-static void rna_RenderLayer_passes_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
-{
- RenderLayer *rl = (RenderLayer *)ptr->data;
- rna_iterator_listbase_begin(iter, &rl->passes, NULL);
+ if (engine->re) {
+ Object *cam = RE_GetCamera(engine->re);
+ return rna_pointer_inherit_refine(ptr, &RNA_Object, cam);
+ }
+ else {
+ return rna_pointer_inherit_refine(ptr, &RNA_Object, engine->camera_override);
+ }
}
-static int rna_RenderLayer_rect_get_length(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION])
+static void rna_RenderResult_views_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
{
- RenderLayer *rl = (RenderLayer *)ptr->data;
-
- length[0] = rl->rectx * rl->recty;
- length[1] = 4;
-
- return length[0] * length[1];
+ RenderResult *rr = (RenderResult *)ptr->data;
+ rna_iterator_listbase_begin(iter, &rr->views, NULL);
}
-static void rna_RenderLayer_rect_get(PointerRNA *ptr, float *values)
+static void rna_RenderResult_layers_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
{
- RenderLayer *rl = (RenderLayer *)ptr->data;
- memcpy(values, rl->rectf, sizeof(float) * rl->rectx * rl->recty * 4);
+ RenderResult *rr = (RenderResult *)ptr->data;
+ rna_iterator_listbase_begin(iter, &rr->layers, NULL);
}
-void rna_RenderLayer_rect_set(PointerRNA *ptr, const float *values)
+static void rna_RenderLayer_passes_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
{
RenderLayer *rl = (RenderLayer *)ptr->data;
- memcpy(rl->rectf, values, sizeof(float) * rl->rectx * rl->recty * 4);
+ rna_iterator_listbase_begin(iter, &rl->passes, NULL);
}
static int rna_RenderPass_rect_get_length(PointerRNA *ptr, int length[RNA_MAX_ARRAY_DIMENSION])
@@ -384,6 +382,18 @@ static PointerRNA rna_BakePixel_next_get(PointerRNA *ptr)
return rna_pointer_inherit_refine(ptr, &RNA_BakePixel, bp + 1);
}
+static RenderPass *rna_RenderPass_find_by_type(RenderLayer *rl, int passtype, const char *view)
+{
+ RenderPass *rp;
+ for (rp = rl->passes.first; rp; rp = rp->next) {
+ if (rp->passtype == passtype) {
+ if (STREQ(rp->view, view))
+ return rp;
+ }
+ }
+ return NULL;
+}
+
#else /* RNA_RUNTIME */
static void rna_def_render_engine(BlenderRNA *brna)
@@ -467,6 +477,7 @@ static void rna_def_render_engine(BlenderRNA *brna)
prop = RNA_def_int(func, "h", 0, 0, INT_MAX, "Height", "", 0, INT_MAX);
RNA_def_property_flag(prop, PROP_REQUIRED);
RNA_def_string(func, "layer", NULL, 0, "Layer", "Single layer to get render result for"); /* NULL ok here */
+ RNA_def_string(func, "view", NULL, 0, "View", "Single view to get render result for"); /* NULL ok here */
prop = RNA_def_pointer(func, "result", "RenderResult", "Result", "");
RNA_def_function_return(func, prop);
@@ -487,6 +498,22 @@ static void rna_def_render_engine(BlenderRNA *brna)
prop = RNA_def_boolean(func, "do_break", 0, "Break", "");
RNA_def_function_return(func, prop);
+ func = RNA_def_function(srna, "active_view_set", "RE_engine_active_view_set");
+ RNA_def_string(func, "view", NULL, 0, "View", "Single view to set as active"); /* NULL ok here */
+ RNA_def_property_flag(prop, PROP_REQUIRED);
+
+ func = RNA_def_function(srna, "camera_shift_x", "RE_engine_get_camera_shift_x");
+ prop = RNA_def_pointer(func, "camera", "Object", "", "");
+ RNA_def_property_flag(prop, PROP_REQUIRED);
+ prop = RNA_def_float(func, "shift_x", 0.0f, 0.0f, FLT_MAX, "Shift X", "", 0.0f, FLT_MAX);
+ RNA_def_function_return(func, prop);
+
+ func = RNA_def_function(srna, "camera_model_matrix", "RE_engine_get_camera_model_matrix");
+ prop = RNA_def_pointer(func, "camera", "Object", "", "");
+ RNA_def_property_flag(prop, PROP_REQUIRED);
+ prop = RNA_def_float_matrix(func, "r_model_matrix", 4, 4, NULL, 0.0f, 0.0f, "Model Matrix", "Normalized camera model matrix", 0.0f, 0.0f);
+ RNA_def_property_flag(prop, PROP_REQUIRED);
+
func = RNA_def_function(srna, "update_stats", "RE_engine_update_stats");
RNA_def_function_ui_description(func, "Update and signal to redraw render status text");
prop = RNA_def_string(func, "stats", NULL, 0, "Stats", "");
@@ -548,7 +575,7 @@ static void rna_def_render_engine(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "flag", RE_ENGINE_PREVIEW);
prop = RNA_def_property(srna, "camera_override", PROP_POINTER, PROP_NONE);
- RNA_def_property_pointer_sdna(prop, NULL, "camera_override");
+ RNA_def_property_pointer_funcs(prop, "rna_RenderEngine_camera_override_get", NULL, NULL, NULL);
RNA_def_property_struct_type(prop, "Object");
prop = RNA_def_property(srna, "layer_override", PROP_BOOLEAN, PROP_LAYER_MEMBER);
@@ -647,9 +674,56 @@ static void rna_def_render_result(BlenderRNA *brna)
"rna_iterator_listbase_end", "rna_iterator_listbase_get",
NULL, NULL, NULL, NULL);
+ parm = RNA_def_property(srna, "views", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(parm, "RenderView");
+ RNA_def_property_collection_funcs(parm, "rna_RenderResult_views_begin", "rna_iterator_listbase_next",
+ "rna_iterator_listbase_end", "rna_iterator_listbase_get",
+ NULL, NULL, NULL, NULL);
+
+ RNA_define_verify_sdna(1);
+}
+
+static void rna_def_render_view(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "RenderView", NULL);
+ RNA_def_struct_ui_text(srna, "Render View", "");
+
+ RNA_define_verify_sdna(0);
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "name");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+ RNA_def_struct_name_property(srna, prop);
+
RNA_define_verify_sdna(1);
}
+static void rna_def_render_passes(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ RNA_def_property_srna(cprop, "RenderPasses");
+ srna = RNA_def_struct(brna, "RenderPasses", NULL);
+ RNA_def_struct_sdna(srna, "RenderLayer");
+ RNA_def_struct_ui_text(srna, "Render Passes", "Collection of render passes");
+
+ func = RNA_def_function(srna, "find_by_type", "rna_RenderPass_find_by_type");
+ RNA_def_function_ui_description(func, "Get the render pass for a given type and view");
+ parm = RNA_def_enum(func, "pass_type", render_pass_type_items, SCE_PASS_COMBINED, "Pass", "");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ parm = RNA_def_string(func, "view", NULL, 0, "View", "Render view to get pass from"); /* NULL ok here */
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ parm = RNA_def_pointer(func, "render_pass", "RenderPass", "", "The matching render pass");
+ RNA_def_function_return(func, parm);
+
+}
+
static void rna_def_render_layer(BlenderRNA *brna)
{
StructRNA *srna;
@@ -679,12 +753,7 @@ static void rna_def_render_layer(BlenderRNA *brna)
RNA_def_property_collection_funcs(prop, "rna_RenderLayer_passes_begin", "rna_iterator_listbase_next",
"rna_iterator_listbase_end", "rna_iterator_listbase_get",
NULL, NULL, NULL, NULL);
-
- prop = RNA_def_property(srna, "rect", PROP_FLOAT, PROP_NONE);
- RNA_def_property_flag(prop, PROP_DYNAMIC);
- RNA_def_property_multi_array(prop, 2, NULL);
- RNA_def_property_dynamic_array_funcs(prop, "rna_RenderLayer_rect_get_length");
- RNA_def_property_float_funcs(prop, "rna_RenderLayer_rect_get", "rna_RenderLayer_rect_set", NULL);
+ rna_def_render_passes(brna, prop);
RNA_define_verify_sdna(1);
}
@@ -728,6 +797,10 @@ static void rna_def_render_pass(BlenderRNA *brna)
RNA_def_property_dynamic_array_funcs(prop, "rna_RenderPass_rect_get_length");
RNA_def_property_float_funcs(prop, "rna_RenderPass_rect_get", "rna_RenderPass_rect_set", NULL);
+ prop = RNA_def_property(srna, "view_id", PROP_INT, PROP_NONE);
+ RNA_def_property_int_sdna(prop, NULL, "view_id");
+ RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+
prop = RNA_def_property(srna, "debug_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_sdna(prop, NULL, "debug_type");
RNA_def_property_enum_items(prop, render_pass_debug_type_items);
@@ -783,6 +856,7 @@ void RNA_def_render(BlenderRNA *brna)
{
rna_def_render_engine(brna);
rna_def_render_result(brna);
+ rna_def_render_view(brna);
rna_def_render_layer(brna);
rna_def_render_pass(brna);
rna_def_render_bake_pixel(brna);
diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c
index c9d1a4b6429..a59724f4a16 100644
--- a/source/blender/makesrna/intern/rna_scene.c
+++ b/source/blender/makesrna/intern/rna_scene.c
@@ -36,6 +36,8 @@
#include "DNA_userdef_types.h"
#include "DNA_world_types.h"
+#include "IMB_imbuf_types.h"
+
#include "BLI_math.h"
#include "BLF_translation.h"
@@ -217,13 +219,13 @@ EnumPropertyItem snap_uv_element_items[] = {
#endif
#ifdef WITH_OPENEXR
-# define R_IMF_ENUM_EXR_MULTI {R_IMF_IMTYPE_MULTILAYER, "OPEN_EXR_MULTILAYER", ICON_FILE_IMAGE, \
+# define R_IMF_ENUM_EXR_MULTILAYER {R_IMF_IMTYPE_MULTILAYER, "OPEN_EXR_MULTILAYER", ICON_FILE_IMAGE, \
"OpenEXR MultiLayer", \
"Output image in multilayer OpenEXR format"},
# define R_IMF_ENUM_EXR {R_IMF_IMTYPE_OPENEXR, "OPEN_EXR", ICON_FILE_IMAGE, "OpenEXR", \
"Output image in OpenEXR format"},
#else
-# define R_IMF_ENUM_EXR_MULTI
+# define R_IMF_ENUM_EXR_MULTILAYER
# define R_IMF_ENUM_EXR
#endif
@@ -252,7 +254,7 @@ EnumPropertyItem snap_uv_element_items[] = {
{0, "", 0, " ", NULL}, \
R_IMF_ENUM_CINEON \
R_IMF_ENUM_DPX \
- R_IMF_ENUM_EXR_MULTI \
+ R_IMF_ENUM_EXR_MULTILAYER \
R_IMF_ENUM_EXR \
R_IMF_ENUM_HDR \
R_IMF_ENUM_TIFF \
@@ -336,6 +338,58 @@ EnumPropertyItem bake_save_mode_items[] = {
{0, NULL, 0, NULL, NULL}
};
+#define R_IMF_VIEWS_ENUM_IND {R_IMF_VIEWS_INDIVIDUAL, "INDIVIDUAL", 0, "Individual", "Individual files for each view with the prefix as defined by the scene views"},
+#define R_IMF_VIEWS_ENUM_S3D {R_IMF_VIEWS_STEREO_3D, "STEREO_3D", 0, "Stereo 3D", "Single file with an encoded stereo pair"},
+#define R_IMF_VIEWS_ENUM_MV {R_IMF_VIEWS_MULTIVIEW, "MULTIVIEW", 0, "Multi-View", "Single file with all the views"},
+
+EnumPropertyItem views_format_items[] = {
+ R_IMF_VIEWS_ENUM_IND
+ R_IMF_VIEWS_ENUM_S3D
+ {0, NULL, 0, NULL, NULL}
+};
+
+EnumPropertyItem views_format_multilayer_items[] = {
+ R_IMF_VIEWS_ENUM_IND
+ R_IMF_VIEWS_ENUM_MV
+ {0, NULL, 0, NULL, NULL}
+};
+
+EnumPropertyItem views_format_multiview_items[] = {
+ R_IMF_VIEWS_ENUM_IND
+ R_IMF_VIEWS_ENUM_S3D
+ R_IMF_VIEWS_ENUM_MV
+ {0, NULL, 0, NULL, NULL}
+};
+
+#undef R_IMF_VIEWS_ENUM_IND
+#undef R_IMF_VIEWS_ENUM_S3D
+#undef R_IMF_VIEWS_ENUM_MV
+
+EnumPropertyItem stereo3d_display_items[] = {
+ {S3D_DISPLAY_ANAGLYPH, "ANAGLYPH", 0, "Anaglyph", "Render two differently filtered colored images for each eye. Anaglyph glasses are required"},
+ {S3D_DISPLAY_INTERLACE, "INTERLACE", 0, "Interlace", "Render two images for each eye into one interlaced image. 3D-ready monitor is requiered"},
+#ifdef DEBUG /* MULTIVIEW_TODO: quadbuffer mode not fully working */
+ {S3D_DISPLAY_PAGEFLIP, "TIMESEQUENTIAL", 0, "Time Sequential", "Renders alternate eyes (also known as pageflip). It requires Quadbuffer support in the graphic card"},
+#endif
+ {S3D_DISPLAY_SIDEBYSIDE, "SIDEBYSIDE", 0, "Side-by-Side", "Render images for left and right eye side-by-side"},
+ {S3D_DISPLAY_TOPBOTTOM, "TOPBOTTOM", 0, "Top-Bottom", "Render images for left and right eye one above another"},
+ {0, NULL, 0, NULL, NULL}
+};
+
+EnumPropertyItem stereo3d_anaglyph_type_items[] = {
+ {S3D_ANAGLYPH_REDCYAN, "RED_CYAN", 0, "Red-Cyan", ""},
+ {S3D_ANAGLYPH_GREENMAGENTA, "GREEN_MAGENTA", 0, "Green-Magenta", ""},
+ {S3D_ANAGLYPH_YELLOWBLUE, "YELLOW_BLUE", 0, "Yellow-Blue", ""},
+ {0, NULL, 0, NULL, NULL}
+};
+
+EnumPropertyItem stereo3d_interlace_type_items[] = {
+ {S3D_INTERLACE_ROW, "ROW_INTERLEAVED", 0, "Row Interleaved", ""},
+ {S3D_INTERLACE_COLUMN, "COLUMN_INTERLEAVED", 0, "Column Interleaved", ""},
+ {S3D_INTERLACE_CHECKERBOARD, "CHECKERBOARD_INTERLEAVED", 0, "Checkerboard Interleaved", ""},
+ {0, NULL, 0, NULL, NULL}
+};
+
#ifdef RNA_RUNTIME
#include "DNA_anim_types.h"
@@ -723,6 +777,26 @@ static void rna_Scene_all_keyingsets_next(CollectionPropertyIterator *iter)
iter->valid = (internal->link != NULL);
}
+static int rna_RenderSettings_stereoViews_skip(CollectionPropertyIterator *iter, void *UNUSED(data))
+{
+ ListBaseIterator *internal = &iter->internal.listbase;
+ SceneRenderView *srv = (SceneRenderView *)internal->link;
+
+ if ((STREQ(srv->name, STEREO_LEFT_NAME)) ||
+ (STREQ(srv->name, STEREO_RIGHT_NAME)))
+ {
+ return 0;
+ }
+
+ return 1;
+};
+
+static void rna_RenderSettings_stereoViews_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+{
+ RenderData *rd = (RenderData *)ptr->data;
+ rna_iterator_listbase_begin(iter, &rd->views, rna_RenderSettings_stereoViews_skip);
+}
+
static char *rna_RenderSettings_path(PointerRNA *UNUSED(ptr))
{
return BLI_sprintfN("render");
@@ -945,6 +1019,25 @@ static EnumPropertyItem *rna_ImageFormatSettings_color_depth_itemf(bContext *UNU
}
}
+static EnumPropertyItem *rna_ImageFormatSettings_views_format_itemf(bContext *UNUSED(C), PointerRNA *ptr,
+PropertyRNA *UNUSED(prop), bool *UNUSED(r_free))
+{
+ ImageFormatData *imf = (ImageFormatData *)ptr->data;
+
+ if (imf == NULL) {
+ return views_format_items;
+ }
+ else if (imf->imtype == R_IMF_IMTYPE_OPENEXR) {
+ return views_format_multiview_items;
+ }
+ else if (imf->imtype == R_IMF_IMTYPE_MULTILAYER) {
+ return views_format_multilayer_items;
+ }
+ else {
+ return views_format_items;
+ }
+}
+
#ifdef WITH_OPENEXR
/* OpenEXR */
@@ -1158,6 +1251,69 @@ static void rna_RenderLayer_remove(ID *id, RenderData *UNUSED(rd), Main *bmain,
WM_main_add_notifier(NC_SCENE | ND_RENDER_OPTIONS, NULL);
}
+static int rna_RenderSettings_active_view_index_get(PointerRNA *ptr)
+{
+ RenderData *rd = (RenderData *)ptr->data;
+ return rd->actview;
+}
+
+static void rna_RenderSettings_active_view_index_set(PointerRNA *ptr, int value)
+{
+ RenderData *rd = (RenderData *)ptr->data;
+ rd->actview = value;
+}
+
+static void rna_RenderSettings_active_view_index_range(PointerRNA *ptr, int *min, int *max, int *UNUSED(softmin), int *UNUSED(softmax))
+{
+ RenderData *rd = (RenderData *)ptr->data;
+
+ *min = 0;
+ *max = max_ii(0, BLI_listbase_count(&rd->views) - 1);
+}
+
+static PointerRNA rna_RenderSettings_active_view_get(PointerRNA *ptr)
+{
+ RenderData *rd = (RenderData *)ptr->data;
+ SceneRenderView *srv = BLI_findlink(&rd->views, rd->actview);
+
+ return rna_pointer_inherit_refine(ptr, &RNA_SceneRenderView, srv);
+}
+
+static void rna_RenderSettings_active_view_set(PointerRNA *ptr, PointerRNA value)
+{
+ RenderData *rd = (RenderData *)ptr->data;
+ SceneRenderView *srv = (SceneRenderView *)value.data;
+ const int index = BLI_findindex(&rd->views, srv);
+ if (index != -1) rd->actview = index;
+}
+
+static SceneRenderView *rna_RenderView_new(ID *id, RenderData *UNUSED(rd), const char *name)
+{
+ Scene *scene = (Scene *)id;
+ SceneRenderView *srv = BKE_scene_add_render_view(scene, name);
+
+ WM_main_add_notifier(NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+ return srv;
+}
+
+static void rna_RenderView_remove(ID *id, RenderData *UNUSED(rd), Main *UNUSED(bmain), ReportList *reports,
+ PointerRNA *srv_ptr)
+{
+ SceneRenderView *srv = srv_ptr->data;
+ Scene *scene = (Scene *)id;
+
+ if (!BKE_scene_remove_render_view(scene, srv)) {
+ BKE_reportf(reports, RPT_ERROR, "Render view '%s' could not be removed from scene '%s'",
+ srv->name, scene->id.name + 2);
+ return;
+ }
+
+ RNA_POINTER_INVALIDATE(srv_ptr);
+
+ WM_main_add_notifier(NC_SCENE | ND_RENDER_OPTIONS, NULL);
+}
+
static void rna_RenderSettings_engine_set(PointerRNA *ptr, int value)
{
RenderData *rd = (RenderData *)ptr->data;
@@ -1263,6 +1419,34 @@ static char *rna_SceneRenderLayer_path(PointerRNA *ptr)
return BLI_sprintfN("render.layers[\"%s\"]", name_esc);
}
+static void rna_SceneRenderView_name_set(PointerRNA *ptr, const char *value)
+{
+ Scene *scene = (Scene *)ptr->id.data;
+ SceneRenderView *rv = (SceneRenderView *)ptr->data;
+ BLI_strncpy_utf8(rv->name, value, sizeof(rv->name));
+ BLI_uniquename(&scene->r.views, rv, DATA_("RenderView"), '.', offsetof(SceneRenderView, name), sizeof(rv->name));
+}
+
+static char *rna_SceneRenderView_path(PointerRNA *ptr)
+{
+ SceneRenderView *srv = (SceneRenderView *)ptr->data;
+ return BLI_sprintfN("render.views[\"%s\"]", srv->name);
+}
+
+static void rna_RenderSettings_views_format_set(PointerRNA *ptr, int value)
+{
+ RenderData *rd = (RenderData *)ptr->data;
+
+ if (rd->views_format == SCE_VIEWS_FORMAT_MULTIVIEW &&
+ value == SCE_VIEWS_FORMAT_STEREO_3D)
+ {
+ /* make sure the actview is visible */
+ if (rd->actview > 1) rd->actview = 1;
+ }
+
+ rd->views_format = value;
+}
+
static int rna_RenderSettings_multiple_engines_get(PointerRNA *UNUSED(ptr))
{
return (BLI_listbase_count(&R_engines) > 1);
@@ -1759,6 +1943,26 @@ static void rna_GPUDOFSettings_blades_set(PointerRNA *ptr, const int value)
dofsettings->num_blades = value;
}
+static void rna_Stereo3dFormat_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
+{
+ ID *id = ptr->id.data;
+
+ if (id && GS(id->name) == ID_IM) {
+ Image *ima = (Image *)id;
+ ImBuf *ibuf;
+ void *lock;
+
+ if ((ima->flag & IMA_IS_STEREO) == 0)
+ return;
+
+ ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock);
+
+ if (ibuf) {
+ BKE_image_signal(ima, NULL, IMA_SIGNAL_FREE);
+ }
+ BKE_image_release_ibuf(ima, ibuf, lock);
+ }
+}
static int rna_gpu_is_hq_supported_get(PointerRNA *UNUSED(ptr))
{
@@ -4103,6 +4307,134 @@ static void rna_def_render_layers(BlenderRNA *brna, PropertyRNA *cprop)
RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
}
+/* Render Views - MultiView */
+static void rna_def_scene_render_view(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "SceneRenderView", NULL);
+ RNA_def_struct_ui_text(srna, "Scene Render View", "Render viewpoint for 3D stereo and multiview rendering");
+ RNA_def_struct_ui_icon(srna, ICON_RESTRICT_RENDER_OFF);
+ RNA_def_struct_path_func(srna, "rna_SceneRenderView_path");
+
+ prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_funcs(prop, NULL, NULL, "rna_SceneRenderView_name_set");
+ RNA_def_property_ui_text(prop, "Name", "Render view name");
+ RNA_def_struct_name_property(srna, prop);
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+ prop = RNA_def_property(srna, "file_suffix", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "suffix");
+ RNA_def_property_ui_text(prop, "File Suffix", "Suffix added to the render images for this view");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+ prop = RNA_def_property(srna, "camera_suffix", PROP_STRING, PROP_NONE);
+ RNA_def_property_string_sdna(prop, NULL, "suffix");
+ RNA_def_property_ui_text(prop, "Camera Suffix", "Suffix to identify the cameras to use, and added to the render images for this view");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+ prop = RNA_def_property(srna, "use", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_negative_sdna(prop, NULL, "viewflag", SCE_VIEW_DISABLE);
+ RNA_def_property_ui_text(prop, "Enabled", "Disable or enable the render view");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+}
+
+static void rna_def_render_views(BlenderRNA *brna, PropertyRNA *cprop)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ FunctionRNA *func;
+ PropertyRNA *parm;
+
+ RNA_def_property_srna(cprop, "RenderViews");
+ srna = RNA_def_struct(brna, "RenderViews", NULL);
+ RNA_def_struct_sdna(srna, "RenderData");
+ RNA_def_struct_ui_text(srna, "Render Views", "Collection of render views");
+
+ prop = RNA_def_property(srna, "active_index", PROP_INT, PROP_UNSIGNED);
+ RNA_def_property_int_sdna(prop, NULL, "actview");
+ RNA_def_property_int_funcs(prop, "rna_RenderSettings_active_view_index_get",
+ "rna_RenderSettings_active_view_index_set",
+ "rna_RenderSettings_active_view_index_range");
+ RNA_def_property_ui_text(prop, "Active View Index", "Active index in render view array");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+ prop = RNA_def_property(srna, "active", PROP_POINTER, PROP_NONE);
+ RNA_def_property_struct_type(prop, "SceneRenderView");
+ RNA_def_property_pointer_funcs(prop, "rna_RenderSettings_active_view_get",
+ "rna_RenderSettings_active_view_set", NULL, NULL);
+ RNA_def_property_flag(prop, PROP_EDITABLE | PROP_NEVER_NULL);
+ RNA_def_property_ui_text(prop, "Active Render View", "Active Render View");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+ func = RNA_def_function(srna, "new", "rna_RenderView_new");
+ RNA_def_function_ui_description(func, "Add a render view to scene");
+ RNA_def_function_flag(func, FUNC_USE_SELF_ID);
+ parm = RNA_def_string(func, "name", "RenderView", 0, "", "New name for the marker (not unique)");
+ RNA_def_property_flag(parm, PROP_REQUIRED);
+ parm = RNA_def_pointer(func, "result", "SceneRenderView", "", "Newly created render view");
+ RNA_def_function_return(func, parm);
+
+ func = RNA_def_function(srna, "remove", "rna_RenderView_remove");
+ RNA_def_function_ui_description(func, "Remove a render view");
+ RNA_def_function_flag(func, FUNC_USE_MAIN | FUNC_USE_REPORTS | FUNC_USE_SELF_ID);
+ parm = RNA_def_pointer(func, "view", "SceneRenderView", "", "Render view to remove");
+ RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL | PROP_RNAPTR);
+ RNA_def_property_clear_flag(parm, PROP_THICK_WRAP);
+}
+
+static void rna_def_image_format_stereo3d_format(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ static EnumPropertyItem stereo3d_display_items[] = {
+ {S3D_DISPLAY_ANAGLYPH, "ANAGLYPH", 0, "Anaglyph", "Render two differently filtered colored images for each eye. Anaglyph glasses are required"},
+ {S3D_DISPLAY_INTERLACE, "INTERLACE", 0, "Interlace", "Render two images for each eye into one interlaced image. 3D-ready monitor is requiered"},
+ {S3D_DISPLAY_SIDEBYSIDE, "SIDEBYSIDE", 0, "Side-by-Side", "Render images for left and right eye side-by-side"},
+ {S3D_DISPLAY_TOPBOTTOM, "TOPBOTTOM", 0, "Top-Bottom", "Render images for left and right eye one above another"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+ srna = RNA_def_struct(brna, "Stereo3dFormat", NULL);
+ RNA_def_struct_sdna(srna, "Stereo3dFormat");
+ RNA_def_struct_clear_flag(srna, STRUCT_UNDO);
+ RNA_def_struct_ui_text(srna, "Stereo Output", "Settings for stereo output");
+
+ prop = RNA_def_property(srna, "display_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "display_mode");
+ RNA_def_property_enum_items(prop, stereo3d_display_items);
+ RNA_def_property_ui_text(prop, "Stereo Mode", "");
+ RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Stereo3dFormat_update");
+
+ prop = RNA_def_property(srna, "anaglyph_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, stereo3d_anaglyph_type_items);
+ RNA_def_property_ui_text(prop, "Anaglyph Type", "");
+ RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Stereo3dFormat_update");
+
+ prop = RNA_def_property(srna, "interlace_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, stereo3d_interlace_type_items);
+ RNA_def_property_ui_text(prop, "Interlace Type", "");
+ RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Stereo3dFormat_update");
+
+ prop = RNA_def_property(srna, "use_interlace_swap", PROP_BOOLEAN, PROP_BOOLEAN);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", S3D_INTERLACE_SWAP);
+ RNA_def_property_ui_text(prop, "Swap Left/Right", "Swap left and right stereo channels");
+ RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Stereo3dFormat_update");
+
+ prop = RNA_def_property(srna, "use_sidebyside_crosseyed", PROP_BOOLEAN, PROP_BOOLEAN);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", S3D_SIDEBYSIDE_CROSSEYED);
+ RNA_def_property_ui_text(prop, "Cross-Eyed", "Right eye should see left image and vice-versa");
+ RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Stereo3dFormat_update");
+
+ prop = RNA_def_property(srna, "use_squeezed_frame", PROP_BOOLEAN, PROP_BOOLEAN);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", S3D_SQUEEZED_FRAME);
+ RNA_def_property_ui_text(prop, "Squeezed Frame", "Combine both views in a squeezed image");
+ RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Stereo3dFormat_update");
+}
+
/* use for render output and image save operator,
* note: there are some cases where the members act differently when this is
* used from a scene, video formats can only be selected for render output
@@ -4122,6 +4454,8 @@ static void rna_def_scene_image_format_data(BlenderRNA *brna)
StructRNA *srna;
PropertyRNA *prop;
+ rna_def_image_format_stereo3d_format(brna);
+
srna = RNA_def_struct(brna, "ImageFormatSettings", NULL);
RNA_def_struct_sdna(srna, "ImageFormatData");
RNA_def_struct_nested(brna, srna, "Scene");
@@ -4190,7 +4524,6 @@ static void rna_def_scene_image_format_data(BlenderRNA *brna)
RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_ImageFormatSettings_exr_codec_itemf");
RNA_def_property_ui_text(prop, "Codec", "Codec settings for OpenEXR");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
-
#endif
#ifdef WITH_OPENJPEG
@@ -4242,6 +4575,20 @@ static void rna_def_scene_image_format_data(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "G", "Log conversion gamma");
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+ /* multiview */
+ prop = RNA_def_property(srna, "views_format", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "views_format");
+ RNA_def_property_enum_items(prop, views_format_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_ImageFormatSettings_views_format_itemf");
+ RNA_def_property_ui_text(prop, "Views Format", "Format of multiview media");
+ RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
+ prop = RNA_def_property(srna, "stereo_3d_format", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "stereo3d_format");
+ RNA_def_property_flag(prop, PROP_NEVER_NULL);
+ RNA_def_property_struct_type(prop, "Stereo3dFormat");
+ RNA_def_property_ui_text(prop, "Stereo 3D Format", "Settings for stereo 3d");
+
/* color management */
prop = RNA_def_property(srna, "view_settings", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "view_settings");
@@ -4644,6 +4991,16 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
"resolution to 480 pixels"},
{0, NULL, 0, NULL, NULL}};
+ static EnumPropertyItem views_format_items[] = {
+ {SCE_VIEWS_FORMAT_STEREO_3D, "STEREO_3D", 0, "Stereo 3D",
+ "Single stereo camera system, adjust the stereo settings in the camera panel"},
+ {SCE_VIEWS_FORMAT_MULTIVIEW, "MULTIVIEW", 0, "Multi-View",
+ "Multi camera system, adjust the cameras individually"},
+ {0, NULL, 0, NULL, NULL}
+ };
+
+
+
rna_def_scene_ffmpeg_settings(brna);
#ifdef WITH_QUICKTIME
rna_def_scene_quicktime_settings(brna);
@@ -5326,6 +5683,32 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
RNA_def_property_ui_icon(prop, ICON_UNPINNED, 1);
RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+ /* views (stereoscopy et al) */
+ prop = RNA_def_property(srna, "views", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_struct_type(prop, "SceneRenderView");
+ RNA_def_property_ui_text(prop, "Render Views", "");
+ rna_def_render_views(brna, prop);
+
+ prop = RNA_def_property(srna, "stereo_views", PROP_COLLECTION, PROP_NONE);
+ RNA_def_property_collection_sdna(prop, NULL, "views", NULL);
+ RNA_def_property_collection_funcs(prop, "rna_RenderSettings_stereoViews_begin", "rna_iterator_listbase_next",
+ "rna_iterator_listbase_end", "rna_iterator_listbase_get",
+ NULL, NULL, NULL, NULL);
+ RNA_def_property_struct_type(prop, "SceneRenderView");
+ RNA_def_property_ui_text(prop, "Render Views", "");
+
+ prop = RNA_def_property(srna, "use_multiview", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "scemode", R_MULTIVIEW);
+ RNA_def_property_ui_text(prop, "Multiple Views", "Use multiple views in the scene");
+ RNA_def_property_update(prop, NC_WINDOW, NULL);
+
+ prop = RNA_def_property(srna, "views_format", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, views_format_items);
+ RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
+ RNA_def_property_ui_text(prop, "Setup Stereo Mode", "");
+ RNA_def_property_enum_funcs(prop, NULL, "rna_RenderSettings_views_format_set", NULL);
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+
/* engine */
prop = RNA_def_property(srna, "engine", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, engine_items);
@@ -6076,8 +6459,8 @@ void RNA_def_scene(BlenderRNA *brna)
/* *** Animated *** */
rna_def_scene_render_data(brna);
rna_def_scene_render_layer(brna);
-
rna_def_gpu_fx(brna);
+ rna_def_scene_render_view(brna);
/* Scene API */
RNA_api_scene(srna);
diff --git a/source/blender/makesrna/intern/rna_scene_api.c b/source/blender/makesrna/intern/rna_scene_api.c
index 0d8d58eae43..0d70b6f3ba8 100644
--- a/source/blender/makesrna/intern/rna_scene_api.c
+++ b/source/blender/makesrna/intern/rna_scene_api.c
@@ -119,15 +119,15 @@ static void rna_Scene_update_tagged(Scene *scene)
#endif
}
-static void rna_SceneRender_get_frame_path(RenderData *rd, int frame, int preview, char *name)
+static void rna_SceneRender_get_frame_path(RenderData *rd, int frame, int preview, const char *view, char *name)
{
if (BKE_imtype_is_movie(rd->im_format.imtype)) {
- BKE_movie_filepath_get(name, rd, preview != 0);
+ BKE_movie_filepath_get(name, rd, preview != 0, view);
}
else {
BKE_image_path_from_imformat(
name, rd->pic, G.main->name, (frame == INT_MIN) ? rd->cfra : frame,
- &rd->im_format, (rd->scemode & R_EXTENSION) != 0, true);
+ &rd->im_format, (rd->scemode & R_EXTENSION) != 0, true, view);
}
}
@@ -288,6 +288,8 @@ void RNA_api_scene_render(StructRNA *srna)
RNA_def_int(func, "frame", INT_MIN, INT_MIN, INT_MAX, "",
"Frame number to use, if unset the current frame will be used", MINAFRAME, MAXFRAME);
parm = RNA_def_boolean(func, "preview", 0, "Preview", "Use preview range");
+ parm = RNA_def_string_file_path(func, "view", NULL, FILE_MAX, "View",
+ "The name of the view to use to replace the \"%\" chars");
parm = RNA_def_string_file_path(func, "filepath", NULL, FILE_MAX, "File Path",
"The resulting filepath from the scenes render settings");
diff --git a/source/blender/makesrna/intern/rna_sequencer.c b/source/blender/makesrna/intern/rna_sequencer.c
index fc9a501dae8..ee4ec1cb659 100644
--- a/source/blender/makesrna/intern/rna_sequencer.c
+++ b/source/blender/makesrna/intern/rna_sequencer.c
@@ -186,6 +186,11 @@ static void rna_SequenceEditor_elements_begin(CollectionPropertyIterator *iter,
rna_SequenceEditor_elements_length(ptr), 0, NULL);
}
+static void rna_Sequence_views_format_update(Main *bmain, Scene *scene, PointerRNA *ptr)
+{
+ rna_Sequence_update(bmain, scene, ptr);
+}
+
static void do_sequence_frame_change_update(Scene *scene, Sequence *seq)
{
Editing *ed = BKE_sequencer_editing_get(scene, false);
@@ -1834,6 +1839,24 @@ static void rna_def_image(BlenderRNA *brna)
"rna_SequenceEditor_elements_length", NULL, NULL, NULL);
RNA_api_sequence_elements(brna, prop);
+ /* multiview */
+ prop = RNA_def_property(srna, "use_multiview", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_USE_VIEWS);
+ RNA_def_property_ui_text(prop, "Use Multi-View", "Use Multiple Views (when available)");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_views_format_update");
+
+ prop = RNA_def_property(srna, "views_format", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "views_format");
+ RNA_def_property_enum_items(prop, views_format_items);
+ RNA_def_property_ui_text(prop, "Views Format", "Mode to load image views");
+ RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Sequence_views_format_update");
+
+ prop = RNA_def_property(srna, "stereo_3d_format", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "stereo3d_format");
+ RNA_def_property_flag(prop, PROP_NEVER_NULL);
+ RNA_def_property_struct_type(prop, "Stereo3dFormat");
+ RNA_def_property_ui_text(prop, "Stereo 3D Format", "Settings for stereo 3d");
+
rna_def_filter_video(srna);
rna_def_proxy(srna);
rna_def_input(srna);
@@ -1925,6 +1948,24 @@ static void rna_def_movie(BlenderRNA *brna)
"rna_Sequence_filepath_set");
RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_filepath_update");
+ /* multiview */
+ prop = RNA_def_property(srna, "use_multiview", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", SEQ_USE_VIEWS);
+ RNA_def_property_ui_text(prop, "Use Multi-View", "Use Multiple Views (when available)");
+ RNA_def_property_update(prop, NC_SCENE | ND_SEQUENCER, "rna_Sequence_views_format_update");
+
+ prop = RNA_def_property(srna, "views_format", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "views_format");
+ RNA_def_property_enum_items(prop, views_format_items);
+ RNA_def_property_ui_text(prop, "Views Format", "Mode to load movie views");
+ RNA_def_property_update(prop, NC_IMAGE | ND_DISPLAY, "rna_Sequence_views_format_update");
+
+ prop = RNA_def_property(srna, "stereo_3d_format", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "stereo3d_format");
+ RNA_def_property_flag(prop, PROP_NEVER_NULL);
+ RNA_def_property_struct_type(prop, "Stereo3dFormat");
+ RNA_def_property_ui_text(prop, "Stereo 3D Format", "Settings for stereo 3d");
+
rna_def_filter_video(srna);
rna_def_proxy(srna);
rna_def_input(srna);
diff --git a/source/blender/makesrna/intern/rna_sequencer_api.c b/source/blender/makesrna/intern/rna_sequencer_api.c
index 2d3e3c56672..881688281f3 100644
--- a/source/blender/makesrna/intern/rna_sequencer_api.c
+++ b/source/blender/makesrna/intern/rna_sequencer_api.c
@@ -198,6 +198,7 @@ static Sequence *rna_Sequences_new_movie(ID *id, Editing *ed, ReportList *report
{
Scene *scene = (Scene *)id;
Sequence *seq;
+ StripAnim *sanim;
struct anim *an = openanim(file, IB_rect, 0, NULL);
@@ -207,7 +208,11 @@ static Sequence *rna_Sequences_new_movie(ID *id, Editing *ed, ReportList *report
}
seq = alloc_generic_sequence(ed, name, frame_start, channel, SEQ_TYPE_MOVIE, file);
- seq->anim = an;
+
+ sanim = MEM_mallocN(sizeof(StripAnim), "Strip Anim");
+ BLI_addtail(&seq->anims, sanim);
+ sanim->anim = an;
+
seq->anim_preseek = IMB_anim_get_preseek(an);
seq->len = IMB_anim_get_duration(an, IMB_TC_RECORD_RUN);
diff --git a/source/blender/makesrna/intern/rna_space.c b/source/blender/makesrna/intern/rna_space.c
index 6aaa947e9e4..4a4c2a34fdf 100644
--- a/source/blender/makesrna/intern/rna_space.c
+++ b/source/blender/makesrna/intern/rna_space.c
@@ -31,6 +31,7 @@
#include "BLF_translation.h"
+#include "BKE_image.h"
#include "BKE_key.h"
#include "BKE_movieclip.h"
#include "BKE_node.h"
@@ -90,6 +91,34 @@ EnumPropertyItem space_type_items[] = {
{0, NULL, 0, NULL, NULL}
};
+#define V3D_S3D_CAMERA_LEFT {STEREO_LEFT_ID, "LEFT", ICON_RESTRICT_RENDER_OFF, "Left", ""},
+#define V3D_S3D_CAMERA_RIGHT {STEREO_RIGHT_ID, "RIGHT", ICON_RESTRICT_RENDER_OFF, "Right", ""},
+#define V3D_S3D_CAMERA_S3D {STEREO_3D_ID, "S3D", ICON_CAMERA_STEREO, "3D", ""},
+#ifdef RNA_RUNTIME
+#define V3D_S3D_CAMERA_VIEWS {STEREO_MONO_ID, "MONO", ICON_RESTRICT_RENDER_OFF, "Views", ""},
+#endif
+
+static EnumPropertyItem stereo3d_camera_items[] = {
+ V3D_S3D_CAMERA_LEFT
+ V3D_S3D_CAMERA_RIGHT
+ V3D_S3D_CAMERA_S3D
+ {0, NULL, 0, NULL, NULL}
+};
+
+#ifdef RNA_RUNTIME
+static EnumPropertyItem multiview_camera_items[] = {
+ V3D_S3D_CAMERA_VIEWS
+ V3D_S3D_CAMERA_S3D
+ {0, NULL, 0, NULL, NULL}
+};
+#endif
+
+#undef V3D_S3D_CAMERA_LEFT
+#undef V3D_S3D_CAMERA_RIGHT
+#undef V3D_S3D_CAMERA_S3D
+#undef V3D_S3D_CAMERA_VIEWS
+
+
static EnumPropertyItem pivot_items_full[] = {
{V3D_CENTER, "BOUNDING_BOX_CENTER", ICON_ROTATE, "Bounding Box Center",
"Pivot around bounding box center of selected object(s)"},
@@ -659,6 +688,17 @@ static EnumPropertyItem *rna_SpaceView3D_viewport_shade_itemf(bContext *UNUSED(C
return item;
}
+static EnumPropertyItem *rna_SpaceView3D_stereo3d_camera_itemf(bContext *UNUSED(C), PointerRNA *ptr,
+ PropertyRNA *UNUSED(prop), bool *UNUSED(r_free))
+{
+ Scene *scene = ((bScreen *)ptr->id.data)->scene;
+
+ if (scene->r.views_format == SCE_VIEWS_FORMAT_MULTIVIEW)
+ return multiview_camera_items;
+ else
+ return stereo3d_camera_items;
+}
+
/* Space Image Editor */
static PointerRNA rna_SpaceImageEditor_uvedit_get(PointerRNA *ptr)
@@ -671,6 +711,38 @@ static void rna_SpaceImageEditor_mode_update(Main *bmain, Scene *scene, PointerR
ED_space_image_paint_update(bmain->wm.first, scene->toolsettings);
}
+
+static void rna_SpaceImageEditor_show_stereo_set(PointerRNA *ptr, int value)
+{
+ SpaceImage *sima = (SpaceImage *)(ptr->data);
+
+ if (value)
+ sima->iuser.flag |= IMA_SHOW_STEREO;
+ else
+ sima->iuser.flag &= ~IMA_SHOW_STEREO;
+}
+
+static int rna_SpaceImageEditor_show_stereo_get(PointerRNA *ptr)
+{
+ SpaceImage *sima = (SpaceImage *)(ptr->data);
+ return (sima->iuser.flag & IMA_SHOW_STEREO) != 0;
+}
+
+static void rna_SpaceImageEditor_show_stereo_update(Main *UNUSED(bmain), Scene *UNUSED(unused), PointerRNA *ptr)
+{
+ SpaceImage *sima = (SpaceImage *)(ptr->data);
+ Image *ima = sima->image;
+
+ if (ima) {
+ if (ima->rr) {
+ BKE_image_multilayer_index(ima->rr, &sima->iuser);
+ }
+ else {
+ BKE_image_multiview_index(ima, &sima->iuser);
+ }
+ }
+}
+
static int rna_SpaceImageEditor_show_render_get(PointerRNA *ptr)
{
SpaceImage *sima = (SpaceImage *)(ptr->data);
@@ -798,6 +870,24 @@ static void rna_SpaceImageEditor_cursor_location_set(PointerRNA *ptr, const floa
}
}
+static void rna_SpaceImageEditor_image_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
+{
+ SpaceImage *sima = (SpaceImage *)ptr->data;
+ Image *ima = sima->image;
+
+ /* make sure all the iuser settings are valid for the sima image */
+ if (ima) {
+ if (ima->rr) {
+ if (BKE_image_multilayer_index(sima->image->rr, &sima->iuser) == NULL) {
+ BKE_image_init_imageuser(sima->image, &sima->iuser);
+ }
+ }
+ else {
+ BKE_image_multiview_index(ima, &sima->iuser);
+ }
+ }
+}
+
static void rna_SpaceImageEditor_scopes_update(struct bContext *C, struct PointerRNA *ptr)
{
SpaceImage *sima = (SpaceImage *)ptr->data;
@@ -2472,6 +2562,41 @@ static void rna_def_space_view3d(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "FX Options", "Options used for real time compositing");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+ /* Stereo Settings */
+ prop = RNA_def_property(srna, "stereo_3d_camera", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_sdna(prop, NULL, "stereo3d_camera");
+ RNA_def_property_enum_items(prop, stereo3d_camera_items);
+ RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_SpaceView3D_stereo3d_camera_itemf");
+ RNA_def_property_ui_text(prop, "Camera", "");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+
+ prop = RNA_def_property(srna, "show_stereo_3d_cameras", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "stereo3d_flag", V3D_S3D_DISPCAMERAS);
+ RNA_def_property_ui_text(prop, "Cameras", "Show the left and right cameras");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+
+ prop = RNA_def_property(srna, "show_stereo_3d_convergence_plane", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "stereo3d_flag", V3D_S3D_DISPPLANE);
+ RNA_def_property_ui_text(prop, "Plane", "Show the stereo 3d convergence plane");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+
+ prop = RNA_def_property(srna, "stereo_3d_convergence_plane_alpha", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "stereo3d_convergence_alpha");
+ RNA_def_property_ui_text(prop, "Plane Alpha", "Opacity (alpha) of the convergence plane");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+
+ prop = RNA_def_property(srna, "show_stereo_3d_volume", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_sdna(prop, NULL, "stereo3d_flag", V3D_S3D_DISPVOLUME);
+ RNA_def_property_ui_text(prop, "Volume", "Show the stereo 3d frustum volume");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+
+ prop = RNA_def_property(srna, "stereo_3d_volume_alpha", PROP_FLOAT, PROP_FACTOR);
+ RNA_def_property_float_sdna(prop, NULL, "stereo3d_volume_alpha");
+ RNA_def_property_ui_text(prop, "Volume Alpha", "Opacity (alpha) of the cameras frustum volume");
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_VIEW3D, NULL);
+
+ /* *** Animated *** */
+ RNA_define_animate_sdna(true);
/* region */
srna = RNA_def_struct(brna, "RegionView3D", NULL);
@@ -2645,7 +2770,7 @@ static void rna_def_space_image(BlenderRNA *brna)
RNA_def_property_pointer_funcs(prop, NULL, "rna_SpaceImageEditor_image_set", NULL, NULL);
RNA_def_property_ui_text(prop, "Image", "Image displayed and edited in this space");
RNA_def_property_flag(prop, PROP_EDITABLE);
- RNA_def_property_update(prop, NC_GEOM | ND_DATA, NULL); /* is handled in image editor too */
+ RNA_def_property_update(prop, NC_GEOM | ND_DATA, "rna_SpaceImageEditor_image_update"); /* is handled in image editor too */
prop = RNA_def_property(srna, "image_user", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_NEVER_NULL);
@@ -2697,6 +2822,12 @@ static void rna_def_space_image(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Draw Channels", "Channels of the image to draw");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, NULL);
+ prop = RNA_def_property(srna, "show_stereo_3d", PROP_BOOLEAN, PROP_NONE);
+ RNA_def_property_boolean_funcs(prop, "rna_SpaceImageEditor_show_stereo_get", "rna_SpaceImageEditor_show_stereo_set");
+ RNA_def_property_ui_text(prop, "Show Stereo", "Display the image in Stereo 3D");
+ RNA_def_property_ui_icon(prop, ICON_CAMERA_STEREO, 0);
+ RNA_def_property_update(prop, NC_SPACE | ND_SPACE_IMAGE, "rna_SpaceImageEditor_show_stereo_update");
+
/* uv */
prop = RNA_def_property(srna, "uv_editor", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_NEVER_NULL);
diff --git a/source/blender/makesrna/intern/rna_ui_api.c b/source/blender/makesrna/intern/rna_ui_api.c
index fea8b630af6..832a29a2761 100644
--- a/source/blender/makesrna/intern/rna_ui_api.c
+++ b/source/blender/makesrna/intern/rna_ui_api.c
@@ -779,6 +779,16 @@ void RNA_api_ui_layout(StructRNA *srna)
RNA_def_property_flag(parm, PROP_REQUIRED | PROP_RNAPTR | PROP_NEVER_NULL);
RNA_def_boolean(func, "color_management", false, "", "Show color management settings");
+ func = RNA_def_function(srna, "template_image_stereo_3d", "uiTemplateImageStereo3d");
+ RNA_def_function_ui_description(func, "User interface for setting image stereo 3d options");
+ parm = RNA_def_pointer(func, "stereo_3d_format", "Stereo3dFormat", "", "");
+ RNA_def_property_flag(parm, PROP_REQUIRED | PROP_RNAPTR | PROP_NEVER_NULL);
+
+ func = RNA_def_function(srna, "template_image_views", "uiTemplateImageViews");
+ RNA_def_function_ui_description(func, "User interface for setting image views output options");
+ parm = RNA_def_pointer(func, "image_settings", "ImageFormatSettings", "", "");
+ RNA_def_property_flag(parm, PROP_REQUIRED | PROP_RNAPTR | PROP_NEVER_NULL);
+
func = RNA_def_function(srna, "template_movieclip", "uiTemplateMovieClip");
RNA_def_function_ui_description(func, "Item(s). User interface for selecting movie clips and their source paths");
RNA_def_function_flag(func, FUNC_USE_CONTEXT);
diff --git a/source/blender/makesrna/intern/rna_userdef.c b/source/blender/makesrna/intern/rna_userdef.c
index fc6d28e67c2..23412a549fa 100644
--- a/source/blender/makesrna/intern/rna_userdef.c
+++ b/source/blender/makesrna/intern/rna_userdef.c
@@ -4124,8 +4124,8 @@ static void rna_def_userdef_system(BlenderRNA *brna)
RNA_def_property_boolean_sdna(prop, NULL, "uiflag2", USER_REGION_OVERLAP);
RNA_def_property_ui_text(prop, "Region Overlap",
"Draw tool/property regions over the main region, when using Triple Buffer");
- RNA_def_property_update(prop, 0, "rna_userdef_dpi_update");
-
+ RNA_def_property_update(prop, 0, "rna_userdef_dpi_update");
+
#ifdef WITH_CYCLES
prop = RNA_def_property(srna, "compute_device_type", PROP_ENUM, PROP_NONE);
RNA_def_property_flag(prop, PROP_ENUM_NO_CONTEXT);
diff --git a/source/blender/makesrna/intern/rna_wm.c b/source/blender/makesrna/intern/rna_wm.c
index 64ecf61f774..dcd77e5a0f6 100644
--- a/source/blender/makesrna/intern/rna_wm.c
+++ b/source/blender/makesrna/intern/rna_wm.c
@@ -1750,6 +1750,37 @@ static void rna_def_piemenu(BlenderRNA *brna)
RNA_define_verify_sdna(1); /* not in sdna */
}
+static void rna_def_window_stereo3d(BlenderRNA *brna)
+{
+ StructRNA *srna;
+ PropertyRNA *prop;
+
+ srna = RNA_def_struct(brna, "Stereo3dDisplay", NULL);
+ RNA_def_struct_sdna(srna, "Stereo3dFormat");
+ RNA_def_struct_clear_flag(srna, STRUCT_UNDO);
+ RNA_def_struct_ui_text(srna, "Stereo 3d Display", "Settings for stereo 3d display");
+
+ prop = RNA_def_property(srna, "display_mode", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, stereo3d_display_items);
+ RNA_def_property_ui_text(prop, "Display Mode", "");
+
+ prop = RNA_def_property(srna, "anaglyph_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, stereo3d_anaglyph_type_items);
+ RNA_def_property_ui_text(prop, "Anaglyph Type", "");
+
+ prop = RNA_def_property(srna, "interlace_type", PROP_ENUM, PROP_NONE);
+ RNA_def_property_enum_items(prop, stereo3d_interlace_type_items);
+ RNA_def_property_ui_text(prop, "Interlace Type", "");
+
+ prop = RNA_def_property(srna, "use_interlace_swap", PROP_BOOLEAN, PROP_BOOLEAN);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", S3D_INTERLACE_SWAP);
+ RNA_def_property_ui_text(prop, "Swap Left/Right", "Swap left and right stereo channels");
+
+ prop = RNA_def_property(srna, "use_sidebyside_crosseyed", PROP_BOOLEAN, PROP_BOOLEAN);
+ RNA_def_property_boolean_sdna(prop, NULL, "flag", S3D_SIDEBYSIDE_CROSSEYED);
+ RNA_def_property_ui_text(prop, "Cross-Eyed", "Right eye should see left image and vice-versa");
+}
+
static void rna_def_window(BlenderRNA *brna)
{
StructRNA *srna;
@@ -1759,6 +1790,8 @@ static void rna_def_window(BlenderRNA *brna)
RNA_def_struct_ui_text(srna, "Window", "Open window");
RNA_def_struct_sdna(srna, "wmWindow");
+ rna_def_window_stereo3d(brna);
+
prop = RNA_def_property(srna, "screen", PROP_POINTER, PROP_NONE);
RNA_def_property_flag(prop, PROP_NEVER_NULL);
RNA_def_property_struct_type(prop, "Screen");
@@ -1788,6 +1821,12 @@ static void rna_def_window(BlenderRNA *brna)
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
RNA_def_property_ui_text(prop, "Height", "Window height");
+ prop = RNA_def_property(srna, "stereo_3d_display", PROP_POINTER, PROP_NONE);
+ RNA_def_property_pointer_sdna(prop, NULL, "stereo3d_format");
+ RNA_def_property_flag(prop, PROP_NEVER_NULL);
+ RNA_def_property_struct_type(prop, "Stereo3dDisplay");
+ RNA_def_property_ui_text(prop, "Stereo 3D Display", "Settings for stereo 3d display");
+
RNA_api_window(srna);
}
diff --git a/source/blender/nodes/CMakeLists.txt b/source/blender/nodes/CMakeLists.txt
index 9eb6c1674a3..c95daffe557 100644
--- a/source/blender/nodes/CMakeLists.txt
+++ b/source/blender/nodes/CMakeLists.txt
@@ -122,6 +122,7 @@ set(SRC
composite/nodes/node_composite_boxmask.c
composite/nodes/node_composite_ellipsemask.c
composite/nodes/node_composite_switch.c
+ composite/nodes/node_composite_switchview.c
composite/nodes/node_composite_colorcorrection.c
composite/nodes/node_composite_pixelate.c
diff --git a/source/blender/nodes/NOD_composite.h b/source/blender/nodes/NOD_composite.h
index 961fdbfc0fb..0215db1dd55 100644
--- a/source/blender/nodes/NOD_composite.h
+++ b/source/blender/nodes/NOD_composite.h
@@ -133,6 +133,7 @@ void register_node_type_cmp_ellipsemask(void);
void register_node_type_cmp_bokehimage(void);
void register_node_type_cmp_bokehblur(void);
void register_node_type_cmp_switch(void);
+void register_node_type_cmp_switch_view(void);
void register_node_type_cmp_pixelate(void);
void register_node_type_cmp_trackpos(void);
void register_node_type_cmp_planetrackdeform(void);
diff --git a/source/blender/nodes/NOD_static_types.h b/source/blender/nodes/NOD_static_types.h
index 6b9a97c0b18..b33bec51c2c 100644
--- a/source/blender/nodes/NOD_static_types.h
+++ b/source/blender/nodes/NOD_static_types.h
@@ -202,6 +202,7 @@ DefNode( CompositorNode, CMP_NODE_MASK_ELLIPSE, def_cmp_ellipsemask, "ELLIP
DefNode( CompositorNode, CMP_NODE_BOKEHIMAGE, def_cmp_bokehimage, "BOKEHIMAGE", BokehImage, "Bokeh Image", "" )
DefNode( CompositorNode, CMP_NODE_BOKEHBLUR, def_cmp_bokehblur, "BOKEHBLUR", BokehBlur, "Bokeh Blur", "" )
DefNode( CompositorNode, CMP_NODE_SWITCH, def_cmp_switch, "SWITCH", Switch, "Switch", "" )
+DefNode( CompositorNode, CMP_NODE_SWITCH_VIEW, def_cmp_switch_view, "VIEWSWITCH", SwitchView, "View Switch", "" )
DefNode( CompositorNode, CMP_NODE_COLORCORRECTION,def_cmp_colorcorrection,"COLORCORRECTION",ColorCorrection, "Color Correction", "" )
DefNode( CompositorNode, CMP_NODE_MASK, def_cmp_mask, "MASK", Mask, "Mask", "" )
DefNode( CompositorNode, CMP_NODE_KEYINGSCREEN, def_cmp_keyingscreen, "KEYINGSCREEN", KeyingScreen, "Keying Screen", "" )
diff --git a/source/blender/nodes/composite/node_composite_tree.c b/source/blender/nodes/composite/node_composite_tree.c
index e616680647e..8bc61862458 100644
--- a/source/blender/nodes/composite/node_composite_tree.c
+++ b/source/blender/nodes/composite/node_composite_tree.c
@@ -226,12 +226,13 @@ void *COM_linker_hack = NULL;
void ntreeCompositExecTree(Scene *scene, bNodeTree *ntree, RenderData *rd, int rendering, int do_preview,
const ColorManagedViewSettings *view_settings,
- const ColorManagedDisplaySettings *display_settings)
+ const ColorManagedDisplaySettings *display_settings,
+ const char *view_name)
{
#ifdef WITH_COMPOSITOR
- COM_execute(rd, scene, ntree, rendering, view_settings, display_settings);
+ COM_execute(rd, scene, ntree, rendering, view_settings, display_settings, view_name);
#else
- UNUSED_VARS(scene, ntree, rd, rendering, view_settings, display_settings);
+ UNUSED_VARS(scene, ntree, rd, rendering, view_settings, display_settings, view_name);
#endif
UNUSED_VARS(do_preview);
diff --git a/source/blender/nodes/composite/nodes/node_composite_image.c b/source/blender/nodes/composite/nodes/node_composite_image.c
index 34f3350c2ff..23d47065f59 100644
--- a/source/blender/nodes/composite/nodes/node_composite_image.c
+++ b/source/blender/nodes/composite/nodes/node_composite_image.c
@@ -168,14 +168,21 @@ static void cmp_node_image_add_multilayer_outputs(bNodeTree *ntree, bNode *node,
NodeImageLayer *sockdata;
RenderPass *rpass;
int index;
+ int passflag = 0;
for (rpass = rl->passes.first, index = 0; rpass; rpass = rpass->next, ++index) {
int type;
if (rpass->channels == 1)
type = SOCK_FLOAT;
else
type = SOCK_RGBA;
-
- sock = nodeAddStaticSocket(ntree, node, SOCK_OUT, type, PROP_NONE, rpass->name, rpass->name);
+
+ /* we only need one socket per type */
+ if (passflag & rpass->passtype)
+ continue;
+
+ passflag |= rpass->passtype;
+
+ sock = nodeAddStaticSocket(ntree, node, SOCK_OUT, type, PROP_NONE, rpass->internal_name, rpass->internal_name);
/* extra socket info */
sockdata = MEM_callocN(sizeof(NodeImageLayer), "node image layer");
sock->storage = sockdata;
diff --git a/source/blender/nodes/composite/nodes/node_composite_outputFile.c b/source/blender/nodes/composite/nodes/node_composite_outputFile.c
index 8e602c6179c..7d1087435c2 100644
--- a/source/blender/nodes/composite/nodes/node_composite_outputFile.c
+++ b/source/blender/nodes/composite/nodes/node_composite_outputFile.c
@@ -127,7 +127,7 @@ bNodeSocket *ntreeCompositOutputFileAddSocket(bNodeTree *ntree, bNode *node, con
BKE_imformat_defaults(&sockdata->format);
/* use node data format by default */
sockdata->use_node_format = true;
-
+
nimf->active_input = BLI_findindex(&node->inputs, sock);
return sock;
@@ -189,7 +189,7 @@ static void init_output_file(const bContext *C, PointerRNA *ptr)
}
else
BKE_imformat_defaults(&nimf->format);
-
+
/* add one socket by default */
ntreeCompositOutputFileAddSocket(ntree, node, "Image", format);
}
diff --git a/source/blender/nodes/composite/nodes/node_composite_switchview.c b/source/blender/nodes/composite/nodes/node_composite_switchview.c
new file mode 100644
index 00000000000..d805cf4d87f
--- /dev/null
+++ b/source/blender/nodes/composite/nodes/node_composite_switchview.c
@@ -0,0 +1,153 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2006 Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s):
+ * Dalai Felinto
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/nodes/composite/nodes/node_composite_switchview.c
+ * \ingroup cmpnodes
+ */
+
+#include "BKE_context.h"
+#include "../node_composite_util.h"
+
+/* **************** SWITCH VIEW ******************** */
+static bNodeSocketTemplate cmp_node_switch_view_out[] = {
+ { SOCK_RGBA, 0, N_("Image"), 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f},
+ { -1, 0, "" }
+};
+
+static bNodeSocket *ntreeCompositSwitchViewAddSocket(bNodeTree *ntree, bNode *node, const char *name)
+{
+ bNodeSocket *sock = nodeAddStaticSocket(ntree, node, SOCK_IN, SOCK_RGBA, PROP_NONE, NULL, name);
+ return sock;
+}
+
+static void cmp_node_switch_view_sanitycheck(bNodeTree *ntree, bNode *node)
+{
+ bNodeSocket *sock;
+
+ if (!BLI_listbase_is_empty(&node->inputs))
+ return;
+
+ sock = ntreeCompositSwitchViewAddSocket(ntree, node, "No View");
+ sock->flag |= SOCK_HIDDEN;
+}
+
+static void cmp_node_switch_view_update(bNodeTree *ntree, bNode *node)
+{
+ bNodeSocket *sock;
+ SceneRenderView *srv;
+ Scene *scene = (Scene *)node->id;
+
+ /* only update when called from the operator button */
+ if (node->update != NODE_UPDATE_OPERATOR)
+ return;
+
+ if (scene == NULL) {
+ nodeRemoveAllSockets(ntree, node);
+ /* make sure there is always one socket */
+ cmp_node_switch_view_sanitycheck(ntree, node);
+ return;
+ }
+
+ /* remove the views that were removed */
+ sock = node->inputs.last;
+ while (sock) {
+ srv = BLI_findstring(&scene->r.views, sock->name, offsetof(SceneRenderView, name));
+
+ if (srv == NULL) {
+ bNodeSocket *sock_del = sock;
+ sock = sock->prev;
+ nodeRemoveSocket(ntree, node, sock_del);
+ }
+ else {
+ if (srv->viewflag & SCE_VIEW_DISABLE)
+ sock->flag |= SOCK_HIDDEN;
+ else
+ sock->flag &= ~SOCK_HIDDEN;
+
+ sock = sock->prev;
+ }
+ }
+
+ /* add the new views */
+ for (srv = scene->r.views.first; srv; srv = srv->next) {
+ sock = BLI_findstring(&node->inputs, srv->name, offsetof(bNodeSocket, name));
+
+ if (sock == NULL)
+ sock = ntreeCompositSwitchViewAddSocket(ntree, node, srv->name);
+
+ if (srv->viewflag & SCE_VIEW_DISABLE)
+ sock->flag |= SOCK_HIDDEN;
+ else
+ sock->flag &= ~SOCK_HIDDEN;
+ }
+
+ /* make sure there is always one socket */
+ cmp_node_switch_view_sanitycheck(ntree, node);
+}
+
+static void init_switch_view(const bContext *C, PointerRNA *ptr)
+{
+ Scene *scene = CTX_data_scene(C);
+ bNodeTree *ntree = ptr->id.data;
+ bNode *node = ptr->data;
+ SceneRenderView *srv;
+ bNodeSocket *sock;
+ int nr;
+
+ /* store scene for updates */
+ node->id = (ID *)scene;
+
+ if (scene) {
+ RenderData *rd = &scene->r;
+
+ for (nr = 0, srv = rd->views.first; srv; srv = srv->next, nr++) {
+ sock = ntreeCompositSwitchViewAddSocket(ntree, node, srv->name);
+
+ if ((srv->viewflag & SCE_VIEW_DISABLE))
+ sock->flag |= SOCK_HIDDEN;
+ }
+ }
+
+ /* make sure there is always one socket */
+ cmp_node_switch_view_sanitycheck(ntree, node);
+}
+
+/* custom1 = mix type */
+void register_node_type_cmp_switch_view(void)
+{
+ static bNodeType ntype;
+
+ cmp_node_type_base(&ntype, CMP_NODE_SWITCH_VIEW, "Switch View", NODE_CLASS_CONVERTOR, 0);
+ node_type_socket_templates(&ntype, NULL, cmp_node_switch_view_out);
+
+ ntype.initfunc_api = init_switch_view;
+
+ node_type_update(&ntype, cmp_node_switch_view_update, NULL);
+
+ nodeRegisterType(&ntype);
+}
diff --git a/source/blender/quicktime/apple/qtkit_export.m b/source/blender/quicktime/apple/qtkit_export.m
index 4214fa9ac32..db2d4e72dc5 100644
--- a/source/blender/quicktime/apple/qtkit_export.m
+++ b/source/blender/quicktime/apple/qtkit_export.m
@@ -98,8 +98,6 @@ typedef struct QuicktimeExport {
} QuicktimeExport;
-static struct QuicktimeExport *qtexport;
-
#define AUDIOOUTPUTBUFFERSIZE 65536
#pragma mark rna helper functions
@@ -245,7 +243,7 @@ void makeqtstring(RenderData *rd, char *string, bool preview)
}
}
-void filepath_qt(char *string, RenderData *rd, bool preview)
+void filepath_qt(char *string, RenderData *rd, bool preview, const char *suffix)
{
int sfra, efra;
@@ -276,8 +274,23 @@ void filepath_qt(char *string, RenderData *rd, bool preview)
BLI_path_frame_range(string, sfra, efra, 4);
}
}
+
+ BLI_path_suffix(string, FILE_MAX, suffix, "");
}
+void *context_create_qt(void)
+{
+ QuicktimeExport *qtexport = MEM_callocN(sizeof(QuicktimeExport), "QuicktimeExport");
+ return qtexport;
+}
+
+void context_free_qt(void *context_v)
+{
+ QuicktimeExport *qtexport = context_v;
+ if (qtexport) {
+ MEM_freeN(qtexport);
+ }
+}
#pragma mark audio export functions
@@ -302,12 +315,13 @@ static OSStatus write_cookie(AudioConverterRef converter, AudioFileID outfile)
}
/* AudioConverter input stream callback */
-static OSStatus AudioConverterInputCallback(AudioConverterRef inAudioConverter,
+static OSStatus AudioConverterInputCallback(AudioConverterRef inAudioConverter,
UInt32* ioNumberDataPackets,
AudioBufferList* ioData,
AudioStreamPacketDescription** outDataPacketDescription,
void* inUserData)
-{
+{
+ QuicktimeExport *qtexport = inUserData;
if (qtexport->audioTotalExportedFrames >= qtexport->audioLastFrame) { /* EOF */
*ioNumberDataPackets = 0;
return noErr;
@@ -334,7 +348,7 @@ static OSStatus AudioConverterInputCallback(AudioConverterRef inAudioConverter,
#pragma mark export functions
-int start_qt(struct Scene *scene, struct RenderData *rd, int rectx, int recty, ReportList *reports, bool preview)
+int start_qt(void *context_v, struct Scene *scene, struct RenderData *rd, int rectx, int recty, ReportList *reports, bool preview, const char *UNUSED(suffix))
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSError *error;
@@ -342,9 +356,8 @@ int start_qt(struct Scene *scene, struct RenderData *rd, int rectx, int recty, R
int success = 1;
OSStatus err = noErr;
int sfra, efra;
+ QuicktimeExport *qtexport = context_v;
- if (qtexport == NULL) qtexport = MEM_callocN(sizeof(QuicktimeExport), "QuicktimeExport");
-
if (preview) {
sfra = rd->psfra;
efra = rd->pefra;
@@ -639,7 +652,7 @@ int start_qt(struct Scene *scene, struct RenderData *rd, int rectx, int recty, R
return success;
}
-int append_qt(struct RenderData *rd, int start_frame, int frame, int *pixels, int rectx, int recty, ReportList *reports)
+int append_qt(void *context_v, struct RenderData *rd, int start_frame, int frame, int *pixels, int rectx, int recty, const char *UNUSED(suffix), ReportList *reports)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSBitmapImageRep *blBitmapFormatImage;
@@ -647,6 +660,7 @@ int append_qt(struct RenderData *rd, int start_frame, int frame, int *pixels, in
OSStatus err = noErr;
unsigned char *from_Ptr,*to_Ptr;
int y,from_i,to_i;
+ QuicktimeExport *qtexport = context_v;
/* Create bitmap image rep in blender format (32bit RGBA) */
blBitmapFormatImage = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
@@ -704,7 +718,7 @@ int append_qt(struct RenderData *rd, int start_frame, int frame, int *pixels, in
audioPacketsConverted = 1;
err = AudioConverterFillComplexBuffer(qtexport->audioConverter, AudioConverterInputCallback,
- NULL, &audioPacketsConverted, &qtexport->audioBufferList, qtexport->audioOutputPktDesc);
+ qtexport, &audioPacketsConverted, &qtexport->audioBufferList, qtexport->audioOutputPktDesc);
if (audioPacketsConverted) {
AudioFileWritePackets(qtexport->audioFile, false, qtexport->audioBufferList.mBuffers[0].mDataByteSize,
qtexport->audioOutputPktDesc, qtexport->audioOutputPktPos, &audioPacketsConverted, qtexport->audioOutputBuffer);
@@ -737,9 +751,11 @@ int append_qt(struct RenderData *rd, int start_frame, int frame, int *pixels, in
}
-void end_qt(void)
+void end_qt(void *context_v)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+ QuicktimeExport *qtexport = context_v;
+
if (qtexport->movie) {
if (qtexport->audioFile)
@@ -830,11 +846,6 @@ void end_qt(void)
}
[QTMovie exitQTKitOnThread];
-
- if (qtexport) {
- MEM_freeN(qtexport);
- qtexport = NULL;
- }
[pool drain];
}
diff --git a/source/blender/quicktime/quicktime_export.h b/source/blender/quicktime/quicktime_export.h
index 360c475fdb5..8a10a4a05d6 100644
--- a/source/blender/quicktime/quicktime_export.h
+++ b/source/blender/quicktime/quicktime_export.h
@@ -55,10 +55,12 @@ struct RenderData;
struct ReportList;
struct Scene;
-int start_qt(struct Scene *scene, struct RenderData *rd, int rectx, int recty, struct ReportList *reports, bool preview); //for movie handle (BKE writeavi.c now)
-int append_qt(struct RenderData *rd, int start_frame, int frame, int *pixels, int rectx, int recty, struct ReportList *reports);
-void end_qt(void);
-void filepath_qt(char *string, struct RenderData *rd, bool preview);
+int start_qt(void *context_v, struct Scene *scene, struct RenderData *rd, int rectx, int recty, struct ReportList *reports, bool preview, const char *suffix); //for movie handle (BKE writeavi.c now)
+int append_qt(void *context_v, struct RenderData *rd, int start_frame, int frame, int *pixels, int rectx, int recty, const char *suffix, struct ReportList *reports);
+void end_qt(void *context_v);
+void filepath_qt(char *string, struct RenderData *rd, bool preview, const char *suffix);
+void *context_create_qt(void);
+void context_free_qt(void *context_v);
/*RNA helper functions */
void quicktime_verify_image_type(struct RenderData *rd, struct ImageFormatData *imf); //used by RNA for defaults values init, if needed
diff --git a/source/blender/render/extern/include/RE_engine.h b/source/blender/render/extern/include/RE_engine.h
index a52244ff87f..4857c409f08 100644
--- a/source/blender/render/extern/include/RE_engine.h
+++ b/source/blender/render/extern/include/RE_engine.h
@@ -135,10 +135,14 @@ void RE_engine_free(RenderEngine *engine);
void RE_layer_load_from_file(struct RenderLayer *layer, struct ReportList *reports, const char *filename, int x, int y);
void RE_result_load_from_file(struct RenderResult *result, struct ReportList *reports, const char *filename);
-struct RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h, const char *layername);
+struct RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h, const char *layername, const char *viewname);
void RE_engine_update_result(RenderEngine *engine, struct RenderResult *result);
void RE_engine_end_result(RenderEngine *engine, struct RenderResult *result, int cancel, int merge_results);
+void RE_engine_active_view_set(RenderEngine *engine, const char *viewname);
+float RE_engine_get_camera_shift_x(RenderEngine *engine, struct Object *camera);
+void RE_engine_get_camera_model_matrix(RenderEngine *engine, struct Object *camera, float *r_modelmat);
+
int RE_engine_test_break(RenderEngine *engine);
void RE_engine_update_stats(RenderEngine *engine, const char *stats, const char *info);
void RE_engine_update_progress(RenderEngine *engine, float progress);
diff --git a/source/blender/render/extern/include/RE_pipeline.h b/source/blender/render/extern/include/RE_pipeline.h
index d1b6673c792..2a07246eaba 100644
--- a/source/blender/render/extern/include/RE_pipeline.h
+++ b/source/blender/render/extern/include/RE_pipeline.h
@@ -35,8 +35,10 @@
#include "DNA_listBase.h"
#include "DNA_vec_types.h"
+struct bMovieHandle;
struct bNodeTree;
struct Image;
+struct ImageFormatData;
struct Main;
struct NodeBlurData;
struct Object;
@@ -46,6 +48,7 @@ struct ReportList;
struct Scene;
struct SceneRenderLayer;
struct EnvMap;
+struct RenderResult;
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
/* this include is what is exposed of render to outside world */
@@ -65,6 +68,19 @@ typedef struct Render Render;
* and how it's converted
*/
+typedef struct RenderView {
+ struct RenderView *next, *prev;
+ char name[64]; /* EXR_VIEW_MAXNAME */
+
+ /* if this exists, result of composited layers */
+ float *rectf;
+ /* if this exists, result of composited layers */
+ float *rectz;
+ /* optional, 32 bits version of picture, used for sequencer, ogl render and image curves */
+ int *rect32;
+
+} RenderView;
+
typedef struct RenderPass {
struct RenderPass *next, *prev;
int passtype, channels;
@@ -72,6 +88,11 @@ typedef struct RenderPass {
char chan_id[8]; /* amount defined in openexr_multi.h */
float *rect;
int rectx, recty;
+
+ char internal_name[64]; /* EXR_PASS_MAXNAME */
+ char view[64]; /* EXR_VIEW_MAXNAME */
+ int view_id; /* quick lookup */
+
int debug_type;
} RenderPass;
@@ -81,7 +102,7 @@ enum {
/* a renderlayer is a full image, but with all passes and samples */
/* size of the rects is defined in RenderResult */
-/* after render, the Combined pass is in rectf, for renderlayers read from files it is a real pass */
+/* after render, the Combined pass is in combined, for renderlayers read from files it is a real pass */
typedef struct RenderLayer {
struct RenderLayer *next, *prev;
@@ -92,8 +113,10 @@ typedef struct RenderLayer {
struct Material *mat_override;
struct Group *light_override;
-
- float *rectf; /* 4 float, standard rgba buffer (read not above!) */
+
+ /* MULTIVIEW_TODO: acolrect and scolrect are not supported by multiview at the moment.
+ * If they are really required they should be in RenderView instead */
+
float *acolrect; /* 4 float, optional transparent buffer, needs storage for display updates */
float *scolrect; /* 4 float, optional strand buffer, needs storage for display updates */
int *display_buffer; /* 4 char, optional color managed display buffer which is used when
@@ -129,6 +152,9 @@ typedef struct RenderResult {
/* the main buffers */
ListBase layers;
+ /* multiView maps to a StringVector in OpenEXR */
+ ListBase views;
+
/* allowing live updates: */
volatile rcti renrect;
volatile RenderLayer *renlay;
@@ -183,16 +209,18 @@ void RE_FreeRenderResult(struct RenderResult *rr);
struct RenderResult *RE_AcquireResultRead(struct Render *re);
struct RenderResult *RE_AcquireResultWrite(struct Render *re);
void RE_ReleaseResult(struct Render *re);
-void RE_AcquireResultImage(struct Render *re, struct RenderResult *rr);
+void RE_AcquireResultImageViews(struct Render *re, struct RenderResult *rr);
+void RE_ReleaseResultImageViews(struct Render *re, struct RenderResult *rr);
+void RE_AcquireResultImage(struct Render *re, struct RenderResult *rr, const int view_id);
void RE_ReleaseResultImage(struct Render *re);
void RE_SwapResult(struct Render *re, struct RenderResult **rr);
struct RenderStats *RE_GetStats(struct Render *re);
void RE_ResultGet32(struct Render *re, unsigned int *rect);
-void RE_AcquiredResultGet32(struct Render *re, struct RenderResult *result, unsigned int *rect);
+void RE_AcquiredResultGet32(struct Render *re, struct RenderResult *result, unsigned int *rect, const int view_id);
struct RenderLayer *RE_GetRenderLayer(struct RenderResult *rr, const char *name);
-float *RE_RenderLayerGetPass(struct RenderLayer *rl, int passtype);
+float *RE_RenderLayerGetPass(volatile struct RenderLayer *rl, int passtype, const char *viewname);
/* obligatory initialize call, disprect is optional */
void RE_InitState(struct Render *re, struct Render *source, struct RenderData *rd,
@@ -203,6 +231,7 @@ void RE_ChangeModeFlag(struct Render *re, int flag, bool clear);
/* set up the viewplane/perspective matrix, three choices */
struct Object *RE_GetCamera(struct Render *re); /* return camera override if set */
+void RE_SetOverrideCamera(struct Render *re, struct Object *camera);
void RE_SetCamera(struct Render *re, struct Object *camera);
void RE_SetEnvmapCamera(struct Render *re, struct Object *cam_ob, float viewscale, float clipsta, float clipend);
void RE_SetWindow(struct Render *re, rctf *viewplane, float clipsta, float clipend);
@@ -232,6 +261,11 @@ void RE_init_threadcount(Render *re);
/* the main processor, assumes all was set OK! */
void RE_TileProcessor(struct Render *re);
+bool RE_WriteRenderViewsImage(struct ReportList *reports, struct RenderResult *rr, struct Scene *scene, struct Object *camera, const bool stamp, char *name);
+bool RE_WriteRenderViewsMovie(struct ReportList *reports, struct RenderResult *rr, struct Scene *scene, struct RenderData *rd,
+ struct bMovieHandle *mh, const size_t width, const size_t height, void **movie_ctx_arr,
+ const size_t totvideos);
+
/* only RE_NewRender() needed, main Blender render calls */
void RE_BlenderFrame(struct Render *re, struct Main *bmain, struct Scene *scene,
struct SceneRenderLayer *srl, struct Object *camera_override,
@@ -243,6 +277,9 @@ void RE_RenderFreestyleStrokes(struct Render *re, struct Main *bmain, struct Sce
void RE_RenderFreestyleExternal(struct Render *re);
#endif
+void RE_SetActiveRenderView(struct Render *re, const char *viewname);
+const char *RE_GetActiveRenderView(struct Render *re);
+
/* error reporting */
void RE_SetReports(struct Render *re, struct ReportList *reports);
@@ -250,7 +287,7 @@ void RE_SetReports(struct Render *re, struct ReportList *reports);
void RE_PreviewRender(struct Render *re, struct Main *bmain, struct Scene *scene);
bool RE_ReadRenderResult(struct Scene *scene, struct Scene *scenode);
-bool RE_WriteRenderResult(struct ReportList *reports, RenderResult *rr, const char *filename, int compress);
+bool RE_WriteRenderResult(struct ReportList *reports, RenderResult *rr, const char *filename, struct ImageFormatData *imf, const bool multiview, const char *view);
struct RenderResult *RE_MultilayerConvert(void *exrhandle, const char *colorspace, bool predivide, int rectx, int recty);
extern const float default_envmap_layout[];
@@ -276,6 +313,8 @@ void RE_zbuf_accumulate_vecblur(struct NodeBlurData *nbd, int xsize, int ysize,
int RE_seq_render_active(struct Scene *scene, struct RenderData *rd);
+bool RE_layers_have_name(struct RenderResult *result);
+
/* shaded view or baking options */
#define RE_BAKE_LIGHT 0 /* not listed in rna_scene.c -> can't be enabled! */
#define RE_BAKE_ALL 1
@@ -297,6 +336,7 @@ void RE_Database_Baking(struct Render *re, struct Main *bmain, struct Scene *sce
void RE_DataBase_GetView(struct Render *re, float mat[4][4]);
void RE_GetCameraWindow(struct Render *re, struct Object *camera, int frame, float mat[4][4]);
+void RE_GetCameraModelMatrix(struct Render *re, struct Object *camera, float r_mat[4][4]);
struct Scene *RE_GetScene(struct Render *re);
bool RE_force_single_renderlayer(struct Scene *scene);
@@ -304,5 +344,16 @@ bool RE_is_rendering_allowed(struct Scene *scene, struct Object *camera_override
bool RE_allow_render_generic_object(struct Object *ob);
+/******* defined in render_result.c *********/
+
+bool RE_HasFakeLayer(RenderResult *res);
+bool RE_RenderResult_is_stereo(RenderResult *res);
+
+float *RE_RenderViewGetRectf(struct RenderResult *rr, const int view_id);
+float *RE_RenderViewGetRectz(struct RenderResult *rr, const int view_id);
+int *RE_RenderViewGetRect32(struct RenderResult *rr, const int view_id);
+void RE_RenderViewSetRectf(struct RenderResult *res, const int view_id, float *rect);
+void RE_RenderViewSetRectz(struct RenderResult *res, const int view_id, float *rect);
+
#endif /* __RE_PIPELINE_H__ */
diff --git a/source/blender/render/intern/include/render_result.h b/source/blender/render/intern/include/render_result.h
index 90ff69dbfbe..e7a2cbf99cd 100644
--- a/source/blender/render/intern/include/render_result.h
+++ b/source/blender/render/intern/include/render_result.h
@@ -38,6 +38,7 @@
#define RR_USE_EXR 1
#define RR_ALL_LAYERS NULL
+#define RR_ALL_VIEWS NULL
struct ImBuf;
struct ListBase;
@@ -53,12 +54,14 @@ struct ColorManagedViewSettings;
/* New */
struct RenderResult *render_result_new(struct Render *re,
- struct rcti *partrct, int crop, int savebuffers, const char *layername);
+ struct rcti *partrct, int crop, int savebuffers, const char *layername, const char *viewname);
struct RenderResult *render_result_new_full_sample(struct Render *re,
- struct ListBase *lb, struct rcti *partrct, int crop, int savebuffers);
+ struct ListBase *lb, struct rcti *partrct, int crop, int savebuffers, const char *viewname);
struct RenderResult *render_result_new_from_exr(void *exrhandle, const char *colorspace, bool predivide, int rectx, int recty);
+void render_result_views_new(struct RenderResult *rr, struct RenderData *rd);
+
/* Merge */
void render_result_merge(struct RenderResult *rr, struct RenderResult *rrpart);
@@ -78,7 +81,7 @@ void render_result_single_layer_end(struct Render *re);
void render_result_exr_file_begin(struct Render *re);
void render_result_exr_file_end(struct Render *re);
-void render_result_exr_file_merge(struct RenderResult *rr, struct RenderResult *rrpart);
+void render_result_exr_file_merge(struct RenderResult *rr, struct RenderResult *rrpart, const char *viewname);
void render_result_exr_file_path(struct Scene *scene, const char *layname, int sample, char *filepath);
int render_result_exr_file_read_sample(struct Render *re, int sample);
@@ -91,15 +94,19 @@ bool render_result_exr_file_cache_read(struct Render *re);
/* Combined Pixel Rect */
-struct ImBuf *render_result_rect_to_ibuf(struct RenderResult *rr, struct RenderData *rd);
+struct ImBuf *render_result_rect_to_ibuf(struct RenderResult *rr, struct RenderData *rd, const int view_id);
void render_result_rect_from_ibuf(struct RenderResult *rr, struct RenderData *rd,
- struct ImBuf *ibuf);
+ struct ImBuf *ibuf, const int view_id);
-void render_result_rect_fill_zero(struct RenderResult *rr);
+void render_result_rect_fill_zero(struct RenderResult *rr, const int view_id);
void render_result_rect_get_pixels(struct RenderResult *rr,
unsigned int *rect, int rectx, int recty,
const struct ColorManagedViewSettings *view_settings,
- const struct ColorManagedDisplaySettings *display_settings);
+ const struct ColorManagedDisplaySettings *display_settings,
+ const int view_id);
+
+void render_result_views_shallowcopy(struct RenderResult *dst, struct RenderResult *src);
+void render_result_views_shallowdelete(struct RenderResult *rr);
#endif /* __RENDER_RESULT_H__ */
diff --git a/source/blender/render/intern/include/render_types.h b/source/blender/render/intern/include/render_types.h
index 0d23c81afb0..8fe12015e87 100644
--- a/source/blender/render/intern/include/render_types.h
+++ b/source/blender/render/intern/include/render_types.h
@@ -275,6 +275,9 @@ struct Render
struct ImagePool *pool;
struct EvaluationContext *eval_ctx;
+
+ void **movie_ctx_arr;
+ char viewname[MAX_NAME];
};
/* ------------------------------------------------------------------------- */
diff --git a/source/blender/render/intern/source/convertblender.c b/source/blender/render/intern/source/convertblender.c
index c0ba2f43c36..d36ab46d145 100644
--- a/source/blender/render/intern/source/convertblender.c
+++ b/source/blender/render/intern/source/convertblender.c
@@ -5171,8 +5171,7 @@ void RE_Database_FromScene(Render *re, Main *bmain, Scene *scene, unsigned int l
* above call to BKE_scene_update_for_newframe, fixes bug. [#22702].
* following calls don't depend on 'RE_SetCamera' */
RE_SetCamera(re, camera);
-
- normalize_m4_m4(mat, camera->obmat);
+ RE_GetCameraModelMatrix(re, camera, mat);
invert_m4(mat);
RE_SetView(re, mat);
@@ -5342,7 +5341,8 @@ static void database_fromscene_vectors(Render *re, Scene *scene, unsigned int la
/* if no camera, viewmat should have been set! */
if (camera) {
- normalize_m4_m4(mat, camera->obmat);
+ RE_GetCameraModelMatrix(re, camera, mat);
+ normalize_m4(mat);
invert_m4(mat);
RE_SetView(re, mat);
}
diff --git a/source/blender/render/intern/source/envmap.c b/source/blender/render/intern/source/envmap.c
index 0698f849073..ed3033d8393 100644
--- a/source/blender/render/intern/source/envmap.c
+++ b/source/blender/render/intern/source/envmap.c
@@ -495,9 +495,11 @@ static void render_envmap(Render *re, EnvMap *env)
RenderLayer *rl = envre->result->layers.first;
int y;
float *alpha;
-
+ float *rect;
+
+ rect = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, re->viewname);
ibuf = IMB_allocImBuf(envre->rectx, envre->recty, 24, IB_rect | IB_rectfloat);
- memcpy(ibuf->rect_float, rl->rectf, ibuf->channels * ibuf->x * ibuf->y * sizeof(float));
+ memcpy(ibuf->rect_float, rect, ibuf->channels * ibuf->x * ibuf->y * sizeof(float));
/* envmap renders without alpha */
alpha = ibuf->rect_float + 3;
diff --git a/source/blender/render/intern/source/external_engine.c b/source/blender/render/intern/source/external_engine.c
index e56466bdf48..343c4a9387a 100644
--- a/source/blender/render/intern/source/external_engine.c
+++ b/source/blender/render/intern/source/external_engine.c
@@ -42,6 +42,7 @@
#include "BLI_string.h"
#include "BLI_utildefines.h"
+#include "BKE_camera.h"
#include "BKE_global.h"
#include "BKE_report.h"
#include "BKE_scene.h"
@@ -181,7 +182,7 @@ static RenderPart *get_part_from_result(Render *re, RenderResult *result)
return NULL;
}
-RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h, const char *layername)
+RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h, const char *layername, const char *viewname)
{
Render *re = engine->re;
RenderResult *result;
@@ -204,7 +205,7 @@ RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w,
disprect.ymin = y;
disprect.ymax = y + h;
- result = render_result_new(re, &disprect, 0, RR_USE_MEM, layername);
+ result = render_result_new(re, &disprect, 0, RR_USE_MEM, layername, viewname);
/* todo: make this thread safe */
@@ -269,7 +270,7 @@ void RE_engine_end_result(RenderEngine *engine, RenderResult *result, int cancel
if (!cancel || merge_results) {
if (re->result->do_exr_tile) {
if (!cancel) {
- render_result_exr_file_merge(re->result, result);
+ render_result_exr_file_merge(re->result, result, re->viewname);
}
}
else if (!(re->test_break(re->tbh) && (re->r.scemode & R_BUTS_PREVIEW)))
@@ -368,6 +369,24 @@ void RE_engine_set_error_message(RenderEngine *engine, const char *msg)
}
}
+void RE_engine_active_view_set(RenderEngine *engine, const char *viewname)
+{
+ Render *re = engine->re;
+ RE_SetActiveRenderView(re, viewname);
+}
+
+float RE_engine_get_camera_shift_x(RenderEngine *engine, Object *camera)
+{
+ Render *re = engine->re;
+ return BKE_camera_multiview_shift_x(re ? &re->r : NULL, camera, re->viewname);
+}
+
+void RE_engine_get_camera_model_matrix(RenderEngine *engine, Object *camera, float *r_modelmat)
+{
+ Render *re = engine->re;
+ BKE_camera_multiview_model_matrix(re ? &re->r : NULL, camera, re->viewname, (float (*)[4])r_modelmat);
+}
+
rcti* RE_engine_get_current_tiles(Render *re, int *r_total_tiles, bool *r_needs_free)
{
static rcti tiles_static[BLENDER_MAX_THREADS];
@@ -612,7 +631,7 @@ int RE_engine_render(Render *re, int do_all)
if ((type->flag & RE_USE_SAVE_BUFFERS) && (re->r.scemode & R_EXR_TILE_FILE))
savebuffers = RR_USE_EXR;
- re->result = render_result_new(re, &re->disprect, 0, savebuffers, RR_ALL_LAYERS);
+ re->result = render_result_new(re, &re->disprect, 0, savebuffers, RR_ALL_LAYERS, RR_ALL_VIEWS);
}
BLI_rw_mutex_unlock(&re->resultmutex);
diff --git a/source/blender/render/intern/source/initrender.c b/source/blender/render/intern/source/initrender.c
index a0fcc7cdcd2..970a3937657 100644
--- a/source/blender/render/intern/source/initrender.c
+++ b/source/blender/render/intern/source/initrender.c
@@ -426,10 +426,10 @@ void make_sample_tables(Render *re)
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
-
struct Object *RE_GetCamera(Render *re)
{
- return re->camera_override ? re->camera_override : re->scene->camera;
+ Object *camera = re->camera_override ? re->camera_override : re->scene->camera;
+ return BKE_camera_multiview_render(re->scene, camera, re->viewname);
}
static void re_camera_params_get(Render *re, CameraParams *params, Object *cam_ob)
@@ -470,6 +470,16 @@ void RE_SetEnvmapCamera(Render *re, Object *cam_ob, float viewscale, float clips
re_camera_params_get(re, &params, cam_ob);
}
+void RE_SetOverrideCamera(Render *re, Object *camera)
+{
+ re->camera_override = camera;
+}
+
+static void re_camera_params_stereo3d(Render *re, CameraParams *params, Object *cam_ob)
+{
+ BKE_camera_multiview_params(&re->r, params, cam_ob, re->viewname);
+}
+
/* call this after InitState() */
/* per render, there's one persistent viewplane. Parts will set their own viewplanes */
void RE_SetCamera(Render *re, Object *cam_ob)
@@ -479,6 +489,7 @@ void RE_SetCamera(Render *re, Object *cam_ob)
/* setup parameters */
BKE_camera_params_init(&params);
BKE_camera_params_from_object(&params, cam_ob);
+ re_camera_params_stereo3d(re, &params, cam_ob);
params.use_fields = (re->r.mode & R_FIELDS);
params.field_second = (re->flag & R_SEC_FIELD);
@@ -505,6 +516,11 @@ void RE_GetCameraWindow(struct Render *re, struct Object *camera, int frame, flo
copy_m4_m4(mat, re->winmat);
}
+void RE_GetCameraModelMatrix(Render *re, struct Object *camera, float r_mat[4][4])
+{
+ BKE_camera_multiview_model_matrix(&re->r, camera, re->viewname, r_mat);
+}
+
/* ~~~~~~~~~~~~~~~~ part (tile) calculus ~~~~~~~~~~~~~~~~~~~~~~ */
diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c
index 4bde3dddaff..25adb4db4f7 100644
--- a/source/blender/render/intern/source/pipeline.c
+++ b/source/blender/render/intern/source/pipeline.c
@@ -70,6 +70,7 @@
#include "BKE_scene.h"
#include "BKE_sequencer.h"
#include "BKE_writeavi.h" /* <------ should be replaced once with generic movie module */
+#include "BKE_object.h"
#include "PIL_time.h"
#include "IMB_colormanagement.h"
@@ -131,7 +132,7 @@ Render R;
/* ********* alloc and free ******** */
-static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovieHandle *mh, const char *name_override);
+static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovieHandle *mh, const size_t totvideos, const char *name_override);
static volatile int g_break = 0;
static int thread_break(void *UNUSED(arg))
@@ -190,14 +191,23 @@ void RE_FreeRenderResult(RenderResult *res)
render_result_free(res);
}
-float *RE_RenderLayerGetPass(RenderLayer *rl, int passtype)
+float *RE_RenderLayerGetPass(volatile RenderLayer *rl, int passtype, const char *viewname)
{
RenderPass *rpass;
-
- for (rpass = rl->passes.first; rpass; rpass = rpass->next)
- if (rpass->passtype == passtype)
- return rpass->rect;
- return NULL;
+ float *rect = NULL;
+
+ for (rpass = rl->passes.last; rpass; rpass = rpass->prev) {
+ if (rpass->passtype == passtype) {
+ rect = rpass->rect;
+
+ if (viewname == NULL)
+ break;
+ else if (strcmp(rpass->view, viewname) == 0)
+ break;
+ }
+ }
+
+ return rect;
}
RenderLayer *RE_GetRenderLayer(RenderResult *rr, const char *name)
@@ -307,8 +317,69 @@ Scene *RE_GetScene(Render *re)
return NULL;
}
+/**
+ * Same as #RE_AcquireResultImage but creating the necessary views to store the result
+ * fill provided result struct with a copy of thew views of what is done so far the
+ * #RenderResult.views #ListBase needs to be freed after with #RE_ReleaseResultImageViews
+*/
+void RE_AcquireResultImageViews(Render *re, RenderResult *rr)
+{
+ memset(rr, 0, sizeof(RenderResult));
+
+ if (re) {
+ BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_READ);
+
+ if (re->result) {
+ RenderLayer *rl;
+ RenderView *rv, *rview;
+
+ rr->rectx = re->result->rectx;
+ rr->recty = re->result->recty;
+
+ /* creates a temporary duplication of views */
+ render_result_views_shallowcopy(rr, re->result);
+
+ rv = rr->views.first;
+
+ rr->have_combined = (rv->rectf != NULL);
+
+ /* active layer */
+ rl = render_get_active_layer(re, re->result);
+
+ if (rl) {
+ if (rv->rectf == NULL) {
+ for (rview = (RenderView *)rr->views.first; rview; rview = rview->next) {
+ rview->rectf = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, rview->name);
+ }
+ }
+
+ if (rv->rectz == NULL) {
+ for (rview = (RenderView *)rr->views.first; rview; rview = rview->next) {
+ rview->rectz = RE_RenderLayerGetPass(rl, SCE_PASS_Z, rview->name);
+ }
+ }
+ }
+
+ rr->layers = re->result->layers;
+ rr->xof = re->disprect.xmin;
+ rr->yof = re->disprect.ymin;
+ }
+ }
+}
+
+/* clear temporary renderresult struct */
+void RE_ReleaseResultImageViews(Render *re, RenderResult *rr)
+{
+ if (re) {
+ if (rr) {
+ render_result_views_shallowdelete(rr);
+ }
+ BLI_rw_mutex_unlock(&re->resultmutex);
+ }
+}
+
/* fill provided result struct with what's currently active or done */
-void RE_AcquireResultImage(Render *re, RenderResult *rr)
+void RE_AcquireResultImage(Render *re, RenderResult *rr, const int view_id)
{
memset(rr, 0, sizeof(RenderResult));
@@ -317,27 +388,35 @@ void RE_AcquireResultImage(Render *re, RenderResult *rr)
if (re->result) {
RenderLayer *rl;
+ RenderView *rv;
rr->rectx = re->result->rectx;
rr->recty = re->result->recty;
- rr->rectf = re->result->rectf;
- rr->rectz = re->result->rectz;
- rr->rect32 = re->result->rect32;
-
+ /* actview view */
+ rv = BLI_findlink(&re->result->views, view_id);
+ if (rv == NULL)
+ rv = (RenderView *)re->result->views.first;
+
+ rr->rectf = rv ? rv->rectf : NULL;
+ rr->rectz = rv ? rv->rectz : NULL;
+ rr->rect32 = rv ? rv->rect32 : NULL;
+
/* active layer */
rl = render_get_active_layer(re, re->result);
- if (rl) {
- if (rr->rectf == NULL)
- rr->rectf = rl->rectf;
- if (rr->rectz == NULL)
- rr->rectz = RE_RenderLayerGetPass(rl, SCE_PASS_Z);
+ if (rl && rv) {
+ if (rv->rectf == NULL)
+ rr->rectf = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, rv->name);
+
+ if (rv->rectz == NULL)
+ rr->rectz = RE_RenderLayerGetPass(rl, SCE_PASS_Z, rv->name);
}
- rr->have_combined = (re->result->rectf != NULL);
+ rr->have_combined = rv ? (rv->rectf != NULL) : false;
rr->layers = re->result->layers;
-
+ rr->views = re->result->views;
+
rr->xof = re->disprect.xmin;
rr->yof = re->disprect.ymin;
}
@@ -354,17 +433,18 @@ void RE_ReleaseResultImage(Render *re)
void RE_ResultGet32(Render *re, unsigned int *rect)
{
RenderResult rres;
-
- RE_AcquireResultImage(re, &rres);
- render_result_rect_get_pixels(&rres, rect, re->rectx, re->recty, &re->scene->view_settings, &re->scene->display_settings);
+ const size_t view_id = BKE_scene_multiview_view_id_get(&re->r, re->viewname);
+
+ RE_AcquireResultImage(re, &rres, view_id);
+ render_result_rect_get_pixels(&rres, rect, re->rectx, re->recty, &re->scene->view_settings, &re->scene->display_settings, 0);
RE_ReleaseResultImage(re);
}
/* caller is responsible for allocating rect in correct size! */
/* Only for acquired results, for lock */
-void RE_AcquiredResultGet32(Render *re, RenderResult *result, unsigned int *rect)
+void RE_AcquiredResultGet32(Render *re, RenderResult *result, unsigned int *rect, const int view_id)
{
- render_result_rect_get_pixels(result, rect, re->rectx, re->recty, &re->scene->view_settings, &re->scene->display_settings);
+ render_result_rect_get_pixels(result, rect, re->rectx, re->recty, &re->scene->view_settings, &re->scene->display_settings, view_id);
}
RenderStats *RE_GetStats(Render *re)
@@ -683,6 +763,8 @@ void RE_InitState(Render *re, Render *source, RenderData *rd,
RE_init_threadcount(re);
}
+/* This function is only called by view3d rendering, which doesn't support
+ * multiview at the moment. so handle only one view here */
static void render_result_rescale(Render *re)
{
RenderResult *result = re->result;
@@ -694,7 +776,7 @@ static void render_result_rescale(Render *re)
if (src_rectf == NULL) {
RenderLayer *rl = render_get_active_layer(re, re->result);
if (rl != NULL) {
- src_rectf = rl->rectf;
+ src_rectf = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, "");
}
}
@@ -704,7 +786,8 @@ static void render_result_rescale(Render *re)
&re->disprect,
0,
RR_USE_MEM,
- RR_ALL_LAYERS);
+ RR_ALL_LAYERS,
+ "");
if (re->result != NULL) {
dst_rectf = re->result->rectf;
@@ -712,7 +795,7 @@ static void render_result_rescale(Render *re)
RenderLayer *rl;
rl = render_get_active_layer(re, re->result);
if (rl != NULL) {
- dst_rectf = rl->rectf;
+ dst_rectf = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, "");
}
}
@@ -910,9 +993,9 @@ static void *do_part_thread(void *pa_v)
if (R.test_break(R.tbh) == 0) {
if (!R.sss_points && (R.r.scemode & R_FULL_SAMPLE))
- pa->result = render_result_new_full_sample(&R, &pa->fullresult, &pa->disprect, pa->crop, RR_USE_MEM);
+ pa->result = render_result_new_full_sample(&R, &pa->fullresult, &pa->disprect, pa->crop, RR_USE_MEM, R.viewname);
else
- pa->result = render_result_new(&R, &pa->disprect, pa->crop, RR_USE_MEM, RR_ALL_LAYERS);
+ pa->result = render_result_new(&R, &pa->disprect, pa->crop, RR_USE_MEM, RR_ALL_LAYERS, R.viewname);
/* Copy EXR tile settings, so pipeline knows whether this is a result
* for Save Buffers enabled rendering.
@@ -935,7 +1018,7 @@ static void *do_part_thread(void *pa_v)
/* merge too on break! */
if (R.result->do_exr_tile) {
- render_result_exr_file_merge(R.result, pa->result);
+ render_result_exr_file_merge(R.result, pa->result, R.viewname);
}
else if (render_display_update_enabled(&R)) {
/* on break, don't merge in result for preview renders, looks nicer */
@@ -1115,16 +1198,23 @@ static void *do_render_thread(void *thread_v)
return NULL;
}
-static void threaded_tile_processor(Render *re)
+static void main_render_result_end(Render *re)
+{
+ if (re->result->do_exr_tile) {
+ BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
+ render_result_exr_file_end(re);
+ BLI_rw_mutex_unlock(&re->resultmutex);
+ }
+
+ if (re->r.scemode & R_EXR_CACHE_FILE) {
+ BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
+ render_result_exr_file_cache_write(re);
+ BLI_rw_mutex_unlock(&re->resultmutex);
+ }
+}
+
+static void main_render_result_new(Render *re)
{
- RenderThread thread[BLENDER_MAX_THREADS];
- ThreadQueue *workqueue, *donequeue;
- ListBase threads;
- RenderPart *pa;
- rctf viewplane = re->viewplane;
- double lastdraw, elapsed, redrawtime = 1.0f;
- int totpart = 0, minx = 0, slice = 0, a, wait;
-
BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
/* first step; free the entire render result, make new, and/or prepare exr buffer saving */
@@ -1132,25 +1222,35 @@ static void threaded_tile_processor(Render *re)
render_result_free(re->result);
if (re->sss_points && render_display_update_enabled(re))
- re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS);
+ re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS);
else if (re->r.scemode & R_FULL_SAMPLE)
- re->result = render_result_new_full_sample(re, &re->fullresult, &re->disprect, 0, RR_USE_EXR);
+ re->result = render_result_new_full_sample(re, &re->fullresult, &re->disprect, 0, RR_USE_EXR, RR_ALL_VIEWS);
else
re->result = render_result_new(re, &re->disprect, 0,
- (re->r.scemode & R_EXR_TILE_FILE) ? RR_USE_EXR : RR_USE_MEM, RR_ALL_LAYERS);
+ (re->r.scemode & R_EXR_TILE_FILE) ? RR_USE_EXR : RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS);
}
BLI_rw_mutex_unlock(&re->resultmutex);
+
+ if (re->result->do_exr_tile)
+ render_result_exr_file_begin(re);
+}
+
+static void threaded_tile_processor(Render *re)
+{
+ RenderThread thread[BLENDER_MAX_THREADS];
+ ThreadQueue *workqueue, *donequeue;
+ ListBase threads;
+ RenderPart *pa;
+ rctf viewplane = re->viewplane;
+ double lastdraw, elapsed, redrawtime = 1.0f;
+ int totpart = 0, minx = 0, slice = 0, a, wait;
if (re->result == NULL)
return;
/* warning; no return here without closing exr file */
-
RE_parts_init(re, true);
-
- if (re->result->do_exr_tile)
- render_result_exr_file_begin(re);
/* assuming no new data gets added to dbase... */
R = *re;
@@ -1256,18 +1356,6 @@ static void threaded_tile_processor(Render *re)
BLI_thread_queue_free(donequeue);
BLI_thread_queue_free(workqueue);
- if (re->result->do_exr_tile) {
- BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
- render_result_exr_file_end(re);
- BLI_rw_mutex_unlock(&re->resultmutex);
- }
-
- if (re->r.scemode & R_EXR_CACHE_FILE) {
- BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
- render_result_exr_file_cache_write(re);
- BLI_rw_mutex_unlock(&re->resultmutex);
- }
-
/* unset threadsafety */
g_break = 0;
BLI_rw_mutex_lock(&re->partsmutex, THREAD_LOCK_WRITE);
@@ -1284,6 +1372,7 @@ static void free_all_freestyle_renders(void);
/* currently only called by preview renders and envmap */
void RE_TileProcessor(Render *re)
{
+ main_render_result_new(re);
threaded_tile_processor(re);
re->i.lastframetime = PIL_check_seconds_timer() - re->i.starttime;
@@ -1309,6 +1398,7 @@ void RE_TileProcessor(Render *re)
static void do_render_3d(Render *re)
{
+ RenderView *rv;
int cfra_backup;
re->current_scene_update(re->suh, re->scene);
@@ -1325,40 +1415,49 @@ static void do_render_3d(Render *re)
BKE_scene_frame_set(re->scene, (double)re->scene->r.cfra + (double)re->mblur_offs + (double)re->field_offs);
- /* lock drawing in UI during data phase */
- if (re->draw_lock)
- re->draw_lock(re->dlh, 1);
-
- /* make render verts/faces/halos/lamps */
- if (render_scene_needs_vector(re)) {
- RE_Database_FromScene_Vectors(re, re->main, re->scene, re->lay);
- }
- else {
- RE_Database_FromScene(re, re->main, re->scene, re->lay, 1);
- RE_Database_Preprocess(re);
- }
+ /* init main render result */
+ main_render_result_new(re);
+
+ /* we need a new database for each view */
+ for (rv = re->result->views.first; rv; rv = rv->next) {
+ RE_SetActiveRenderView(re, rv->name);
+
+ /* lock drawing in UI during data phase */
+ if (re->draw_lock)
+ re->draw_lock(re->dlh, 1);
+
+ /* make render verts/faces/halos/lamps */
+ if (render_scene_needs_vector(re))
+ RE_Database_FromScene_Vectors(re, re->main, re->scene, re->lay);
+ else {
+ RE_Database_FromScene(re, re->main, re->scene, re->lay, 1);
+ RE_Database_Preprocess(re);
+ }
- /* clear UI drawing locks */
- if (re->draw_lock)
- re->draw_lock(re->dlh, 0);
+ /* clear UI drawing locks */
+ if (re->draw_lock)
+ re->draw_lock(re->dlh, 0);
- threaded_tile_processor(re);
+ threaded_tile_processor(re);
#ifdef WITH_FREESTYLE
- /* Freestyle */
- if (re->r.mode & R_EDGE_FRS)
- if (!re->test_break(re->tbh))
- add_freestyle(re, 1);
+ /* Freestyle */
+ if (re->r.mode & R_EDGE_FRS)
+ if (!re->test_break(re->tbh))
+ add_freestyle(re, 1);
#endif
- /* do left-over 3d post effects (flares) */
- if (re->flag & R_HALO)
- if (!re->test_break(re->tbh))
- add_halo_flare(re);
-
- /* free all render verts etc */
- RE_Database_Free(re);
-
+ /* do left-over 3d post effects (flares) */
+ if (re->flag & R_HALO)
+ if (!re->test_break(re->tbh))
+ add_halo_flare(re);
+
+ /* free all render verts etc */
+ RE_Database_Free(re);
+ }
+
+ main_render_result_end(re);
+
re->scene->r.cfra = cfra_backup;
re->scene->r.subframe = 0.f;
}
@@ -1431,19 +1530,13 @@ static void merge_renderresult_blur(RenderResult *rr, RenderResult *brr, float b
rl1 = brr->layers.first;
for (rl = rr->layers.first; rl && rl1; rl = rl->next, rl1 = rl1->next) {
-
- /* combined */
- if (rl->rectf && rl1->rectf) {
- if (key_alpha)
- addblur_rect_key(rr, rl->rectf, rl1->rectf, blurfac);
- else
- addblur_rect(rr, rl->rectf, rl1->rectf, blurfac, 4);
- }
-
/* passes are allocated in sync */
rpass1 = rl1->passes.first;
for (rpass = rl->passes.first; rpass && rpass1; rpass = rpass->next, rpass1 = rpass1->next) {
- addblur_rect(rr, rpass->rect, rpass1->rect, blurfac, rpass->channels);
+ if ((rpass->passtype & SCE_PASS_COMBINED) && key_alpha)
+ addblur_rect_key(rr, rpass->rect, rpass1->rect, blurfac);
+ else
+ addblur_rect(rr, rpass->rect, rpass1->rect, blurfac, rpass->channels);
}
}
}
@@ -1456,7 +1549,7 @@ static void do_render_blur_3d(Render *re)
int blur = re->r.mblur_samples;
/* create accumulation render result */
- rres = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS);
+ rres = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS);
/* do the blur steps */
while (blur--) {
@@ -1518,10 +1611,6 @@ static void merge_renderresult_fields(RenderResult *rr, RenderResult *rr1, Rende
rl2 = rr2->layers.first;
for (rl = rr->layers.first; rl && rl1 && rl2; rl = rl->next, rl1 = rl1->next, rl2 = rl2->next) {
- /* combined */
- if (rl->rectf && rl1->rectf && rl2->rectf)
- interleave_rect(rr, rl->rectf, rl1->rectf, rl2->rectf, 4);
-
/* passes are allocated in sync */
rpass1 = rl1->passes.first;
rpass2 = rl2->passes.first;
@@ -1589,7 +1678,7 @@ static void do_render_fields_3d(Render *re)
re->disprect.ymax *= 2;
BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
- re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS);
+ re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS);
if (rr2) {
if (re->r.mode & R_ODDFIELD)
@@ -1661,7 +1750,7 @@ static void do_render_fields_blur_3d(Render *re)
/* weak is: it chances disprect from border */
render_result_disprect_to_full_resolution(re);
- rres = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS);
+ rres = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS);
render_result_merge(rres, re->result);
render_result_free(re->result);
@@ -2068,11 +2157,14 @@ static void free_all_freestyle_renders(void)
}
#endif
-/* reads all buffers, calls optional composite, merges in first result->rectf */
+/* reads all buffers, calls optional composite, merges in first result->views rectf */
static void do_merge_fullsample(Render *re, bNodeTree *ntree)
{
+ ListBase *rectfs;
+ RenderView *rv;
float *rectf, filt[3][3];
int x, y, sample;
+ int nr, numviews;
/* interaction callbacks */
if (ntree) {
@@ -2087,9 +2179,18 @@ static void do_merge_fullsample(Render *re, bNodeTree *ntree)
/* filtmask needs it */
R = *re;
- /* we accumulate in here */
- rectf = MEM_mapallocN(re->rectx * re->recty * sizeof(float) * 4, "fullsample rgba");
-
+ /* temporary storage of the acccumulation buffers */
+ rectfs = MEM_callocN(sizeof(ListBase), "fullsample accumulation buffers");
+
+ numviews = BLI_listbase_count(&re->result->views);
+ for (nr = 0; nr < numviews; nr++) {
+ rv = MEM_callocN(sizeof(RenderView), "fullsample renderview");
+
+ /* we accumulate in here */
+ rv->rectf = MEM_mapallocN(re->rectx * re->recty * sizeof(float) * 4, "fullsample rgba");
+ BLI_addtail(rectfs, rv);
+ }
+
for (sample = 0; sample < re->r.osa; sample++) {
Scene *sce;
Render *re1;
@@ -2126,54 +2227,70 @@ static void do_merge_fullsample(Render *re, bNodeTree *ntree)
if (ntree) {
ntreeCompositTagRender(re->scene);
ntreeCompositTagAnimated(ntree);
-
- ntreeCompositExecTree(re->scene, ntree, &re->r, true, G.background == 0, &re->scene->view_settings, &re->scene->display_settings);
+
+ for (rv = re->result->views.first; rv; rv = rv->next) {
+ ntreeCompositExecTree(re->scene, ntree, &re->r, true, G.background == 0, &re->scene->view_settings, &re->scene->display_settings, rv->name);
+ }
}
-
- /* ensure we get either composited result or the active layer */
- RE_AcquireResultImage(re, &rres);
-
- /* accumulate with filter, and clip */
- mask = (1 << sample);
- mask_array(mask, filt);
- for (y = 0; y < re->recty; y++) {
- float *rf = rectf + 4 * y * re->rectx;
- float *col = rres.rectf + 4 * y * re->rectx;
-
- for (x = 0; x < re->rectx; x++, rf += 4, col += 4) {
- /* clamping to 1.0 is needed for correct AA */
- if (col[0] < 0.0f) col[0] = 0.0f; else if (col[0] > 1.0f) col[0] = 1.0f;
- if (col[1] < 0.0f) col[1] = 0.0f; else if (col[1] > 1.0f) col[1] = 1.0f;
- if (col[2] < 0.0f) col[2] = 0.0f; else if (col[2] > 1.0f) col[2] = 1.0f;
+ for (nr = 0, rv = re->result->views.first; rv; rv = rv->next, nr++) {
+ rectf = rv->rectf;
+
+ /* ensure we get either composited result or the active layer */
+ RE_AcquireResultImage(re, &rres, nr);
+
+ /* accumulate with filter, and clip */
+ mask = (1 << sample);
+ mask_array(mask, filt);
+
+ for (y = 0; y < re->recty; y++) {
+ float *rf = rectf + 4 * y * re->rectx;
+ float *col = rres.rectf + 4 * y * re->rectx;
- add_filt_fmask_coord(filt, col, rf, re->rectx, re->recty, x, y);
+ for (x = 0; x < re->rectx; x++, rf += 4, col += 4) {
+ /* clamping to 1.0 is needed for correct AA */
+ if (col[0] < 0.0f) col[0] = 0.0f; else if (col[0] > 1.0f) col[0] = 1.0f;
+ if (col[1] < 0.0f) col[1] = 0.0f; else if (col[1] > 1.0f) col[1] = 1.0f;
+ if (col[2] < 0.0f) col[2] = 0.0f; else if (col[2] > 1.0f) col[2] = 1.0f;
+
+ add_filt_fmask_coord(filt, col, rf, re->rectx, re->recty, x, y);
+ }
}
- }
- RE_ReleaseResultImage(re);
+ RE_ReleaseResultImage(re);
- /* show stuff */
- if (sample != re->osa - 1) {
- /* weak... the display callback wants an active renderlayer pointer... */
- re->result->renlay = render_get_active_layer(re, re->result);
- re->display_update(re->duh, re->result, NULL);
+ /* show stuff */
+ if (sample != re->osa - 1) {
+ /* weak... the display callback wants an active renderlayer pointer... */
+ re->result->renlay = render_get_active_layer(re, re->result);
+ RE_SetActiveRenderView(re, rv->name);
+ re->display_update(re->duh, re->result, NULL);
+ }
}
-
- if (re->test_break(re->tbh))
- break;
}
- /* clamp alpha and RGB to 0..1 and 0..inf, can go outside due to filter */
- for (y = 0; y < re->recty; y++) {
- float *rf = rectf + 4 * y * re->rectx;
+ for (nr = 0; nr < numviews; nr++) {
+ rectf = ((RenderView *)BLI_findlink(rectfs, nr))->rectf;
+
+ /* clamp alpha and RGB to 0..1 and 0..inf, can go outside due to filter */
+ for (y = 0; y < re->recty; y++) {
+ float *rf = rectf + 4 * y * re->rectx;
- for (x = 0; x < re->rectx; x++, rf += 4) {
- rf[0] = MAX2(rf[0], 0.0f);
- rf[1] = MAX2(rf[1], 0.0f);
- rf[2] = MAX2(rf[2], 0.0f);
- CLAMP(rf[3], 0.0f, 1.0f);
+ for (x = 0; x < re->rectx; x++, rf += 4) {
+ rf[0] = MAX2(rf[0], 0.0f);
+ rf[1] = MAX2(rf[1], 0.0f);
+ rf[2] = MAX2(rf[2], 0.0f);
+ CLAMP(rf[3], 0.0f, 1.0f);
+ }
}
+
+ /* store the final result */
+ BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
+ rv = BLI_findlink(&re->result->views, nr);
+ if (rv->rectf)
+ MEM_freeN(rv->rectf);
+ rv->rectf = rectf;
+ BLI_rw_mutex_unlock(&re->resultmutex);
}
/* clear interaction callbacks */
@@ -2186,12 +2303,14 @@ static void do_merge_fullsample(Render *re, bNodeTree *ntree)
/* disable full sample print */
R.i.curfsa = 0;
-
- BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
- if (re->result->rectf)
- MEM_freeN(re->result->rectf);
- re->result->rectf = rectf;
- BLI_rw_mutex_unlock(&re->resultmutex);
+
+ /* garbage collection */
+ while (rectfs->first) {
+ RenderView *rv = rectfs->first;
+ BLI_remlink(rectfs, rv);
+ MEM_freeN(rv);
+ }
+ MEM_freeN(rectfs);
}
/* called externally, via compositor */
@@ -2267,7 +2386,11 @@ static void do_render_composite_fields_blur_3d(Render *re)
if (composite_needs_render(re->scene, 1)) {
/* save memory... free all cached images */
ntreeFreeCache(ntree);
-
+
+ /* render the frames
+ * it could be optimized to render only the needed view
+ * but what if a scene has a different number of views
+ * than the main scene? */
do_render_fields_blur_3d(re);
}
else {
@@ -2280,7 +2403,7 @@ static void do_render_composite_fields_blur_3d(Render *re)
if ((re->r.mode & R_CROP) == 0) {
render_result_disprect_to_full_resolution(re);
}
- re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS);
+ re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS);
BLI_rw_mutex_unlock(&re->resultmutex);
@@ -2325,7 +2448,10 @@ static void do_render_composite_fields_blur_3d(Render *re)
if (re->r.scemode & R_FULL_SAMPLE)
do_merge_fullsample(re, ntree);
else {
- ntreeCompositExecTree(re->scene, ntree, &re->r, true, G.background == 0, &re->scene->view_settings, &re->scene->display_settings);
+ RenderView *rv;
+ for (rv = re->result->views.first; rv; rv = rv->next) {
+ ntreeCompositExecTree(re->scene, ntree, &re->r, true, G.background == 0, &re->scene->view_settings, &re->scene->display_settings, rv->name);
+ }
}
ntree->stats_draw = NULL;
@@ -2350,13 +2476,17 @@ static void do_render_composite_fields_blur_3d(Render *re)
static void renderresult_stampinfo(Render *re)
{
RenderResult rres;
+ RenderView *rv;
+ int nr;
/* this is the basic trick to get the displayed float or char rect from render result */
- RE_AcquireResultImage(re, &rres);
- BKE_image_stamp_buf(
- re->scene, RE_GetCamera(re),
- (unsigned char *)rres.rect32, rres.rectf, rres.rectx, rres.recty, 4);
- RE_ReleaseResultImage(re);
+ nr = 0;
+ for (rv = re->result->views.first;rv;rv = rv->next, nr++) {
+ RE_SetActiveRenderView(re, rv->name);
+ RE_AcquireResultImage(re, &rres, nr);
+ BKE_image_stamp_buf(re->scene, RE_GetCamera(re), (unsigned char *)rv->rect32, rv->rectf, rres.rectx, rres.recty, 4);
+ RE_ReleaseResultImage(re);
+ }
}
int RE_seq_render_active(Scene *scene, RenderData *rd)
@@ -2380,10 +2510,12 @@ int RE_seq_render_active(Scene *scene, RenderData *rd)
static void do_render_seq(Render *re)
{
static int recurs_depth = 0;
- struct ImBuf *ibuf, *out;
+ struct ImBuf *out;
RenderResult *rr; /* don't assign re->result here as it might change during give_ibuf_seq */
int cfra = re->r.cfra;
SeqRenderData context;
+ size_t view_id, tot_views;
+ struct ImBuf **ibuf_arr;
int re_x, re_y;
re->i.cfra = cfra;
@@ -2406,45 +2538,67 @@ static void do_render_seq(Render *re)
re_y = re->result->recty;
}
+ tot_views = BKE_scene_multiview_num_views_get(&re->r);
+ ibuf_arr = MEM_mallocN(sizeof(ImBuf *) * tot_views, "Sequencer Views ImBufs");
+
BKE_sequencer_new_render_data(
re->eval_ctx, re->main, re->scene,
re_x, re_y, 100,
&context);
- out = BKE_sequencer_give_ibuf(&context, cfra, 0);
+ /* the renderresult gets destroyed during the rendering, so we first collect all ibufs
+ * and then we populate the final renderesult */
- if (out) {
- ibuf = IMB_dupImBuf(out);
- IMB_freeImBuf(out);
- BKE_sequencer_imbuf_from_sequencer_space(re->scene, ibuf);
- }
- else {
- ibuf = NULL;
- }
+ for (view_id = 0; view_id < tot_views; view_id++) {
+ context.view_id = view_id;
+ out = BKE_sequencer_give_ibuf(&context, cfra, 0);
- recurs_depth--;
+ if (out) {
+ ibuf_arr[view_id] = IMB_dupImBuf(out);
+ IMB_freeImBuf(out);
+ BKE_sequencer_imbuf_from_sequencer_space(re->scene, ibuf_arr[view_id]);
+ }
+ else {
+ ibuf_arr[view_id] = NULL;
+ }
+ }
rr = re->result;
-
+
BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
+ render_result_views_new(rr, &re->r);
+ BLI_rw_mutex_unlock(&re->resultmutex);
- if (ibuf) {
- /* copy ibuf into combined pixel rect */
- render_result_rect_from_ibuf(rr, &re->r, ibuf);
-
- if (recurs_depth == 0) { /* with nested scenes, only free on toplevel... */
- Editing *ed = re->scene->ed;
- if (ed)
- BKE_sequencer_free_imbuf(re->scene, &ed->seqbase, true);
+ for (view_id = 0; view_id < tot_views; view_id++) {
+ RenderView *rv = BLI_findlink(&rr->views, view_id);
+ BLI_rw_mutex_lock(&re->resultmutex, THREAD_LOCK_WRITE);
+
+ if (ibuf_arr[view_id]) {
+ /* copy ibuf into combined pixel rect */
+ render_result_rect_from_ibuf(rr, &re->r, ibuf_arr[view_id], view_id);
+
+ if (recurs_depth == 0) { /* with nested scenes, only free on toplevel... */
+ Editing *ed = re->scene->ed;
+ if (ed)
+ BKE_sequencer_free_imbuf(re->scene, &ed->seqbase, true);
+ }
+ IMB_freeImBuf(ibuf_arr[view_id]);
}
- IMB_freeImBuf(ibuf);
- }
- else {
- /* render result is delivered empty in most cases, nevertheless we handle all cases */
- render_result_rect_fill_zero(rr);
+ else {
+ /* render result is delivered empty in most cases, nevertheless we handle all cases */
+ render_result_rect_fill_zero(rr, view_id);
+ }
+
+ BLI_rw_mutex_unlock(&re->resultmutex);
+
+ /* would mark display buffers as invalid */
+ RE_SetActiveRenderView(re, rv->name);
+ re->display_update(re->duh, re->result, NULL);
}
- BLI_rw_mutex_unlock(&re->resultmutex);
+ MEM_freeN(ibuf_arr);
+
+ recurs_depth--;
/* just in case this flag went missing at some point */
re->r.scemode |= R_DOSEQ;
@@ -2454,9 +2608,6 @@ static void do_render_seq(Render *re)
re->progress(re->prh, (float)(cfra - re->r.sfra) / (re->r.efra - re->r.sfra));
else
re->progress(re->prh, 1.0f);
-
- /* would mark display buffers as invalid */
- re->display_update(re->duh, re->result, NULL);
}
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
@@ -2543,11 +2694,50 @@ static bool check_valid_compositing_camera(Scene *scene, Object *camera_override
}
}
+static bool check_valid_camera_multiview(Scene *scene, Object *camera, ReportList *reports)
+{
+ SceneRenderView *srv;
+ bool active_view = false;
+
+ if ((scene->r.scemode & R_MULTIVIEW) == 0)
+ return true;
+
+ for (srv = scene->r.views.first; srv; srv = srv->next) {
+ if (BKE_scene_multiview_is_render_view_active(&scene->r, srv)) {
+ active_view = true;
+
+ if (scene->r.views_format == SCE_VIEWS_FORMAT_MULTIVIEW) {
+ Object *view_camera;
+ view_camera = BKE_camera_multiview_render(scene, camera, srv->name);
+
+ if (view_camera == camera) {
+ /* if the suffix is not in the camera, means we are using the fallback camera */
+ if (!BLI_str_endswith(view_camera->id.name + 2, srv->suffix)) {
+ BKE_reportf(reports, RPT_ERROR, "Camera \"%s\" is not a multi-view camera",
+ camera->id.name + 2);
+ return false;
+ }
+ }
+ }
+ }
+ }
+
+ if (!active_view) {
+ BKE_reportf(reports, RPT_ERROR, "No active view found in scene \"%s\"", scene->id.name + 2);
+ return false;
+ }
+
+ return true;
+}
+
static int check_valid_camera(Scene *scene, Object *camera_override, ReportList *reports)
{
if (camera_override == NULL && scene->camera == NULL)
scene->camera = BKE_scene_camera_find(scene);
+ if (!check_valid_camera_multiview(scene, scene->camera, reports))
+ return false;
+
if (RE_seq_render_active(scene, &scene->r)) {
if (scene->ed) {
Sequence *seq = scene->ed->seqbase.first;
@@ -2565,6 +2755,8 @@ static int check_valid_camera(Scene *scene, Object *camera_override, ReportList
}
}
}
+ else if (!check_valid_camera_multiview(seq->scene, seq->scene_camera, reports))
+ return false;
}
seq = seq->next;
@@ -2729,6 +2921,17 @@ static void update_physics_cache(Render *re, Scene *scene, int UNUSED(anim_init)
BKE_ptcache_bake(&baker);
}
+
+void RE_SetActiveRenderView(Render *re, const char *viewname)
+{
+ BLI_strncpy(re->viewname, viewname, sizeof(re->viewname));
+}
+
+const char *RE_GetActiveRenderView(Render *re)
+{
+ return re->viewname;
+}
+
/* evaluating scene options for general Blender render */
static int render_initialize_from_main(Render *re, RenderData *rd, Main *bmain, Scene *scene, SceneRenderLayer *srl,
Object *camera_override, unsigned int lay_override, int anim, int anim_init)
@@ -2764,7 +2967,8 @@ static int render_initialize_from_main(Render *re, RenderData *rd, Main *bmain,
re->lay = lay_override ? lay_override : scene->lay;
re->layer_override = lay_override;
re->i.localview = (re->lay & 0xFF000000) != 0;
-
+ re->viewname[0] = '\0';
+
/* not too nice, but it survives anim-border render */
if (anim) {
render_update_anim_renderdata(re, &scene->r);
@@ -2838,10 +3042,10 @@ void RE_BlenderFrame(Render *re, Main *bmain, Scene *scene, SceneRenderLayer *sr
char name[FILE_MAX];
BKE_image_path_from_imformat(
name, scene->r.pic, bmain->name, scene->r.cfra,
- &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, false);
+ &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, false, NULL);
/* reports only used for Movie */
- do_write_image_or_movie(re, bmain, scene, NULL, name);
+ do_write_image_or_movie(re, bmain, scene, NULL, 0, name);
}
}
@@ -2879,96 +3083,269 @@ void RE_RenderFreestyleExternal(Render *re)
}
#endif
-static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovieHandle *mh, const char *name_override)
+bool RE_WriteRenderViewsImage(ReportList *reports, RenderResult *rr, Scene *scene, struct Object *camera, const bool stamp, char *name)
{
- char name[FILE_MAX];
- RenderResult rres;
- Object *camera = RE_GetCamera(re);
- double render_time;
- int ok = 1;
-
- RE_AcquireResultImage(re, &rres);
+ bool is_mono;
+ bool ok = true;
+ RenderData *rd = &scene->r;
- /* write movie or image */
- if (BKE_imtype_is_movie(scene->r.im_format.imtype)) {
- bool do_free = false;
- ImBuf *ibuf = render_result_rect_to_ibuf(&rres, &scene->r);
+ if (!rr)
+ return false;
- /* note; the way it gets 32 bits rects is weak... */
- if (ibuf->rect == NULL) {
- ibuf->rect = MEM_mapallocN(sizeof(int) * rres.rectx * rres.recty, "temp 32 bits rect");
- ibuf->mall |= IB_rect;
- RE_AcquiredResultGet32(re, &rres, ibuf->rect);
- do_free = true;
- }
+ is_mono = BLI_listbase_count_ex(&rr->views, 2) < 2;
+ if (ELEM(rd->im_format.imtype, R_IMF_IMTYPE_OPENEXR, R_IMF_IMTYPE_MULTILAYER) &&
+ rd->im_format.views_format == R_IMF_VIEWS_MULTIVIEW)
+ {
+ RE_WriteRenderResult(reports, rr, name, &rd->im_format, true, NULL);
+ printf("Saved: %s\n", name);
+ }
- IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings,
- &scene->display_settings, &scene->r.im_format);
+ /* mono, legacy code */
+ else if (is_mono || (rd->im_format.views_format == R_IMF_VIEWS_INDIVIDUAL))
+ {
+ RenderView *rv;
+ size_t view_id;
+ char filepath[FILE_MAX];
- ok = mh->append_movie(&re->r, scene->r.sfra, scene->r.cfra, (int *) ibuf->rect,
- ibuf->x, ibuf->y, re->reports);
- if (do_free) {
- MEM_freeN(ibuf->rect);
- ibuf->rect = NULL;
- ibuf->mall &= ~IB_rect;
- }
+ BLI_strncpy(filepath, name, sizeof(filepath));
- /* imbuf knows which rects are not part of ibuf */
- IMB_freeImBuf(ibuf);
+ for (view_id = 0, rv = rr->views.first; rv; rv = rv->next, view_id++) {
+ BKE_scene_multiview_view_filepath_get(&scene->r, filepath, rv->name, name);
- printf("Append frame %d", scene->r.cfra);
- }
- else {
- if (name_override)
- BLI_strncpy(name, name_override, sizeof(name));
- else
- BKE_image_path_from_imformat(
- name, scene->r.pic, bmain->name, scene->r.cfra,
- &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true);
-
- if (re->r.im_format.imtype == R_IMF_IMTYPE_MULTILAYER) {
- if (re->result) {
- RE_WriteRenderResult(re->reports, re->result, name, scene->r.im_format.exr_codec);
- printf("Saved: %s", name);
+ if (rd->im_format.imtype == R_IMF_IMTYPE_MULTILAYER) {
+
+ RE_WriteRenderResult(reports, rr, name, &rd->im_format, false, rv->name);
+ printf("Saved: %s\n", name);
}
+ else {
+ ImBuf *ibuf = render_result_rect_to_ibuf(rr, rd, view_id);
+
+ IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings,
+ &scene->display_settings, &rd->im_format);
+
+ if (stamp) {
+ /* writes the name of the individual cameras */
+ Object *view_camera = BKE_camera_multiview_render(scene, camera, rv->name);
+ ok = BKE_imbuf_write_stamp(scene, view_camera, ibuf, name, &rd->im_format);
+ }
+ else {
+ ok = BKE_imbuf_write(ibuf, name, &rd->im_format);
+ }
+
+ if (ok == false) {
+ printf("Render error: cannot save %s\n", name);
+ }
+ else printf("Saved: %s\n", name);
+
+ /* optional preview images for exr */
+ if (ok && rd->im_format.imtype == R_IMF_IMTYPE_OPENEXR && (rd->im_format.flag & R_IMF_FLAG_PREVIEW_JPG)) {
+ ImageFormatData imf = rd->im_format;
+ imf.imtype = R_IMF_IMTYPE_JPEG90;
+
+ if (BLI_testextensie(name, ".exr"))
+ name[strlen(name) - 4] = 0;
+ BKE_image_path_ensure_ext_from_imformat(name, &imf);
+ ibuf->planes = 24;
+
+ IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings,
+ &scene->display_settings, &rd->im_format);
+
+ if (stamp) {
+ /* writes the name of the individual cameras */
+ Object *view_camera = BKE_camera_multiview_render(scene, camera, rv->name);
+ ok = BKE_imbuf_write_stamp(scene, view_camera, ibuf, name, &rd->im_format);
+ }
+ else {
+ ok = BKE_imbuf_write(ibuf, name, &rd->im_format);
+ }
+ printf("Saved: %s\n", name);
+ }
+
+ /* imbuf knows which rects are not part of ibuf */
+ IMB_freeImBuf(ibuf);
+ }
+ }
+ }
+ else { /* R_IMF_VIEWS_STEREO_3D */
+ BLI_assert(scene->r.im_format.views_format == R_IMF_VIEWS_STEREO_3D);
+
+ if (rd->im_format.imtype == R_IMF_IMTYPE_MULTILAYER) {
+ printf("Stereo 3D not support for MultiLayer image: %s\n", name);
}
else {
- ImBuf *ibuf = render_result_rect_to_ibuf(&rres, &scene->r);
+ ImBuf *ibuf_arr[3] = {NULL};
+ const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME};
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ int view_id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name));
+ ibuf_arr[i] = render_result_rect_to_ibuf(rr, rd, view_id);
+ IMB_colormanagement_imbuf_for_write(ibuf_arr[i], true, false, &scene->view_settings,
+ &scene->display_settings, &scene->r.im_format);
+ IMB_prepare_write_ImBuf(IMB_isfloat(ibuf_arr[i]), ibuf_arr[i]);
+ }
- IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings,
- &scene->display_settings, &scene->r.im_format);
+ ibuf_arr[2] = IMB_stereo3d_ImBuf(&scene->r.im_format, ibuf_arr[0], ibuf_arr[1]);
- ok = BKE_imbuf_write_stamp(scene, camera, ibuf, name, &scene->r.im_format);
-
- if (ok == 0) {
+ if (stamp)
+ ok = BKE_imbuf_write_stamp(scene, camera, ibuf_arr[2], name, &rd->im_format);
+ else
+ ok = BKE_imbuf_write(ibuf_arr[2], name, &rd->im_format);
+
+ if (ok == false)
printf("Render error: cannot save %s\n", name);
- }
- else printf("Saved: %s", name);
-
+ else
+ printf("Saved: %s\n", name);
+
/* optional preview images for exr */
- if (ok && scene->r.im_format.imtype == R_IMF_IMTYPE_OPENEXR && (scene->r.im_format.flag & R_IMF_FLAG_PREVIEW_JPG)) {
- ImageFormatData imf = scene->r.im_format;
+ if (ok && rd->im_format.imtype == R_IMF_IMTYPE_OPENEXR &&
+ (rd->im_format.flag & R_IMF_FLAG_PREVIEW_JPG))
+ {
+ ImageFormatData imf = rd->im_format;
imf.imtype = R_IMF_IMTYPE_JPEG90;
if (BLI_testextensie(name, ".exr"))
name[strlen(name) - 4] = 0;
+
BKE_image_path_ensure_ext_from_imformat(name, &imf);
- ibuf->planes = 24;
+ ibuf_arr[2]->planes = 24;
- IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings,
+ IMB_colormanagement_imbuf_for_write(ibuf_arr[2], true, false, &scene->view_settings,
&scene->display_settings, &imf);
- BKE_imbuf_write_stamp(scene, camera, ibuf, name, &imf);
- printf("\nSaved: %s", name);
+ if (stamp)
+ ok = BKE_imbuf_write_stamp(scene, camera, ibuf_arr[2], name, &rd->im_format);
+ else
+ ok = BKE_imbuf_write(ibuf_arr[2], name, &imf);
+
+ printf("Saved: %s\n", name);
}
-
+
+ /* imbuf knows which rects are not part of ibuf */
+ for (i = 0; i < 3; i++) {
+ IMB_freeImBuf(ibuf_arr[i]);
+ }
+ }
+ }
+
+ return ok;
+}
+
+bool RE_WriteRenderViewsMovie(ReportList *reports, RenderResult *rr, Scene *scene, RenderData *rd, bMovieHandle *mh,
+ const size_t width, const size_t height, void **movie_ctx_arr, const size_t totvideos)
+{
+ bool is_mono;
+ bool ok = true;
+
+ if (!rr)
+ return false;
+
+ is_mono = BLI_listbase_count_ex(&rr->views, 2) < 2;
+
+ if (is_mono || (scene->r.im_format.views_format == R_IMF_VIEWS_INDIVIDUAL)) {
+ size_t view_id;
+ for (view_id = 0; view_id < totvideos; view_id++) {
+ bool do_free = false;
+ const char *suffix = BKE_scene_multiview_view_id_suffix_get(&scene->r, view_id);
+ ImBuf *ibuf = render_result_rect_to_ibuf(rr, &scene->r, view_id);
+
+ /* note; the way it gets 32 bits rects is weak... */
+ if (ibuf->rect == NULL) {
+ ibuf->rect = MEM_mapallocN(sizeof(int) * rr->rectx * rr->recty, "temp 32 bits rect");
+ ibuf->mall |= IB_rect;
+ render_result_rect_get_pixels(rr, ibuf->rect, width, height, &scene->view_settings, &scene->display_settings, view_id);
+ do_free = true;
+ }
+
+ IMB_colormanagement_imbuf_for_write(ibuf, true, false, &scene->view_settings,
+ &scene->display_settings, &scene->r.im_format);
+
+ ok &= mh->append_movie(movie_ctx_arr[view_id], rd, scene->r.sfra, scene->r.cfra,
+ (int *) ibuf->rect, ibuf->x, ibuf->y, suffix, reports);
+
+ if (do_free) {
+ MEM_freeN(ibuf->rect);
+ ibuf->rect = NULL;
+ ibuf->mall &= ~IB_rect;
+ }
+
/* imbuf knows which rects are not part of ibuf */
IMB_freeImBuf(ibuf);
}
+ printf("Append frame %d\n", scene->r.cfra);
+ }
+ else { /* R_IMF_VIEWS_STEREO_3D */
+ const char *names[2] = {STEREO_LEFT_NAME, STEREO_RIGHT_NAME};
+ ImBuf *ibuf_arr[3] = {NULL};
+ bool do_free[2] = {false, false};
+ size_t i;
+
+ BLI_assert((totvideos == 1) && (scene->r.im_format.views_format == R_IMF_VIEWS_STEREO_3D));
+
+ for (i = 0; i < 2; i++) {
+ int view_id = BLI_findstringindex(&rr->views, names[i], offsetof(RenderView, name));
+ ibuf_arr[i] = render_result_rect_to_ibuf(rr, &scene->r, view_id);
+
+ /* note; the way it gets 32 bits rects is weak... */
+ if (ibuf_arr[i]->rect == NULL) {
+ ibuf_arr[i]->rect = MEM_mapallocN(sizeof(int) * width * height, "temp 32 bits rect");
+ ibuf_arr[i]->mall |= IB_rect;
+ render_result_rect_get_pixels(rr, ibuf_arr[i]->rect, width, height, &scene->view_settings, &scene->display_settings, view_id);
+ do_free[i] = true;
+ }
+
+ IMB_colormanagement_imbuf_for_write(ibuf_arr[i], true, false, &scene->view_settings,
+ &scene->display_settings, &scene->r.im_format);
+ }
+
+ ibuf_arr[2] = IMB_stereo3d_ImBuf(&scene->r.im_format, ibuf_arr[0], ibuf_arr[1]);
+
+ ok = mh->append_movie(movie_ctx_arr[0], rd, scene->r.sfra, scene->r.cfra, (int *) ibuf_arr[2]->rect,
+ ibuf_arr[2]->x, ibuf_arr[2]->y, "", reports);
+
+ for (i = 0; i < 2; i++) {
+ if (do_free[i]) {
+ MEM_freeN(ibuf_arr[i]->rect);
+ ibuf_arr[i]->rect = NULL;
+ ibuf_arr[i]->mall &= ~IB_rect;
+ }
+
+ /* imbuf knows which rects are not part of ibuf */
+ IMB_freeImBuf(ibuf_arr[i]);
+ }
+ }
+
+ return ok;
+}
+
+static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovieHandle *mh, const size_t totvideos, const char *name_override)
+{
+ char name[FILE_MAX];
+ RenderResult rres;
+ double render_time;
+ bool ok = true;
+ Object *camera = RE_GetCamera(re);
+
+ RE_AcquireResultImageViews(re, &rres);
+
+ /* write movie or image */
+ if (BKE_imtype_is_movie(scene->r.im_format.imtype)) {
+ RE_WriteRenderViewsMovie(re->reports, &rres, scene, &re->r, mh, re->rectx, re->recty, re->movie_ctx_arr, totvideos);
+ }
+ else {
+ if (name_override)
+ BLI_strncpy(name, name_override, sizeof(name));
+ else
+ BKE_image_path_from_imformat(
+ name, scene->r.pic, bmain->name, scene->r.cfra,
+ &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true, NULL);
+
+ /* write images as individual images or stereo */
+ ok = RE_WriteRenderViewsImage(re->reports, &rres, scene, camera, true, name);
}
- RE_ReleaseResultImage(re);
+ RE_ReleaseResultImageViews(re, &rres);
render_time = re->i.lastframetime;
re->i.lastframetime = PIL_check_seconds_timer() - re->i.starttime;
@@ -2987,20 +3364,51 @@ static int do_write_image_or_movie(Render *re, Main *bmain, Scene *scene, bMovie
return ok;
}
+static void get_videos_dimensions(Render *re, RenderData *rd, size_t *r_width, size_t *r_height)
+{
+ size_t width, height;
+ if (re->r.mode & R_BORDER) {
+ if ((re->r.mode & R_CROP) == 0) {
+ width = re->winx;
+ height = re->winy;
+ }
+ else {
+ width = re->rectx;
+ height = re->recty;
+ }
+ }
+ else {
+ width = re->rectx;
+ height = re->recty;
+ }
+
+ BKE_scene_multiview_videos_dimensions_get(rd, width, height, r_width, r_height);
+}
+
/* saves images to disk */
void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_override,
unsigned int lay_override, int sfra, int efra, int tfra)
{
RenderData rd = scene->r;
- bMovieHandle *mh = BKE_movie_handle_get(scene->r.im_format.imtype);
+ bMovieHandle *mh = NULL;
int cfrao = scene->r.cfra;
int nfra, totrendered = 0, totskipped = 0;
-
+ const size_t totvideos = BKE_scene_multiview_num_videos_get(&rd);
+ const bool is_movie = BKE_imtype_is_movie(scene->r.im_format.imtype);
+ const bool is_multiview_name = ((scene->r.scemode & R_MULTIVIEW) != 0 &&
+ (scene->r.im_format.views_format == R_IMF_VIEWS_INDIVIDUAL));
+
BLI_callback_exec(re->main, (ID *)scene, BLI_CB_EVT_RENDER_INIT);
/* do not fully call for each frame, it initializes & pops output window */
if (!render_initialize_from_main(re, &rd, bmain, scene, NULL, camera_override, lay_override, 0, 1))
return;
+
+ /* we don't support Frame Server and streaming of individual views */
+ if ((rd.im_format.imtype == R_IMF_IMTYPE_FRAMESERVER) && (totvideos > 1)) {
+ BKE_report(re->reports, RPT_ERROR, "Frame Server only support stereo output for multiview rendering");
+ return;
+ }
/* ugly global still... is to prevent renderwin events and signal subsurfs etc to make full resol */
/* is also set by caller renderwin.c */
@@ -3008,31 +3416,33 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri
re->flag |= R_ANIMATION;
- if (BKE_imtype_is_movie(scene->r.im_format.imtype)) {
- int width, height;
- if (re->r.mode & R_BORDER) {
- if ((re->r.mode & R_CROP) == 0) {
- width = re->winx;
- height = re->winy;
- }
- else {
- width = re->rectx;
- height = re->recty;
- }
- }
- else {
- width = re->rectx;
- height = re->recty;
- }
+ if (is_movie) {
+ size_t i, width, height;
+
+ get_videos_dimensions(re, &rd, &width, &height);
+
+ mh = BKE_movie_handle_get(scene->r.im_format.imtype);
+ re->movie_ctx_arr = MEM_mallocN(sizeof(void *) * totvideos, "Movies' Context");
+
+ for (i = 0; i < totvideos; i++) {
+ const char *suffix = BKE_scene_multiview_view_id_suffix_get(&re->r, i);
- /* last argument here depends on users really, but no users using preview have been found so far */
- if (!mh->start_movie(scene, &re->r, width, height, re->reports, false))
- G.is_break = true;
+ re->movie_ctx_arr[i] = mh->context_create();
+
+ if (!mh->start_movie(re->movie_ctx_arr[i], scene, &re->r, width, height, re->reports, false, suffix))
+ G.is_break = true;
+ }
}
- if (mh->get_next_frame) {
+ if (mh && mh->get_next_frame) {
+ /* MULTIVIEW_TODO:
+ * in case a new video format is added that implements get_next_frame multiview has to be addressed
+ * or the error throwing for R_IMF_IMTYPE_FRAMESERVER has to be extended for those cases as well
+ */
+ BLI_assert(totvideos < 2);
+
while (!(G.is_break == 1)) {
- int nf = mh->get_next_frame(&re->r, re->reports);
+ int nf = mh->get_next_frame(re->movie_ctx_arr[0], &re->r, re->reports);
if (nf >= 0 && nf >= scene->r.sfra && nf <= scene->r.efra) {
scene->r.cfra = re->r.cfra = nf;
@@ -3042,7 +3452,7 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri
totrendered++;
if (re->test_break(re->tbh) == 0) {
- if (!do_write_image_or_movie(re, bmain, scene, mh, NULL))
+ if (!do_write_image_or_movie(re, bmain, scene, mh, totvideos, NULL))
G.is_break = true;
}
@@ -3085,20 +3495,67 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri
nfra += tfra;
/* Touch/NoOverwrite options are only valid for image's */
- if (BKE_imtype_is_movie(scene->r.im_format.imtype) == 0) {
+ if (is_movie == false) {
if (scene->r.mode & (R_NO_OVERWRITE | R_TOUCH))
BKE_image_path_from_imformat(
name, scene->r.pic, bmain->name, scene->r.cfra,
- &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true);
+ &scene->r.im_format, (scene->r.scemode & R_EXTENSION) != 0, true, NULL);
+
+ if (scene->r.mode & R_NO_OVERWRITE) {
+ if (!is_multiview_name) {
+ if (BLI_exists(name)) {
+ printf("skipping existing frame \"%s\"\n", name);
+ totskipped++;
+ continue;
+ }
+ }
+ else {
+ SceneRenderView *srv;
+ bool is_skip = false;
+ char filepath[FILE_MAX];
- if (scene->r.mode & R_NO_OVERWRITE && BLI_exists(name)) {
- printf("skipping existing frame \"%s\"\n", name);
- totskipped++;
- continue;
+ for (srv = scene->r.views.first; srv; srv = srv->next) {
+ if (!BKE_scene_multiview_is_render_view_active(&scene->r, srv))
+ continue;
+
+ BKE_scene_multiview_filepath_get(srv, name, filepath);
+
+ if (BLI_exists(filepath)) {
+ is_skip = true;
+ printf("skipping existing frame \"%s\" for view \"%s\"\n", filepath, srv->name);
+ }
+ }
+
+ if (is_skip) {
+ totskipped++;
+ continue;
+ }
+ }
}
- if (scene->r.mode & R_TOUCH && !BLI_exists(name)) {
- BLI_make_existing_file(name); /* makes the dir if its not there */
- BLI_file_touch(name);
+
+ if (scene->r.mode & R_TOUCH) {
+ if (!is_multiview_name) {
+ if (!BLI_exists(name)) {
+ BLI_make_existing_file(name); /* makes the dir if its not there */
+ BLI_file_touch(name);
+ }
+ }
+ else {
+ SceneRenderView *srv;
+ char filepath[FILE_MAX];
+
+ for (srv = scene->r.views.first; srv; srv = srv->next) {
+ if (!BKE_scene_multiview_is_render_view_active(&scene->r, srv))
+ continue;
+
+ BKE_scene_multiview_filepath_get(srv, name, filepath);
+
+ if (!BLI_exists(filepath)) {
+ BLI_make_existing_file(filepath); /* makes the dir if its not there */
+ BLI_file_touch(filepath);
+ }
+ }
+ }
}
}
@@ -3113,7 +3570,7 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri
if (re->test_break(re->tbh) == 0) {
if (!G.is_break)
- if (!do_write_image_or_movie(re, bmain, scene, mh, NULL))
+ if (!do_write_image_or_movie(re, bmain, scene, mh, totvideos, NULL))
G.is_break = true;
}
else
@@ -3121,10 +3578,30 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri
if (G.is_break == true) {
/* remove touched file */
- if (BKE_imtype_is_movie(scene->r.im_format.imtype) == 0) {
- if ((scene->r.mode & R_TOUCH) && (BLI_file_size(name) == 0)) {
- /* BLI_exists(name) is implicit */
- BLI_delete(name, false, false);
+ if (is_movie == false) {
+ if ((scene->r.mode & R_TOUCH)) {
+ if (!is_multiview_name) {
+ if ((BLI_file_size(name) == 0)) {
+ /* BLI_exists(name) is implicit */
+ BLI_delete(name, false, false);
+ }
+ }
+ else {
+ SceneRenderView *srv;
+ char filepath[FILE_MAX];
+
+ for (srv = scene->r.views.first; srv; srv = srv->next) {
+ if (!BKE_scene_multiview_is_render_view_active(&scene->r, srv))
+ continue;
+
+ BKE_scene_multiview_filepath_get(srv, name, filepath);
+
+ if ((BLI_file_size(filepath) == 0)) {
+ /* BLI_exists(filepath) is implicit */
+ BLI_delete(filepath, false, false);
+ }
+ }
+ }
}
}
@@ -3139,8 +3616,17 @@ void RE_BlenderAnim(Render *re, Main *bmain, Scene *scene, Object *camera_overri
}
/* end movie */
- if (BKE_imtype_is_movie(scene->r.im_format.imtype))
- mh->end_movie();
+ if (is_movie) {
+ size_t i;
+ for (i = 0; i < totvideos; i++) {
+ mh->end_movie(re->movie_ctx_arr[i]);
+ mh->context_free(re->movie_ctx_arr[i]);
+ }
+
+ if (re->movie_ctx_arr) {
+ MEM_freeN(re->movie_ctx_arr);
+ }
+ }
if (totskipped && totrendered == 0)
BKE_report(re->reports, RPT_INFO, "No frames rendered, skipped to not overwrite");
@@ -3238,13 +3724,22 @@ void RE_layer_load_from_file(RenderLayer *layer, ReportList *reports, const char
{
/* OCIO_TODO: assume layer was saved in defaule color space */
ImBuf *ibuf = IMB_loadiffname(filename, IB_rect, NULL);
+ RenderPass *rpass = NULL;
+
+ /* multiview: since the API takes no 'view', we use the first combined pass found */
+ for (rpass = layer->passes.first; rpass; rpass = rpass->next)
+ if (rpass->passtype == SCE_PASS_COMBINED)
+ break;
+
+ if (rpass == NULL)
+ BKE_reportf(reports, RPT_ERROR, "RE_layer_load_from_file: no Combined pass found in the render layer '%s'", filename);
if (ibuf && (ibuf->rect || ibuf->rect_float)) {
if (ibuf->x == layer->rectx && ibuf->y == layer->recty) {
if (ibuf->rect_float == NULL)
IMB_float_from_rect(ibuf);
- memcpy(layer->rectf, ibuf->rect_float, sizeof(float) * 4 * layer->rectx * layer->recty);
+ memcpy(rpass->rect, ibuf->rect_float, sizeof(float) * 4 * layer->rectx * layer->recty);
}
else {
if ((ibuf->x - x >= layer->rectx) && (ibuf->y - y >= layer->recty)) {
@@ -3257,7 +3752,7 @@ void RE_layer_load_from_file(RenderLayer *layer, ReportList *reports, const char
if (ibuf_clip) {
IMB_rectcpy(ibuf_clip, ibuf, 0, 0, x, y, layer->rectx, layer->recty);
- memcpy(layer->rectf, ibuf_clip->rect_float, sizeof(float) * 4 * layer->rectx * layer->recty);
+ memcpy(rpass->rect, ibuf_clip->rect_float, sizeof(float) * 4 * layer->rectx * layer->recty);
IMB_freeImBuf(ibuf_clip);
}
else {
@@ -3345,3 +3840,21 @@ bool RE_WriteEnvmapResult(struct ReportList *reports, Scene *scene, EnvMap *env,
}
}
+/* used in the interface to decide whether to show layers */
+bool RE_layers_have_name(struct RenderResult *rr)
+{
+ switch (BLI_listbase_count_ex(&rr->layers, 2)) {
+ case 0:
+ return false;
+ break;
+ case 1:
+ return (((RenderLayer *)rr->layers.first)->name[0] != '\0');
+ break;
+ default:
+ return true;
+ break;
+ }
+ return false;
+}
+
+
diff --git a/source/blender/render/intern/source/render_result.c b/source/blender/render/intern/source/render_result.c
index 6486844bacf..90f6eab0cc1 100644
--- a/source/blender/render/intern/source/render_result.c
+++ b/source/blender/render/intern/source/render_result.c
@@ -48,9 +48,8 @@
#include "BKE_global.h"
#include "BKE_main.h"
#include "BKE_report.h"
-#ifdef WITH_CYCLES_DEBUG
-# include "BKE_scene.h"
-#endif
+#include "BKE_camera.h"
+#include "BKE_scene.h"
#include "IMB_imbuf.h"
#include "IMB_imbuf_types.h"
@@ -63,14 +62,32 @@
/********************************** Free *************************************/
+static void render_result_views_free(RenderResult *res)
+{
+ while (res->views.first) {
+ RenderView *rv = res->views.first;
+ BLI_remlink(&res->views, rv);
+
+ if (rv->rect32)
+ MEM_freeN(rv->rect32);
+
+ if (rv->rectz)
+ MEM_freeN(rv->rectz);
+
+ if (rv->rectf)
+ MEM_freeN(rv->rectf);
+
+ MEM_freeN(rv);
+ }
+}
+
void render_result_free(RenderResult *res)
{
if (res == NULL) return;
while (res->layers.first) {
RenderLayer *rl = res->layers.first;
-
- if (rl->rectf) MEM_freeN(rl->rectf);
+
/* acolrect and scolrect are optionally allocated in shade_tile, only free here since it can be used for drawing */
if (rl->acolrect) MEM_freeN(rl->acolrect);
if (rl->scolrect) MEM_freeN(rl->scolrect);
@@ -85,7 +102,9 @@ void render_result_free(RenderResult *res)
BLI_remlink(&res->layers, rl);
MEM_freeN(rl);
}
-
+
+ render_result_views_free(res);
+
if (res->rect32)
MEM_freeN(res->rect32);
if (res->rectz)
@@ -115,13 +134,44 @@ void render_result_free_list(ListBase *lb, RenderResult *rr)
}
}
-/********************************* Names *************************************/
+/********************************* multiview *************************************/
-/* NOTE: OpenEXR only supports 32 chars for layer+pass names
- * In blender we now use max 10 chars for pass, max 20 for layer */
-static const char *get_pass_name(int passtype, int channel)
+/* create a new views Listbase in rr without duplicating the memory pointers */
+void render_result_views_shallowcopy(RenderResult *dst, RenderResult *src)
+{
+ RenderView *rview;
+
+ if (dst == NULL || src == NULL)
+ return;
+
+ for (rview = src->views.first; rview; rview = rview->next) {
+ RenderView *rv;
+
+ rv = MEM_mallocN(sizeof(RenderView), "new render view");
+ BLI_addtail(&dst->views, rv);
+
+ BLI_strncpy(rv->name, rview->name, sizeof(rv->name));
+ rv->rectf = rview->rectf;
+ rv->rectz = rview->rectz;
+ rv->rect32 = rview->rect32;
+ }
+}
+
+/* free the views created temporarily */
+void render_result_views_shallowdelete(RenderResult *rr)
+{
+ if (rr == NULL)
+ return;
+
+ while (rr->views.first) {
+ RenderView *rv = rr->views.first;
+ BLI_remlink(&rr->views, rv);
+ MEM_freeN(rv);
+ }
+}
+
+static const char *name_from_passtype(int passtype, int channel)
{
-
if (passtype == SCE_PASS_COMBINED) {
if (channel == -1) return "Combined";
if (channel == 0) return "Combined.R";
@@ -308,108 +358,131 @@ static const char *get_pass_name(int passtype, int channel)
static int passtype_from_name(const char *str)
{
-
- if (STREQ(str, "Combined"))
+ if (STRPREFIX(str, "Combined"))
return SCE_PASS_COMBINED;
- if (STREQ(str, "Depth"))
+ if (STRPREFIX(str, "Depth"))
return SCE_PASS_Z;
- if (STREQ(str, "Vector"))
+ if (STRPREFIX(str, "Vector"))
return SCE_PASS_VECTOR;
- if (STREQ(str, "Normal"))
+ if (STRPREFIX(str, "Normal"))
return SCE_PASS_NORMAL;
- if (STREQ(str, "UV"))
+ if (STRPREFIX(str, "UV"))
return SCE_PASS_UV;
- if (STREQ(str, "Color"))
+ if (STRPREFIX(str, "Color"))
return SCE_PASS_RGBA;
- if (STREQ(str, "Emit"))
+ if (STRPREFIX(str, "Emit"))
return SCE_PASS_EMIT;
- if (STREQ(str, "Diffuse"))
+ if (STRPREFIX(str, "Diffuse"))
return SCE_PASS_DIFFUSE;
- if (STREQ(str, "Spec"))
+ if (STRPREFIX(str, "Spec"))
return SCE_PASS_SPEC;
- if (STREQ(str, "Shadow"))
+ if (STRPREFIX(str, "Shadow"))
return SCE_PASS_SHADOW;
- if (STREQ(str, "AO"))
+ if (STRPREFIX(str, "AO"))
return SCE_PASS_AO;
- if (STREQ(str, "Env"))
+ if (STRPREFIX(str, "Env"))
return SCE_PASS_ENVIRONMENT;
- if (STREQ(str, "Indirect"))
+ if (STRPREFIX(str, "Indirect"))
return SCE_PASS_INDIRECT;
- if (STREQ(str, "Reflect"))
+ if (STRPREFIX(str, "Reflect"))
return SCE_PASS_REFLECT;
- if (STREQ(str, "Refract"))
+ if (STRPREFIX(str, "Refract"))
return SCE_PASS_REFRACT;
- if (STREQ(str, "IndexOB"))
+ if (STRPREFIX(str, "IndexOB"))
return SCE_PASS_INDEXOB;
- if (STREQ(str, "IndexMA"))
+ if (STRPREFIX(str, "IndexMA"))
return SCE_PASS_INDEXMA;
- if (STREQ(str, "Mist"))
+ if (STRPREFIX(str, "Mist"))
return SCE_PASS_MIST;
- if (STREQ(str, "RayHits"))
+ if (STRPREFIX(str, "RayHits"))
return SCE_PASS_RAYHITS;
- if (STREQ(str, "DiffDir"))
+ if (STRPREFIX(str, "DiffDir"))
return SCE_PASS_DIFFUSE_DIRECT;
- if (STREQ(str, "DiffInd"))
+ if (STRPREFIX(str, "DiffInd"))
return SCE_PASS_DIFFUSE_INDIRECT;
- if (STREQ(str, "DiffCol"))
+ if (STRPREFIX(str, "DiffCol"))
return SCE_PASS_DIFFUSE_COLOR;
- if (STREQ(str, "GlossDir"))
+ if (STRPREFIX(str, "GlossDir"))
return SCE_PASS_GLOSSY_DIRECT;
- if (STREQ(str, "GlossInd"))
+ if (STRPREFIX(str, "GlossInd"))
return SCE_PASS_GLOSSY_INDIRECT;
- if (STREQ(str, "GlossCol"))
+ if (STRPREFIX(str, "GlossCol"))
return SCE_PASS_GLOSSY_COLOR;
- if (STREQ(str, "TransDir"))
+ if (STRPREFIX(str, "TransDir"))
return SCE_PASS_TRANSM_DIRECT;
- if (STREQ(str, "TransInd"))
+ if (STRPREFIX(str, "TransInd"))
return SCE_PASS_TRANSM_INDIRECT;
- if (STREQ(str, "TransCol"))
+ if (STRPREFIX(str, "TransCol"))
return SCE_PASS_TRANSM_COLOR;
- if (STREQ(str, "SubsurfaceDir"))
+ if (STRPREFIX(str, "SubsurfaceDir"))
return SCE_PASS_SUBSURFACE_DIRECT;
- if (STREQ(str, "SubsurfaceInd"))
+ if (STRPREFIX(str, "SubsurfaceInd"))
return SCE_PASS_SUBSURFACE_INDIRECT;
- if (STREQ(str, "SubsurfaceCol"))
+ if (STRPREFIX(str, "SubsurfaceCol"))
return SCE_PASS_SUBSURFACE_COLOR;
return 0;
}
+
+static void set_pass_name(char *passname, int passtype, int channel, const char *view)
+{
+ const char *end;
+ const char *token;
+ int len;
+
+ const char *passtype_name = name_from_passtype(passtype, channel);
+
+ if (view == NULL || view[0] == '\0') {
+ BLI_strncpy(passname, passtype_name, EXR_PASS_MAXNAME);
+ return;
+ }
+
+ end = passtype_name + strlen(passtype_name);
+ len = IMB_exr_split_token(passtype_name, end, &token);
+
+ if (len == strlen(passtype_name))
+ sprintf(passname, "%s.%s", passtype_name, view);
+ else
+ sprintf(passname, "%.*s%s.%s", (int)(end-passtype_name) - len, passtype_name, view, token);
+}
+
/********************************** New **************************************/
-static RenderPass *render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int channels, int passtype)
+static RenderPass *render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int channels, int passtype, const char *viewname)
{
- const char *typestr = get_pass_name(passtype, 0);
+ const size_t view_id = BLI_findstringindex(&rr->views, viewname, offsetof(RenderView, name));
+ const char *typestr = name_from_passtype(passtype, -1);
RenderPass *rpass = MEM_callocN(sizeof(RenderPass), typestr);
int rectsize = rr->rectx * rr->recty * channels;
@@ -418,12 +491,16 @@ static RenderPass *render_layer_add_pass(RenderResult *rr, RenderLayer *rl, int
rpass->channels = channels;
rpass->rectx = rl->rectx;
rpass->recty = rl->recty;
- BLI_strncpy(rpass->name, get_pass_name(rpass->passtype, -1), sizeof(rpass->name));
+ rpass->view_id = view_id;
+
+ set_pass_name(rpass->name, rpass->passtype, -1, viewname);
+ BLI_strncpy(rpass->internal_name, typestr, sizeof(rpass->internal_name));
+ BLI_strncpy(rpass->view, viewname, sizeof(rpass->view));
if (rl->exrhandle) {
int a;
for (a = 0; a < channels; a++)
- IMB_exr_add_channel(rl->exrhandle, rl->name, get_pass_name(passtype, a), 0, 0, NULL);
+ IMB_exr_add_channel(rl->exrhandle, rl->name, name_from_passtype(passtype, a), viewname, 0, 0, NULL);
}
else {
float *rect;
@@ -460,9 +537,10 @@ static RenderPass *render_layer_add_debug_pass(RenderResult *rr,
RenderLayer *rl,
int channels,
int pass_type,
- int debug_type)
+ int debug_type,
+ const char *view)
{
- RenderPass *rpass = render_layer_add_pass(rr, rl, channels, pass_type);
+ RenderPass *rpass = render_layer_add_pass(rr, rl, channels, pass_type, view);
rpass->debug_type = debug_type;
BLI_strncpy(rpass->name,
debug_pass_type_name_get(debug_type),
@@ -475,12 +553,14 @@ static RenderPass *render_layer_add_debug_pass(RenderResult *rr,
/* will read info from Render *re to define layers */
/* called in threads */
/* re->winx,winy is coordinate space of entire image, partrct the part within */
-RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuffers, const char *layername)
+RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuffers, const char *layername, const char *viewname)
{
RenderResult *rr;
RenderLayer *rl;
+ RenderView *rv;
SceneRenderLayer *srl;
- int rectx, recty, nr;
+ int rectx, recty;
+ int nr, i;
rectx = BLI_rcti_size_x(partrct);
recty = BLI_rcti_size_y(partrct);
@@ -505,6 +585,8 @@ RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuf
rr->do_exr_tile = true;
}
+ render_result_views_new(rr, &re->r);
+
/* check renderdata for amount of layers */
for (nr = 0, srl = re->r.layers.first; srl; srl = srl->next, nr++) {
@@ -538,84 +620,90 @@ RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuf
if (rr->do_exr_tile) {
rl->display_buffer = MEM_mapallocN(rectx * recty * sizeof(unsigned int), "Combined display space rgba");
-
rl->exrhandle = IMB_exr_get_handle();
-
- IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.R", 0, 0, NULL);
- IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.G", 0, 0, NULL);
- IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.B", 0, 0, NULL);
- IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.A", 0, 0, NULL);
}
- else
- rl->rectf = MEM_mapallocN(rectx * recty * sizeof(float) * 4, "Combined rgba");
-
- if (srl->passflag & SCE_PASS_Z)
- render_layer_add_pass(rr, rl, 1, SCE_PASS_Z);
- if (srl->passflag & SCE_PASS_VECTOR)
- render_layer_add_pass(rr, rl, 4, SCE_PASS_VECTOR);
- if (srl->passflag & SCE_PASS_NORMAL)
- render_layer_add_pass(rr, rl, 3, SCE_PASS_NORMAL);
- if (srl->passflag & SCE_PASS_UV)
- render_layer_add_pass(rr, rl, 3, SCE_PASS_UV);
- if (srl->passflag & SCE_PASS_RGBA)
- render_layer_add_pass(rr, rl, 4, SCE_PASS_RGBA);
- if (srl->passflag & SCE_PASS_EMIT)
- render_layer_add_pass(rr, rl, 3, SCE_PASS_EMIT);
- if (srl->passflag & SCE_PASS_DIFFUSE)
- render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE);
- if (srl->passflag & SCE_PASS_SPEC)
- render_layer_add_pass(rr, rl, 3, SCE_PASS_SPEC);
- if (srl->passflag & SCE_PASS_AO)
- render_layer_add_pass(rr, rl, 3, SCE_PASS_AO);
- if (srl->passflag & SCE_PASS_ENVIRONMENT)
- render_layer_add_pass(rr, rl, 3, SCE_PASS_ENVIRONMENT);
- if (srl->passflag & SCE_PASS_INDIRECT)
- render_layer_add_pass(rr, rl, 3, SCE_PASS_INDIRECT);
- if (srl->passflag & SCE_PASS_SHADOW)
- render_layer_add_pass(rr, rl, 3, SCE_PASS_SHADOW);
- if (srl->passflag & SCE_PASS_REFLECT)
- render_layer_add_pass(rr, rl, 3, SCE_PASS_REFLECT);
- if (srl->passflag & SCE_PASS_REFRACT)
- render_layer_add_pass(rr, rl, 3, SCE_PASS_REFRACT);
- if (srl->passflag & SCE_PASS_INDEXOB)
- render_layer_add_pass(rr, rl, 1, SCE_PASS_INDEXOB);
- if (srl->passflag & SCE_PASS_INDEXMA)
- render_layer_add_pass(rr, rl, 1, SCE_PASS_INDEXMA);
- if (srl->passflag & SCE_PASS_MIST)
- render_layer_add_pass(rr, rl, 1, SCE_PASS_MIST);
- if (rl->passflag & SCE_PASS_RAYHITS)
- render_layer_add_pass(rr, rl, 4, SCE_PASS_RAYHITS);
- if (srl->passflag & SCE_PASS_DIFFUSE_DIRECT)
- render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE_DIRECT);
- if (srl->passflag & SCE_PASS_DIFFUSE_INDIRECT)
- render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE_INDIRECT);
- if (srl->passflag & SCE_PASS_DIFFUSE_COLOR)
- render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE_COLOR);
- if (srl->passflag & SCE_PASS_GLOSSY_DIRECT)
- render_layer_add_pass(rr, rl, 3, SCE_PASS_GLOSSY_DIRECT);
- if (srl->passflag & SCE_PASS_GLOSSY_INDIRECT)
- render_layer_add_pass(rr, rl, 3, SCE_PASS_GLOSSY_INDIRECT);
- if (srl->passflag & SCE_PASS_GLOSSY_COLOR)
- render_layer_add_pass(rr, rl, 3, SCE_PASS_GLOSSY_COLOR);
- if (srl->passflag & SCE_PASS_TRANSM_DIRECT)
- render_layer_add_pass(rr, rl, 3, SCE_PASS_TRANSM_DIRECT);
- if (srl->passflag & SCE_PASS_TRANSM_INDIRECT)
- render_layer_add_pass(rr, rl, 3, SCE_PASS_TRANSM_INDIRECT);
- if (srl->passflag & SCE_PASS_TRANSM_COLOR)
- render_layer_add_pass(rr, rl, 3, SCE_PASS_TRANSM_COLOR);
- if (srl->passflag & SCE_PASS_SUBSURFACE_DIRECT)
- render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_DIRECT);
- if (srl->passflag & SCE_PASS_SUBSURFACE_INDIRECT)
- render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_INDIRECT);
- if (srl->passflag & SCE_PASS_SUBSURFACE_COLOR)
- render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_COLOR);
+
+ for (rv = rr->views.first; rv; rv = rv->next) {
+ const char *view = rv->name;
+
+ if (viewname && viewname[0])
+ if (!STREQ(view, viewname))
+ continue;
+
+ if (rr->do_exr_tile)
+ IMB_exr_add_view(rl->exrhandle, view);
+
+ /* a renderlayer should always have a Combined pass*/
+ render_layer_add_pass(rr, rl, 4, SCE_PASS_COMBINED, view);
+
+ if (srl->passflag & SCE_PASS_Z)
+ render_layer_add_pass(rr, rl, 1, SCE_PASS_Z, view);
+ if (srl->passflag & SCE_PASS_VECTOR)
+ render_layer_add_pass(rr, rl, 4, SCE_PASS_VECTOR, view);
+ if (srl->passflag & SCE_PASS_NORMAL)
+ render_layer_add_pass(rr, rl, 3, SCE_PASS_NORMAL, view);
+ if (srl->passflag & SCE_PASS_UV)
+ render_layer_add_pass(rr, rl, 3, SCE_PASS_UV, view);
+ if (srl->passflag & SCE_PASS_RGBA)
+ render_layer_add_pass(rr, rl, 4, SCE_PASS_RGBA, view);
+ if (srl->passflag & SCE_PASS_EMIT)
+ render_layer_add_pass(rr, rl, 3, SCE_PASS_EMIT, view);
+ if (srl->passflag & SCE_PASS_DIFFUSE)
+ render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE, view);
+ if (srl->passflag & SCE_PASS_SPEC)
+ render_layer_add_pass(rr, rl, 3, SCE_PASS_SPEC, view);
+ if (srl->passflag & SCE_PASS_AO)
+ render_layer_add_pass(rr, rl, 3, SCE_PASS_AO, view);
+ if (srl->passflag & SCE_PASS_ENVIRONMENT)
+ render_layer_add_pass(rr, rl, 3, SCE_PASS_ENVIRONMENT, view);
+ if (srl->passflag & SCE_PASS_INDIRECT)
+ render_layer_add_pass(rr, rl, 3, SCE_PASS_INDIRECT, view);
+ if (srl->passflag & SCE_PASS_SHADOW)
+ render_layer_add_pass(rr, rl, 3, SCE_PASS_SHADOW, view);
+ if (srl->passflag & SCE_PASS_REFLECT)
+ render_layer_add_pass(rr, rl, 3, SCE_PASS_REFLECT, view);
+ if (srl->passflag & SCE_PASS_REFRACT)
+ render_layer_add_pass(rr, rl, 3, SCE_PASS_REFRACT, view);
+ if (srl->passflag & SCE_PASS_INDEXOB)
+ render_layer_add_pass(rr, rl, 1, SCE_PASS_INDEXOB, view);
+ if (srl->passflag & SCE_PASS_INDEXMA)
+ render_layer_add_pass(rr, rl, 1, SCE_PASS_INDEXMA, view);
+ if (srl->passflag & SCE_PASS_MIST)
+ render_layer_add_pass(rr, rl, 1, SCE_PASS_MIST, view);
+ if (rl->passflag & SCE_PASS_RAYHITS)
+ render_layer_add_pass(rr, rl, 4, SCE_PASS_RAYHITS, view);
+ if (srl->passflag & SCE_PASS_DIFFUSE_DIRECT)
+ render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE_DIRECT, view);
+ if (srl->passflag & SCE_PASS_DIFFUSE_INDIRECT)
+ render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE_INDIRECT, view);
+ if (srl->passflag & SCE_PASS_DIFFUSE_COLOR)
+ render_layer_add_pass(rr, rl, 3, SCE_PASS_DIFFUSE_COLOR, view);
+ if (srl->passflag & SCE_PASS_GLOSSY_DIRECT)
+ render_layer_add_pass(rr, rl, 3, SCE_PASS_GLOSSY_DIRECT, view);
+ if (srl->passflag & SCE_PASS_GLOSSY_INDIRECT)
+ render_layer_add_pass(rr, rl, 3, SCE_PASS_GLOSSY_INDIRECT, view);
+ if (srl->passflag & SCE_PASS_GLOSSY_COLOR)
+ render_layer_add_pass(rr, rl, 3, SCE_PASS_GLOSSY_COLOR, view);
+ if (srl->passflag & SCE_PASS_TRANSM_DIRECT)
+ render_layer_add_pass(rr, rl, 3, SCE_PASS_TRANSM_DIRECT, view);
+ if (srl->passflag & SCE_PASS_TRANSM_INDIRECT)
+ render_layer_add_pass(rr, rl, 3, SCE_PASS_TRANSM_INDIRECT, view);
+ if (srl->passflag & SCE_PASS_TRANSM_COLOR)
+ render_layer_add_pass(rr, rl, 3, SCE_PASS_TRANSM_COLOR, view);
+ if (srl->passflag & SCE_PASS_SUBSURFACE_DIRECT)
+ render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_DIRECT, view);
+ if (srl->passflag & SCE_PASS_SUBSURFACE_INDIRECT)
+ render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_INDIRECT, view);
+ if (srl->passflag & SCE_PASS_SUBSURFACE_COLOR)
+ render_layer_add_pass(rr, rl, 3, SCE_PASS_SUBSURFACE_COLOR, view);
#ifdef WITH_CYCLES_DEBUG
- if (BKE_scene_use_new_shading_nodes(re->scene)) {
- render_layer_add_debug_pass(rr, rl, 1, SCE_PASS_DEBUG,
- RENDER_PASS_DEBUG_BVH_TRAVERSAL_STEPS);
- }
+ if (BKE_scene_use_new_shading_nodes(re->scene)) {
+ render_layer_add_debug_pass(rr, rl, 1, SCE_PASS_DEBUG,
+ RENDER_PASS_DEBUG_BVH_TRAVERSAL_STEPS, view);
+ }
#endif
+ }
}
/* sss, previewrender and envmap don't do layers, so we make a default one */
if (BLI_listbase_is_empty(&rr->layers) && !(layername && layername[0])) {
@@ -628,18 +716,27 @@ RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuf
/* duplicate code... */
if (rr->do_exr_tile) {
rl->display_buffer = MEM_mapallocN(rectx * recty * sizeof(unsigned int), "Combined display space rgba");
-
rl->exrhandle = IMB_exr_get_handle();
-
- IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.R", 0, 0, NULL);
- IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.G", 0, 0, NULL);
- IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.B", 0, 0, NULL);
- IMB_exr_add_channel(rl->exrhandle, rl->name, "Combined.A", 0, 0, NULL);
}
- else {
- rl->rectf = MEM_mapallocN(rectx * recty * sizeof(float) * 4, "Combined rgba");
+
+ for (rv = rr->views.first; rv; rv = rv->next) {
+ const char *view = rv->name;
+
+ if (viewname && viewname[0])
+ if (strcmp(view, viewname) != 0)
+ continue;
+
+ if (rr->do_exr_tile) {
+ IMB_exr_add_view(rl->exrhandle, view);
+
+ for (i=0; i < 4; i++)
+ IMB_exr_add_channel(rl->exrhandle, rl->name, name_from_passtype(SCE_PASS_COMBINED, i), view, 0, 0, NULL);
+ }
+ else {
+ render_layer_add_pass(rr, rl, 4, SCE_PASS_COMBINED, view);
+ }
}
-
+
/* note, this has to be in sync with scene.c */
rl->lay = (1 << 20) - 1;
rl->layflag = 0x7FFF; /* solid ztra halo strand */
@@ -657,15 +754,15 @@ RenderResult *render_result_new(Render *re, rcti *partrct, int crop, int savebuf
}
/* allocate osa new results for samples */
-RenderResult *render_result_new_full_sample(Render *re, ListBase *lb, rcti *partrct, int crop, int savebuffers)
+RenderResult *render_result_new_full_sample(Render *re, ListBase *lb, rcti *partrct, int crop, int savebuffers, const char *viewname)
{
int a;
if (re->osa == 0)
- return render_result_new(re, partrct, crop, savebuffers, RR_ALL_LAYERS);
+ return render_result_new(re, partrct, crop, savebuffers, RR_ALL_LAYERS, viewname);
for (a = 0; a < re->osa; a++) {
- RenderResult *rr = render_result_new(re, partrct, crop, savebuffers, RR_ALL_LAYERS);
+ RenderResult *rr = render_result_new(re, partrct, crop, savebuffers, RR_ALL_LAYERS, viewname);
BLI_addtail(lb, rr);
rr->sample_nr = a;
}
@@ -686,28 +783,98 @@ static void *ml_addlayer_cb(void *base, const char *str)
return rl;
}
-static void ml_addpass_cb(void *UNUSED(base), void *lay, const char *str, float *rect, int totchan, const char *chan_id)
+static void ml_addpass_cb(void *base, void *lay, const char *str, float *rect, int totchan, const char *chan_id, const char *view)
{
+ RenderResult *rr = base;
RenderLayer *rl = lay;
RenderPass *rpass = MEM_callocN(sizeof(RenderPass), "loaded pass");
int a;
BLI_addtail(&rl->passes, rpass);
rpass->channels = totchan;
-
rpass->passtype = passtype_from_name(str);
if (rpass->passtype == 0) printf("unknown pass %s\n", str);
rl->passflag |= rpass->passtype;
- BLI_strncpy(rpass->name, str, EXR_PASS_MAXNAME);
/* channel id chars */
for (a = 0; a < totchan; a++)
rpass->chan_id[a] = chan_id[a];
-
+
rpass->rect = rect;
+ if (view[0] != '\0') {
+ BLI_snprintf(rpass->name, sizeof(rpass->name), "%s.%s", str, view);
+ rpass->view_id = BLI_findstringindex(&rr->views, view, offsetof(RenderView, name));
+ }
+ else {
+ BLI_strncpy(rpass->name, str, sizeof(rpass->name));
+ rpass->view_id = 0;
+ }
+
+ BLI_strncpy(rpass->view, view, sizeof(rpass->view));
+ BLI_strncpy(rpass->internal_name, str, sizeof(rpass->internal_name));
}
-/* from imbuf, if a handle was returned we convert this to render result */
+static void *ml_addview_cb(void *base, const char *str)
+{
+ RenderResult *rr = base;
+ RenderView *rv;
+
+ rv = MEM_callocN(sizeof(RenderView), "new render view");
+ BLI_strncpy(rv->name, str, EXR_VIEW_MAXNAME);
+
+ /* For stereo drawing we need to ensure:
+ * STEREO_LEFT_NAME == STEREO_LEFT_ID and
+ * STEREO_RIGHT_NAME == STEREO_RIGHT_ID */
+
+ if (STREQ(str, STEREO_LEFT_NAME)) {
+ BLI_addhead(&rr->views, rv);
+ }
+ else if (STREQ(str, STEREO_RIGHT_NAME)) {
+ RenderView *left_rv = BLI_findstring(&rr->views, STEREO_LEFT_NAME, offsetof(RenderView, name));
+
+ if (left_rv == NULL) {
+ BLI_addhead(&rr->views, rv);
+ }
+ else {
+ BLI_insertlinkafter(&rr->views, left_rv, rv);
+ }
+ }
+ else {
+ BLI_addtail(&rr->views, rv);
+ }
+
+ return rv;
+}
+
+static int order_render_passes(const void *a, const void *b)
+{
+ // 1 if a is after b
+ RenderPass *rpa = (RenderPass *) a;
+ RenderPass *rpb = (RenderPass *) b;
+
+ if (rpa->passtype > rpb->passtype)
+ return 1;
+ else if (rpa->passtype < rpb->passtype)
+ return 0;
+
+ /* they have the same type */
+ /* left first */
+ if (STREQ(rpa->view, STEREO_LEFT_NAME))
+ return 0;
+ else if (STREQ(rpb->view, STEREO_LEFT_NAME))
+ return 1;
+
+ /* right second */
+ if (STREQ(rpa->view, STEREO_RIGHT_NAME))
+ return 0;
+ else if (STREQ(rpb->view, STEREO_RIGHT_NAME))
+ return 1;
+
+ /* remaining in ascending id order */
+ return (rpa->view_id < rpb->view_id);
+}
+
+/* from imbuf, if a handle was returned and it's not a singlelayer multiview we convert this to render result */
RenderResult *render_result_new_from_exr(void *exrhandle, const char *colorspace, bool predivide, int rectx, int recty)
{
RenderResult *rr = MEM_callocN(sizeof(RenderResult), __func__);
@@ -718,13 +885,17 @@ RenderResult *render_result_new_from_exr(void *exrhandle, const char *colorspace
rr->rectx = rectx;
rr->recty = recty;
- IMB_exr_multilayer_convert(exrhandle, rr, ml_addlayer_cb, ml_addpass_cb);
+ IMB_exr_multilayer_convert(exrhandle, rr, ml_addview_cb, ml_addlayer_cb, ml_addpass_cb);
for (rl = rr->layers.first; rl; rl = rl->next) {
+ int c=0;
rl->rectx = rectx;
rl->recty = recty;
+ BLI_listbase_sort(&rl->passes, order_render_passes);
+
for (rpass = rl->passes.first; rpass; rpass = rpass->next) {
+ printf("%d: %s\n", c++, rpass->name);
rpass->rectx = rectx;
rpass->recty = recty;
@@ -738,6 +909,33 @@ RenderResult *render_result_new_from_exr(void *exrhandle, const char *colorspace
return rr;
}
+void render_result_views_new(RenderResult *rr, RenderData *rd)
+{
+ SceneRenderView *srv;
+ RenderView *rv;
+
+ /* clear previously existing views - for sequencer */
+ render_result_views_free(rr);
+
+ /* check renderdata for amount of views */
+ if ((rd->scemode & R_MULTIVIEW)) {
+ for (srv = rd->views.first; srv; srv = srv->next) {
+ if (BKE_scene_multiview_is_render_view_active(rd, srv) == false) continue;
+
+ rv = MEM_callocN(sizeof(RenderView), "new render view");
+ BLI_addtail(&rr->views, rv);
+
+ BLI_strncpy(rv->name, srv->name, sizeof(rv->name));
+ }
+ }
+
+ /* we always need at least one view */
+ if (BLI_listbase_count_ex(&rr->views, 1) == 0) {
+ rv = MEM_callocN(sizeof(RenderView), "new render view");
+ BLI_addtail(&rr->views, rv);
+ }
+}
+
/*********************************** Merge ***********************************/
static void do_merge_tile(RenderResult *rr, RenderResult *rrpart, float *target, float *tile, int pixsize)
@@ -783,16 +981,19 @@ void render_result_merge(RenderResult *rr, RenderResult *rrpart)
for (rl = rr->layers.first; rl; rl = rl->next) {
rlp = RE_GetRenderLayer(rrpart, rl->name);
if (rlp) {
- /* combined */
- if (rl->rectf && rlp->rectf)
- do_merge_tile(rr, rrpart, rl->rectf, rlp->rectf, 4);
-
/* passes are allocated in sync */
for (rpass = rl->passes.first, rpassp = rlp->passes.first;
rpass && rpassp;
- rpass = rpass->next, rpassp = rpassp->next)
+ rpass = rpass->next)
{
+ /* renderresult have all passes, renderpart only the active view's passes */
+ if (strcmp(rpassp->name, rpass->name) != 0)
+ continue;
+
do_merge_tile(rr, rrpart, rpass->rect, rpassp->rect, rpass->channels);
+
+ /* manually get next render pass */
+ rpassp = rpassp->next;
}
}
}
@@ -801,7 +1002,7 @@ void render_result_merge(RenderResult *rr, RenderResult *rrpart)
/* for passes read from files, these have names stored */
static char *make_pass_name(RenderPass *rpass, int chan)
{
- static char name[16];
+ static char name[EXR_PASS_MAXNAME];
int len;
BLI_strncpy(name, rpass->name, EXR_PASS_MAXNAME);
@@ -813,65 +1014,111 @@ static char *make_pass_name(RenderPass *rpass, int chan)
return name;
}
-/* filename already made absolute */
-/* called from within UI, saves both rendered result as a file-read result */
-bool RE_WriteRenderResult(ReportList *reports, RenderResult *rr, const char *filename, int compress)
+/* called from within UI and render pipeline, saves both rendered result as a file-read result
+ * if multiview is true saves all views in a multiview exr
+ * else if view is not NULL saves single view
+ * else saves stereo3d
+ */
+bool RE_WriteRenderResult(ReportList *reports, RenderResult *rr, const char *filename, ImageFormatData *imf, const bool multiview, const char *view)
{
RenderLayer *rl;
RenderPass *rpass;
+ RenderView *rview;
void *exrhandle = IMB_exr_get_handle();
- bool success;
-
- BLI_make_existing_file(filename);
-
- /* composite result */
- if (rr->rectf) {
- IMB_exr_add_channel(exrhandle, "Composite", "Combined.R", 4, 4 * rr->rectx, rr->rectf);
- IMB_exr_add_channel(exrhandle, "Composite", "Combined.G", 4, 4 * rr->rectx, rr->rectf + 1);
- IMB_exr_add_channel(exrhandle, "Composite", "Combined.B", 4, 4 * rr->rectx, rr->rectf + 2);
- IMB_exr_add_channel(exrhandle, "Composite", "Combined.A", 4, 4 * rr->rectx, rr->rectf + 3);
+ bool success = false;
+ int a, nr;
+ const char *chan_view = NULL;
+ int compress = (imf ? imf->exr_codec : 0);
+ size_t width, height;
+
+ const bool is_mono = view && !multiview;
+
+ width = rr->rectx;
+ height = rr->recty;
+
+ if (imf && imf->imtype == R_IMF_IMTYPE_OPENEXR && multiview) {
+ /* single layer OpenEXR */
+ const char *RGBAZ[] = {"R", "G", "B", "A", "Z"};
+ for (nr = 0, rview = rr->views.first; rview; rview = rview->next, nr++) {
+ IMB_exr_add_view(exrhandle, rview->name);
+
+ if (rview->rectf) {
+ for (a = 0; a < 4; a++)
+ IMB_exr_add_channel(exrhandle, "", RGBAZ[a],
+ rview->name, 4, 4 * width, rview->rectf + a);
+ if (rview->rectz)
+ IMB_exr_add_channel(exrhandle, "", RGBAZ[4],
+ rview->name, 1, width, rview->rectz);
+ }
+ }
}
-
- /* add layers/passes and assign channels */
- for (rl = rr->layers.first; rl; rl = rl->next) {
-
- /* combined */
- if (rl->rectf) {
- int a, xstride = 4;
- for (a = 0; a < xstride; a++) {
- IMB_exr_add_channel(exrhandle, rl->name, get_pass_name(SCE_PASS_COMBINED, a),
- xstride, xstride * rr->rectx, rl->rectf + a);
+ else {
+ for (nr = 0, rview = rr->views.first; rview; rview = rview->next, nr++) {
+ if (is_mono) {
+ if (!STREQ(view, rview->name)) {
+ continue;
+ }
+ chan_view = "";
+ }
+ else {
+ /* if rendered only one view, we treat as a a non-view render */
+ chan_view = rview->name;
+ }
+
+ IMB_exr_add_view(exrhandle, rview->name);
+
+ if (rview->rectf) {
+ for (a = 0; a < 4; a++)
+ IMB_exr_add_channel(exrhandle, "Composite", name_from_passtype(SCE_PASS_COMBINED, a),
+ chan_view, 4, 4 * width, rview->rectf + a);
}
}
-
- /* passes are allocated in sync */
- for (rpass = rl->passes.first; rpass; rpass = rpass->next) {
- int a, xstride = rpass->channels;
- for (a = 0; a < xstride; a++) {
- if (rpass->passtype) {
- IMB_exr_add_channel(exrhandle, rl->name, get_pass_name(rpass->passtype, a),
- xstride, xstride * rr->rectx, rpass->rect + a);
+
+ /* add layers/passes and assign channels */
+ for (rl = rr->layers.first; rl; rl = rl->next) {
+
+ /* passes are allocated in sync */
+ for (rpass = rl->passes.first; rpass; rpass = rpass->next) {
+ const int xstride = rpass->channels;
+
+ if (is_mono) {
+ if (!STREQ(view, rpass->view)) {
+ continue;
+ }
+ chan_view = "";
}
else {
- IMB_exr_add_channel(exrhandle, rl->name, make_pass_name(rpass, a),
- xstride, xstride * rr->rectx, rpass->rect + a);
+ /* if rendered only one view, we treat as a a non-view render */
+ chan_view = (nr > 1 ? rpass->view :"");
+ }
+
+ for (a = 0; a < xstride; a++) {
+
+ if (rpass->passtype) {
+ IMB_exr_add_channel(exrhandle, rl->name, name_from_passtype(rpass->passtype, a), chan_view,
+ xstride, xstride * width, rpass->rect + a);
+ }
+ else {
+ IMB_exr_add_channel(exrhandle, rl->name, make_pass_name(rpass, a), chan_view,
+ xstride, xstride * width, rpass->rect + a);
+ }
}
}
}
}
- /* when the filename has no permissions, this can fail */
- if (IMB_exr_begin_write(exrhandle, filename, rr->rectx, rr->recty, compress)) {
+ BLI_make_existing_file(filename);
+
+ if (IMB_exr_begin_write(exrhandle, filename, width, height, compress)) {
IMB_exr_write_channels(exrhandle);
- success = true;
}
else {
/* TODO, get the error from openexr's exception */
BKE_report(reports, RPT_ERROR, "Error writing render result (see console)");
success = false;
}
- IMB_exr_close(exrhandle);
+ IMB_exr_close(exrhandle);
return success;
}
@@ -932,7 +1179,7 @@ void render_result_single_layer_end(Render *re)
/************************* EXR Tile File Rendering ***************************/
-static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart)
+static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart, const char *viewname)
{
RenderLayer *rlp, *rl;
RenderPass *rpassp;
@@ -955,21 +1202,17 @@ static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart)
else {
offs = 0;
}
-
- /* combined */
- if (rlp->rectf) {
- int a, xstride = 4;
- for (a = 0; a < xstride; a++) {
- IMB_exr_set_channel(rl->exrhandle, rlp->name, get_pass_name(SCE_PASS_COMBINED, a),
- xstride, xstride * rrpart->rectx, rlp->rectf + a + xstride * offs);
- }
- }
-
+
/* passes are allocated in sync */
for (rpassp = rlp->passes.first; rpassp; rpassp = rpassp->next) {
- int a, xstride = rpassp->channels;
+ const int xstride = rpassp->channels;
+ int a;
+ char passname[EXR_PASS_MAXNAME];
+
for (a = 0; a < xstride; a++) {
- IMB_exr_set_channel(rl->exrhandle, rlp->name, get_pass_name(rpassp->passtype, a),
+ set_pass_name(passname, rpassp->passtype, a, rpassp->view);
+
+ IMB_exr_set_channel(rl->exrhandle, rlp->name, passname,
xstride, xstride * rrpart->rectx, rpassp->rect + a + xstride * offs);
}
}
@@ -988,7 +1231,7 @@ static void save_render_result_tile(RenderResult *rr, RenderResult *rrpart)
continue;
}
- IMB_exrtile_write_channels(rl->exrhandle, partx, party, 0);
+ IMB_exrtile_write_channels(rl->exrhandle, partx, party, 0, viewname);
}
BLI_unlock_thread(LOCK_IMAGE);
@@ -1002,13 +1245,13 @@ static void save_empty_result_tiles(Render *re)
for (rr = re->result; rr; rr = rr->next) {
for (rl = rr->layers.first; rl; rl = rl->next) {
- IMB_exrtile_clear_channels(rl->exrhandle);
+ IMB_exr_clear_channels(rl->exrhandle);
for (pa = re->parts.first; pa; pa = pa->next) {
if (pa->status != PART_STATUS_READY) {
int party = pa->disprect.ymin - re->disprect.ymin + pa->crop;
int partx = pa->disprect.xmin - re->disprect.xmin + pa->crop;
- IMB_exrtile_write_channels(rl->exrhandle, partx, party, 0);
+ IMB_exrtile_write_channels(rl->exrhandle, partx, party, 0, re->viewname);
}
}
}
@@ -1055,10 +1298,10 @@ void render_result_exr_file_end(Render *re)
}
/* save part into exr file */
-void render_result_exr_file_merge(RenderResult *rr, RenderResult *rrpart)
+void render_result_exr_file_merge(RenderResult *rr, RenderResult *rrpart, const char *viewname)
{
for (; rr && rrpart; rr = rr->next, rrpart = rrpart->next)
- save_render_result_tile(rr, rrpart);
+ save_render_result_tile(rr, rrpart, viewname);
}
/* path to temporary exr file */
@@ -1088,7 +1331,7 @@ int render_result_exr_file_read_sample(Render *re, int sample)
bool success = true;
RE_FreeRenderResult(re->result);
- re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS);
+ re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS);
for (rl = re->result->layers.first; rl; rl = rl->next) {
render_result_exr_file_path(re->scene, rl->name, sample, str);
@@ -1129,23 +1372,20 @@ int render_result_exr_file_read_path(RenderResult *rr, RenderLayer *rl_single, c
for (rl = rr->layers.first; rl; rl = rl->next) {
if (rl_single && rl_single != rl)
continue;
-
- /* combined */
- if (rl->rectf) {
- int a, xstride = 4;
- for (a = 0; a < xstride; a++)
- IMB_exr_set_channel(exrhandle, rl->name, get_pass_name(SCE_PASS_COMBINED, a),
- xstride, xstride * rectx, rl->rectf + a);
- }
/* passes are allocated in sync */
for (rpass = rl->passes.first; rpass; rpass = rpass->next) {
- int a, xstride = rpass->channels;
- for (a = 0; a < xstride; a++)
- IMB_exr_set_channel(exrhandle, rl->name, get_pass_name(rpass->passtype, a),
+ const int xstride = rpass->channels;
+ int a;
+ char passname[EXR_PASS_MAXNAME];
+
+ for (a = 0; a < xstride; a++) {
+ set_pass_name(passname, rpass->passtype, a, rpass->view);
+ IMB_exr_set_channel(exrhandle, rl->name, passname,
xstride, xstride * rectx, rpass->rect + a);
+ }
- BLI_strncpy(rpass->name, get_pass_name(rpass->passtype, -1), sizeof(rpass->name));
+ set_pass_name(rpass->name, rpass->passtype, -1, rpass->view);
}
}
@@ -1191,7 +1431,8 @@ void render_result_exr_file_cache_write(Render *re)
render_result_exr_file_cache_path(re->scene, root, str);
printf("Caching exr file, %dx%d, %s\n", rr->rectx, rr->recty, str);
- RE_WriteRenderResult(NULL, rr, str, 0);
+
+ RE_WriteRenderResult(NULL, rr, str, NULL, true, NULL);
}
/* For cache, makes exact copy of render result */
@@ -1201,7 +1442,7 @@ bool render_result_exr_file_cache_read(Render *re)
char *root = U.render_cachedir;
RE_FreeRenderResult(re->result);
- re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS);
+ re->result = render_result_new(re, &re->disprect, 0, RR_USE_MEM, RR_ALL_LAYERS, RR_ALL_VIEWS);
/* First try cache. */
render_result_exr_file_cache_path(re->scene, root, str);
@@ -1216,14 +1457,14 @@ bool render_result_exr_file_cache_read(Render *re)
/*************************** Combined Pixel Rect *****************************/
-ImBuf *render_result_rect_to_ibuf(RenderResult *rr, RenderData *rd)
+ImBuf *render_result_rect_to_ibuf(RenderResult *rr, RenderData *rd, const int view_id)
{
ImBuf *ibuf = IMB_allocImBuf(rr->rectx, rr->recty, rd->im_format.planes, 0);
/* if not exists, BKE_imbuf_write makes one */
- ibuf->rect = (unsigned int *)rr->rect32;
- ibuf->rect_float = rr->rectf;
- ibuf->zbuf_float = rr->rectz;
+ ibuf->rect = (unsigned int *) RE_RenderViewGetRect32(rr, view_id);
+ ibuf->rect_float = RE_RenderViewGetRectf(rr, view_id);
+ ibuf->zbuf_float = RE_RenderViewGetRectz(rr, view_id);
/* float factor for random dither, imbuf takes care of it */
ibuf->dither = rd->dither_intensity;
@@ -1259,53 +1500,73 @@ ImBuf *render_result_rect_to_ibuf(RenderResult *rr, RenderData *rd)
return ibuf;
}
-void render_result_rect_from_ibuf(RenderResult *rr, RenderData *UNUSED(rd), ImBuf *ibuf)
+void render_result_rect_from_ibuf(RenderResult *rr, RenderData *UNUSED(rd), ImBuf *ibuf, const int view_id)
{
+ RenderView *rv = BLI_findlink(&rr->views, view_id);
+
if (ibuf->rect_float) {
- if (!rr->rectf)
- rr->rectf = MEM_mallocN(4 * sizeof(float) * rr->rectx * rr->recty, "render_seq rectf");
+ if (!rv->rectf)
+ rv->rectf = MEM_mallocN(4 * sizeof(float) * rr->rectx * rr->recty, "render_seq rectf");
- memcpy(rr->rectf, ibuf->rect_float, 4 * sizeof(float) * rr->rectx * rr->recty);
+ memcpy(rv->rectf, ibuf->rect_float, 4 * sizeof(float) * rr->rectx * rr->recty);
/* TSK! Since sequence render doesn't free the *rr render result, the old rect32
* can hang around when sequence render has rendered a 32 bits one before */
- if (rr->rect32) {
- MEM_freeN(rr->rect32);
- rr->rect32 = NULL;
+ if (rv->rect32) {
+ MEM_freeN(rv->rect32);
+ rv->rect32 = NULL;
}
}
else if (ibuf->rect) {
- if (!rr->rect32)
- rr->rect32 = MEM_mallocN(sizeof(int) * rr->rectx * rr->recty, "render_seq rect");
+ if (!rv->rect32)
+ rv->rect32 = MEM_mallocN(sizeof(int) * rr->rectx * rr->recty, "render_seq rect");
- memcpy(rr->rect32, ibuf->rect, 4 * rr->rectx * rr->recty);
+ memcpy(rv->rect32, ibuf->rect, 4 * rr->rectx * rr->recty);
/* Same things as above, old rectf can hang around from previous render. */
+ if (rv->rectf) {
+ MEM_freeN(rv->rectf);
+ rv->rectf = NULL;
+ }
+ }
+
+ /* clean up non-view buffers */
+ if (rr) {
if (rr->rectf) {
MEM_freeN(rr->rectf);
rr->rectf = NULL;
}
+
+ if (rr->rect32) {
+ MEM_freeN(rr->rect32);
+ rr->rect32 = NULL;
+ }
}
}
-void render_result_rect_fill_zero(RenderResult *rr)
+void render_result_rect_fill_zero(RenderResult *rr, const int view_id)
{
- if (rr->rectf)
- memset(rr->rectf, 0, 4 * sizeof(float) * rr->rectx * rr->recty);
- else if (rr->rect32)
- memset(rr->rect32, 0, 4 * rr->rectx * rr->recty);
+ RenderView *rv = BLI_findlink(&rr->views, view_id);
+
+ if (rv->rectf)
+ memset(rv->rectf, 0, 4 * sizeof(float) * rr->rectx * rr->recty);
+ else if (rv->rect32)
+ memset(rv->rect32, 0, 4 * rr->rectx * rr->recty);
else
- rr->rect32 = MEM_callocN(sizeof(int) * rr->rectx * rr->recty, "render_seq rect");
+ rv->rect32 = MEM_callocN(sizeof(int) * rr->rectx * rr->recty, "render_seq rect");
}
void render_result_rect_get_pixels(RenderResult *rr, unsigned int *rect, int rectx, int recty,
- const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings)
+ const ColorManagedViewSettings *view_settings, const ColorManagedDisplaySettings *display_settings,
+ const int view_id)
{
if (rr->rect32) {
- memcpy(rect, rr->rect32, sizeof(int) * rr->rectx * rr->recty);
+ int *rect32 = RE_RenderViewGetRect32(rr, view_id);
+ memcpy(rect, (rect32 ? rect32 : rr->rect32), sizeof(int) * rr->rectx * rr->recty);
}
else if (rr->rectf) {
- IMB_display_buffer_transform_apply((unsigned char *) rect, rr->rectf, rr->rectx, rr->recty, 4,
+ float *rectf = RE_RenderViewGetRectf(rr, view_id);
+ IMB_display_buffer_transform_apply((unsigned char *) rect, (rectf ? rectf : rr->rectf), rr->rectx, rr->recty, 4,
view_settings, display_settings, true);
}
else
@@ -1313,3 +1574,75 @@ void render_result_rect_get_pixels(RenderResult *rr, unsigned int *rect, int rec
memset(rect, 0, sizeof(int) * rectx * recty);
}
+
+/*************************** multiview functions *****************************/
+
+bool RE_HasFakeLayer(RenderResult *res)
+{
+ RenderView *rv;
+
+ if (res == NULL)
+ return false;
+
+ rv = res->views.first;
+ if (rv == NULL)
+ return false;
+
+ return (rv->rect32 || rv->rectf);
+}
+
+bool RE_RenderResult_is_stereo(RenderResult *res)
+{
+ if (! BLI_findstring(&res->views, STEREO_LEFT_NAME, offsetof(RenderView, name)))
+ return false;
+
+ if (! BLI_findstring(&res->views, STEREO_RIGHT_NAME, offsetof(RenderView, name)))
+ return false;
+
+ return true;
+}
+
+void RE_RenderViewSetRectf(RenderResult *res, const int view_id, float *rect)
+{
+ RenderView *rv = BLI_findlink(&res->views, view_id);
+ if (rv) {
+ rv->rectf = rect;
+ }
+}
+
+void RE_RenderViewSetRectz(RenderResult *res, const int view_id, float *rect)
+{
+ RenderView *rv = BLI_findlink(&res->views, view_id);
+ if (rv) {
+ rv->rectz = rect;
+ }
+}
+
+float *RE_RenderViewGetRectz(RenderResult *res, const int view_id)
+{
+ RenderView *rv = BLI_findlink(&res->views, view_id);
+ if (rv) {
+ return rv->rectz;
+ }
+ return res->rectz;
+}
+
+float *RE_RenderViewGetRectf(RenderResult *res, const int view_id)
+{
+ RenderView *rv = BLI_findlink(&res->views, view_id);
+ if (rv) {
+ return rv->rectf;
+ }
+ return res->rectf;
+}
+
+int *RE_RenderViewGetRect32(RenderResult *res, const int view_id)
+{
+ RenderView *rv = BLI_findlink(&res->views, view_id);
+ if (rv) {
+ return rv->rect32;
+ }
+ return res->rect32;
+}
+
+
diff --git a/source/blender/render/intern/source/rendercore.c b/source/blender/render/intern/source/rendercore.c
index 5b054005bac..99611a9c5a8 100644
--- a/source/blender/render/intern/source/rendercore.c
+++ b/source/blender/render/intern/source/rendercore.c
@@ -181,8 +181,10 @@ static void halo_pixelstruct(HaloRen *har, RenderLayer **rlpp, int totsample, in
if (fullsample) {
for (sample=0; sample<totsample; sample++)
- if (ps->mask & (1 << sample))
- addalphaAddfacFloat(rlpp[sample]->rectf + od*4, col, har->add);
+ if (ps->mask & (1 << sample)) {
+ float *pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname);
+ addalphaAddfacFloat(pass + od*4, col, har->add);
+ }
}
else {
fac= ((float)amountm)/(float)R.osa;
@@ -214,8 +216,10 @@ static void halo_pixelstruct(HaloRen *har, RenderLayer **rlpp, int totsample, in
if (fullsample) {
for (sample=0; sample<totsample; sample++)
- if (!(mask & (1 << sample)))
- addalphaAddfacFloat(rlpp[sample]->rectf + od*4, col, har->add);
+ if (!(mask & (1 << sample))) {
+ float *pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname);
+ addalphaAddfacFloat(pass + od*4, col, har->add);
+ }
}
else {
col[0]= accol[0];
@@ -223,8 +227,10 @@ static void halo_pixelstruct(HaloRen *har, RenderLayer **rlpp, int totsample, in
col[2]= accol[2];
col[3]= accol[3];
- for (sample=0; sample<totsample; sample++)
- addalphaAddfacFloat(rlpp[sample]->rectf + od*4, col, har->add);
+ for (sample=0; sample<totsample; sample++) {
+ float *pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname);
+ addalphaAddfacFloat(pass + od*4, col, har->add);
+ }
}
}
@@ -305,8 +311,10 @@ static void halo_tile(RenderPart *pa, RenderLayer *rl)
zz= calchalo_z(har, *rz);
if ((zz> har->zs) || (har->mat && (har->mat->mode & MA_HALO_SOFT))) {
if (shadeHaloFloat(har, col, zz, dist, xn, yn, har->flarec)) {
- for (sample=0; sample<totsample; sample++)
- addalphaAddfacFloat(rlpp[sample]->rectf + od*4, col, har->add);
+ for (sample=0; sample<totsample; sample++) {
+ float * rect= RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname);
+ addalphaAddfacFloat(rect + od*4, col, har->add);
+ }
}
}
}
@@ -359,7 +367,8 @@ static void lamphalo_tile(RenderPart *pa, RenderLayer *rl)
if (fullsample) {
for (sample=0; sample<totsample; sample++) {
if (ps->mask & (1 << sample)) {
- pass= rlpp[sample]->rectf + od*4;
+ pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname);
+ pass += od * 4;
pass[0]+= col[0];
pass[1]+= col[1];
pass[2]+= col[2];
@@ -370,7 +379,8 @@ static void lamphalo_tile(RenderPart *pa, RenderLayer *rl)
}
else {
fac= ((float)count)/(float)R.osa;
- pass= rl->rectf + od*4;
+ pass = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, R.viewname);
+ pass += od * 4;
pass[0]+= fac*col[0];
pass[1]+= fac*col[1];
pass[2]+= fac*col[2];
@@ -390,7 +400,9 @@ static void lamphalo_tile(RenderPart *pa, RenderLayer *rl)
if (fullsample) {
for (sample=0; sample<totsample; sample++) {
if (!(mask & (1 << sample))) {
- pass= rlpp[sample]->rectf + od*4;
+
+ pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname);
+ pass += od * 4;
pass[0]+= col[0];
pass[1]+= col[1];
pass[2]+= col[2];
@@ -401,7 +413,8 @@ static void lamphalo_tile(RenderPart *pa, RenderLayer *rl)
}
else {
fac= ((float)R.osa-totsamp)/(float)R.osa;
- pass= rl->rectf + od*4;
+ pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname);
+ pass += od * 4;
pass[0]+= fac*col[0];
pass[1]+= fac*col[1];
pass[2]+= fac*col[2];
@@ -420,7 +433,8 @@ static void lamphalo_tile(RenderPart *pa, RenderLayer *rl)
renderspothalo(&shi, col, 1.0f);
for (sample=0; sample<totsample; sample++) {
- pass= rlpp[sample]->rectf + od*4;
+ pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname);
+ pass += od * 4;
pass[0]+= col[0];
pass[1]+= col[1];
pass[2]+= col[2];
@@ -444,14 +458,14 @@ static void add_filt_passes(RenderLayer *rl, int curmask, int rectx, int offset,
{
RenderPass *rpass;
- /* combined rgb */
- add_filt_fmask(curmask, shr->combined, rl->rectf + 4*offset, rectx);
-
for (rpass= rl->passes.first; rpass; rpass= rpass->next) {
float *fp, *col= NULL;
int pixsize= 3;
switch (rpass->passtype) {
+ case SCE_PASS_COMBINED:
+ add_filt_fmask(curmask, shr->combined, rpass->rect + 4*offset, rectx);
+ break;
case SCE_PASS_Z:
fp= rpass->rect + offset;
*fp= shr->z;
@@ -555,15 +569,16 @@ static void add_passes(RenderLayer *rl, int offset, ShadeInput *shi, ShadeResult
{
RenderPass *rpass;
float *fp;
-
- fp= rl->rectf + 4*offset;
- copy_v4_v4(fp, shr->combined);
-
+
for (rpass= rl->passes.first; rpass; rpass= rpass->next) {
float *col= NULL, uvcol[3];
int a, pixsize= 3;
switch (rpass->passtype) {
+ case SCE_PASS_COMBINED:
+ /* copy combined to use for preview */
+ copy_v4_v4(rpass->rect + 4*offset, shr->combined);
+ break;
case SCE_PASS_Z:
fp= rpass->rect + offset;
*fp= shr->z;
@@ -681,7 +696,8 @@ static void sky_tile(RenderPart *pa, RenderLayer *rl)
bool done = false;
for (sample= 0; sample<totsample; sample++) {
- float *pass= rlpp[sample]->rectf + od;
+ float *pass = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname);
+ pass += od;
if (pass[3]<1.0f) {
@@ -742,8 +758,8 @@ static void atm_tile(RenderPart *pa, RenderLayer *rl)
int sample;
for (sample=0; sample<totsample; sample++) {
- const float *zrect= RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_Z) + od;
- float *rgbrect = rlpp[sample]->rectf + 4*od;
+ const float *zrect = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_Z, R.viewname) + od;
+ float *rgbrect = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname) + 4*od;
float rgb[3] = {0};
bool done = false;
@@ -978,8 +994,8 @@ static void clamp_alpha_rgb_range(RenderPart *pa, RenderLayer *rl)
return;
for (sample= 0; sample<totsample; sample++) {
- float *rectf= rlpp[sample]->rectf;
-
+ float *rectf = RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_COMBINED, R.viewname);
+
for (y= pa->rectx*pa->recty; y>0; y--, rectf+=4) {
rectf[0] = MAX2(rectf[0], 0.0f);
rectf[1] = MAX2(rectf[1], 0.0f);
@@ -1060,7 +1076,7 @@ static void reset_sky_speed(RenderPart *pa, RenderLayer *rl)
totsample= get_sample_layers(pa, rl, rlpp);
for (sample= 0; sample<totsample; sample++) {
- fp= RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_VECTOR);
+ fp= RE_RenderLayerGetPass(rlpp[sample], SCE_PASS_VECTOR, R.viewname);
if (fp==NULL) break;
for (a= 4*pa->rectx*pa->recty - 1; a>=0; a--)
@@ -1171,6 +1187,8 @@ void zbufshadeDA_tile(RenderPart *pa)
pa->rectp= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectp");
pa->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz");
for (rl= rr->layers.first; rl; rl= rl->next) {
+ float *rect = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, R.viewname);
+
if ((rl->layflag & SCE_LAY_ZMASK) && (rl->layflag & SCE_LAY_NEG_ZMASK))
pa->rectmask= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectmask");
@@ -1211,7 +1229,7 @@ void zbufshadeDA_tile(RenderPart *pa)
if (R.flag & R_ZTRA || R.totstrand) {
if (rl->layflag & (SCE_LAY_ZTRA|SCE_LAY_STRAND)) {
if (pa->fullresult.first) {
- zbuffer_transp_shade(pa, rl, rl->rectf, &psmlist);
+ zbuffer_transp_shade(pa, rl, rect, &psmlist);
}
else {
unsigned short *ztramask, *solidmask= NULL; /* 16 bits, MAX_OSA */
@@ -1220,9 +1238,9 @@ void zbufshadeDA_tile(RenderPart *pa)
rl->acolrect= MEM_callocN(4*sizeof(float)*pa->rectx*pa->recty, "alpha layer");
/* swap for live updates, and it is used in zbuf.c!!! */
- SWAP(float *, rl->acolrect, rl->rectf);
- ztramask= zbuffer_transp_shade(pa, rl, rl->rectf, &psmlist);
- SWAP(float *, rl->acolrect, rl->rectf);
+ SWAP(float *, rl->acolrect, rect);
+ ztramask = zbuffer_transp_shade(pa, rl, rect, &psmlist);
+ SWAP(float *, rl->acolrect, rect);
/* zbuffer transp only returns ztramask if there's solid rendered */
if (ztramask)
@@ -1231,7 +1249,8 @@ void zbufshadeDA_tile(RenderPart *pa)
if (ztramask && solidmask) {
unsigned short *sps= solidmask, *spz= ztramask;
unsigned short fullmask= (1<<R.osa)-1;
- float *fcol= rl->rectf; float *acol= rl->acolrect;
+ float *fcol= rect;
+ float *acol= rl->acolrect;
int x;
for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4, sps++, spz++) {
@@ -1242,7 +1261,8 @@ void zbufshadeDA_tile(RenderPart *pa)
}
}
else {
- float *fcol= rl->rectf; float *acol= rl->acolrect;
+ float *fcol= rect;
+ float *acol= rl->acolrect;
int x;
for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4) {
addAlphaOverFloat(fcol, acol);
@@ -1265,7 +1285,7 @@ void zbufshadeDA_tile(RenderPart *pa)
/* extra layers */
if (rl->layflag & SCE_LAY_EDGE)
if (R.r.mode & R_EDGE)
- edge_enhance_add(pa, rl->rectf, edgerect);
+ edge_enhance_add(pa, rect, edgerect);
if (rl->passflag & SCE_PASS_VECTOR)
reset_sky_speed(pa, rl);
@@ -1319,6 +1339,7 @@ void zbufshade_tile(RenderPart *pa)
pa->rectz= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectz");
for (rl= rr->layers.first; rl; rl= rl->next) {
+ float *rect= RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, R.viewname);
if ((rl->layflag & SCE_LAY_ZMASK) && (rl->layflag & SCE_LAY_NEG_ZMASK))
pa->rectmask= MEM_mallocN(sizeof(int)*pa->rectx*pa->recty, "rectmask");
@@ -1342,7 +1363,7 @@ void zbufshade_tile(RenderPart *pa)
rr->renlay= rl;
if (rl->layflag & SCE_LAY_SOLID) {
- const float *fcol= rl->rectf;
+ const float *fcol = rect;
const int *ro= pa->recto, *rp= pa->rectp, *rz= pa->rectz;
int x, y, offs=0, seed;
@@ -1405,11 +1426,11 @@ void zbufshade_tile(RenderPart *pa)
rl->acolrect= MEM_callocN(4*sizeof(float)*pa->rectx*pa->recty, "alpha layer");
/* swap for live updates */
- SWAP(float *, rl->acolrect, rl->rectf);
- zbuffer_transp_shade(pa, rl, rl->rectf, NULL);
- SWAP(float *, rl->acolrect, rl->rectf);
+ SWAP(float *, rl->acolrect, rect);
+ zbuffer_transp_shade(pa, rl, rect, NULL);
+ SWAP(float *, rl->acolrect, rect);
- fcol= rl->rectf; acol= rl->acolrect;
+ fcol= rect; acol= rl->acolrect;
for (x=pa->rectx*pa->recty; x>0; x--, acol+=4, fcol+=4) {
addAlphaOverFloat(fcol, acol);
}
@@ -1427,7 +1448,7 @@ void zbufshade_tile(RenderPart *pa)
if (!R.test_break(R.tbh)) {
if (rl->layflag & SCE_LAY_EDGE)
if (R.r.mode & R_EDGE)
- edge_enhance_add(pa, rl->rectf, edgerect);
+ edge_enhance_add(pa, rect, edgerect);
}
if (rl->passflag & SCE_PASS_VECTOR)
@@ -1654,7 +1675,7 @@ void zbufshade_sss_tile(RenderPart *pa)
return;
}
- fcol= rl->rectf;
+ fcol= RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, R.viewname);
co= MEM_mallocN(sizeof(float)*3*handle.totps, "SSSCo");
color= MEM_mallocN(sizeof(float)*3*handle.totps, "SSSColor");
@@ -1938,6 +1959,7 @@ void add_halo_flare(Render *re)
RenderLayer *rl;
HaloRen *har;
int a, mode;
+ float *rect;
/* for now, we get the first renderlayer in list with halos set */
for (rl= rr->layers.first; rl; rl= rl->next) {
@@ -1945,8 +1967,11 @@ void add_halo_flare(Render *re)
if ((rl->layflag & SCE_LAY_HALO) == 0)
continue;
- if (rl->rectf==NULL)
- continue;
+
+ rect = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, re->viewname);
+
+ if (rl==NULL || rect)
+ return;
mode= R.r.mode;
R.r.mode &= ~R_PANORAMA;
@@ -1958,7 +1983,7 @@ void add_halo_flare(Render *re)
if (har->flarec && (har->lay & rl->lay)) {
do_draw = true;
- renderflare(rr, rl->rectf, har);
+ renderflare(rr, rect, har);
}
}
diff --git a/source/blender/render/intern/source/zbuf.c b/source/blender/render/intern/source/zbuf.c
index d5d8595bf3f..b6628796e0d 100644
--- a/source/blender/render/intern/source/zbuf.c
+++ b/source/blender/render/intern/source/zbuf.c
@@ -3959,7 +3959,7 @@ static void reset_sky_speedvectors(RenderPart *pa, RenderLayer *rl, float *rectf
float *fp, *col;
int a;
- fp= RE_RenderLayerGetPass(rl, SCE_PASS_VECTOR);
+ fp = RE_RenderLayerGetPass(rl, SCE_PASS_VECTOR, R.viewname);
if (fp==NULL) return;
col= rectf+3;
@@ -4052,9 +4052,10 @@ unsigned short *zbuffer_transp_shade(RenderPart *pa, RenderLayer *rl, float *pas
/* zero alpha pixels get speed vector max again */
if (addpassflag & SCE_PASS_VECTOR)
- if (rl->layflag & SCE_LAY_SOLID)
- reset_sky_speedvectors(pa, rl, rl->acolrect?rl->acolrect:rl->rectf); /* if acolrect is set we use it */
-
+ if (rl->layflag & SCE_LAY_SOLID) {
+ float *rect = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, R.viewname);
+ reset_sky_speedvectors(pa, rl, rl->acolrect ? rl->acolrect : rect); /* if acolrect is set we use it */
+ }
/* filtered render, for now we assume only 1 filter size */
if (pa->crop) {
crop= 1;
@@ -4239,8 +4240,9 @@ unsigned short *zbuffer_transp_shade(RenderPart *pa, RenderLayer *rl, float *pas
alpha= samp_shr[a].combined[3];
if (alpha!=0.0f) {
RenderLayer *rl= ssamp.rlpp[a];
-
- addAlphaOverFloat(rl->rectf + 4*od, samp_shr[a].combined);
+
+ float *rect = RE_RenderLayerGetPass(rl, SCE_PASS_COMBINED, R.viewname);
+ addAlphaOverFloat(rect + 4 * od, samp_shr[a].combined);
add_transp_passes(rl, od, &samp_shr[a], alpha);
if (addpassflag & SCE_PASS_VECTOR)
diff --git a/source/blender/windowmanager/CMakeLists.txt b/source/blender/windowmanager/CMakeLists.txt
index 78b5d499644..86f5fff4aef 100644
--- a/source/blender/windowmanager/CMakeLists.txt
+++ b/source/blender/windowmanager/CMakeLists.txt
@@ -64,6 +64,7 @@ set(SRC
intern/wm_operators.c
intern/wm_subwindow.c
intern/wm_window.c
+ intern/wm_stereo.c
WM_api.h
WM_keymap.h
diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h
index e64d08f38e1..dbeea80ba82 100644
--- a/source/blender/windowmanager/WM_api.h
+++ b/source/blender/windowmanager/WM_api.h
@@ -103,6 +103,7 @@ void WM_window_open_temp (struct bContext *C, struct rcti *position, int type);
/* returns true if draw method is triple buffer */
bool WM_is_draw_triple(struct wmWindow *win);
+bool WM_stereo3d_enabled(struct wmWindow *win, bool only_fullscreen_test);
/* files */
diff --git a/source/blender/windowmanager/intern/wm_draw.c b/source/blender/windowmanager/intern/wm_draw.c
index 7440570f4a0..16fe9ca5142 100644
--- a/source/blender/windowmanager/intern/wm_draw.c
+++ b/source/blender/windowmanager/intern/wm_draw.c
@@ -48,9 +48,11 @@
#include "BIF_gl.h"
#include "BKE_context.h"
+#include "BKE_image.h"
#include "GHOST_C-api.h"
+#include "ED_node.h"
#include "ED_view3d.h"
#include "ED_screen.h"
@@ -165,6 +167,7 @@ static void wm_method_draw_full(bContext *C, wmWindow *win)
if (ar->swinid) {
CTX_wm_region_set(C, ar);
ED_region_do_draw(C, ar);
+ ar->do_draw = false;
wm_paintcursor_draw(C, ar);
CTX_wm_region_set(C, NULL);
}
@@ -175,12 +178,14 @@ static void wm_method_draw_full(bContext *C, wmWindow *win)
}
ED_screen_draw(win);
+ win->screen->do_draw = false;
/* draw overlapping regions */
for (ar = screen->regionbase.first; ar; ar = ar->next) {
if (ar->swinid) {
CTX_wm_menu_set(C, ar);
ED_region_do_draw(C, ar);
+ ar->do_draw = false;
CTX_wm_menu_set(C, NULL);
}
}
@@ -278,6 +283,7 @@ static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange)
if (ar->do_draw) {
CTX_wm_region_set(C, ar);
ED_region_do_draw(C, ar);
+ ar->do_draw = false;
wm_paintcursor_draw(C, ar);
CTX_wm_region_set(C, NULL);
@@ -288,6 +294,7 @@ static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange)
if (ar->swap == WIN_FRONT_OK) {
CTX_wm_region_set(C, ar);
ED_region_do_draw(C, ar);
+ ar->do_draw = false;
wm_paintcursor_draw(C, ar);
CTX_wm_region_set(C, NULL);
@@ -300,7 +307,7 @@ static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange)
}
}
}
-
+
wm_area_mark_invalid_backbuf(sa);
CTX_wm_area_set(C, NULL);
}
@@ -308,6 +315,7 @@ static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange)
/* after area regions so we can do area 'overlay' drawing */
if (screen->do_draw) {
ED_screen_draw(win);
+ win->screen->do_draw = false;
if (exchange)
screen->swap = WIN_FRONT_OK;
@@ -315,6 +323,7 @@ static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange)
else if (exchange) {
if (screen->swap == WIN_FRONT_OK) {
ED_screen_draw(win);
+ win->screen->do_draw = false;
screen->swap = WIN_BOTH_OK;
}
else if (screen->swap == WIN_BACK_OK)
@@ -328,6 +337,7 @@ static void wm_method_draw_overlap_all(bContext *C, wmWindow *win, int exchange)
if (ar->swinid && ar->do_draw) {
CTX_wm_menu_set(C, ar);
ED_region_do_draw(C, ar);
+ ar->do_draw = false;
CTX_wm_menu_set(C, NULL);
}
}
@@ -360,15 +370,6 @@ static void wm_method_draw_damage(bContext *C, wmWindow *win)
/* used. if not, multiple smaller ones are used, with */
/* worst case wasted space being 23.4% for 3x3 textures */
-#define MAX_N_TEX 3
-
-typedef struct wmDrawTriple {
- GLuint bind[MAX_N_TEX * MAX_N_TEX];
- int x[MAX_N_TEX], y[MAX_N_TEX];
- int nx, ny;
- GLenum target;
-} wmDrawTriple;
-
static void split_width(int x, int n, int *splitx, int *nx)
{
int a, newnx, waste;
@@ -405,16 +406,13 @@ static void split_width(int x, int n, int *splitx, int *nx)
}
}
-static void wm_draw_triple_free(wmWindow *win)
+static void wm_draw_triple_free(wmDrawTriple *triple)
{
- if (win->drawdata) {
- wmDrawTriple *triple = win->drawdata;
+ if (triple) {
glDeleteTextures(triple->nx * triple->ny, triple->bind);
MEM_freeN(triple);
-
- win->drawdata = NULL;
}
}
@@ -499,7 +497,7 @@ static int wm_triple_gen_textures(wmWindow *win, wmDrawTriple *triple)
return 1;
}
-static void wm_triple_draw_textures(wmWindow *win, wmDrawTriple *triple, float alpha)
+void wm_triple_draw_textures(wmWindow *win, wmDrawTriple *triple, float alpha)
{
const int winsize_x = WM_window_pixels_x(win);
const int winsize_y = WM_window_pixels_y(win);
@@ -571,7 +569,7 @@ static void wm_triple_copy_textures(wmWindow *win, wmDrawTriple *triple)
glBindTexture(triple->target, 0);
}
-static void wm_draw_region_blend(wmWindow *win, ARegion *ar)
+static void wm_draw_region_blend(wmWindow *win, ARegion *ar, wmDrawTriple *triple)
{
float fac = ED_region_blend_factor(ar);
@@ -580,7 +578,7 @@ static void wm_draw_region_blend(wmWindow *win, ARegion *ar)
wmSubWindowScissorSet(win, win->screen->mainwin, &ar->winrct, true);
glEnable(GL_BLEND);
- wm_triple_draw_textures(win, win->drawdata, 1.0f - fac);
+ wm_triple_draw_textures(win, triple, 1.0f - fac);
glDisable(GL_BLEND);
}
}
@@ -589,29 +587,46 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win)
{
wmWindowManager *wm = CTX_wm_manager(C);
wmDrawTriple *triple;
+ wmDrawData *dd, *dd_next, *drawdata = win->drawdata.first;
bScreen *screen = win->screen;
ScrArea *sa;
ARegion *ar;
- int copytex = 0;
+ int copytex = false;
- if (win->drawdata) {
+ if (drawdata && drawdata->triple) {
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
wmSubWindowSet(win, screen->mainwin);
- wm_triple_draw_textures(win, win->drawdata, 1.0f);
+ wm_triple_draw_textures(win, drawdata->triple, 1.0f);
}
else {
- win->drawdata = MEM_callocN(sizeof(wmDrawTriple), "wmDrawTriple");
+ /* we run it when we start OR when we turn stereo on */
+ if (drawdata == NULL) {
+ drawdata = MEM_callocN(sizeof(wmDrawData), "wmDrawData");
+ BLI_addhead(&win->drawdata, drawdata);
+ }
+
+ drawdata->triple = MEM_callocN(sizeof(wmDrawTriple), "wmDrawTriple");
- if (!wm_triple_gen_textures(win, win->drawdata)) {
+ if (!wm_triple_gen_textures(win, drawdata->triple)) {
wm_draw_triple_fail(C, win);
return;
}
}
- triple = win->drawdata;
+ /* it means stereo was just turned off */
+ /* note: we are removing all drawdatas that are not the first */
+ for (dd = drawdata->next; dd; dd = dd_next) {
+ dd_next = dd->next;
+
+ BLI_remlink(&win->drawdata, dd);
+ wm_draw_triple_free(dd->triple);
+ MEM_freeN(dd);
+ }
+
+ triple = drawdata->triple;
/* draw marked area regions */
for (sa = screen->areabase.first; sa; sa = sa->next) {
@@ -619,16 +634,17 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win)
for (ar = sa->regionbase.first; ar; ar = ar->next) {
if (ar->swinid && ar->do_draw) {
-
- if (ar->overlap == 0) {
+
+ if (ar->overlap == false) {
CTX_wm_region_set(C, ar);
ED_region_do_draw(C, ar);
+ ar->do_draw = false;
CTX_wm_region_set(C, NULL);
- copytex = 1;
+ copytex = true;
}
}
}
-
+
wm_area_mark_invalid_backbuf(sa);
CTX_wm_area_set(C, NULL);
}
@@ -662,14 +678,15 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win)
/* draw overlapping area regions (always like popups) */
for (sa = screen->areabase.first; sa; sa = sa->next) {
CTX_wm_area_set(C, sa);
-
+
for (ar = sa->regionbase.first; ar; ar = ar->next) {
if (ar->swinid && ar->overlap) {
CTX_wm_region_set(C, ar);
ED_region_do_draw(C, ar);
+ ar->do_draw = false;
CTX_wm_region_set(C, NULL);
-
- wm_draw_region_blend(win, ar);
+
+ wm_draw_region_blend(win, ar, triple);
}
}
@@ -678,12 +695,14 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win)
/* after area regions so we can do area 'overlay' drawing */
ED_screen_draw(win);
+ win->screen->do_draw = false;
/* draw floating regions (menus) */
for (ar = screen->regionbase.first; ar; ar = ar->next) {
if (ar->swinid) {
CTX_wm_menu_set(C, ar);
ED_region_do_draw(C, ar);
+ ar->do_draw = false;
CTX_wm_menu_set(C, NULL);
}
}
@@ -691,13 +710,188 @@ static void wm_method_draw_triple(bContext *C, wmWindow *win)
/* always draw, not only when screen tagged */
if (win->gesture.first)
wm_gesture_draw(win);
-
+
/* needs pixel coords in screen */
if (wm->drags.first) {
wm_drags_draw(C, win, NULL);
}
}
+static void wm_method_draw_triple_multiview(bContext *C, wmWindow *win, StereoViews sview)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmDrawData *drawdata;
+ wmDrawTriple *triple_data, *triple_all;
+ bScreen *screen = win->screen;
+ ScrArea *sa;
+ ARegion *ar;
+ int copytex = false;
+ int id;
+
+ /* we store the triple_data in sequence to triple_all */
+ for (id = 0; id < 2; id++) {
+ drawdata = BLI_findlink(&win->drawdata, (sview * 2) + id);
+
+ if (drawdata && drawdata->triple) {
+ if (id == 0) {
+ glClearColor(0, 0, 0, 0);
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+ wmSubWindowSet(win, screen->mainwin);
+
+ wm_triple_draw_textures(win, drawdata->triple, 1.0f);
+ }
+ }
+ else {
+ /* we run it when we start OR when we turn stereo on */
+ if (drawdata == NULL) {
+ drawdata = MEM_callocN(sizeof(wmDrawData), "wmDrawData");
+ BLI_addtail(&win->drawdata, drawdata);
+ }
+
+ drawdata->triple = MEM_callocN(sizeof(wmDrawTriple), "wmDrawTriple");
+
+ if (!wm_triple_gen_textures(win, drawdata->triple)) {
+ wm_draw_triple_fail(C, win);
+ return;
+ }
+ }
+ }
+
+ triple_data = ((wmDrawData *) BLI_findlink(&win->drawdata, sview * 2))->triple;
+ triple_all = ((wmDrawData *) BLI_findlink(&win->drawdata, (sview * 2) + 1))->triple;
+
+ /* draw marked area regions */
+ for (sa = screen->areabase.first; sa; sa = sa->next) {
+ CTX_wm_area_set(C, sa);
+
+ switch (sa->spacetype) {
+ case SPACE_IMAGE:
+ {
+ SpaceImage *sima = sa->spacedata.first;
+ sima->iuser.multiview_eye = sview;
+ break;
+ }
+ case SPACE_VIEW3D:
+ {
+ View3D *v3d = sa->spacedata.first;
+ BGpic *bgpic = v3d->bgpicbase.first;
+ v3d->multiview_eye = sview;
+ if (bgpic) bgpic->iuser.multiview_eye = sview;
+ break;
+ }
+ case SPACE_NODE:
+ {
+ SpaceNode *snode = sa->spacedata.first;
+ if ((snode->flag & SNODE_BACKDRAW) && ED_node_is_compositor(snode)) {
+ Image *ima = BKE_image_verify_viewer(IMA_TYPE_COMPOSITE, "Viewer Node");
+ ima->eye = sview;
+ }
+ break;
+ }
+ case SPACE_SEQ:
+ {
+ SpaceSeq *sseq = sa->spacedata.first;
+ sseq->multiview_eye = sview;
+ break;
+ }
+ }
+
+ /* draw marked area regions */
+ for (ar = sa->regionbase.first; ar; ar = ar->next) {
+ if (ar->swinid && ar->do_draw) {
+
+ if (ar->overlap == false) {
+ CTX_wm_region_set(C, ar);
+ ED_region_do_draw(C, ar);
+
+ if (sview == STEREO_RIGHT_ID)
+ ar->do_draw = false;
+
+ CTX_wm_region_set(C, NULL);
+ copytex = true;
+ }
+ }
+ }
+
+ wm_area_mark_invalid_backbuf(sa);
+ CTX_wm_area_set(C, NULL);
+ }
+
+ if (copytex) {
+ wmSubWindowSet(win, screen->mainwin);
+
+ wm_triple_copy_textures(win, triple_data);
+ }
+
+ if (wm->paintcursors.first) {
+ for (sa = screen->areabase.first; sa; sa = sa->next) {
+ for (ar = sa->regionbase.first; ar; ar = ar->next) {
+ if (ar->swinid && ar->swinid == screen->subwinactive) {
+ CTX_wm_area_set(C, sa);
+ CTX_wm_region_set(C, ar);
+
+ /* make region ready for draw, scissor, pixelspace */
+ ED_region_set(C, ar);
+ wm_paintcursor_draw(C, ar);
+
+ CTX_wm_region_set(C, NULL);
+ CTX_wm_area_set(C, NULL);
+ }
+ }
+ }
+
+ wmSubWindowSet(win, screen->mainwin);
+ }
+
+ /* draw overlapping area regions (always like popups) */
+ for (sa = screen->areabase.first; sa; sa = sa->next) {
+ CTX_wm_area_set(C, sa);
+
+ for (ar = sa->regionbase.first; ar; ar = ar->next) {
+ if (ar->swinid && ar->overlap) {
+ CTX_wm_region_set(C, ar);
+ ED_region_do_draw(C, ar);
+ if (sview == STEREO_RIGHT_ID)
+ ar->do_draw = false;
+ CTX_wm_region_set(C, NULL);
+
+ wm_draw_region_blend(win, ar, triple_data);
+ }
+ }
+
+ CTX_wm_area_set(C, NULL);
+ }
+
+ /* after area regions so we can do area 'overlay' drawing */
+ ED_screen_draw(win);
+ if (sview == STEREO_RIGHT_ID)
+ win->screen->do_draw = false;
+
+ /* draw floating regions (menus) */
+ for (ar = screen->regionbase.first; ar; ar = ar->next) {
+ if (ar->swinid) {
+ CTX_wm_menu_set(C, ar);
+ ED_region_do_draw(C, ar);
+ if (sview == STEREO_RIGHT_ID)
+ ar->do_draw = false;
+ CTX_wm_menu_set(C, NULL);
+ }
+ }
+
+ /* always draw, not only when screen tagged */
+ if (win->gesture.first)
+ wm_gesture_draw(win);
+
+ /* needs pixel coords in screen */
+ if (wm->drags.first) {
+ wm_drags_draw(C, win, NULL);
+ }
+
+ /* copy the ui + overlays */
+ wmSubWindowSet(win, screen->mainwin);
+ wm_triple_copy_textures(win, triple_all);
+}
/****************** main update call **********************/
@@ -848,8 +1042,16 @@ void wm_draw_update(bContext *C)
wm_method_draw_overlap_all(C, win, 0);
else if (drawmethod == USER_DRAW_OVERLAP_FLIP)
wm_method_draw_overlap_all(C, win, 1);
- else // if (drawmethod == USER_DRAW_TRIPLE)
- wm_method_draw_triple(C, win);
+ else { /* USER_DRAW_TRIPLE */
+ if ((WM_stereo3d_enabled(win, false)) == false) {
+ wm_method_draw_triple(C, win);
+ }
+ else {
+ wm_method_draw_triple_multiview(C, win, STEREO_LEFT_ID);
+ wm_method_draw_triple_multiview(C, win, STEREO_RIGHT_ID);
+ wm_method_draw_stereo3d(C, win);
+ }
+ }
win->screen->do_draw_gesture = false;
win->screen->do_draw_paintcursor = false;
@@ -862,15 +1064,23 @@ void wm_draw_update(bContext *C)
}
}
+void wm_draw_data_free(wmWindow *win)
+{
+ wmDrawData *dd;
+
+ for (dd = win->drawdata.first; dd; dd = dd->next) {
+ wm_draw_triple_free(dd->triple);
+ }
+ BLI_freelistN(&win->drawdata);
+}
+
void wm_draw_window_clear(wmWindow *win)
{
bScreen *screen = win->screen;
ScrArea *sa;
ARegion *ar;
- int drawmethod = wm_automatic_draw_method(win);
- if (drawmethod == USER_DRAW_TRIPLE)
- wm_draw_triple_free(win);
+ wm_draw_data_free(win);
/* clear screen swap flags */
if (screen) {
diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c
index 3fd1fe427c9..f3e9c416e7b 100644
--- a/source/blender/windowmanager/intern/wm_files.c
+++ b/source/blender/windowmanager/intern/wm_files.c
@@ -846,11 +846,11 @@ static ImBuf *blend_file_thumb(Scene *scene, bScreen *screen, int **thumb_pt)
if (scene->camera) {
ibuf = ED_view3d_draw_offscreen_imbuf_simple(scene, scene->camera,
BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2,
- IB_rect, OB_SOLID, false, false, false, R_ALPHAPREMUL, err_out);
+ IB_rect, OB_SOLID, false, false, false, R_ALPHAPREMUL, NULL, err_out);
}
else {
ibuf = ED_view3d_draw_offscreen_imbuf(scene, v3d, ar, BLEN_THUMB_SIZE * 2, BLEN_THUMB_SIZE * 2,
- IB_rect, false, R_ALPHAPREMUL, err_out);
+ IB_rect, false, R_ALPHAPREMUL, NULL, err_out);
}
if (ibuf) {
diff --git a/source/blender/windowmanager/intern/wm_operators.c b/source/blender/windowmanager/intern/wm_operators.c
index c2cd96165f3..499adf5704d 100644
--- a/source/blender/windowmanager/intern/wm_operators.c
+++ b/source/blender/windowmanager/intern/wm_operators.c
@@ -1268,6 +1268,13 @@ void WM_operator_properties_filesel(wmOperatorType *ot, int filter, short type,
if (flag & WM_FILESEL_RELPATH)
RNA_def_boolean(ot->srna, "relative_path", true, "Relative Path", "Select the file relative to the blend file");
+ if ((filter & FILE_TYPE_IMAGE) || (filter & FILE_TYPE_MOVIE)) {
+ prop = RNA_def_boolean(ot->srna, "show_multiview", 0, "Enable Multi-View", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna, "use_multiview", 0, "Use Multi-View", "");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ }
+
prop = RNA_def_enum(ot->srna, "display_type", file_display_items, display, "Display Type", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
@@ -4592,8 +4599,10 @@ static int redraw_timer_exec(bContext *C, wmOperator *op)
for (a = 0; a < iter; a++) {
if (type == 0) {
- if (ar)
+ if (ar) {
ED_region_do_draw(C, ar);
+ ar->do_draw = false;
+ }
}
else if (type == 1) {
wmWindow *win = CTX_wm_window(C);
@@ -4621,6 +4630,7 @@ static int redraw_timer_exec(bContext *C, wmOperator *op)
if (ar_iter->swinid) {
CTX_wm_region_set(C, ar_iter);
ED_region_do_draw(C, ar_iter);
+ ar->do_draw = false;
}
}
}
@@ -4832,6 +4842,36 @@ static void operatortype_ghash_free_cb(wmOperatorType *ot)
}
/* ******************************************************* */
+/* toggle 3D for current window, turning it fullscreen if needed */
+static void WM_OT_stereo3d_set(wmOperatorType *ot)
+{
+ PropertyRNA *prop;
+
+ ot->name = "Set Stereo 3D";
+ ot->idname = "WM_OT_set_stereo_3d";
+ ot->description = "Toggle 3D stereo support for current window (or change the display mode)";
+
+ ot->exec = wm_stereo3d_set_exec;
+ ot->invoke = wm_stereo3d_set_invoke;
+ ot->poll = WM_operator_winactive;
+ ot->ui = wm_stereo3d_set_draw;
+ ot->cancel = wm_stereo3d_set_cancel;
+
+ prop = RNA_def_enum(ot->srna, "display_mode", stereo3d_display_items, S3D_DISPLAY_ANAGLYPH, "Display Mode", "");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_enum(ot->srna, "anaglyph_type", stereo3d_anaglyph_type_items, S3D_ANAGLYPH_REDCYAN, "Anaglyph Type", "");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_enum(ot->srna, "interlace_type", stereo3d_interlace_type_items, S3D_INTERLACE_ROW, "Interlace Type", "");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna, "use_interlace_swap", false, "Swap Left/Right",
+ "Swap left and right stereo channels");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ prop = RNA_def_boolean(ot->srna, "use_sidebyside_crosseyed", false, "Cross-Eyed",
+ "Right eye should see left image and vice-versa");
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+}
+
+/* ******************************************************* */
/* called on initialize WM_exit() */
void wm_operatortype_free(void)
{
@@ -4873,6 +4913,7 @@ void wm_operatortype_init(void)
WM_operatortype_append(WM_OT_call_menu);
WM_operatortype_append(WM_OT_call_menu_pie);
WM_operatortype_append(WM_OT_radial_control);
+ WM_operatortype_append(WM_OT_stereo3d_set);
#if defined(WIN32)
WM_operatortype_append(WM_OT_console_toggle);
#endif
diff --git a/source/blender/windowmanager/intern/wm_stereo.c b/source/blender/windowmanager/intern/wm_stereo.c
new file mode 100644
index 00000000000..5d7b11272c6
--- /dev/null
+++ b/source/blender/windowmanager/intern/wm_stereo.c
@@ -0,0 +1,534 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2015 by Blender Foundation
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Dalai Felinto
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/windowmanager/intern/wm_stereo.c
+ * \ingroup wm
+ */
+
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "DNA_listBase.h"
+
+#include "RNA_access.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_listbase.h"
+#include "BLI_rect.h"
+#include "BLI_utildefines.h"
+
+#include "BIF_gl.h"
+
+#include "BKE_context.h"
+#include "BKE_global.h"
+#include "BKE_report.h"
+
+#include "GHOST_C-api.h"
+
+#include "ED_screen.h"
+
+#include "GPU_glew.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+#include "wm.h"
+#include "wm_draw.h" /* wmDrawTriple */
+#include "wm_window.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+static void wm_method_draw_stereo3d_pageflip(wmWindow *win)
+{
+ wmDrawData *drawdata;
+ int view;
+
+ for (view = 0; view < 2; view ++) {
+ drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1);
+
+ if (view == STEREO_LEFT_ID)
+ glDrawBuffer(GL_BACK_LEFT);
+ else //STEREO_RIGHT_ID
+ glDrawBuffer(GL_BACK_RIGHT);
+
+ wm_triple_draw_textures(win, drawdata->triple, 1.0f);
+ }
+}
+
+static GLuint left_interlace_mask[32];
+static GLuint right_interlace_mask[32];
+static enum eStereo3dInterlaceType interlace_prev_type = -1;
+static char interlace_prev_swap = -1;
+
+static void wm_interlace_masks_create(wmWindow *win)
+{
+ GLuint pattern;
+ char i;
+ bool swap = (win->stereo3d_format->flag & S3D_INTERLACE_SWAP) != 0;
+ enum eStereo3dInterlaceType interlace_type = win->stereo3d_format->interlace_type;
+
+ if (interlace_prev_type == interlace_type && interlace_prev_swap == swap)
+ return;
+
+ switch (interlace_type) {
+ case S3D_INTERLACE_ROW:
+ pattern = 0x00000000;
+ pattern = swap ? ~pattern : pattern;
+ for (i = 0; i < 32; i += 2) {
+ left_interlace_mask[i] = pattern;
+ right_interlace_mask[i] = ~pattern;
+ }
+ for (i = 1; i < 32; i += 2) {
+ left_interlace_mask[i] = ~pattern;
+ right_interlace_mask[i] = pattern;
+ }
+ break;
+ case S3D_INTERLACE_COLUMN:
+ pattern = 0x55555555;
+ pattern = swap ? ~pattern : pattern;
+ for (i = 0; i < 32; i++) {
+ left_interlace_mask[i] = pattern;
+ right_interlace_mask[i] = ~pattern;
+ }
+ break;
+ case S3D_INTERLACE_CHECKERBOARD:
+ default:
+ pattern = 0x55555555;
+ pattern = swap ? ~pattern : pattern;
+ for (i = 0; i < 32; i += 2) {
+ left_interlace_mask[i] = pattern;
+ right_interlace_mask[i] = ~pattern;
+ }
+ for (i = 1; i < 32; i += 2) {
+ left_interlace_mask[i] = ~pattern;
+ right_interlace_mask[i] = pattern;
+ }
+ break;
+ }
+ interlace_prev_type = interlace_type;
+ interlace_prev_swap = swap;
+}
+
+static void wm_method_draw_stereo3d_interlace(wmWindow *win)
+{
+ wmDrawData *drawdata;
+ int view;
+
+ wm_interlace_masks_create(win);
+
+ for (view = 0; view < 2; view ++) {
+ drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1);
+
+ glEnable(GL_POLYGON_STIPPLE);
+ glPolygonStipple(view ? (GLubyte *) right_interlace_mask : (GLubyte *) left_interlace_mask);
+
+ wm_triple_draw_textures(win, drawdata->triple, 1.0f);
+ glDisable(GL_POLYGON_STIPPLE);
+ }
+}
+
+static void wm_method_draw_stereo3d_anaglyph(wmWindow *win)
+{
+ wmDrawData *drawdata;
+ int view, bit;
+
+ for (view = 0; view < 2; view ++) {
+ drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1);
+
+ bit = view + 1;
+ switch (win->stereo3d_format->anaglyph_type) {
+ case S3D_ANAGLYPH_REDCYAN:
+ glColorMask((1&bit) ? GL_TRUE : GL_FALSE,
+ (2&bit) ? GL_TRUE : GL_FALSE,
+ (2&bit) ? GL_TRUE : GL_FALSE,
+ GL_FALSE);
+ break;
+ case S3D_ANAGLYPH_GREENMAGENTA:
+ glColorMask((2&bit) ? GL_TRUE : GL_FALSE,
+ (1&bit) ? GL_TRUE : GL_FALSE,
+ (2&bit) ? GL_TRUE : GL_FALSE,
+ GL_FALSE);
+ break;
+ case S3D_ANAGLYPH_YELLOWBLUE:
+ glColorMask((1&bit) ? GL_TRUE : GL_FALSE,
+ (1&bit) ? GL_TRUE : GL_FALSE,
+ (2&bit) ? GL_TRUE : GL_FALSE,
+ GL_FALSE);
+ break;
+ }
+
+ wm_triple_draw_textures(win, drawdata->triple, 1.0f);
+
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ }
+}
+
+static void wm_method_draw_stereo3d_sidebyside(wmWindow *win)
+{
+ wmDrawData *drawdata;
+ wmDrawTriple *triple;
+ float halfx, halfy, ratiox, ratioy;
+ int x, y, offx, offy;
+ float alpha = 1.0f;
+ int view;
+ int soffx;
+ bool cross_eyed = (win->stereo3d_format->flag & S3D_SIDEBYSIDE_CROSSEYED) != 0;
+
+ for (view = 0; view < 2; view ++) {
+ drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1);
+ triple = drawdata->triple;
+
+ soffx = WM_window_pixels_x(win) * 0.5f;
+ if (view == STEREO_LEFT_ID) {
+ if (!cross_eyed)
+ soffx = 0;
+ }
+ else { //RIGHT_LEFT_ID
+ if (cross_eyed)
+ soffx = 0;
+ }
+
+ glEnable(triple->target);
+
+ for (y = 0, offy = 0; y < triple->ny; offy += triple->y[y], y++) {
+ for (x = 0, offx = 0; x < triple->nx; offx += triple->x[x], x++) {
+ const int sizex = triple->x[x];
+ const int sizey = triple->y[y];
+
+ /* wmOrtho for the screen has this same offset */
+ ratiox = sizex;
+ ratioy = sizey;
+ halfx = GLA_PIXEL_OFS;
+ halfy = GLA_PIXEL_OFS;
+
+ /* texture rectangle has unnormalized coordinates */
+ if (triple->target == GL_TEXTURE_2D) {
+ ratiox /= triple->x[x];
+ ratioy /= triple->y[y];
+ halfx /= triple->x[x];
+ halfy /= triple->y[y];
+ }
+
+ glBindTexture(triple->target, triple->bind[x + y * triple->nx]);
+
+ glColor4f(1.0f, 1.0f, 1.0f, alpha);
+ glBegin(GL_QUADS);
+ glTexCoord2f(halfx, halfy);
+ glVertex2f(soffx + (offx * 0.5f), offy);
+
+ glTexCoord2f(ratiox + halfx, halfy);
+ glVertex2f(soffx + ((offx + sizex) * 0.5f), offy);
+
+ glTexCoord2f(ratiox + halfx, ratioy + halfy);
+ glVertex2f(soffx + ((offx + sizex) * 0.5f), offy + sizey);
+
+ glTexCoord2f(halfx, ratioy + halfy);
+ glVertex2f(soffx + (offx * 0.5f), offy + sizey);
+ glEnd();
+ }
+ }
+
+ glBindTexture(triple->target, 0);
+ glDisable(triple->target);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ }
+}
+
+static void wm_method_draw_stereo3d_topbottom(wmWindow *win)
+{
+ wmDrawData *drawdata;
+ wmDrawTriple *triple;
+ float halfx, halfy, ratiox, ratioy;
+ int x, y, offx, offy;
+ float alpha = 1.0f;
+ int view;
+ int soffy;
+
+ for (view = 0; view < 2; view ++) {
+ drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1);
+ triple = drawdata->triple;
+
+ if (view == STEREO_LEFT_ID) {
+ soffy = WM_window_pixels_y(win) * 0.5f;
+ }
+ else { /* STEREO_RIGHT_ID */
+ soffy = 0;
+ }
+
+ glEnable(triple->target);
+
+ for (y = 0, offy = 0; y < triple->ny; offy += triple->y[y], y++) {
+ for (x = 0, offx = 0; x < triple->nx; offx += triple->x[x], x++) {
+ const int sizex = triple->x[x];
+ const int sizey = triple->y[y];
+
+ /* wmOrtho for the screen has this same offset */
+ ratiox = sizex;
+ ratioy = sizey;
+ halfx = GLA_PIXEL_OFS;
+ halfy = GLA_PIXEL_OFS;
+
+ /* texture rectangle has unnormalized coordinates */
+ if (triple->target == GL_TEXTURE_2D) {
+ ratiox /= triple->x[x];
+ ratioy /= triple->y[y];
+ halfx /= triple->x[x];
+ halfy /= triple->y[y];
+ }
+
+ glBindTexture(triple->target, triple->bind[x + y * triple->nx]);
+
+ glColor4f(1.0f, 1.0f, 1.0f, alpha);
+ glBegin(GL_QUADS);
+ glTexCoord2f(halfx, halfy);
+ glVertex2f(offx, soffy + (offy * 0.5f));
+
+ glTexCoord2f(ratiox + halfx, halfy);
+ glVertex2f(offx + sizex, soffy + (offy * 0.5f));
+
+ glTexCoord2f(ratiox + halfx, ratioy + halfy);
+ glVertex2f(offx + sizex, soffy + ((offy + sizey) * 0.5f));
+
+ glTexCoord2f(halfx, ratioy + halfy);
+ glVertex2f(offx, soffy + ((offy + sizey) * 0.5f));
+ glEnd();
+ }
+ }
+
+ glBindTexture(triple->target, 0);
+ glDisable(triple->target);
+ glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
+ }
+}
+
+void wm_method_draw_stereo3d(const bContext *UNUSED(C), wmWindow *win)
+{
+ switch (win->stereo3d_format->display_mode) {
+ case S3D_DISPLAY_ANAGLYPH:
+ wm_method_draw_stereo3d_anaglyph(win);
+ break;
+ case S3D_DISPLAY_INTERLACE:
+ wm_method_draw_stereo3d_interlace(win);
+ break;
+ case S3D_DISPLAY_PAGEFLIP:
+ wm_method_draw_stereo3d_pageflip(win);
+ break;
+ case S3D_DISPLAY_SIDEBYSIDE:
+ wm_method_draw_stereo3d_sidebyside(win);
+ break;
+ case S3D_DISPLAY_TOPBOTTOM:
+ wm_method_draw_stereo3d_topbottom(win);
+ break;
+ default:
+ break;
+ }
+}
+
+static bool wm_stereo3d_is_fullscreen_required(eStereoDisplayMode stereo_display)
+{
+ return ELEM(stereo_display,
+ S3D_DISPLAY_SIDEBYSIDE,
+ S3D_DISPLAY_TOPBOTTOM,
+ S3D_DISPLAY_PAGEFLIP);
+}
+
+bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check)
+{
+ bScreen *screen = win->screen;
+
+ if ((skip_stereo3d_check == false) && (ED_screen_stereo3d_required(screen) == false))
+ return false;
+
+ if (wm_stereo3d_is_fullscreen_required(win->stereo3d_format->display_mode))
+ return WM_window_is_fullscreen(win);
+
+ return true;
+}
+
+/************************** Stereo 3D operator **********************************/
+typedef struct Stereo3dData {
+ Stereo3dFormat stereo3d_format;
+} Stereo3dData;
+
+static bool wm_stereo3d_set_properties(bContext *C, wmOperator *op)
+{
+ wmWindow *win = CTX_wm_window(C);
+ Stereo3dFormat *s3d = win->stereo3d_format;
+ PropertyRNA *prop;
+ bool is_set = false;
+
+ prop = RNA_struct_find_property(op->ptr, "display_mode");
+ if (RNA_property_is_set(op->ptr, prop)) {
+ s3d->display_mode = RNA_property_enum_get(op->ptr, prop);
+ is_set = true;
+ }
+
+ prop = RNA_struct_find_property(op->ptr, "anaglyph_type");
+ if (RNA_property_is_set(op->ptr, prop)) {
+ s3d->anaglyph_type = RNA_property_enum_get(op->ptr, prop);
+ is_set = true;
+ }
+
+ prop = RNA_struct_find_property(op->ptr, "interlace_type");
+ if (RNA_property_is_set(op->ptr, prop)) {
+ s3d->interlace_type = RNA_property_enum_get(op->ptr, prop);
+ is_set = true;
+ }
+
+ prop = RNA_struct_find_property(op->ptr, "use_interlace_swap");
+ if (RNA_property_is_set(op->ptr, prop)) {
+ if (RNA_property_boolean_get(op->ptr, prop))
+ s3d->flag |= S3D_INTERLACE_SWAP;
+ else
+ s3d->flag &= ~S3D_INTERLACE_SWAP;
+ is_set = true;
+ }
+
+ prop = RNA_struct_find_property(op->ptr, "use_sidebyside_crosseyed");
+ if (RNA_property_is_set(op->ptr, prop)) {
+ if (RNA_property_boolean_get(op->ptr, prop))
+ s3d->flag |= S3D_SIDEBYSIDE_CROSSEYED;
+ else
+ s3d->flag &= ~S3D_SIDEBYSIDE_CROSSEYED;
+ is_set = true;
+ }
+
+ return is_set;
+}
+
+static void wm_stereo3d_set_init(bContext *C, wmOperator *op)
+{
+ Stereo3dData *s3dd;
+ wmWindow *win = CTX_wm_window(C);
+
+ op->customdata = s3dd = MEM_callocN(sizeof(Stereo3dData), __func__);
+
+ /* store the original win stereo 3d settings in case of cancel */
+ s3dd->stereo3d_format = *win->stereo3d_format;
+}
+
+int wm_stereo3d_set_exec(bContext *C, wmOperator *op)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ wmWindow *win = CTX_wm_window(C);
+ const bool is_fullscreen = WM_window_is_fullscreen(win);
+
+ if (G.background)
+ return OPERATOR_CANCELLED;
+
+ /* pagelfip requires a new window to be created with the proper OS flags */
+ if (win->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) {
+ if (wm_window_duplicate_exec(C, op) == OPERATOR_FINISHED) {
+ wm_window_close(C, wm, win);
+ win = wm->windows.last;
+ }
+ else {
+ BKE_reportf(op->reports, RPT_ERROR,
+ "Fail to create a window compatible with time sequential (page-flip) display method");
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ if (wm_stereo3d_is_fullscreen_required(win->stereo3d_format->display_mode)) {
+ if (!is_fullscreen) {
+ wm_window_fullscreen_toggle_exec(C, op);
+ }
+ }
+
+ if (op->customdata) {
+ MEM_freeN(op->customdata);
+ }
+
+ WM_event_add_notifier(C, NC_WINDOW, NULL);
+ return OPERATOR_FINISHED;
+}
+
+int wm_stereo3d_set_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ wm_stereo3d_set_init(C, op);
+
+ if (wm_stereo3d_set_properties(C, op))
+ return wm_stereo3d_set_exec(C, op);
+ else
+ return WM_operator_props_dialog_popup(C, op, 250, 100);
+}
+
+void wm_stereo3d_set_draw(bContext *C, wmOperator *op)
+{
+ wmWindow *win = CTX_wm_window(C);
+ Stereo3dFormat *stereo3d_format;
+ PointerRNA stereo3d_format_ptr;
+ uiLayout *layout = op->layout;
+ uiLayout *col;
+
+ stereo3d_format = win->stereo3d_format;
+ RNA_pointer_create(NULL, &RNA_Stereo3dDisplay, stereo3d_format, &stereo3d_format_ptr);
+
+ col = uiLayoutColumn(layout, false);
+ uiItemR(col, &stereo3d_format_ptr, "display_mode", 0, NULL, ICON_NONE);
+
+ switch (stereo3d_format->display_mode) {
+ case S3D_DISPLAY_ANAGLYPH:
+ {
+ uiItemR(col, &stereo3d_format_ptr, "anaglyph_type", 0, NULL, ICON_NONE);
+ break;
+ }
+ case S3D_DISPLAY_INTERLACE:
+ {
+ uiItemR(col, &stereo3d_format_ptr, "interlace_type", 0, NULL, ICON_NONE);
+ uiItemR(col, &stereo3d_format_ptr, "use_interlace_swap", 0, NULL, ICON_NONE);
+ break;
+ }
+ case S3D_DISPLAY_SIDEBYSIDE:
+ {
+ uiItemR(col, &stereo3d_format_ptr, "use_sidebyside_crosseyed", 0, NULL, ICON_NONE);
+ /* fall-through */
+ }
+ case S3D_DISPLAY_PAGEFLIP:
+ case S3D_DISPLAY_TOPBOTTOM:
+ default:
+ {
+ break;
+ }
+ }
+}
+
+void wm_stereo3d_set_cancel(bContext *C, wmOperator *op)
+{
+ Stereo3dData *s3dd = op->customdata;
+ wmWindow *win = CTX_wm_window(C);
+
+ /* roll back to the original */
+ if (win) {
+ *win->stereo3d_format = s3dd->stereo3d_format;
+ }
+
+ MEM_freeN(op->customdata);
+ op->customdata = NULL;
+}
diff --git a/source/blender/windowmanager/intern/wm_window.c b/source/blender/windowmanager/intern/wm_window.c
index 1af20e6036d..4b98f8bf71d 100644
--- a/source/blender/windowmanager/intern/wm_window.c
+++ b/source/blender/windowmanager/intern/wm_window.c
@@ -206,12 +206,13 @@ void wm_window_free(bContext *C, wmWindowManager *wm, wmWindow *win)
wm_event_free_all(win);
wm_subwindows_free(win);
-
- if (win->drawdata)
- MEM_freeN(win->drawdata);
-
+
+ wm_draw_data_free(win);
+
wm_ghostwindow_destroy(win);
-
+
+ MEM_freeN(win->stereo3d_format);
+
MEM_freeN(win);
}
@@ -236,6 +237,8 @@ wmWindow *wm_window_new(bContext *C)
BLI_addtail(&wm->windows, win);
win->winid = find_free_winid(wm);
+ win->stereo3d_format = MEM_callocN(sizeof(Stereo3dFormat), "Stereo 3D Format (window)");
+
return win;
}
@@ -259,8 +262,11 @@ wmWindow *wm_window_copy(bContext *C, wmWindow *winorig)
win->screen->do_draw = true;
win->drawmethod = U.wmdrawmethod;
- win->drawdata = NULL;
-
+
+ BLI_listbase_clear(&win->drawdata);
+
+ *win->stereo3d_format = *winorig->stereo3d_format;
+
return win;
}
@@ -367,6 +373,10 @@ static void wm_window_add_ghostwindow(wmWindowManager *wm, const char *title, wm
glSettings.numOfAASamples = multisamples;
+ /* a new window is created when pageflip mode is required for a window */
+ if (win->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP)
+ glSettings.flags |= GHOST_glStereoVisual;
+
if (!(U.uiflag2 & USER_OPENGL_NO_WARN_SUPPORT))
glSettings.flags |= GHOST_glWarnSupport;
@@ -519,8 +529,7 @@ wmWindow *WM_window_open(bContext *C, const rcti *rect)
win->sizey = BLI_rcti_size_y(rect);
win->drawmethod = U.wmdrawmethod;
- win->drawdata = NULL;
-
+
WM_check(C);
return win;
diff --git a/source/blender/windowmanager/wm.h b/source/blender/windowmanager/wm.h
index de04129a0ef..bc777b74b51 100644
--- a/source/blender/windowmanager/wm.h
+++ b/source/blender/windowmanager/wm.h
@@ -76,6 +76,13 @@ void wm_autosave_delete(void);
void wm_autosave_read(bContext *C, struct ReportList *reports);
void wm_autosave_location(char *filepath);
+/* wm_stereo.c */
+void wm_method_draw_stereo3d(const bContext *C, wmWindow *win);
+int wm_stereo3d_set_exec(bContext *C, wmOperator *op);
+int wm_stereo3d_set_invoke(bContext *C, wmOperator *op, const wmEvent *event);
+void wm_stereo3d_set_draw(bContext *C, wmOperator *op);
+void wm_stereo3d_set_cancel(bContext *C, wmOperator *op);
+
/* init operator properties */
void wm_open_init_load_ui(wmOperator *op, bool use_prefs);
void wm_open_init_use_scripts(wmOperator *op, bool use_prefs);
diff --git a/source/blender/windowmanager/wm_draw.h b/source/blender/windowmanager/wm_draw.h
index 3d72fe17c79..5dc52b2e4fb 100644
--- a/source/blender/windowmanager/wm_draw.h
+++ b/source/blender/windowmanager/wm_draw.h
@@ -32,6 +32,23 @@
#ifndef __WM_DRAW_H__
#define __WM_DRAW_H__
+#include "GPU_glew.h"
+
+
+#define MAX_N_TEX 3
+
+typedef struct wmDrawTriple {
+ GLuint bind[MAX_N_TEX * MAX_N_TEX];
+ int x[MAX_N_TEX], y[MAX_N_TEX];
+ int nx, ny;
+ GLenum target;
+} wmDrawTriple;
+
+typedef struct wmDrawData {
+ struct wmDrawData *next, *prev;
+ wmDrawTriple *triple;
+} wmDrawData;
+
struct bContext;
struct wmWindow;
struct ARegion;
@@ -43,5 +60,9 @@ void wm_draw_region_clear (struct wmWindow *win, struct ARegion *ar);
void wm_tag_redraw_overlay (struct wmWindow *win, struct ARegion *ar);
+void wm_triple_draw_textures (struct wmWindow *win, struct wmDrawTriple *triple, float alpha);
+
+void wm_draw_data_free (struct wmWindow *win);
+
#endif /* __WM_DRAW_H__ */
diff --git a/source/blenderplayer/bad_level_call_stubs/stubs.c b/source/blenderplayer/bad_level_call_stubs/stubs.c
index 893eae8485a..8fbc3c5be9c 100644
--- a/source/blenderplayer/bad_level_call_stubs/stubs.c
+++ b/source/blenderplayer/bad_level_call_stubs/stubs.c
@@ -221,12 +221,20 @@ void EDBM_mesh_normals_update(struct BMEditMesh *em) RET_NONE
void *g_system;
bool EDBM_mtexpoly_check(struct BMEditMesh *em) RET_ZERO
-float *RE_RenderLayerGetPass(struct RenderLayer *rl, int passtype) RET_NULL
+float *RE_RenderLayerGetPass(volatile struct RenderLayer *rl, int passtype, const char *viewname) RET_NULL
float RE_filter_value(int type, float x) RET_ZERO
struct RenderLayer *RE_GetRenderLayer(struct RenderResult *rr, const char *name) RET_NULL
void RE_init_texture_rng() RET_NONE
void RE_exit_texture_rng() RET_NONE
+float *RE_RenderViewGetRectf(struct RenderResult *rr, int view_id) {STUB_ASSERT(0); return (float *) NULL;}
+float *RE_RenderViewGetRectz(struct RenderResult *rr, int view_id) {STUB_ASSERT(0); return (float *) NULL;}
+bool RE_layers_have_name(struct RenderResult *result) {STUB_ASSERT(0); return 0;}
+void RE_engine_active_view_set(struct RenderEngine *engine, const char *viewname) {STUB_ASSERT(0);}
+void RE_engine_get_camera_model_matrix(struct RenderEngine *engine, struct Object *camera, float *r_modelmat) {STUB_ASSERT(0);}
+float RE_engine_get_camera_shift_x(struct RenderEngine *engine, struct Object *camera) RET_ZERO
+void RE_SetActiveRenderView(struct Render *re, const char *viewname) {STUB_ASSERT(0);}
+
/* zbuf.c stub */
void antialias_tagbuf(int xsize, int ysize, char *rectmove) RET_NONE
void RE_zbuf_accumulate_vecblur(struct NodeBlurData *nbd, int xsize, int ysize, float *newrect, float *imgrect, float *vecbufrect, float *zbufrect) RET_NONE
@@ -250,6 +258,7 @@ void RE_sample_material_color(struct Material *mat, float color[3], float *alpha
/* nodes */
struct Render *RE_GetRender(const char *name) RET_NULL
+struct Object *RE_GetCamera(struct Render *re) RET_NULL
float RE_lamp_get_data(struct ShadeInput *shi, struct Object *lamp_obj, float col[4], float lv[3], float *dist, float shadow[4]) RET_ZERO
/* blenkernel */
@@ -584,9 +593,10 @@ void uiTemplateColormanagedViewSettings(struct uiLayout *layout, struct bContext
void uiTemplateComponentMenu(struct uiLayout *layout, struct PointerRNA *ptr, const char *propname, const char *name) RET_NONE
void uiTemplateNodeSocket(struct uiLayout *layout, struct bContext *C, float *color) RET_NONE
void uiTemplatePalette(struct uiLayout *layout, struct PointerRNA *ptr, const char *propname, int color) RET_NONE
+void uiTemplateImageStereo3d(struct uiLayout *layout, struct PointerRNA *stereo3d_format_ptr) RET_NONE
/* rna render */
-struct RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h, const char *layername) RET_NULL
+struct RenderResult *RE_engine_begin_result(RenderEngine *engine, int x, int y, int w, int h, const char *layername, const char *viewname) RET_NULL
struct RenderResult *RE_AcquireResultRead(struct Render *re) RET_NULL
struct RenderResult *RE_AcquireResultWrite(struct Render *re) RET_NULL
struct RenderStats *RE_GetStats(struct Render *re) RET_NULL
@@ -598,7 +608,7 @@ void RE_engine_end_result(RenderEngine *engine, struct RenderResult *result, int
void RE_engine_update_stats(RenderEngine *engine, const char *stats, const char *info) RET_NONE
void RE_layer_load_from_file(struct RenderLayer *layer, struct ReportList *reports, const char *filename, int x, int y) RET_NONE
void RE_result_load_from_file(struct RenderResult *result, struct ReportList *reports, const char *filename) RET_NONE
-void RE_AcquireResultImage(struct Render *re, struct RenderResult *rr) RET_NONE
+void RE_AcquireResultImage(struct Render *re, struct RenderResult *rr, const int view_id) RET_NONE
void RE_ReleaseResult(struct Render *re) RET_NONE
void RE_ReleaseResultImage(struct Render *re) RET_NONE
int RE_engine_test_break(struct RenderEngine *engine) RET_ZERO
@@ -713,6 +723,11 @@ struct CCLDeviceInfo *CCL_compute_device_list(int opencl) RET_NULL
/* compositor */
void COM_execute(RenderData *rd, Scene *scene, bNodeTree *editingtree, int rendering,
- const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings) RET_NONE
+ const ColorManagedViewSettings *viewSettings, const ColorManagedDisplaySettings *displaySettings,
+ const char *viewName) RET_NONE
+
+/*multiview*/
+bool RE_RenderResult_is_stereo(RenderResult *res) RET_ZERO
+void uiTemplateImageViews(uiLayout *layout, struct PointerRNA *imfptr) RET_NONE
#endif // WITH_GAMEENGINE