A couple more changes to the file and image nodes to improve access to layers that...
authorLukas Toenne <lukas.toenne@googlemail.com>
Fri, 11 May 2012 08:06:01 +0000 (08:06 +0000)
committerLukas Toenne <lukas.toenne@googlemail.com>
Fri, 11 May 2012 08:06:01 +0000 (08:06 +0000)
--- Changes to File Output node ---
* Flat layer names in EXR multilayer files.

 For a socket with name "AAA" the previous resulting EXR layer name would be "AAA.AAA", i.e. the render layer as well as render pass would use the socket name.

 Now the "render_layer.render_pass" scheme is ignored in multilayer files, socket names are directly written to EXR layers (EXR layer name is "AAA" in this example). If sockets should have a notion of "render layer" this can still be achieved by explicitly adding a separator, e.g. "AAA.BBB". When loading such layers into a Blender Image struct, the name is interpreted as a "render_layer.render_pass" again (although the image node does not care about it, see below).

* Socket sub-paths (for singlelayer) or layer names (for multilayer) are stored in dedicated string variables in the socket storage data. This way the RNA can define precise string subtypes (PROP_FILEPATH) and length. The file/layer slots are defined as separate structs with own name properties in the RNA as well, so they can be used nicely with the list template.

* Ensure unique socket paths/layer names to prevent overwriting of files and layers respectively.

--- Changes to Image node ---
* Loading multilayer OpenEXR files has improved layer name splitting into render layer + render pass names now. This properly supports arbitrary EXR layer names now.
  Example:

 OpenEXR layer name:   AAA.BBB.CCC

 is split into

 Render layer name:    AAA.BBB
 Render pass name:     CCC

 If the layer name has no '.' separators the render layer name is empty.

* Image node ignores the selected render layer in the image user data. Instead all existing layers are displayed at the same time by combining the render layer names with render pass names again, to reconstruct the original EXR layer name. This avoids the problem that render layers with empty name are not selectetable in the dropdown and allows using all image layers at the same time without duplicating the node.

source/blender/blenkernel/BKE_blender.h
source/blender/blenkernel/BKE_node.h
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/writefile.c
source/blender/editors/space_node/drawnode.c
source/blender/imbuf/intern/openexr/openexr_api.cpp
source/blender/makesdna/DNA_node_types.h
source/blender/makesrna/RNA_access.h
source/blender/makesrna/intern/rna_nodetree.c
source/blender/nodes/composite/nodes/node_composite_image.c
source/blender/nodes/composite/nodes/node_composite_outputFile.c

index 62f1dbc..29e0256 100644 (file)
@@ -42,7 +42,7 @@ extern "C" {
  * and keep comment above the defines.
  * Use STRINGIFY() rather than defining with quotes */
 #define BLENDER_VERSION                        263
-#define BLENDER_SUBVERSION             4
+#define BLENDER_SUBVERSION             5
 
 #define BLENDER_MINVERSION             250
 #define BLENDER_MINSUBVERSION  0
index fcb56bf..68b3e01 100644 (file)
@@ -684,6 +684,11 @@ void ntreeCompositClearTags(struct bNodeTree *ntree);
 struct bNodeSocket *ntreeCompositOutputFileAddSocket(struct bNodeTree *ntree, struct bNode *node,
                                                      const char *name, struct ImageFormatData *im_format);
 int ntreeCompositOutputFileRemoveActiveSocket(struct bNodeTree *ntree, struct bNode *node);
+void ntreeCompositOutputFileSetPath(struct bNode *node, struct bNodeSocket *sock, const char *name);
+void ntreeCompositOutputFileSetLayer(struct bNode *node, struct bNodeSocket *sock, const char *name);
+/* needed in do_versions */
+void ntreeCompositOutputFileUniquePath(struct ListBase *list, struct bNodeSocket *sock, const char defname[], char delim);
+void ntreeCompositOutputFileUniqueLayer(struct ListBase *list, struct bNodeSocket *sock, const char defname[], char delim);
 
 /* ************** TEXTURE NODES *************** */
 
index 5bf2c8e..c762614 100644 (file)
@@ -6840,6 +6840,52 @@ static void do_versions_nodetree_multi_file_output_path_2_64_0(bNodeTree *ntree)
        }
 }
 
+static void do_versions_nodetree_file_output_layers_2_64_5(bNodeTree *ntree)
+{
+       bNode *node;
+       
+       for (node=ntree->nodes.first; node; node=node->next) {
+               if (node->type==CMP_NODE_OUTPUT_FILE) {
+                       bNodeSocket *sock;
+                       for (sock=node->inputs.first; sock; sock=sock->next) {
+                               NodeImageMultiFileSocket *input = sock->storage;
+                               
+                               /* multilayer names are stored as separate strings now,
+                                * used the path string before, so copy it over.
+                                */
+                               BLI_strncpy(input->layer, input->path, sizeof(input->layer));
+                               
+                               /* paths/layer names also have to be unique now, initial check */
+                               ntreeCompositOutputFileUniquePath(&node->inputs, sock, input->path, '_');
+                               ntreeCompositOutputFileUniqueLayer(&node->inputs, sock, input->layer, '_');
+                       }
+               }
+       }
+}
+
+static void do_versions_nodetree_image_layer_2_64_5(bNodeTree *ntree)
+{
+       bNode *node;
+       
+       for (node=ntree->nodes.first; node; node=node->next) {
+               if (node->type==CMP_NODE_IMAGE) {
+                       ImageUser *iuser= (ImageUser *)node->storage;
+                       bNodeSocket *sock;
+                       for (sock=node->outputs.first; sock; sock=sock->next) {
+                               NodeImageLayer *output = MEM_callocN(sizeof(NodeImageLayer), "node image layer");
+                               
+                               /* take layer index from image user (this is ignored from now on) */
+                               output->layer_index = iuser->layer;
+                               /* take pass index both from current storage ptr (actually an int) */
+                               output->pass_index = GET_INT_FROM_POINTER(sock->storage);
+                               
+                               /* replace socket data pointer */
+                               sock->storage = output;
+                       }
+               }
+       }
+}
+
 static void do_versions(FileData *fd, Library *lib, Main *main)
 {
        /* WATCH IT!!!: pointers from libdata have not been converted */
@@ -7456,6 +7502,26 @@ static void do_versions(FileData *fd, Library *lib, Main *main)
                }
        }
 
