A number of improvements for the file output node(s).
authorLukas Toenne <lukas.toenne@googlemail.com>
Thu, 1 Mar 2012 07:56:15 +0000 (07:56 +0000)
committerLukas Toenne <lukas.toenne@googlemail.com>
Thu, 1 Mar 2012 07:56:15 +0000 (07:56 +0000)
1) Old CMP_NODE_OUTPUT_FILE and CMP_NODE_OUTPUT_MULTI_FILE have been merged,
only CMP_NODE_OUTPUT_FILE remains. All functions renamed accordingly.

2) do_versions code for converting single-file output nodes into multi-file
output nodes. If a Z buffer input is used, the node is made into a multilayer
exr with two inputs. (see below). Also re-identifies multi-file output nodes
with the CMP_NODE_OUTPUT_FILE type.

3) "Global" format is stored in node now. By default this overrides any
per-socket settings.

4) Multilayer EXR output implemented. When M.EXR format is selected for node
format, all socket format details are ignored. Socket names are used for layer
names.

5) Input buffer types are used as-is when possible, i.e. stored as B/W, RGB or
RGBA. In regular file output the format dictates the number of actual channels,
so the CompBuf is typechecked to the right type first. For multilayer EXR the
number of channels is more flexible, so an input buffer will store only the
channels it actually uses.

6) The editor socket type is updated from linked sockets as an indicator of the
actual data written to files. This may not be totally accurate for regular file
output though, due to restrictions of format setting.

16 files changed:
source/blender/blenkernel/BKE_blender.h
source/blender/blenkernel/BKE_node.h
source/blender/blenkernel/intern/node.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/writefile.c
source/blender/editors/space_node/drawnode.c
source/blender/editors/space_node/node_edit.c
source/blender/editors/space_node/node_intern.h
source/blender/editors/space_node/node_ops.c
source/blender/makesdna/DNA_node_types.h
source/blender/makesrna/RNA_access.h
source/blender/makesrna/intern/rna_nodetree.c
source/blender/makesrna/intern/rna_nodetree_types.h
source/blender/nodes/CMakeLists.txt
source/blender/nodes/NOD_composite.h
source/blender/nodes/composite/nodes/node_composite_outputFile.c

index 1c661b3..392642b 100644 (file)
@@ -42,7 +42,7 @@ extern "C" {
  * and keep comment above the defines.
  * Use STRINGIFY() rather than defining with quotes */
 #define BLENDER_VERSION                        262
-#define BLENDER_SUBVERSION             0
+#define BLENDER_SUBVERSION             1
 
 #define BLENDER_MINVERSION             250
 #define BLENDER_MINSUBVERSION  0
index 753161d..cb161b2 100644 (file)
@@ -364,6 +364,7 @@ int                         nodeUpdateID(struct bNodeTree *ntree, struct ID *id);
 void                   nodeFreePreview(struct bNode *node);
 
 int                            nodeSocketIsHidden(struct bNodeSocket *sock);
+void                   nodeSocketSetType(struct bNodeSocket *sock, int type);
 
 /* ************** NODE TYPE ACCESS *************** */
 
@@ -636,7 +637,7 @@ void                        ntreeGPUMaterialNodes(struct bNodeTree *ntree, struct GPUMaterial *mat);
 #define CMP_NODE_TRANSFORM     264
 #define CMP_NODE_MOVIEDISTORTION       265
 #define CMP_NODE_DOUBLEEDGEMASK    266
-#define CMP_NODE_OUTPUT_MULTI_FILE     267
+#define CMP_NODE_OUTPUT_MULTI_FILE__DEPRECATED 267     /* DEPRECATED multi file node has been merged into regular CMP_NODE_OUTPUT_FILE */
 
 #define CMP_NODE_GLARE         301
 #define CMP_NODE_TONEMAP       302
@@ -676,8 +677,9 @@ void ntreeCompositTagGenerators(struct bNodeTree *ntree);
 void ntreeCompositForceHidden(struct bNodeTree *ntree, struct Scene *scene);
 void ntreeCompositClearTags(struct bNodeTree *ntree);
 
-void ntreeCompositOutputMultiFileAddSocket(struct bNodeTree *ntree, struct bNode *node, struct ImageFormatData *im_format);
-int ntreeCompositOutputMultiFileRemoveActiveSocket(struct bNodeTree *ntree, struct bNode *node);
+struct bNodeSocket *ntreeCompositOutputFileAddSocket(struct bNodeTree *ntree, struct bNode *node,
+                                                     const char *name, struct ImageFormatData *im_format);
+int ntreeCompositOutputFileRemoveActiveSocket(struct bNodeTree *ntree, struct bNode *node);
 
 /* ************** TEXTURE NODES *************** */
 
index c394a53..99edd4b 100644 (file)
@@ -1369,6 +1369,19 @@ int nodeSocketIsHidden(bNodeSocket *sock)
        return ((sock->flag & (SOCK_HIDDEN | SOCK_AUTO_HIDDEN | SOCK_UNAVAIL)) != 0);
 }
 
+void nodeSocketSetType(bNodeSocket *sock, int type)
+{
+       int old_type = sock->type;
+       void *old_default_value = sock->default_value;
+       
+       sock->type = type;
+       
+       sock->default_value = node_socket_make_default_value(sock->type);
+       node_socket_init_default_value(type, sock->default_value);
+       node_socket_convert_default_value(sock->type, sock->default_value, old_type, old_default_value);
+       node_socket_free_default_value(old_type, old_default_value);
+}
+
 /* ************** dependency stuff *********** */
 
 /* node is guaranteed to be not checked before */
@@ -1839,7 +1852,6 @@ static void registerCompositNodes(bNodeTreeType *ttype)
        register_node_type_cmp_viewer(ttype);
        register_node_type_cmp_splitviewer(ttype);
        register_node_type_cmp_output_file(ttype);
-       register_node_type_cmp_output_multi_file(ttype);
        register_node_type_cmp_view_levels(ttype);
        
        register_node_type_cmp_curve_rgb(ttype);
index 180ccad..8abdc97 100644 (file)
@@ -7685,6 +7685,77 @@ static void do_versions_nodetree_socket_auto_hidden_flags_2_62(bNodeTree *ntree)
        }
 }
 
