6
$\begingroup$

I don't know how these duplicated node groups appeared, but I did not create them intentionally. Is there a way to remove them without breaking anything (for instance the materials that use the duplicates)? Or rather - is there a way to merge the duplicates into the original node group?

If yes, is there a way to do it with other types of datablocks, like whole materials?

enter image description here

$\endgroup$
4
$\begingroup$

It is indeed annoying when you append materials from your library file and Blender automatically creates duplicates for all node groups with the same name, even when the groups are supposed to be exactly the same.

I have created a script that eliminates these duplicates efficiently for my usage. It searches all existing node groups, materials, worlds and if finds any duplicates (ending with .001, .002, etc), it replaces them with the original group if it is found.

After running the script the eliminated node groups will have 0 users, so after you save and reopen your file, they should disappear and your group list should be clean :)

import bpy

def main():
    print("\nEliminate Node Group Duplicates:")

    #--- Search for duplicates in actual node groups
    node_groups = bpy.data.node_groups

    for group in node_groups:
        for node in group.nodes:
            if node.type == 'GROUP':
                eliminate(node)

    #--- Search for duplicates in materials
    mats = list(bpy.data.materials)
    worlds = list(bpy.data.worlds)

    for mat in mats + worlds:
        if mat.use_nodes:
            for node in mat.node_tree.nodes:
                if node.type == 'GROUP':
                    eliminate(node)

#--- Eliminate the node group duplicate with the original group if found
def eliminate(node):
    node_groups = bpy.data.node_groups

    # Get the node group name as 3-tuple (base, separator, extension)
    (base, sep, ext) = node.node_tree.name.rpartition('.')

    # Replace the numeric duplicate
    if ext.isnumeric():
        if base in node_groups:
            print("  Replace '%s' with '%s'" % (node.node_tree.name, base))
            node.node_tree.use_fake_user = False
            node.node_tree = node_groups.get(base)

#--- Execute main
main()

A variant to eliminate OSL scripts

I never used OSL scripts, but as @IPv6 have asked, I guess this should work to eliminate duplicates of OSL scripts:

import bpy

print("\nEliminate OSL Scripts:")
texts = bpy.data.texts

for mat in bpy.data.materials:
    if mat.use_nodes:
        for node in mat.node_tree.nodes:
            if node.type == 'SCRIPT':
                name = node.script.name
                (base, sep, ext) = name.rpartition('.')

                # Replace the numbered duplicate with the original if found
                if ext.isnumeric():
                    if base in texts:
                        print("  Replace '%s' with '%s'" % (name, base))

                        # Replace the script duplicate for this node
                        node.script = texts.get(base)

                        # Remove the script duplicate from bpy.data.texts ?
                        texts.get(name).user_clear()      
$\endgroup$
3
  • $\begingroup$ Thanks for code, works like a charm! BTW, is it possible with scrips? Importing node with OSL script of the same name create osl duplications as well (when script is added via "internal" mode of script node) // $\endgroup$
    – IPv6
    Aug 15 '17 at 15:41
  • 1
    $\begingroup$ @IPv6 I haven't used OSL scripts yet, but I guess it should work a similar way like with groups. Just search them by node.type == 'SCRIPT' and the actual scripts are stored in bpy.data.texts. So perhaps this code could work: (oops too long to post here) $\endgroup$
    – Miki
    Aug 24 '17 at 12:02
  • $\begingroup$ Thanks a lot! I modified your script (for proper and separate removing texts after scripts, in particular) and here it is, operator to do nodegroup/script cleanup after re-appending data from other blends: gist.github.com/IPv6/0886731e6e98b6968cb1ffa0b8d5900e $\endgroup$
    – IPv6
    Aug 27 '17 at 9:38
1
$\begingroup$

Any object that isn't being "used" is automatically deleted by Blender when the scene is closed. When they aren't being used, there's an 'O' next to their name, for 'orphaned.'

If these aren't orphaned, it's either because you clicked the 'F' next to their name to 'Force' keep them, or it's because there's some object that's using them.

If you want to delete them, you need to find the object(s) that they're associated with and disassociate them. With materials as an example, you'd click the 'x' next to the material to remove it, then find its name in the drop-down list and see if it's got an 'O'. If it doesn't, find the other object, rinse and repeat.

The only other possibility is if you've clicked the 'F' Blender has associated that thing with a 'non-object,' so that it's not orphaned. You'll have to assign it to some object, and then forcibly orphan it, so that it'll get deleted when you close Blender.

$\endgroup$
3
  • 2
    $\begingroup$ Any way to easily find materials they are in, if you have hundreds of materials? $\endgroup$ May 22 '16 at 10:00
  • $\begingroup$ I'm really not sure... $\endgroup$
    – Matt
    May 23 '16 at 13:11
  • $\begingroup$ @PLyczkowski Shift click the 'X' button to unlink all references to it. Though now you need to find each one and set it the the original... $\endgroup$ Jun 19 '16 at 14:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.