+       if (main->versionfile < 263 || (main->versionfile == 263 && main->subversionfile < 5))
+       {
+               {
+                       /* file output node paths are now stored in the file info struct instead socket name */
+                       Scene *sce;
+                       bNodeTree *ntree;
+                       
+                       for (sce = main->scene.first; sce; sce=sce->id.next) {
+                               if (sce->nodetree) {
+                                       do_versions_nodetree_file_output_layers_2_64_5(sce->nodetree);
+                                       do_versions_nodetree_image_layer_2_64_5(sce->nodetree);
+                               }
+                       }
+                       for (ntree = main->nodetree.first; ntree; ntree=ntree->id.next) {
+                               do_versions_nodetree_file_output_layers_2_64_5(ntree);
+                               do_versions_nodetree_image_layer_2_64_5(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 27ecf25..f065ae2 100644 (file)
@@ -734,6 +734,11 @@ static void write_nodetree(WriteData *wd, bNodeTree *ntree)
                        for (sock=node->inputs.first; sock; sock=sock->next)
                                writestruct(wd, DATA, "NodeImageMultiFileSocket", 1, sock->storage);
                }
+               if (node->type==CMP_NODE_IMAGE) {
+                       /* write extra socket info */
+                       for (sock=node->outputs.first; sock; sock=sock->next)
+                               writestruct(wd, DATA, "NodeImageLayer", 1, sock->storage);
+               }
        }
        
        for (link= ntree->links.first; link; link= link->next)
index c2b7a1d..205202a 100644 (file)
@@ -1262,9 +1262,6 @@ static void node_composit_buts_image(uiLayout *layout, bContext *C, PointerRNA *
        }
 
        col= uiLayoutColumn(layout, 0);
-       
-       if (RNA_enum_get(&imaptr, "type")== IMA_TYPE_MULTILAYER)
-               uiItemR(col, ptr, "layer", 0, NULL, ICON_NONE);
 }
 
 static void node_composit_buts_renderlayers(uiLayout *layout, bContext *C, PointerRNA *ptr)
@@ -1709,25 +1706,30 @@ static void node_draw_input_file_output(const bContext *C, uiBlock *block,
                                          bNodeTree *ntree, bNode *node, bNodeSocket *sock,
                                          const char *UNUSED(name), int x, int y, int width)
 {
-       NodeImageMultiFileSocket *input = sock->storage;
        uiLayout *layout, *row;
        PointerRNA nodeptr, inputptr, imfptr;
        int imtype;
        int rx, ry;
        RNA_pointer_create(&ntree->id, &RNA_Node, node, &nodeptr);
-       RNA_pointer_create(&ntree->id, &RNA_NodeImageFileSocket, input, &inputptr);
        
        layout = uiBlockLayout(block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL, x, y+NODE_DY, width, 20, UI_GetStyle());
-       row = uiLayoutRow(layout, 0);           
-       
-       uiItemL(row, input->path, 0);
+       row = uiLayoutRow(layout, 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) {
+       if (imtype == R_IMF_IMTYPE_MULTILAYER) {
+               NodeImageMultiFileSocket *input = sock->storage;
+               RNA_pointer_create(&ntree->id, &RNA_NodeOutputFileSlotLayer, input, &inputptr);
+               
+               uiItemL(row, input->layer, 0);
+       }
+       else {
+               NodeImageMultiFileSocket *input = sock->storage;
                PropertyRNA *imtype_prop;
                const char *imtype_name;
+               RNA_pointer_create(&ntree->id, &RNA_NodeOutputFileSlotFile, input, &inputptr);
+               
+               uiItemL(row, input->path, 0);
                
                if (!RNA_boolean_get(&inputptr, "use_node_format"))
                        imfptr = RNA_pointer_get(&inputptr, "format");
@@ -1767,10 +1769,18 @@ static void node_composit_buts_file_output_details(uiLayout *layout, bContext *C
        
        uiItemO(layout, "Add Input", ICON_ZOOMIN, "NODE_OT_output_file_add_socket");
        
-       uiTemplateList(layout, C, ptr, "file_inputs", ptr, "active_input_index", NULL, 0, 0, 0);
-       
        active_index = RNA_int_get(ptr, "active_input_index");
-       RNA_property_collection_lookup_int(ptr, RNA_struct_find_property(ptr, "file_inputs"), active_index, &active_input_ptr);
+       /* using different collection properties if multilayer format is enabled */
+       if (multilayer) {
+               uiTemplateList(layout, C, ptr, "layer_slots", ptr, "active_input_index", NULL, 0, 0, 0);
+               RNA_property_collection_lookup_int(ptr, RNA_struct_find_property(ptr, "layer_slots"), active_index, &active_input_ptr);
+       }
+       else {
+               uiTemplateList(layout, C, ptr, "file_slots", ptr, "active_input_index", NULL, 0, 0, 0);
+               RNA_property_collection_lookup_int(ptr, RNA_struct_find_property(ptr, "file_slots"), active_index, &active_input_ptr);
+       }
+       /* XXX collection lookup does not return the ID part of the pointer, setting this manually here */
+       active_input_ptr.id.data = ptr->id.data;
        
        row = uiLayoutRow(layout, 1);
        op_ptr = uiItemFullO(row, "NODE_OT_output_file_move_active_socket", "", ICON_TRIA_UP, NULL, WM_OP_INVOKE_DEFAULT, UI_ITEM_O_RETURN_PROPS);
@@ -1779,19 +1789,25 @@ static void node_composit_buts_file_output_details(uiLayout *layout, bContext *C
        RNA_enum_set(&op_ptr, "direction", 2);
        
        if (active_input_ptr.data) {
-               uiLayout *row, *col;
-               
-               col = uiLayoutColumn(layout, 1);
-               if (multilayer)
-                       uiItemL(col, "Layer Name:", 0);
-               else
+               if (multilayer) {
+                       uiLayout *row, *col;
+                       col = uiLayoutColumn(layout, 1);
+                       
+                       uiItemL(col, "Layer:", 0);
+                       row = uiLayoutRow(col, 0);
+                       uiItemR(row, &active_input_ptr, "name", 0, "", 0);
+                       uiItemFullO(row, "NODE_OT_output_file_remove_active_socket", "", ICON_X, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_R_ICON_ONLY);
+               }
+               else {
+                       uiLayout *row, *col;
+                       col = uiLayoutColumn(layout, 1);
+                       
                        uiItemL(col, "File Path:", 0);
-               row = uiLayoutRow(col, 0);
-               uiItemR(row, &active_input_ptr, "path", 0, "", 0);
-               uiItemFullO(row, "NODE_OT_output_file_remove_active_socket", "", ICON_X, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_R_ICON_ONLY);
-               
-               /* in multilayer format all socket format details are ignored */
-               if (!multilayer) {
+                       row = uiLayoutRow(col, 0);
+                       uiItemR(row, &active_input_ptr, "path", 0, "", 0);
+                       uiItemFullO(row, "NODE_OT_output_file_remove_active_socket", "", ICON_X, NULL, WM_OP_EXEC_DEFAULT, UI_ITEM_R_ICON_ONLY);
+                       
+                       /* format details for individual files */
                        imfptr = RNA_pointer_get(&active_input_ptr, "format");
                        
                        col = uiLayoutColumn(layout, 1);
index 50bc55d..93a5f8e 100644 (file)
@@ -719,41 +719,55 @@ 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 maxlen = end-str;
+       int len = 0;
+       while (len < maxlen && *(end-len-1) != '.')
+               ++len;
+       
+       *token = end-len;
+       return len;
+}
+
 static int imb_exr_split_channel_name(ExrChannel *echan, char *layname, char *passname)
 {
-       int plen, len= strlen(echan->name);
+       const char *name = echan->name;
+       const char *end = name + strlen(name);
+       const char *token;
+       char tokenbuf[EXR_TOT_MAXNAME];
+       int len;
        
-       if (len < 4) {
-               printf("multilayer read: name too short: %s\n", echan->name);
+       /* last token is single character channel identifier */
+       len = imb_exr_split_token(name, end, &token);
+       if (len == 0) {
+               printf("multilayer read: bad channel name: %s\n", name);
                return 0;
        }
-       if (echan->name[len-2]!='.') {
-               printf("multilayer read: name has no Channel: %s\n", echan->name);
+       else if (len > 1) {
+               BLI_strncpy(tokenbuf, token, len);
+               printf("multilayer read: channel token too long: %s\n", tokenbuf);
                return 0;
        }
-       echan->chan_id= echan->name[len-1];
+       echan->chan_id = token[0];
+       end -= len + 1; /* +1 to skip '.' separator */
        
-       len-= 3;
-       while (len>=0) {
-               if (echan->name[len]=='.')
-                       break;
-               len--;
-       }
-       BLI_strncpy(passname, echan->name+len+1, EXR_PASS_MAXNAME);
-       plen= strlen(passname);
-       if (plen < 3) {
-               printf("multilayer read: should not happen: %s\n", echan->name);
+       /* second token is pass name */
+       len = imb_exr_split_token(name, end, &token);
+       if (len == 0) {
+               printf("multilayer read: bad channel name: %s\n", name);
                return 0;
        }
-       passname[plen-2]= 0;
+       BLI_strncpy(passname, token, len+1);
+       end -= len + 1; /* +1 to skip '.' separator */
+       
+       /* all preceding tokens combined as layer name */
+       if (end > name)
+               BLI_strncpy(layname, name, (int)(end-name)+1);
+       else
+               layname[0] = '\0';
        
-       if (len<1)
-               layname[0]= 0;
-       else {
-               BLI_strncpy(layname, echan->name, EXR_LAY_MAXNAME);
-               layname[len]= 0;
-       }
-       // printf("found lay %s pass %s chan %c\n", layname, passname, echan->chan_id);
        return 1;
 }
 
index 6874e8d..6fbaf17 100644 (file)
@@ -324,6 +324,14 @@ typedef struct NodeImageAnim {
        short pad;
 } NodeImageAnim;
 
+/* layer info for image node outputs */
+typedef struct NodeImageLayer {
+       /* index in the Image->layers and Image->layers->passes lists */
+       int layer_index, pass_index;
+       /* render pass flag, in case this is an original render pass */
+       int pass_flag;
+} NodeImageLayer;
+
 typedef struct NodeBlurData {
        short sizex, sizey;
        short samples, maxspeed, minspeed, relative, aspect;
@@ -364,11 +372,16 @@ typedef struct NodeImageMultiFile {
        int pad;
 } NodeImageMultiFile;
 typedef struct NodeImageMultiFileSocket {
+       /* single layer file output */
        short use_render_format  DNA_DEPRECATED;
        short use_node_format;  /* use overall node image format */
-       int pad2;
-       char path[1024];        /* 1024 = FILE_MAX */
+       int pad1;
+       char path[1024];                /* 1024 = FILE_MAX */
        ImageFormatData format;
+       
+       /* multilayer output */
+       char layer[30];         /* EXR_TOT_MAXNAME-2 ('.' and channel char are appended) */
+       char pad2[2];
 } NodeImageMultiFileSocket;
 
 typedef struct NodeChroma {
index 0cc4fa4..826ebb7 100644 (file)
@@ -349,10 +349,10 @@ extern StructRNA RNA_NlaTrack;
 extern StructRNA RNA_Node;
 extern StructRNA RNA_NodeForLoop;
 extern StructRNA RNA_NodeGroup;
-extern StructRNA RNA_NodeImageFileSocket;
+extern StructRNA RNA_NodeOutputFileSlotFile;
+extern StructRNA RNA_NodeOutputFileSlotLayer;
 extern StructRNA RNA_NodeLink;
 extern StructRNA RNA_NodeSocket;
-extern StructRNA RNA_NodeSocketPanel;
 extern StructRNA RNA_NodeTree;
 extern StructRNA RNA_NodeWhileLoop;
 extern StructRNA RNA_NoiseTexture;
index 3d2737f..bb1c50d 100644 (file)
@@ -487,18 +487,6 @@ static void rna_NodeSocketVector_range(PointerRNA *ptr, float *min, float *max,
        *softmax = val->max;
 }
 
-static void rna_Node_image_layer_update(Main *bmain, Scene *scene, PointerRNA *ptr)
-{
-       bNode *node = (bNode*)ptr->data;
-       Image *ima = (Image *)node->id;
-       ImageUser *iuser = node->storage;
-       
-       BKE_image_multilayer_index(ima->rr, iuser);
-       BKE_image_signal(ima, iuser, IMA_SIGNAL_SRC_CHANGE);
-       
-       rna_Node_update(bmain, scene, ptr);
-}
-
 static EnumPropertyItem *renderresult_layers_add_enum(RenderLayer *rl)
 {
        EnumPropertyItem *item = NULL;
@@ -518,24 +506,6 @@ static EnumPropertyItem *renderresult_layers_add_enum(RenderLayer *rl)
        return item;
 }
 
-static EnumPropertyItem *rna_Node_image_layer_itemf(bContext *UNUSED(C), PointerRNA *ptr,
-                                                    PropertyRNA *UNUSED(prop), int *free)
-{
-       bNode *node = (bNode*)ptr->data;
-       Image *ima = (Image *)node->id;
-       EnumPropertyItem *item = NULL;
-       RenderLayer *rl;
-       
-       if (!ima || !(ima->rr)) return NULL;
-
-       rl = ima->rr->layers.first;
-       item = renderresult_layers_add_enum(rl);
-       
-       *free = 1;
-       
-       return item;
-}
-
 static EnumPropertyItem *rna_Node_scene_layer_itemf(bContext *UNUSED(C), PointerRNA *ptr,
                                                     PropertyRNA *UNUSED(prop), int *free)
 {
@@ -853,25 +823,74 @@ static void rna_Mapping_Node_update(Main *bmain, Scene *scene, PointerRNA *ptr)
        rna_Node_update(bmain, scene, ptr);
 }
 
-static void rna_NodeOutputFile_file_inputs_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
+static void rna_NodeOutputFile_slots_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
 {
        bNode *node = ptr->data;
        rna_iterator_listbase_begin(iter, &node->inputs, NULL);
 }
 
-static PointerRNA rna_NodeOutputFile_file_inputs_get(CollectionPropertyIterator *iter)
+static PointerRNA rna_NodeOutputFile_slot_file_get(CollectionPropertyIterator *iter)
 {
        PointerRNA ptr;
        bNodeSocket *sock = rna_iterator_listbase_get(iter);
-       RNA_pointer_create(iter->ptr.id.data, &RNA_NodeImageFileSocket, sock->storage, &ptr);
+       RNA_pointer_create(iter->ptr.id.data, &RNA_NodeOutputFileSlotFile, sock->storage, &ptr);
        return ptr;
 }
 
-#else
+static PointerRNA rna_NodeOutputFile_slot_layer_get(CollectionPropertyIterator *iter)
+{
+       PointerRNA ptr;
+       bNodeSocket *sock = rna_iterator_listbase_get(iter);
+       RNA_pointer_create(iter->ptr.id.data, &RNA_NodeOutputFileSlotLayer, sock->storage, &ptr);
+       return ptr;
+}
 
-static EnumPropertyItem prop_image_layer_items[] = {
-{ 0, "PLACEHOLDER",          0, "Placeholder",          ""},
-{0, NULL, 0, NULL, NULL}};
+static int rna_NodeOutputFileSocket_find_node(bNodeTree *ntree, NodeImageMultiFileSocket *data, bNode **nodep, bNodeSocket **sockp)
+{
+       bNode *node;
+       bNodeSocket *sock;
+       
+       for (node= ntree->nodes.first; node; node= node->next) {
+               for (sock= node->inputs.first; sock; sock= sock->next) {
+                       NodeImageMultiFileSocket *sockdata = sock->storage;
+                       if (sockdata==data) {
+                               *nodep = node;
+                               *sockp = sock;
+                               return 1;
+                       }
+               }
+       }
+       
+       *nodep= NULL;
+       *sockp= NULL;
+       return 0;
+}
+
+static void rna_NodeOutputFileSlotFile_path_set(PointerRNA *ptr, const char *value)
+{
+       bNodeTree *ntree = ptr->id.data;
+       NodeImageMultiFileSocket *sockdata = ptr->data;
+       bNode *node;
+       bNodeSocket *sock;
+       
+       if (rna_NodeOutputFileSocket_find_node(ntree, sockdata, &node, &sock)) {
+               ntreeCompositOutputFileSetPath(node, sock, value);
+       }
+}
+
+static void rna_NodeOutputFileSlotLayer_name_set(PointerRNA *ptr, const char *value)
+{
+       bNodeTree *ntree = ptr->id.data;
+       NodeImageMultiFileSocket *sockdata = ptr->data;
+       bNode *node;
+       bNodeSocket *sock;
+       
+       if (rna_NodeOutputFileSocket_find_node(ntree, sockdata, &node, &sock)) {
+               ntreeCompositOutputFileSetLayer(node, sock, value);
+       }
+}
+
+#else
 
 static EnumPropertyItem prop_scene_layer_items[] = {
 { 0, "PLACEHOLDER",          0, "Placeholder",          ""},
@@ -1792,13 +1811,6 @@ static void def_cmp_image(StructRNA *srna)
                /* copied from the rna_image.c */
        RNA_def_property_ui_text(prop, "Auto-Refresh", "Always refresh image on frame changes");
        RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
-       
-       prop = RNA_def_property(srna, "layer", PROP_ENUM, PROP_NONE);
-       RNA_def_property_enum_sdna(prop, NULL, "layer");
-       RNA_def_property_enum_items(prop, prop_image_layer_items);
-       RNA_def_property_enum_funcs(prop, NULL, NULL, "rna_Node_image_layer_itemf");
-       RNA_def_property_ui_text(prop, "Layer", "");
-       RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_image_layer_update");
 }
 
 static void def_cmp_render_layers(StructRNA *srna)
@@ -1821,14 +1833,14 @@ static void def_cmp_render_layers(StructRNA *srna)
        RNA_def_property_update(prop, NC_NODE|NA_EDITED, "rna_Node_update");
 }
 
-static void rna_def_cmp_output_file_socket(BlenderRNA *brna)
+static void rna_def_cmp_output_file_slot_file(BlenderRNA *brna)
 {
        StructRNA *srna;
        PropertyRNA *prop;
        
-       srna = RNA_def_struct(brna, "NodeImageFileSocket", NULL);
+       srna = RNA_def_struct(brna, "NodeOutputFileSlotFile", NULL);
        RNA_def_struct_sdna(srna, "NodeImageMultiFileSocket");
-       RNA_def_struct_ui_text(srna, "Node Image File Socket", "Socket data of file output node");
+       RNA_def_struct_ui_text(srna, "Output File Slot", "Single layer file slot of the file output node");
        
        prop = RNA_def_property(srna, "use_node_format", PROP_BOOLEAN, PROP_NONE);
        RNA_def_property_boolean_sdna(prop, NULL, "use_node_format", 1);
@@ -1840,8 +1852,25 @@ static void rna_def_cmp_output_file_socket(BlenderRNA *brna)
        
        prop = RNA_def_property(srna, "path", PROP_STRING, PROP_FILEPATH);
        RNA_def_property_string_sdna(prop, NULL, "path");
-       RNA_def_property_ui_text(prop, "Path", "Subpath used for this input");
+       RNA_def_property_string_funcs(prop, NULL, NULL, "rna_NodeOutputFileSlotFile_path_set");
+       RNA_def_struct_name_property(srna, prop);
+       RNA_def_property_ui_text(prop, "Path", "Subpath used for this slot");
+       RNA_def_property_update(prop, NC_NODE|NA_EDITED, NULL);
+}
+static void rna_def_cmp_output_file_slot_layer(BlenderRNA *brna)
+{
+       StructRNA *srna;
+       PropertyRNA *prop;
+       
+       srna = RNA_def_struct(brna, "NodeOutputFileSlotLayer", NULL);
+       RNA_def_struct_sdna(srna, "NodeImageMultiFileSocket");
+       RNA_def_struct_ui_text(srna, "Output File Layer Slot", "Multilayer slot of the file output node");
+       
+       prop = RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+       RNA_def_property_string_sdna(prop, NULL, "layer");
+       RNA_def_property_string_funcs(prop, NULL, NULL, "rna_NodeOutputFileSlotLayer_name_set");
        RNA_def_struct_name_property(srna, prop);
+       RNA_def_property_ui_text(prop, "Name", "OpenEXR layer name used for this slot");
        RNA_def_property_update(prop, NC_NODE|NA_EDITED, NULL);
 }
 static void def_cmp_output_file(StructRNA *srna)
@@ -1863,11 +1892,21 @@ static void def_cmp_output_file(StructRNA *srna)
        prop = RNA_def_property(srna, "format", PROP_POINTER, PROP_NONE);
        RNA_def_property_struct_type(prop, "ImageFormatSettings");
        
-       prop = RNA_def_property(srna, "file_inputs", PROP_COLLECTION, PROP_NONE);
-       RNA_def_property_collection_funcs(prop, "rna_NodeOutputFile_file_inputs_begin", "rna_iterator_listbase_next", "rna_iterator_listbase_end",
-                                         "rna_NodeOutputFile_file_inputs_get", NULL, NULL, NULL, NULL);
-       RNA_def_property_struct_type(prop, "NodeImageFileSocket");
-       RNA_def_property_ui_text(prop, "File Inputs", "");
+       /* XXX using two different collections here for the same basic DNA list!
+        * Details of the output slots depend on whether the node is in Multilayer EXR mode.
+        */
+       
+       prop = RNA_def_property(srna, "file_slots", PROP_COLLECTION, PROP_NONE);
+       RNA_def_property_collection_funcs(prop, "rna_NodeOutputFile_slots_begin", "rna_iterator_listbase_next", "rna_iterator_listbase_end",
+                                         "rna_NodeOutputFile_slot_file_get", NULL, NULL, NULL, NULL);
+       RNA_def_property_struct_type(prop, "NodeOutputFileSlotFile");
+       RNA_def_property_ui_text(prop, "File Slots", "");
+       
+       prop = RNA_def_property(srna, "layer_slots", PROP_COLLECTION, PROP_NONE);
+       RNA_def_property_collection_funcs(prop, "rna_NodeOutputFile_slots_begin", "rna_iterator_listbase_next", "rna_iterator_listbase_end",
+                                         "rna_NodeOutputFile_slot_layer_get", NULL, NULL, NULL, NULL);
+       RNA_def_property_struct_type(prop, "NodeOutputFileSlotLayer");
+       RNA_def_property_ui_text(prop, "EXR Layer Slots", "");
 }
 
 static void def_cmp_dilate_erode(StructRNA *srna)
@@ -3536,7 +3575,8 @@ void RNA_def_nodetree(BlenderRNA *brna)
        define_specific_node(brna, NODE_FRAME, def_frame);
        
        /* special socket types */
-       rna_def_cmp_output_file_socket(brna);
+       rna_def_cmp_output_file_slot_file(brna);
+       rna_def_cmp_output_file_slot_layer(brna);
 }
 
 /* clean up macro definition */
index 02bb16f..4467fb1 100644 (file)
@@ -66,13 +66,17 @@ static bNodeSocketTemplate cmp_node_rlayers_out[]= {
        {       -1, 0, ""       }
 };
 
-static bNodeSocket *cmp_node_image_add_render_pass_output(bNodeTree *ntree, bNode *node, int UNUSED(pass), int rres_index)
+static bNodeSocket *cmp_node_image_add_render_pass_output(bNodeTree *ntree, bNode *node, int pass, int rres_index)
 {
        bNodeSocket *sock;
+       NodeImageLayer *sockdata;
        
        sock = node_add_output_from_template(ntree, node, &cmp_node_rlayers_out[rres_index]);
-       /* for render pass outputs store the pass type index as a lookup key */
-       sock->storage = SET_INT_IN_POINTER(rres_index);
+       /* extra socket info */
+       sockdata = MEM_callocN(sizeof(NodeImageLayer), "node image layer");
+       sock->storage = sockdata;
+       
+       sockdata->pass_flag = pass;
        
        return sock;
 }
@@ -141,21 +145,37 @@ static void cmp_node_image_add_render_pass_outputs(bNodeTree *ntree, bNode *node
                cmp_node_image_add_render_pass_output(ntree, node, SCE_PASS_TRANSM_COLOR, RRES_OUT_TRANSM_COLOR);
 }
 
-static void cmp_node_image_add_multilayer_outputs(bNodeTree *ntree, bNode *node, RenderLayer *rl)
+static void cmp_node_image_add_multilayer_outputs(bNodeTree *ntree, bNode *node, ListBase *layers)
 {
        bNodeSocket *sock;
+       NodeImageLayer *sockdata;
+       RenderLayer *rl;
        RenderPass *rpass;
-       int index;
-       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 = nodeAddSocket(ntree, node, SOCK_OUT, rpass->name, type);
-               /* for multilayer image use pass index directly as key */
-               sock->storage = SET_INT_IN_POINTER(index);
+       int layer_index, pass_index;
+       char name[30];  /* EXR_TOT_MAXNAME-2 ('.' and channel char are appended) */
+       int type;
+       
+       for (rl=layers->first, layer_index=0; rl; rl=rl->next, ++layer_index) {
+               for (rpass=rl->passes.first, pass_index=0; rpass; rpass=rpass->next, ++pass_index) {
+                       /* reconstruct layer name from <render layer>.<render pass> strings */
+                       if (rl->name[0] != '\0')
+                               BLI_snprintf(name, sizeof(name), "%s.%s", rl->name, rpass->name);
+                       else
+                               BLI_strncpy(name, rpass->name, sizeof(name));
+                       
+                       if (rpass->channels == 1)
+                               type = SOCK_FLOAT;
+                       else
+                               type = SOCK_RGBA;
+                       
+                       sock = nodeAddSocket(ntree, node, SOCK_OUT, name, type);
+                       /* extra socket info */
+                       sockdata = MEM_callocN(sizeof(NodeImageLayer), "node image layer");
+                       sock->storage = sockdata;
+                       
+                       sockdata->layer_index = layer_index;
+                       sockdata->pass_index = pass_index;
+               }
        }
 }
 
@@ -169,16 +189,16 @@ static void cmp_node_image_create_outputs(bNodeTree *ntree, bNode *node)
                BKE_image_get_ibuf(ima, iuser);
                
                if (ima->rr) {
-                       RenderLayer *rl= BLI_findlink(&ima->rr->layers, iuser->layer);
-                       
-                       if (rl) {
-                               if (ima->type!=IMA_TYPE_MULTILAYER)
+                       if (ima->type == IMA_TYPE_MULTILAYER) {
+                               cmp_node_image_add_multilayer_outputs(ntree, node, &ima->rr->layers);
+                       }
+                       else {
+                               RenderLayer *rl= BLI_findlink(&ima->rr->layers, iuser->layer);
+                               if (rl)
                                        cmp_node_image_add_render_pass_outputs(ntree, node, rl->passflag);
                                else
-                                       cmp_node_image_add_multilayer_outputs(ntree, node, rl);
+                                       cmp_node_image_add_render_pass_outputs(ntree, node, RRES_OUT_IMAGE|RRES_OUT_ALPHA);
                        }
-                       else
-                               cmp_node_image_add_render_pass_outputs(ntree, node, RRES_OUT_IMAGE|RRES_OUT_ALPHA);
                }
                else
                        cmp_node_image_add_render_pass_outputs(ntree, node, RRES_OUT_IMAGE|RRES_OUT_ALPHA|RRES_OUT_Z);
@@ -261,6 +281,7 @@ static void cmp_node_image_verify_outputs(bNodeTree *ntree, bNode *node)
         */
        for (oldsock=oldsocklist.first; oldsock; oldsock=oldsock_next) {
                oldsock_next = oldsock->next;
+               MEM_freeN(oldsock->storage);
                nodeRemoveSocket(ntree, node, oldsock);
        }
 }
@@ -394,17 +415,21 @@ static CompBuf *node_composit_get_zimage(bNode *node, RenderData *rd)
 }
 
 /* check if layer is available, returns pass buffer */
-static CompBuf *compbuf_multilayer_get(RenderData *rd, RenderLayer *rl, Image *ima, ImageUser *iuser, int passindex)
+static CompBuf *compbuf_multilayer_get(RenderData *rd, Image *ima, ImageUser *iuser, int layer_index, int pass_index)
 {
-       RenderPass *rpass = BLI_findlink(&rl->passes, passindex);
-       if (rpass) {
-               CompBuf *cbuf;
-               
-               iuser->pass = passindex;
-               BKE_image_multilayer_index(ima->rr, iuser);
-               cbuf = node_composit_get_image(rd, ima, iuser);
-               
-               return cbuf;
+       RenderLayer *rl = BLI_findlink(&ima->rr->layers, layer_index);
+       if (rl) {
+               RenderPass *rpass = BLI_findlink(&rl->passes, pass_index);
+               if (rpass) {
+                       CompBuf *cbuf;
+                       
+                       iuser->layer = layer_index;
+                       iuser->pass = pass_index;
+                       BKE_image_multilayer_index(ima->rr, iuser);
+                       cbuf = node_composit_get_image(rd, ima, iuser);
+                       
+                       return cbuf;
+               }
        }
        return NULL;
 }
@@ -422,22 +447,20 @@ static void node_composit_exec_image(void *data, bNode *node, bNodeStack **UNUSE
                /* first set the right frame number in iuser */
                BKE_image_user_frame_calc(iuser, rd->cfra, 0);
                
-               /* force a load, we assume iuser index will be set OK anyway */
-               if (ima->type==IMA_TYPE_MULTILAYER)
+               if (ima->type==IMA_TYPE_MULTILAYER) {
+                       /* force a load, we assume iuser index will be set OK anyway */
                        BKE_image_get_ibuf(ima, iuser);
                
-               if (ima->type==IMA_TYPE_MULTILAYER && ima->rr) {
-                       RenderLayer *rl= BLI_findlink(&ima->rr->layers, iuser->layer);
-                       
-                       if (rl) {
+                       if (ima->rr) {
                                bNodeSocket *sock;
+                               NodeImageLayer *sockdata;
                                int out_index;
                                CompBuf *combinedbuf= NULL, *firstbuf= NULL;
                                
                                for (sock=node->outputs.first, out_index=0; sock; sock=sock->next, ++out_index) {
-                                       int passindex = GET_INT_FROM_POINTER(sock->storage);
+                                       sockdata = sock->storage;
                                        if (out[out_index]->hasoutput) {
-                                               CompBuf *stackbuf = out[out_index]->data = compbuf_multilayer_get(rd, rl, ima, iuser, passindex);
+                                               CompBuf *stackbuf = out[out_index]->data = compbuf_multilayer_get(rd, ima, iuser, sockdata->layer_index, sockdata->pass_index);
                                                if (stackbuf) {
                                                        /* preview policy: take first 'Combined' pass if available,
                                                         * otherwise just use the first layer.
@@ -446,7 +469,7 @@ static void node_composit_exec_image(void *data, bNode *node, bNodeStack **UNUSE
                                                                firstbuf = stackbuf;
                                                        }
                                                        if (!combinedbuf &&
-                                                           (strcmp(sock->name, "Combined") == 0 || strcmp(sock->name, "Image") == 0))
+                                                                       (strcmp(sock->name, "Combined") == 0 || strcmp(sock->name, "Image") == 0))
                                                        {
                                                                combinedbuf = stackbuf;
                                                        }
@@ -520,6 +543,28 @@ static void node_composit_init_image(bNodeTree *ntree, bNode* node, bNodeTemplat
        cmp_node_image_verify_outputs(ntree, node);
 }
 
+static void node_composit_free_image(bNode *node)
+{
+       bNodeSocket *sock;
+       
+       /* free extra socket info */
+       for (sock=node->outputs.first; sock; sock=sock->next)
+               MEM_freeN(sock->storage);
+       
+       MEM_freeN(node->storage);
+}
+
+static void node_composit_copy_image(bNode *orig_node, bNode *new_node)
+{
+       bNodeSocket *sock;
+       
+       new_node->storage= MEM_dupallocN(orig_node->storage);
+       
+       /* copy extra socket info */
+       for (sock=orig_node->outputs.first; sock; sock=sock->next)
+               sock->new_sock->storage = MEM_dupallocN(sock->storage);
+}
+
 void register_node_type_cmp_image(bNodeTreeType *ttype)
 {
        static bNodeType ntype;
@@ -527,7 +572,7 @@ void register_node_type_cmp_image(bNodeTreeType *ttype)
        node_type_base(ttype, &ntype, CMP_NODE_IMAGE, "Image", NODE_CLASS_INPUT, NODE_PREVIEW|NODE_OPTIONS);
        node_type_size(&ntype, 120, 80, 300);
        node_type_init(&ntype, node_composit_init_image);
-       node_type_storage(&ntype, "ImageUser", node_free_standard_storage, node_copy_standard_storage);
+       node_type_storage(&ntype, "ImageUser", node_composit_free_image, node_composit_copy_image);
        node_type_update(&ntype, cmp_node_image_update, NULL);
        node_type_exec(&ntype, node_composit_exec_image);
 
index 18a5350..a571b14 100644 (file)
 
 /* **************** OUTPUT FILE ******************** */
 
+/* find unique path */
+static int unique_path_unique_check(void *arg, const char *name)
+{
+       struct {ListBase *lb; bNodeSocket *sock;} *data= arg;
+       bNodeSocket *sock;
+       for (sock=data->lb->first; sock; sock=sock->next) {
+               if (sock != data->sock) {
+                       NodeImageMultiFileSocket *sockdata = sock->storage;
+                       if (strcmp(sockdata->path, name)==0)
+                               return 1;
+               }
+       }
+       return 0;
+}
+void ntreeCompositOutputFileUniquePath(ListBase *list, bNodeSocket *sock, const char defname[], char delim)
+{
+       NodeImageMultiFileSocket *sockdata;
+       struct {ListBase *lb; bNodeSocket *sock;} data;
+       data.lb = list;
+       data.sock = sock;
+
+       /* See if we are given an empty string */
+       if (ELEM(NULL, sock, defname))
+               return;
+
+       sockdata = sock->storage;
+       BLI_uniquename_cb(unique_path_unique_check, &data, defname, delim, sockdata->path, sizeof(sockdata->path));
+}
+
+/* find unique EXR layer */
+static int unique_layer_unique_check(void *arg, const char *name)
+{
+       struct {ListBase *lb; bNodeSocket *sock;} *data= arg;
+       bNodeSocket *sock;
+       for (sock=data->lb->first; sock; sock=sock->next) {
+               if (sock != data->sock) {
+                       NodeImageMultiFileSocket *sockdata = sock->storage;
+                       if (strcmp(sockdata->layer, name)==0)
+                               return 1;
+               }
+       }
+       return 0;
+}
+void ntreeCompositOutputFileUniqueLayer(ListBase *list, bNodeSocket *sock, const char defname[], char delim)
+{
+       NodeImageMultiFileSocket *sockdata;
+       struct {ListBase *lb; bNodeSocket *sock;} data;
+       data.lb = list;
+       data.sock = sock;
+
+       /* See if we are given an empty string */
+       if (ELEM(NULL, sock, defname))
+               return;
+
+       sockdata = sock->storage;
+       BLI_uniquename_cb(unique_layer_unique_check, &data, defname, delim, sockdata->layer, sizeof(sockdata->layer));
+}
+
 bNodeSocket *ntreeCompositOutputFileAddSocket(bNodeTree *ntree, bNode *node, const char *name, ImageFormatData *im_format)
 {
        NodeImageMultiFile *nimf = node->storage;
@@ -54,7 +112,10 @@ bNodeSocket *ntreeCompositOutputFileAddSocket(bNodeTree *ntree, bNode *node, con
        NodeImageMultiFileSocket *sockdata = MEM_callocN(sizeof(NodeImageMultiFileSocket), "socket image format");
        sock->storage = sockdata;
        
-       BLI_strncpy(sockdata->path, name, sizeof(sockdata->path));
+       BLI_strncpy_utf8(sockdata->path, name, sizeof(sockdata->path));
+       ntreeCompositOutputFileUniquePath(&node->inputs, sock, name, '_');
+       BLI_strncpy_utf8(sockdata->layer, name, sizeof(sockdata->layer));
+       ntreeCompositOutputFileUniqueLayer(&node->inputs, sock, name, '_');
        
        if (im_format) {
                sockdata->format= *im_format;
@@ -89,6 +150,20 @@ int ntreeCompositOutputFileRemoveActiveSocket(bNodeTree *ntree, bNode *node)
        return 1;
 }
 
+void ntreeCompositOutputFileSetPath(bNode *node, bNodeSocket *sock, const char *name)
+{
+       NodeImageMultiFileSocket *sockdata = sock->storage;
+       BLI_strncpy_utf8(sockdata->path, name, sizeof(sockdata->path));
+       ntreeCompositOutputFileUniquePath(&node->inputs, sock, name, '_');
+}
+
+void ntreeCompositOutputFileSetLayer(bNode *node, bNodeSocket *sock, const char *name)
+{
+       NodeImageMultiFileSocket *sockdata = sock->storage;
+       BLI_strncpy_utf8(sockdata->layer, name, sizeof(sockdata->layer));
+       ntreeCompositOutputFileUniqueLayer(&node->inputs, sock, name, '_');
+}
+
 static void init_output_file(bNodeTree *ntree, bNode* node, bNodeTemplate *ntemp)
 {
        NodeImageMultiFile *nimf= MEM_callocN(sizeof(NodeImageMultiFile), "node image multi file");
@@ -232,8 +307,7 @@ static void exec_output_file_multilayer(RenderData *rd, bNode *node, bNodeStack
                if (in[i]->data) {
                        NodeImageMultiFileSocket *sockdata = sock->storage;
                        CompBuf *cbuf = in[i]->data;
-                       char layname[EXR_LAY_MAXNAME];
-                       char channelname[EXR_PASS_MAXNAME];
+                       char channelname[EXR_TOT_MAXNAME];      /* '.' and single character channel name is appended */
                        char *channelname_ext;
                        
                        if (cbuf->rect_procedural) {
@@ -249,39 +323,38 @@ static void exec_output_file_multilayer(RenderData *rd, bNode *node, bNodeStack
                                continue;
                        }
                        
-                       BLI_strncpy(layname, sockdata->path, sizeof(layname));
-                       BLI_strncpy(channelname, sockdata->path, sizeof(channelname)-2);
+                       BLI_strncpy(channelname, sockdata->layer, 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);
+                               IMB_exr_add_channel(exrhandle, NULL, 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);
+                               IMB_exr_add_channel(exrhandle, NULL, channelname, 2, 2*rectx, cbuf->rect);
                                strcpy(channelname_ext, ".Y");
-                               IMB_exr_add_channel(exrhandle, layname, channelname, 2, 2*rectx, cbuf->rect+1);
+                               IMB_exr_add_channel(exrhandle, NULL, 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);
+                               IMB_exr_add_channel(exrhandle, NULL, channelname, 3, 3*rectx, cbuf->rect);
                                strcpy(channelname_ext, ".Y");
-                               IMB_exr_add_channel(exrhandle, layname, channelname, 3, 3*rectx, cbuf->rect+1);
+                               IMB_exr_add_channel(exrhandle, NULL, channelname, 3, 3*rectx, cbuf->rect+1);
                                strcpy(channelname_ext, ".Z");
-                               IMB_exr_add_channel(exrhandle, layname, channelname, 3, 3*rectx, cbuf->rect+2);
+                               IMB_exr_add_channel(exrhandle, NULL, 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);
+                               IMB_exr_add_channel(exrhandle, NULL, channelname, 4, 4*rectx, cbuf->rect);
                                strcpy(channelname_ext, ".G");
-                               IMB_exr_add_channel(exrhandle, layname, channelname, 4, 4*rectx, cbuf->rect+1);
+                               IMB_exr_add_channel(exrhandle, NULL, channelname, 4, 4*rectx, cbuf->rect+1);
                                strcpy(channelname_ext, ".B");
-                               IMB_exr_add_channel(exrhandle, layname, channelname, 4, 4*rectx, cbuf->rect+2);
+                               IMB_exr_add_channel(exrhandle, NULL, channelname, 4, 4*rectx, cbuf->rect+2);
                                strcpy(channelname_ext, ".A");
-                               IMB_exr_add_channel(exrhandle, layname, channelname, 4, 4*rectx, cbuf->rect+3);
+                               IMB_exr_add_channel(exrhandle, NULL, channelname, 4, 4*rectx, cbuf->rect+3);
                                break;
                        }