+static void do_versions_nodetree_multi_file_output_format_2_62_1(Scene *sce, bNodeTree *ntree)
+{
+       bNode *node;
+       bNodeSocket *sock;
+       
+       for (node=ntree->nodes.first; node; node=node->next) {
+               if (node->type==CMP_NODE_OUTPUT_FILE) {
+                       /* previous CMP_NODE_OUTPUT_FILE nodes get converted to multi-file outputs */
+                       NodeImageFile *old_data = node->storage;
+                       NodeImageMultiFile *nimf= MEM_callocN(sizeof(NodeImageMultiFile), "node image multi file");
+                       bNodeSocket *old_image = BLI_findlink(&node->inputs, 0);
+                       bNodeSocket *old_z = BLI_findlink(&node->inputs, 1);
+                       bNodeSocket *sock;
+                       
+                       node->storage= nimf;
+                       
+                       BLI_strncpy(nimf->base_path, old_data->name, sizeof(nimf->base_path));
+                       nimf->format = old_data->im_format;
+                       
+                       /* if z buffer is saved, change the image type to multilayer exr.
+                        * XXX this is slightly messy, Z buffer was ignored before for anything but EXR and IRIS ...
+                        * i'm just assuming here that IRIZ means IRIS with z buffer ...
+                        */
+                       if (ELEM(old_data->im_format.imtype, R_IMF_IMTYPE_IRIZ, R_IMF_IMTYPE_OPENEXR)) {
+                               nimf->format.imtype = R_IMF_IMTYPE_MULTILAYER;
+                               sock = ntreeCompositOutputFileAddSocket(ntree, node, old_image->name, &nimf->format);
+                               if (old_image->link) {
+                                       old_image->link->tosock = sock;
+                                       sock->link = old_image->link;
+                               }
+                               sock = ntreeCompositOutputFileAddSocket(ntree, node, old_z->name, &nimf->format);
+                               if (old_z->link) {
+                                       old_z->link->tosock = sock;
+                                       sock->link = old_z->link;
+                               }
+                       }
+                       else {
+                               /* saves directly to base path, which is the old image output path */
+                               sock = ntreeCompositOutputFileAddSocket(ntree, node, "", &nimf->format);
+                               if (old_image->link) {
+                                       old_image->link->tosock = sock;
+                                       sock->link = old_image->link;
+                               }
+                       }
+                       
+                       nodeRemoveSocket(ntree, node, old_image);
+                       nodeRemoveSocket(ntree, node, old_z);
+                       MEM_freeN(old_data);
+               }
+               else if (node->type==CMP_NODE_OUTPUT_MULTI_FILE__DEPRECATED) {
+                       NodeImageMultiFile *nimf = node->storage;
+                       
+                       /* CMP_NODE_OUTPUT_MULTI_FILE has been redeclared as CMP_NODE_OUTPUT_FILE */
+                       node->type = CMP_NODE_OUTPUT_FILE;
+                       
+                       /* initialize the node-wide image format from render data, if available */
+                       if (sce)
+                               nimf->format = sce->r.im_format;
+                       
+                       /* transfer render format toggle to node format toggle */
+                       for (sock=node->inputs.first; sock; sock=sock->next) {
+                               NodeImageMultiFileSocket *simf = sock->storage;
+                               simf->use_node_format = simf->use_render_format;
+                       }
+                       
+                       /* we do have preview now */
+                       node->flag |= NODE_PREVIEW;
+               }
+       }
+}
+
 static void do_versions(FileData *fd, Library *lib, Main *main)
 {
        /* WATCH IT!!!: pointers from libdata have not been converted */
@@ -13164,6 +13235,21 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
                }
        }
 
+       if (main->versionfile < 262 || (main->versionfile == 262 && main->subversionfile < 1))
+       {
+               /* update use flags for node sockets (was only temporary before) */
+               Scene *sce;
+               bNodeTree *ntree;
+               
+               for (sce=main->scene.first; sce; sce=sce->id.next)
+                       if (sce->nodetree)
+                               do_versions_nodetree_multi_file_output_format_2_62_1(sce, sce->nodetree);
+               
+               /* XXX can't associate with scene for group nodes, image format will stay uninitialized */
+               for (ntree=main->nodetree.first; ntree; ntree=ntree->id.next)
+                       do_versions_nodetree_multi_file_output_format_2_62_1(NULL, ntree);
+       }
+
 
        /* WATCH IT!!!: pointers from libdata have not been converted yet here! */
        /* WATCH IT 2!: Userdef struct init has to be in editors/interface/resources.c! */
index d5ed309..b063a16 100644 (file)
@@ -727,7 +727,7 @@ static void write_nodetree(WriteData *wd, bNodeTree *ntree)
                                writestruct(wd, DATA, node->typeinfo->storagename, 1, node->storage);
                }
                
-               if (node->type==CMP_NODE_OUTPUT_MULTI_FILE) {
+               if (node->type==CMP_NODE_OUTPUT_FILE) {
                        /* inputs have own storage data */
                        for (sock=node->inputs.first; sock; sock=sock->next)
                                writestruct(wd, DATA, "NodeImageMultiFileSocket", 1, sock->storage);
index a58e03b..bc22e66 100644 (file)
@@ -102,24 +102,71 @@ static void node_socket_button_label(const bContext *UNUSED(C), uiBlock *block,
        uiDefBut(block, LABEL, 0, sock->name, x, y, width, NODE_DY, NULL, 0, 0, 0, 0, "");
 }
 
+/* draw function for file output node sockets.
+ * XXX a bit ugly use atm, called from datatype button functions,
+ * since all node types and callbacks only use data type without struct_type.
+ */
+static void node_socket_button_output_file(const bContext *C, uiBlock *block,
+                                           bNodeTree *ntree, bNode *node, bNodeSocket *sock,
+                                           const char *UNUSED(name), int x, int y, int width)
+{
+       uiLayout *layout, *row;
+       PointerRNA nodeptr, sockptr, imfptr;
+       int imtype;
+       int rx, ry;
+       RNA_pointer_create(&ntree->id, &RNA_Node, node, &nodeptr);
+       RNA_pointer_create(&ntree->id, &RNA_NodeSocket, sock, &sockptr);
+       
+       layout = uiBlockLayout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, x, y+NODE_DY, width, 20, UI_GetStyle());
+       row = uiLayoutRow(layout, 0);           
+       
+       uiItemL(row, sock->name, 0);
+       
+       imfptr = RNA_pointer_get(&nodeptr, "format");
+       imtype = RNA_enum_get(&imfptr, "file_format");
+       /* in multilayer format all socket format details are ignored */
+       if (imtype != R_IMF_IMTYPE_MULTILAYER) {
+               PropertyRNA *imtype_prop;
+               const char *imtype_name;
+               
+               if (!RNA_boolean_get(&sockptr, "use_node_format"))
+                       imfptr = RNA_pointer_get(&sockptr, "format");
+               
+               imtype_prop = RNA_struct_find_property(&imfptr, "file_format");
+               RNA_property_enum_name((bContext*)C, &imfptr, imtype_prop, RNA_property_enum_get(&imfptr, imtype_prop), &imtype_name);
+               uiBlockSetEmboss(block, UI_EMBOSSP);
+               uiItemL(row, imtype_name, 0);
+               uiBlockSetEmboss(block, UI_EMBOSSN);
+       }
+       
+       uiBlockLayoutResolve(block, &rx, &ry);
+}
 
 static void node_socket_button_default(const bContext *C, uiBlock *block,
                                                                bNodeTree *ntree, bNode *node, bNodeSocket *sock,
                                                                const char *name, int x, int y, int width)
 {
-       if (sock->link || (sock->flag & SOCK_HIDE_VALUE))
-               node_socket_button_label(C, block, ntree, node, sock, name, x, y, width);
-       else {
-               PointerRNA ptr;
-               uiBut *bt;
-               
-               RNA_pointer_create(&ntree->id, &RNA_NodeSocket, sock, &ptr);
-               
-               bt = uiDefButR(block, NUM, B_NODE_EXEC, name,
-                                          x, y+1, width, NODE_DY-2, 
-                                          &ptr, "default_value", 0, 0, 0, -1, -1, NULL);
-               if (node)
-                       uiButSetFunc(bt, node_sync_cb, CTX_wm_space_node(C), node);
+       switch (sock->struct_type) {
+       case SOCK_STRUCT_NONE: {
+               if (sock->link || (sock->flag & SOCK_HIDE_VALUE))
+                       node_socket_button_label(C, block, ntree, node, sock, name, x, y, width);
+               else {
+                       PointerRNA ptr;
+                       uiBut *bt;
+                       
+                       RNA_pointer_create(&ntree->id, &RNA_NodeSocket, sock, &ptr);
+                       
+                       bt = uiDefButR(block, NUM, B_NODE_EXEC, name,
+                                                  x, y+1, width, NODE_DY-2, 
+                                                  &ptr, "default_value", 0, 0, 0, -1, -1, NULL);
+                       if (node)
+                               uiButSetFunc(bt, node_sync_cb, CTX_wm_space_node(C), node);
+               }
+               break;
+       }
+       case SOCK_STRUCT_OUTPUT_FILE:
+               node_socket_button_output_file(C, block, ntree, node, sock, name, x, y, width);
+               break;
        }
 }
 
@@ -149,25 +196,33 @@ static void node_socket_button_components(const bContext *C, uiBlock *block,
                                                                   bNodeTree *ntree, bNode *node, bNodeSocket *sock,
                                                                   const char *name, int x, int y, int width)
 {
-       if (sock->link || (sock->flag & SOCK_HIDE_VALUE))
-               node_socket_button_label(C, block, ntree, node, sock, name, x, y, width);
-       else {
-               PointerRNA ptr;
-               SocketComponentMenuArgs *args;
-               
-               RNA_pointer_create(&ntree->id, &RNA_NodeSocket, sock, &ptr);
-               
-               args= MEM_callocN(sizeof(SocketComponentMenuArgs), "SocketComponentMenuArgs");
-               
-               args->ptr = ptr;
-               args->x = x;
-               args->y = y;
-               args->width = width;
-               args->cb = node_sync_cb;
-               args->arg1 = CTX_wm_space_node(C);
-               args->arg2 = node;
-               
-               uiDefBlockButN(block, socket_component_menu, args, name, x, y+1, width, NODE_DY-2, "");
+       switch (sock->struct_type) {
+       case SOCK_STRUCT_NONE: {
+               if (sock->link || (sock->flag & SOCK_HIDE_VALUE))
+                       node_socket_button_label(C, block, ntree, node, sock, name, x, y, width);
+               else {
+                       PointerRNA ptr;
+                       SocketComponentMenuArgs *args;
+                       
+                       RNA_pointer_create(&ntree->id, &RNA_NodeSocket, sock, &ptr);
+                       
+                       args= MEM_callocN(sizeof(SocketComponentMenuArgs), "SocketComponentMenuArgs");
+                       
+                       args->ptr = ptr;
+                       args->x = x;
+                       args->y = y;
+                       args->width = width;
+                       args->cb = node_sync_cb;
+                       args->arg1 = CTX_wm_space_node(C);
+                       args->arg2 = node;
+                       
+                       uiDefBlockButN(block, socket_component_menu, args, name, x, y+1, width, NODE_DY-2, "");
+               }
+               break;
+       }
+       case SOCK_STRUCT_OUTPUT_FILE:
+               node_socket_button_output_file(C, block, ntree, node, sock, name, x, y, width);
+               break;
        }
 }
 
@@ -200,29 +255,10 @@ static void node_socket_button_color(const bContext *C, uiBlock *block,
                }
                break;
        }
-       case SOCK_STRUCT_OUTPUT_MULTI_FILE: {
-               uiLayout *layout, *row;
-               PointerRNA ptr, imfptr;
-               PropertyRNA *imtype_prop;
-               const char *imtype_name;
-               int rx, ry;
-               RNA_pointer_create(&ntree->id, &RNA_NodeSocket, sock, &ptr);
-               imfptr = RNA_pointer_get(&ptr, "format");
-               
-               layout = uiBlockLayout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, x, y+NODE_DY, width, 20, UI_GetStyle());
-               row = uiLayoutRow(layout, 0);           
-               
-               uiItemL(row, sock->name, 0);
-               imtype_prop = RNA_struct_find_property(&imfptr, "file_format");
-               RNA_property_enum_name((bContext*)C, &imfptr, imtype_prop, RNA_property_enum_get(&imfptr, imtype_prop), &imtype_name);
-               uiBlockSetEmboss(block, UI_EMBOSSP);
-               uiItemL(row, imtype_name, 0);
-               uiBlockSetEmboss(block, UI_EMBOSSN);
-               
-               uiBlockLayoutResolve(block, &rx, &ry);
+       case SOCK_STRUCT_OUTPUT_FILE:
+               node_socket_button_output_file(C, block, ntree, node, sock, name, x, y, width);
                break;
        }
-       }
 }
 
 /* ****************** BASE DRAW FUNCTIONS FOR NEW OPERATOR NODES ***************** */
@@ -1699,56 +1735,54 @@ 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)
 {
-       bNode *node= ptr->data;
-       NodeImageFile *nif= node->storage;
-       PointerRNA imfptr;
-
-       uiLayout *row;
-
-       uiItemR(layout, ptr, "filepath", 0, "", ICON_NONE);
-
-       RNA_pointer_create(NULL, &RNA_ImageFormatSettings, &nif->im_format, &imfptr);
-       uiTemplateImageSettings(layout, &imfptr);
-
-       row= uiLayoutRow(layout, 1);
-       uiItemR(row, ptr, "frame_start", 0, "Start", ICON_NONE);
-       uiItemR(row, ptr, "frame_end", 0, "End", ICON_NONE);
-}
-
-static void node_composit_buts_multi_file_output(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr)
-{
-       uiItemL(layout, "Base Path:", 0);
+       PointerRNA imfptr = RNA_pointer_get(ptr, "format");
+       int multilayer = (RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER);
+       
+       if (multilayer)
+               uiItemL(layout, "Path:", 0);
+       else
+               uiItemL(layout, "Base Path:", 0);
        uiItemR(layout, ptr, "base_path", 0, "", ICON_NONE);
 }
-static void node_composit_buts_multi_file_output_details(uiLayout *layout, bContext *C, PointerRNA *ptr)
+static void node_composit_buts_file_output_details(uiLayout *layout, bContext *C, PointerRNA *ptr)
 {
+       PointerRNA imfptr = RNA_pointer_get(ptr, "format");
        PointerRNA active_input_ptr = RNA_pointer_get(ptr, "active_input");
+       int multilayer = (RNA_enum_get(&imfptr, "file_format") == R_IMF_IMTYPE_MULTILAYER);
        
-       node_composit_buts_multi_file_output(layout, C, ptr);
+       node_composit_buts_file_output(layout, C, ptr);
+       uiTemplateImageSettings(layout, &imfptr);
+       
+       uiItemS(layout);
        
-       uiItemO(layout, "Add Input", ICON_ZOOMIN, "NODE_OT_output_multi_file_add_socket");
+       uiItemO(layout, "Add Input", ICON_ZOOMIN, "NODE_OT_output_file_add_socket");
        
        uiTemplateList(layout, C, ptr, "inputs", ptr, "active_input_index", NULL, 0, 0, 0);
        
        if (active_input_ptr.data) {
-               PointerRNA imfptr = RNA_pointer_get(&active_input_ptr, "format");
                uiLayout *row, *col;
                
                col = uiLayoutColumn(layout, 1);
-               uiItemL(col, "File Path:", 0);
+               if (multilayer)
+                       uiItemL(col, "Layer Name:", 0);
+               else
+                       uiItemL(col, "File Path:", 0);
                row = uiLayoutRow(col, 0);
                uiItemR(row, &active_input_ptr, "name", 0, "", 0);
-               uiItemFullO(row, "NODE_OT_output_multi_file_remove_active_socket", "", ICON_X, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_R_ICON_ONLY);
-               
-               uiItemS(layout);
+               uiItemFullO(row, "NODE_OT_output_file_remove_active_socket", "", ICON_X, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_R_ICON_ONLY);
                
-               col = uiLayoutColumn(layout, 1);
-               uiItemL(col, "Format:", 0);
-               uiItemR(col, &active_input_ptr, "use_render_format", 0, NULL, 0);
-               
-               col= uiLayoutColumn(layout, 0);
-               uiLayoutSetActive(col, RNA_boolean_get(&active_input_ptr, "use_render_format")==0);
-               uiTemplateImageSettings(col, &imfptr);
+               /* in multilayer format all socket format details are ignored */
+               if (!multilayer) {
+                       imfptr = RNA_pointer_get(&active_input_ptr, "format");
+                       
+                       col = uiLayoutColumn(layout, 1);
+                       uiItemL(col, "Format:", 0);
+                       uiItemR(col, &active_input_ptr, "use_node_format", 0, NULL, 0);
+                       
+                       col= uiLayoutColumn(layout, 0);
+                       uiLayoutSetActive(col, RNA_boolean_get(&active_input_ptr, "use_node_format")==0);
+                       uiTemplateImageSettings(col, &imfptr);
+               }
        }
 }
 
@@ -1986,10 +2020,7 @@ static void node_composit_set_butfunc(bNodeType *ntype)
                        break;
                case CMP_NODE_OUTPUT_FILE:
                        ntype->uifunc= node_composit_buts_file_output;
-                       break;
-               case CMP_NODE_OUTPUT_MULTI_FILE:
-                       ntype->uifunc= node_composit_buts_multi_file_output;
-                       ntype->uifuncbut= node_composit_buts_multi_file_output_details;
+                       ntype->uifuncbut= node_composit_buts_file_output_details;
                        break;
                case CMP_NODE_DIFF_MATTE:
                        ntype->uifunc=node_composit_buts_diff_matte;
index c89e917..a6d18b5 100644 (file)
@@ -2254,6 +2254,8 @@ static void node_remove_extra_links(SpaceNode *snode, bNodeSocket *tsock, bNodeL
                        }
                        else
                                nodeRemLink(snode->edittree, tlink);
+                       
+                       snode->edittree->update |= NTREE_UPDATE_LINKS;
                }
        }
 }
@@ -3515,49 +3517,47 @@ void NODE_OT_new_node_tree(wmOperatorType *ot)
        RNA_def_string(ot->srna, "name", "NodeTree", MAX_ID_NAME-2, "Name", "");
 }
 
-/* ****************** Multi File Output Add Socket  ******************* */
+/* ****************** File Output Add Socket  ******************* */
 
-static int node_output_multi_file_add_socket_exec(bContext *C, wmOperator *UNUSED(op))
+static int node_output_file_add_socket_exec(bContext *C, wmOperator *op)
 {
        Scene *scene= CTX_data_scene(C);
        SpaceNode *snode= CTX_wm_space_node(C);
        bNodeTree *ntree = snode->edittree;
        bNode *node = nodeGetActive(ntree);
+       char file_path[MAX_NAME];
        
        if (!node)
                return OPERATOR_CANCELLED;
        
-       ntreeCompositOutputMultiFileAddSocket(ntree, node, &scene->r.im_format);
+       RNA_string_get(op->ptr, "file_path", file_path);
+       ntreeCompositOutputFileAddSocket(ntree, node, file_path, &scene->r.im_format);
        
        snode_notify(C, snode);
        
        return OPERATOR_FINISHED;
 }
 
-static int node_output_multi_file_add_socket_invoke(bContext *C, wmOperator *op, wmEvent *UNUSED(event))
-{
-       return node_output_multi_file_add_socket_exec(C, op);
-}
-
-void NODE_OT_output_multi_file_add_socket(wmOperatorType *ot)
+void NODE_OT_output_file_add_socket(wmOperatorType *ot)
 {
        /* identifiers */
-       ot->name= "Add Multi File Node Socket";
-       ot->description= "Add a new input to a multi file output node";
-       ot->idname= "NODE_OT_output_multi_file_add_socket";
+       ot->name= "Add File Node Socket";
+       ot->description= "Add a new input to a file output node";
+       ot->idname= "NODE_OT_output_file_add_socket";
        
        /* callbacks */
-       ot->exec= node_output_multi_file_add_socket_exec;
-       ot->invoke= node_output_multi_file_add_socket_invoke;
+       ot->exec= node_output_file_add_socket_exec;
        ot->poll= composite_node_active;
        
        /* flags */
        ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
+       
+       RNA_def_string(ot->srna, "file_path", "Image", MAX_NAME, "File Path", "Sub-path of the output file");
 }
 
 /* ****************** Multi File Output Remove Socket  ******************* */
 
-static int node_output_multi_file_remove_active_socket_exec(bContext *C, wmOperator *UNUSED(op))
+static int node_output_file_remove_active_socket_exec(bContext *C, wmOperator *UNUSED(op))
 {
        SpaceNode *snode= CTX_wm_space_node(C);
        bNodeTree *ntree = snode->edittree;
@@ -3566,7 +3566,7 @@ static int node_output_multi_file_remove_active_socket_exec(bContext *C, wmOpera
        if (!node)
                return OPERATOR_CANCELLED;
        
-       if (!ntreeCompositOutputMultiFileRemoveActiveSocket(ntree, node))
+       if (!ntreeCompositOutputFileRemoveActiveSocket(ntree, node))
                return OPERATOR_CANCELLED;
        
        snode_notify(C, snode);
@@ -3574,15 +3574,15 @@ static int node_output_multi_file_remove_active_socket_exec(bContext *C, wmOpera
        return OPERATOR_FINISHED;
 }
 
-void NODE_OT_output_multi_file_remove_active_socket(wmOperatorType *ot)
+void NODE_OT_output_file_remove_active_socket(wmOperatorType *ot)
 {
        /* identifiers */
-       ot->name= "Remove Multi File Node Socket";
-       ot->description= "Remove active input from a multi file output node";
-       ot->idname= "NODE_OT_output_multi_file_remove_active_socket";
+       ot->name= "Remove File Node Socket";
+       ot->description= "Remove active input from a file output node";
+       ot->idname= "NODE_OT_output_file_remove_active_socket";
        
        /* callbacks */
-       ot->exec= node_output_multi_file_remove_active_socket_exec;
+       ot->exec= node_output_file_remove_active_socket_exec;
        ot->poll= composite_node_active;
        
        /* flags */
index f6d52aa..2524454 100644 (file)
@@ -157,8 +157,8 @@ void NODE_OT_add_file(struct wmOperatorType *ot);
 
 void NODE_OT_new_node_tree(struct wmOperatorType *ot);
 
-void NODE_OT_output_multi_file_add_socket(struct wmOperatorType *ot);
-void NODE_OT_output_multi_file_remove_active_socket(struct wmOperatorType *ot);
+void NODE_OT_output_file_add_socket(struct wmOperatorType *ot);
+void NODE_OT_output_file_remove_active_socket(struct wmOperatorType *ot);
 
 extern const char *node_context_dir[];
 
index 608c6b5..d358556 100644 (file)
@@ -101,8 +101,8 @@ void node_operatortypes(void)
        
        WM_operatortype_append(NODE_OT_new_node_tree);
        
-       WM_operatortype_append(NODE_OT_output_multi_file_add_socket);
-       WM_operatortype_append(NODE_OT_output_multi_file_remove_active_socket);
+       WM_operatortype_append(NODE_OT_output_file_add_socket);
+       WM_operatortype_append(NODE_OT_output_file_remove_active_socket);
 }
 
 void ED_operatormacros_node(void)
index 93891a9..ed9eaff 100644 (file)
@@ -114,7 +114,7 @@ typedef struct bNodeSocket {
 
 /* sock->struct_type */
 #define SOCK_STRUCT_NONE                               0       /* default, type is defined by sock->type only */
-#define SOCK_STRUCT_OUTPUT_MULTI_FILE  1       /* multi file output node socket */
+#define SOCK_STRUCT_OUTPUT_FILE                        1       /* file output node socket */
 
 /* socket side (input/output) */
 #define SOCK_IN                1
@@ -352,7 +352,7 @@ typedef struct NodeHueSat {
        float hue, sat, val;
 } NodeHueSat;
 
-typedef struct NodeImageFile {
+typedef DNA_DEPRECATED struct NodeImageFile {
        char name[1024]; /* 1024 = FILE_MAX */
        struct ImageFormatData im_format;
        int sfra, efra;
@@ -362,10 +362,11 @@ typedef struct NodeImageMultiFile {
        char base_path[1024];   /* 1024 = FILE_MAX */
        int active_input;               /* selected input in details view list */
        int pad;
+       ImageFormatData format;
 } NodeImageMultiFile;
 typedef struct NodeImageMultiFileSocket {
-       short use_render_format;        /* use global render settings instead of own format */
-       short pad1;
+       short use_render_format  DNA_DEPRECATED;
+       short use_node_format;  /* use overall node image format */
        int pad2;
        ImageFormatData format;
 } NodeImageMultiFileSocket;
index 67d27af..596e348 100644 (file)
@@ -347,7 +347,7 @@ extern StructRNA RNA_NlaTrack;
 extern StructRNA RNA_Node;
 extern StructRNA RNA_NodeForLoop;
 extern StructRNA RNA_NodeGroup;
-extern StructRNA RNA_NodeImageMultiFileSocket;
+extern StructRNA RNA_NodeImageFileSocket;
 extern StructRNA RNA_NodeLink;
 extern StructRNA RNA_NodeSocket;
 extern StructRNA RNA_NodeSocketPanel;
index a6a8d9f..0113170 100644 (file)
@@ -245,32 +245,28 @@ static StructRNA *rna_NodeSocket_refine(PointerRNA *ptr)
                                return &RNA_NodeSocket##stypename##idname; \
                }
                
-               if (sock->struct_type == SOCK_STRUCT_NONE) {
+               switch (sock->struct_type) {
+               case SOCK_STRUCT_NONE:
                        switch (sock->type) {
                        case SOCK_FLOAT:
                                NODE_DEFINE_SUBTYPES_FLOAT
-                                               break;
+                               break;
                        case SOCK_INT:
                                NODE_DEFINE_SUBTYPES_INT
-                                               break;
+                               break;
                        case SOCK_BOOLEAN:
                                return &RNA_NodeSocketBoolean;
-                               break;
                        case SOCK_VECTOR:
                                NODE_DEFINE_SUBTYPES_VECTOR
-                                               break;
+                               break;
                        case SOCK_RGBA:
                                return &RNA_NodeSocketRGBA;
-                               break;
                        case SOCK_SHADER:
                                return &RNA_NodeSocketShader;
                        }
-               }
-               else {
-                       switch (sock->struct_type) {
-                       case SOCK_STRUCT_OUTPUT_MULTI_FILE:
-                               return &RNA_NodeImageMultiFileSocket;
-                       }
+                       break;
+               case SOCK_STRUCT_OUTPUT_FILE:
+                       return &RNA_NodeImageFileSocket;
                }
                
                #undef SUBTYPE
@@ -332,24 +328,6 @@ static void rna_Matte_t2_set(PointerRNA *ptr, float value)
        chroma->t2 = value;
 }
 
-static void rna_Image_start_frame_set(PointerRNA *ptr, int value)
-{
-       bNode *node= (bNode*)ptr->data;
-       NodeImageFile *image = node->storage;
-       
-       CLAMP(value, MINFRAME, image->efra); 
-       image->sfra= value;
-}
-
-static void rna_Image_end_frame_set(PointerRNA *ptr, int value)
-{
-       bNode *node= (bNode*)ptr->data;
-       NodeImageFile *image = node->storage;
-
-       CLAMP(value, image->sfra, MAXFRAME);
-       image->efra= value;
-}
-
 static void rna_Node_scene_set(PointerRNA *ptr, PointerRNA value)
 {
        bNode *node= (bNode*)ptr->data;
@@ -1770,61 +1748,29 @@ static void def_cmp_render_layers(StructRNA *srna)
        RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
 }
 
-static void def_cmp_output_file(StructRNA *srna)
-{
-       PropertyRNA *prop;
-       
-       RNA_def_struct_sdna_from(srna, "NodeImageFile", "storage");
-       
-       prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH);
-       RNA_def_property_string_sdna(prop, NULL, "name");
-       RNA_def_property_ui_text(prop, "File Path", "Output path for the image, same functionality as render output");
-       RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
-       
-       prop= RNA_def_property(srna, "image_settings", PROP_POINTER, PROP_NONE);
-       RNA_def_property_flag(prop, PROP_NEVER_NULL);
-       RNA_def_property_pointer_sdna(prop, NULL, "im_format");
-       RNA_def_property_struct_type(prop, "ImageFormatSettings");
-       RNA_def_property_ui_text(prop, "Image Format", "");
-
-       prop = RNA_def_property(srna, "frame_start", PROP_INT, PROP_NONE);
-       RNA_def_property_int_sdna(prop, NULL, "sfra");
-       RNA_def_property_int_funcs(prop, NULL, "rna_Image_start_frame_set", NULL);
-       RNA_def_property_range(prop, MINFRAMEF, MAXFRAMEF);
-       RNA_def_property_ui_text(prop, "Start Frame", "");
-       RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
-       
-       prop = RNA_def_property(srna, "frame_end", PROP_INT, PROP_NONE);
-       RNA_def_property_int_sdna(prop, NULL, "efra");
-       RNA_def_property_int_funcs(prop, NULL, "rna_Image_end_frame_set", NULL);
-       RNA_def_property_range(prop, MINFRAMEF, MAXFRAMEF);
-       RNA_def_property_ui_text(prop, "End Frame", "");
-       RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
-}
-
-static void rna_def_cmp_output_multi_file_socket(BlenderRNA *brna)
+static void rna_def_cmp_output_file_socket(BlenderRNA *brna)
 {
        StructRNA *srna;
        PropertyRNA *prop;
        
-       srna = RNA_def_struct(brna, "NodeImageMultiFileSocket", "NodeSocketRGBA");
+       srna = RNA_def_struct(brna, "NodeImageFileSocket", "NodeSocketRGBA");
        RNA_def_struct_sdna(srna, "bNodeSocket");
        RNA_def_struct_path_func(srna, "rna_NodeSocket_path");
-       RNA_def_struct_ui_text(srna, "Node Image Multi File Socket", "Socket data of multi file output node");
+       RNA_def_struct_ui_text(srna, "Node Image File Socket", "Socket data of file output node");
        RNA_def_struct_ui_icon(srna, ICON_PLUG);
        RNA_def_struct_sdna_from(srna, "bNodeSocket", NULL);
        
        RNA_def_struct_sdna_from(srna, "NodeImageMultiFileSocket", "storage");
        
-       prop = RNA_def_property(srna, "use_render_format", PROP_BOOLEAN, PROP_NONE);
-       RNA_def_property_boolean_sdna(prop, NULL, "use_render_format", 1);
-       RNA_def_property_ui_text(prop, "Use Render Format", "");
+       prop = RNA_def_property(srna, "use_node_format", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "use_node_format", 1);
+       RNA_def_property_ui_text(prop, "Use Node Format", "");
        RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_NodeSocket_update");
        
        prop = RNA_def_property(srna, "format", PROP_POINTER, PROP_NONE);
        RNA_def_property_struct_type(prop, "ImageFormatSettings");
 }
-static void def_cmp_output_multi_file(StructRNA *srna)
+static void def_cmp_output_file(StructRNA *srna)
 {
        PropertyRNA *prop;
        
@@ -1845,6 +1791,9 @@ static void def_cmp_output_multi_file(StructRNA *srna)
        RNA_def_property_int_sdna(prop, NULL, "active_input");
        RNA_def_property_ui_text(prop, "Active Input Index", "Active input index in details view list");
        RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
+       
+       prop = RNA_def_property(srna, "format", PROP_POINTER, PROP_NONE);
+       RNA_def_property_struct_type(prop, "ImageFormatSettings");
 }
 
 static void def_cmp_dilate_erode(StructRNA *srna)
@@ -3477,7 +3426,7 @@ void RNA_def_nodetree(BlenderRNA *brna)
        define_specific_node(brna, NODE_FRAME, def_frame);
        
        /* special socket types */
-       rna_def_cmp_output_multi_file_socket(brna);
+       rna_def_cmp_output_file_socket(brna);
 }
 
 /* clean up macro definition */
index b336f86..745a956 100644 (file)
@@ -113,7 +113,6 @@ DefNode( CompositorNode, CMP_NODE_IMAGE,          def_cmp_image,          "IMAGE
 DefNode( CompositorNode, CMP_NODE_R_LAYERS,       def_cmp_render_layers,  "R_LAYERS",       RLayers,          "Render Layers",     ""              )
 DefNode( CompositorNode, CMP_NODE_COMPOSITE,      0,                      "COMPOSITE",      Composite,        "Composite",         ""              )
 DefNode( CompositorNode, CMP_NODE_OUTPUT_FILE,    def_cmp_output_file,    "OUTPUT_FILE",    OutputFile,       "Output File",       ""              )
-DefNode( CompositorNode, CMP_NODE_OUTPUT_MULTI_FILE, def_cmp_output_multi_file, "OUTPUT_MULTI_FILE", OutputMultiFile, "Output Multi File", ""      )
 DefNode( CompositorNode, CMP_NODE_TEXTURE,        def_texture,            "TEXTURE",        Texture,          "Texture",           ""              )
 DefNode( CompositorNode, CMP_NODE_TRANSLATE,      0,                      "TRANSLATE",      Translate,        "Translate",         ""              )
 DefNode( CompositorNode, CMP_NODE_ZCOMBINE,       def_cmp_zcombine,              "ZCOMBINE",       Zcombine,         "Z Combine",         ""              )
index b458ae3..aabf13f 100644 (file)
@@ -222,4 +222,8 @@ if(WITH_PYTHON)
        add_definitions(-DWITH_PYTHON)
 endif()
 
+if(WITH_IMAGE_OPENEXR)
+       add_definitions(-DWITH_OPENEXR)
+endif()
+
 blender_add_lib(bf_nodes "${SRC}" "${INC}" "${INC_SYS}")
index e2bbaea..284b89b 100644 (file)
@@ -55,7 +55,6 @@ void register_node_type_cmp_composite(struct bNodeTreeType *ttype);
 void register_node_type_cmp_viewer(struct bNodeTreeType *ttype);
 void register_node_type_cmp_splitviewer(struct bNodeTreeType *ttype);
 void register_node_type_cmp_output_file(struct bNodeTreeType *ttype);
-void register_node_type_cmp_output_multi_file(struct bNodeTreeType *ttype);
 void register_node_type_cmp_view_levels(struct bNodeTreeType *ttype);
 
 void register_node_type_cmp_curve_rgb(struct bNodeTreeType *ttype);
index 66075f1..90aa039 100644 (file)
 
 #include "node_composite_util.h"
 
-/* **************** OUTPUT FILE ******************** */
-static bNodeSocketTemplate cmp_node_output_file_in[]= {
-       {       SOCK_RGBA, 1, "Image",          0.0f, 0.0f, 0.0f, 1.0f},
-       {       SOCK_FLOAT, 1, "Z",             0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, PROP_NONE},
-       {       -1, 0, ""       }
-};
-
-static void node_composit_exec_output_file(void *data, bNode *node, bNodeStack **in, bNodeStack **UNUSED(out))
-{
-       /* image assigned to output */
-       /* stack order input sockets: col, alpha */
-       
-       if(in[0]->data) {
-               RenderData *rd= data;
-               NodeImageFile *nif= node->storage;
-               if(nif->sfra!=nif->efra && (rd->cfra<nif->sfra || rd->cfra>nif->efra)) {
-                       return; /* BAIL OUT RETURN */
-               }
-               else if (!G.rendering) {
-                       /* only output files when rendering a sequence -
-                        * otherwise, it overwrites the output files just 
-                        * scrubbing through the timeline when the compositor updates */
-                       return;
-               } else {
-                       Main *bmain= G.main; /* TODO, have this passed along */
-                       CompBuf *cbuf= typecheck_compbuf(in[0]->data, CB_RGBA);
-                       ImBuf *ibuf= IMB_allocImBuf(cbuf->x, cbuf->y, 32, 0);
-                       char string[FILE_MAX];
-                       
-                       ibuf->rect_float= cbuf->rect;
-                       ibuf->dither= rd->dither_intensity;
-                       
-                       if (rd->color_mgt_flag & R_COLOR_MANAGEMENT)
-                               ibuf->profile = IB_PROFILE_LINEAR_RGB;
-                       
-                       if(in[1]->data) {
-                               CompBuf *zbuf= in[1]->data;
-                               if(zbuf->type==CB_VAL && zbuf->x==cbuf->x && zbuf->y==cbuf->y) {
-                                       nif->im_format.flag |= R_IMF_FLAG_ZBUF;
-                                       ibuf->zbuf_float= zbuf->rect;
-                               }
-                       }
-                       
-                       BKE_makepicstring(string, nif->name, bmain->name, rd->cfra, nif->im_format.imtype, (rd->scemode & R_EXTENSION), TRUE);
-                       
-                       if(0 == BKE_write_ibuf(ibuf, string, &nif->im_format))
-                               printf("Cannot save Node File Output to %s\n", string);
-                       else
-                               printf("Saved: %s\n", string);
-                       
-                       IMB_freeImBuf(ibuf);    
-                       
-                       generate_preview(data, node, cbuf);
-                       
-                       if(in[0]->data != cbuf) 
-                               free_compbuf(cbuf);
-               }
-       }
-}
+#include "IMB_imbuf.h"
+#include "IMB_imbuf_types.h"
 
-static void node_composit_init_output_file(bNodeTree *UNUSED(ntree), bNode* node, bNodeTemplate *ntemp)
-{
-       RenderData *rd = &ntemp->scene->r;
-       NodeImageFile *nif= MEM_callocN(sizeof(NodeImageFile), "node image file");
-       node->storage= nif;
+#include "intern/openexr/openexr_multi.h"
 
-       BLI_strncpy(nif->name, rd->pic, sizeof(nif->name));
-       nif->im_format= rd->im_format;
-       if (BKE_imtype_is_movie(nif->im_format.imtype)) {
-               nif->im_format.imtype= R_IMF_IMTYPE_OPENEXR;
-       }
-       nif->sfra= rd->sfra;
-       nif->efra= rd->efra;
-}
-
-void register_node_type_cmp_output_file(bNodeTreeType *ttype)
-{
-       static bNodeType ntype;
-
-       node_type_base(ttype, &ntype, CMP_NODE_OUTPUT_FILE, "File Output", NODE_CLASS_OUTPUT, NODE_PREVIEW|NODE_OPTIONS);
-       node_type_socket_templates(&ntype, cmp_node_output_file_in, NULL);
-       node_type_size(&ntype, 140, 80, 300);
-       node_type_init(&ntype, node_composit_init_output_file);
-       node_type_storage(&ntype, "NodeImageFile", node_free_standard_storage, node_copy_standard_storage);
-       node_type_exec(&ntype, node_composit_exec_output_file);
-
-       nodeRegisterType(ttype, &ntype);
-}
-
-
-/* =============================================================================== */
 
+/* **************** OUTPUT FILE ******************** */
 
-void ntreeCompositOutputMultiFileAddSocket(bNodeTree *ntree, bNode *node, ImageFormatData *im_format)
+bNodeSocket *ntreeCompositOutputFileAddSocket(bNodeTree *ntree, bNode *node, const char *name, ImageFormatData *im_format)
 {
-       bNodeSocket *sock = nodeAddSocket(ntree, node, SOCK_IN, "", SOCK_RGBA);
+       bNodeSocket *sock = nodeAddSocket(ntree, node, SOCK_IN, name, SOCK_RGBA);
        
        /* create format data for the input socket */
        NodeImageMultiFileSocket *sockdata = MEM_callocN(sizeof(NodeImageMultiFileSocket), "socket image format");
        sock->storage = sockdata;
-       sock->struct_type = SOCK_STRUCT_OUTPUT_MULTI_FILE;
+       sock->struct_type = SOCK_STRUCT_OUTPUT_FILE;
        
        if(im_format) {
                sockdata->format= *im_format;
@@ -145,11 +60,13 @@ void ntreeCompositOutputMultiFileAddSocket(bNodeTree *ntree, bNode *node, ImageF
                        sockdata->format.imtype= R_IMF_IMTYPE_OPENEXR;
                }
        }
-       /* use render data format by default */
-       sockdata->use_render_format = 1;
+       /* use node data format by default */
+       sockdata->use_node_format = 1;
+       
+       return sock;
 }
 
-int ntreeCompositOutputMultiFileRemoveActiveSocket(bNodeTree *ntree, bNode *node)
+int ntreeCompositOutputFileRemoveActiveSocket(bNodeTree *ntree, bNode *node)
 {
        NodeImageMultiFile *nimf = node->storage;
        bNodeSocket *sock = BLI_findlink(&node->inputs, nimf->active_input);
@@ -164,19 +81,20 @@ int ntreeCompositOutputMultiFileRemoveActiveSocket(bNodeTree *ntree, bNode *node
        return 1;
 }
 
-static void init_output_multi_file(bNodeTree *ntree, bNode* node, bNodeTemplate *ntemp)
+static void init_output_file(bNodeTree *ntree, bNode* node, bNodeTemplate *ntemp)
 {
        RenderData *rd = &ntemp->scene->r;
        NodeImageMultiFile *nimf= MEM_callocN(sizeof(NodeImageMultiFile), "node image multi file");
        node->storage= nimf;
 
        BLI_strncpy(nimf->base_path, rd->pic, sizeof(nimf->base_path));
+       nimf->format = rd->im_format;
        
        /* add one socket by default */
-       ntreeCompositOutputMultiFileAddSocket(ntree, node, &rd->im_format);
+       ntreeCompositOutputFileAddSocket(ntree, node, "Image", &rd->im_format);
 }
 
-void free_output_multi_file(bNode *node)
+static void free_output_file(bNode *node)
 {
        bNodeSocket *sock;
        
@@ -188,7 +106,7 @@ void free_output_multi_file(bNode *node)
        MEM_freeN(node->storage);
 }
 
-void copy_output_multi_file(struct bNode *node, struct bNode *target)
+static void copy_output_file(struct bNode *node, struct bNode *target)
 {
        bNodeSocket *sock, *newsock;
        
@@ -200,52 +118,73 @@ void copy_output_multi_file(struct bNode *node, struct bNode *target)
        }
 }
 
-static void exec_output_multi_file(void *data, bNode *node, bNodeStack **in, bNodeStack **UNUSED(out))
+static void update_output_file(bNodeTree *UNUSED(ntree), bNode *node)
 {
-       RenderData *rd= data;
+       bNodeSocket *sock;
+       
+       /* automatically update the socket type based on linked input */
+       for (sock=node->inputs.first; sock; sock=sock->next) {
+               if (sock->link) {
+                       int linktype = sock->link->fromsock->type;
+                       if (linktype != sock->type)
+                               nodeSocketSetType(sock, linktype);
+               }
+       }
+}
+
+/* write input data into individual files */
+static void exec_output_file_singlelayer(RenderData *rd, bNode *node, bNodeStack **in)
+{
+       Main *bmain= G.main; /* TODO, have this passed along */
        NodeImageMultiFile *nimf= node->storage;
        bNodeSocket *sock;
        int i;
+       int has_preview = 0;
        
        for (sock=node->inputs.first, i=0; sock; sock=sock->next, ++i) {
-               if (!in[i]->data)
-                       continue;
-               
-               if (!G.rendering) {
-                       /* only output files when rendering a sequence -
-                        * otherwise, it overwrites the output files just 
-                        * scrubbing through the timeline when the compositor updates */
-                       return;
-               } else {
-                       Main *bmain= G.main; /* TODO, have this passed along */
+               if (in[i]->data) {
                        NodeImageMultiFileSocket *sockdata = sock->storage;
-                       CompBuf *cbuf= typecheck_compbuf(in[i]->data, CB_RGBA);
-                       ImBuf *ibuf= IMB_allocImBuf(cbuf->x, cbuf->y, 32, 0);
-                       ImageFormatData *format = (sockdata->use_render_format ? &rd->im_format : &sockdata->format);
+                       ImageFormatData *format = (sockdata->use_node_format ? &nimf->format : &sockdata->format);
                        char path[FILE_MAX];
-                       char string[FILE_MAX];
+                       char filename[FILE_MAX];
+                       CompBuf *cbuf;
+                       ImBuf *ibuf;
                        
-                       ibuf->rect_float= cbuf->rect;
-                       ibuf->dither= rd->dither_intensity;
+                       switch (format->planes) {
+                       case R_IMF_PLANES_BW:
+                               cbuf = typecheck_compbuf(in[i]->data, CB_VAL);
+                               break;
+                       case R_IMF_PLANES_RGB:
+                               cbuf = typecheck_compbuf(in[i]->data, CB_VEC3);
+                               break;
+                       case R_IMF_PLANES_RGBA:
+                               cbuf = typecheck_compbuf(in[i]->data, CB_RGBA);
+                               break;
+                       }
+                       
+                       ibuf = IMB_allocImBuf(cbuf->x, cbuf->y, format->planes, 0);
+                       ibuf->rect_float = cbuf->rect;
+                       ibuf->dither = rd->dither_intensity;
                        
                        if (rd->color_mgt_flag & R_COLOR_MANAGEMENT)
                                ibuf->profile = IB_PROFILE_LINEAR_RGB;
                        
                        /* get full path */
                        BLI_join_dirfile(path, FILE_MAX, nimf->base_path, sock->name);
+                       BKE_makepicstring(filename, path, bmain->name, rd->cfra, format->imtype, (rd->scemode & R_EXTENSION), TRUE);
                        
-                       BKE_makepicstring(string, path, bmain->name, rd->cfra, format->imtype, (rd->scemode & R_EXTENSION), TRUE);
-                       
-                       if(0 == BKE_write_ibuf(ibuf, string, format))
-                               printf("Cannot save Node File Output to %s\n", string);
+                       if(0 == BKE_write_ibuf(ibuf, filename, format))
+                               printf("Cannot save Node File Output to %s\n", filename);
                        else
-                               printf("Saved: %s\n", string);
+                               printf("Saved: %s\n", filename);
                        
-                       IMB_freeImBuf(ibuf);    
+                       IMB_freeImBuf(ibuf);
                        
-                       #if 0   /* XXX not used yet */
-                       generate_preview(data, node, cbuf);
-                       #endif
+                       /* simply pick the first valid input for preview */
+                       if (!has_preview) {
+                               generate_preview(rd, node, cbuf);
+                               has_preview = 1;
+                       }
                        
                        if(in[i]->data != cbuf) 
                                free_compbuf(cbuf);
@@ -253,16 +192,129 @@ static void exec_output_multi_file(void *data, bNode *node, bNodeStack **in, bNo
        }
 }
 
-void register_node_type_cmp_output_multi_file(bNodeTreeType *ttype)
+/* write input data into layers */
+static void exec_output_file_multilayer(RenderData *rd, bNode *node, bNodeStack **in)
+{
+       Main *bmain= G.main; /* TODO, have this passed along */
+       NodeImageMultiFile *nimf= node->storage;
+       void *exrhandle= IMB_exr_get_handle();
+       char filename[FILE_MAX];
+       bNodeSocket *sock;
+       int i;
+       /* Must have consistent pixel size for exr file, simply take the first valid input size. */
+       int rectx = -1;
+       int recty = -1;
+       int has_preview = 0;
+       
+       BKE_makepicstring(filename, nimf->base_path, bmain->name, rd->cfra, R_IMF_IMTYPE_MULTILAYER, (rd->scemode & R_EXTENSION), TRUE);
+       BLI_make_existing_file(filename);
+       
+       for (sock=node->inputs.first, i=0; sock; sock=sock->next, ++i) {
+               if (in[i]->data) {
+                       CompBuf *cbuf = in[i]->data;
+                       char layname[EXR_LAY_MAXNAME];
+                       char channelname[EXR_PASS_MAXNAME];
+                       char *channelname_ext;
+                       
+                       if (cbuf->rect_procedural) {
+                               printf("Error writing multilayer EXR: Procedural buffer not supported\n");
+                               continue;
+                       }
+                       if (rectx < 0) {
+                               rectx = cbuf->x;
+                               recty = cbuf->y;
+                       }
+                       else if (cbuf->x != rectx || cbuf->y != recty) {
+                               printf("Error: Multilayer EXR output node %s expects same resolution for all input buffers. Layer %s skipped.\n", node->name, sock->name);
+                               continue;
+                       }
+                       
+                       BLI_strncpy(layname, sock->name, sizeof(layname));
+                       BLI_strncpy(channelname, sock->name, sizeof(channelname)-2);
+                       channelname_ext = channelname + strlen(channelname);
+                       
+                       /* create channels */
+                       switch (cbuf->type) {
+                       case CB_VAL:
+                               strcpy(channelname_ext, ".V");
+                               IMB_exr_add_channel(exrhandle, layname, channelname, 1, rectx, cbuf->rect);
+                               break;
+                       case CB_VEC2:
+                               strcpy(channelname_ext, ".X");
+                               IMB_exr_add_channel(exrhandle, layname, channelname, 2, 2*rectx, cbuf->rect);
+                               strcpy(channelname_ext, ".Y");
+                               IMB_exr_add_channel(exrhandle, layname, channelname, 2, 2*rectx, cbuf->rect+1);
+                               break;
+                       case CB_VEC3:
+                               strcpy(channelname_ext, ".X");
+                               IMB_exr_add_channel(exrhandle, layname, channelname, 3, 3*rectx, cbuf->rect);
+                               strcpy(channelname_ext, ".Y");
+                               IMB_exr_add_channel(exrhandle, layname, channelname, 3, 3*rectx, cbuf->rect+1);
+                               strcpy(channelname_ext, ".Z");
+                               IMB_exr_add_channel(exrhandle, layname, channelname, 3, 3*rectx, cbuf->rect+2);
+                               break;
+                       case CB_RGBA:
+                               strcpy(channelname_ext, ".R");
+                               IMB_exr_add_channel(exrhandle, layname, channelname, 4, 4*rectx, cbuf->rect);
+                               strcpy(channelname_ext, ".G");
+                               IMB_exr_add_channel(exrhandle, layname, channelname, 4, 4*rectx, cbuf->rect+1);
+                               strcpy(channelname_ext, ".B");
+                               IMB_exr_add_channel(exrhandle, layname, channelname, 4, 4*rectx, cbuf->rect+2);
+                               strcpy(channelname_ext, ".A");
+                               IMB_exr_add_channel(exrhandle, layname, channelname, 4, 4*rectx, cbuf->rect+3);
+                               break;
+                       }
+                       
+                       /* simply pick the first valid input for preview */
+                       if (!has_preview) {
+                               generate_preview(rd, node, cbuf);
+                               has_preview = 1;
+                       }
+               }
+       }
+       
+       /* when the filename has no permissions, this can fail */
+       if(IMB_exr_begin_write(exrhandle, filename, rectx, recty, nimf->format.compress)) {
+               IMB_exr_write_channels(exrhandle);
+       }
+       else {
+               /* TODO, get the error from openexr's exception */
+               /* XXX nice way to do report? */
+               printf("Error Writing Render Result, see console\n");
+       }
+       
+       IMB_exr_close(exrhandle);
+}
+
+static void exec_output_file(void *data, bNode *node, bNodeStack **in, bNodeStack **UNUSED(out))
+{
+       RenderData *rd= data;
+       NodeImageMultiFile *nimf= node->storage;
+       
+       if (!G.rendering) {
+               /* only output files when rendering a sequence -
+                * otherwise, it overwrites the output files just 
+                * scrubbing through the timeline when the compositor updates */
+               return;
+       }
+       
+       if (nimf->format.imtype==R_IMF_IMTYPE_MULTILAYER)
+               exec_output_file_multilayer(rd, node, in);
+       else
+               exec_output_file_singlelayer(rd, node, in);
+}
+
+void register_node_type_cmp_output_file(bNodeTreeType *ttype)
 {
        static bNodeType ntype;
 
-       node_type_base(ttype, &ntype, CMP_NODE_OUTPUT_MULTI_FILE, "Multi File Output", NODE_CLASS_OUTPUT, NODE_OPTIONS);
+       node_type_base(ttype, &ntype, CMP_NODE_OUTPUT_FILE, "File Output", NODE_CLASS_OUTPUT, NODE_OPTIONS|NODE_PREVIEW);
        node_type_socket_templates(&ntype, NULL, NULL);
        node_type_size(&ntype, 140, 80, 300);
-       node_type_init(&ntype, init_output_multi_file);
-       node_type_storage(&ntype, "NodeImageMultiFile", free_output_multi_file, copy_output_multi_file);
-       node_type_exec(&ntype, exec_output_multi_file);
+       node_type_init(&ntype, init_output_file);
+       node_type_storage(&ntype, "NodeImageMultiFile", free_output_file, copy_output_file);
+       node_type_update(&ntype, update_output_file, NULL);
+       node_type_exec(&ntype, exec_output_file);
 
        nodeRegisterType(ttype, &ntype);
 }