UDIM generation / Rescale UDIM images broken in Blender 3.1

Issue #922 resolved
Uwe Schmidt created an issue

Blender 3.1 changed the way UDIM textures are treated.

When converting to UDIM, the first image is copied to all tiles.

When Rescaling UDIMS, nothing gets rescaled.

When changing the Resolution of UDIMS, even with older files, the resolution is unchanged.

The internal filename now has to have “<UDIM>” instead of the numbers, which probably causes the problems.

Comments (18)

  1. AnonymousBlender

    I’m having huge issues with UDIM’s too, but I think it’s Blender, and not the addon. It’s doing really messy stuff like UDIM tiles are disappearing, and not recognizing uv’s at all.

  2. Uwe Schmidt reporter

    All my imported figures in 3.0 have UDIM textures. I work with Eevee, so I like to have as few shaders as possible. I usually just have one “dry skin” and one “wet skin” shader for the relevant parts, both using the same UDIM image files.

    If you’re having troubles with UDIMs in Blender 3.0, I can make a video showing you my workflow. There are some cases in which I have to manually correct the images, but it’s rare. I did not have any problems with UVs, but you have to make sure that you use the correct uv set. Genesis 8.1 uv has a neck map on tile 1002, Genesis 8.0 uv has not. Use the right uv set for your map, usually the meshes have two sets.

    Most of the time, changing textures to UDIM with Diffeomorphic in the older versions works like a charm. What’s especially great is that the resize option also works, so I can have a background character with very low-res textures to save memory on the GPU.

  3. AnonymousBlender

    After looking into it further it seems the UVs are being destroyed somehow. I also did not “Merge UV’s“ . When I look through the available UV maps in the Object Data properties tab most fo the UV’s are just flat out gone. This is with manually setting the UDIM tiles too. I’m going to do some more experimenting. But I suspect they changed some stuff in 3.1 that is conflicting. AS well that I am an idiot. lol

    A screenie just to show what I’m talking about. LIke I said I’m gonna experiment some more.

  4. AnonymousBlender

    That was
    Import > Save FIle > Save local Textures > Set UDIM Tiles > Merge Geografts (merge UV’s unchecked) > Set UDIM Tile (Set UDIMs to default for most of the model, Geograpft set to 1008 tile) > Make UDIM Materials
    With everything checked because it should all be sorted to the proper tiles. Unless I understood the documentation wrong.

    Like I said I’m probably doing things I’m not supposed to.

  5. Uwe Schmidt reporter

    What is the first “Set UDIM Tile” for? I understand moving the Geograft to 1008, but the other UVs should already be UDIM?

  6. Uwe Schmidt reporter

    I don’t use “Set UDIM Tile” at all. The UVs are already UDIM. After importing, I use “Make UDIM Materials” for all material zones after selecting the main figure, I don’t Merge the materials and I need no fixing. Most of the time, all textures are UDIM afterwards. I almost never touch the UVs. When working with Geografts, I use a separate UV node in the shader to make sure that the textures work correctly. I merge the materials myself in edit mode.

  7. AnonymousBlender

    I was going to 1008 because parts of GP use the torso, and other parts use their own texture. Thats why I was doing that. For the most part just setting single principled takes care of what I need anyway. So it really is a non-issue. I was just experimenting. I’ve been kneck deep in Maya crap for school and needed to change it up.

  8. Uwe Schmidt reporter

    I modified the materials.py so that rescaling and changing the resolution on UDIMs works again. I’m not at the current version, though.

    I added a new parameter to getAllTextures and getTreeTextures, because we need different results for rescaling and changing the resolution.

    In the actual Blender version, the file path must be xxx_<UDIM> instead of _1001 - that’s why the change is needed.

    diff --git a/material.py b/material.py
    index 8099716..9352e42 100644
    --- a/material.py
    +++ b/material.py
    @@ -1361,12 +1361,12 @@ class ChangeResolution():
                 self.filenames.append(fname)
    
    
    -    def getAllTextures(self, context):
    +    def getAllTextures(self, context, resolveUDIM):
             paths = {}
             for ob in getSelectedMeshes(context):
                 for mat in ob.data.materials:
                     if mat:
    -                    self.getTreeTextures(mat.node_tree, paths)
    +                    self.getTreeTextures(mat.node_tree, paths, resolveUDIM)
                 for psys in ob.particle_systems:
                     self.getSlotTextures(psys.settings, paths)
             return paths
    @@ -1378,7 +1378,7 @@ class ChangeResolution():
                     paths[mtex.texture.image.filepath] = True
    
    
    -    def getTreeTextures(self, tree, paths):
    +    def getTreeTextures(self, tree, paths, resolveUDIM):
             for node in tree.nodes.values():
                 if node.type == 'TEX_IMAGE' and node.image:
                     img = node.image
    @@ -1387,12 +1387,17 @@ class ChangeResolution():
                         for file1 in os.listdir(folder):
                             fname1,ext1 = os.path.splitext(file1)
                             if fname1[:-4] == basename and ext1 == ext:
    -                            path = os.path.join(folder, "%s%s" % (fname1, ext1))
    +                            if bpy.app.version >= (3, 1, 0) and resolveUDIM:
    +                                path = os.path.join(folder, "%s%s%s" % (fname1[:-4],'<UDIM>', ext1))
    +                            else:
    +                                path = os.path.join(folder, "%s%s" % (fname1, ext1))
                                 paths[path] = True
    +                            
    +                            
                     else:
                         paths[img.filepath] = True
                 elif node.type == 'GROUP':
    -                self.getTreeTextures(node.node_tree, paths)
    +                self.getTreeTextures(node.node_tree, paths, resolveUDIM)
    
    
         def getTiledPath(self, filepath):
    @@ -1400,8 +1405,11 @@ class ChangeResolution():
             path = bpy.path.reduce_dirs([path])[0]
             folder = os.path.dirname(path)
             fname,ext = os.path.splitext(bpy.path.basename(path))
    -        return folder, fname[:-4], ext
    -
    +        if fname[-6:] == '<UDIM>':
    +            return folder, fname[:-6], ext
    +        else:
    +            return folder, fname[:-4], ext
    +        
    
         def replaceTextures(self, context):
             for ob in getSelectedMeshes(context):
    @@ -1441,6 +1449,11 @@ class ChangeResolution():
                   fname[-5] == "_" and
                   fname[-4:].isdigit()):
                 return "%s%s%s" % (fname[:-10], fname[-5:], ext)
    +        elif (fname[-12:-8] == "-res" and
    +              fname[-8].isdigit() and
    +              fname[-7] == "_" and
    +              fname[-6:] == '<UDIM>'):
    +            return "%s%s%s" % (fname[:-12], fname[-7:], ext)
             else:
                 return path
    
    @@ -1460,7 +1473,10 @@ class ChangeResolution():
    
             newname,newpath = self.getNewPath(path)
             if img.source == 'TILED':
    -            newname = newname[:-5]
    +            if newname[-6:] == '<UDIM>':
    +                newname = newname[:-7]
    +            else:
    +                newname = newname[:-5]
             if newpath == img.filepath:
                 return img
             elif newpath in images.keys():
    @@ -1485,7 +1501,7 @@ class ChangeResolution():
    
         def loadNewImage(self, img, newpath):
             print('Replace "%s" with "%s"' % (img.filepath, newpath))
    -        if img.source == 'TILED':
    +        if img.source == 'TILED' :
                 folder,basename,ext = self.getTiledPath(newpath)
                 newimg = None
                 print("Tiles:")
    @@ -1500,9 +1516,13 @@ class ChangeResolution():
                             newimg.source = 'TILED'
                             tile = img.tiles[0]
                             tile.number = udim
    +                        if bpy.app.version >= (3, 1, 0):
    +                            path2,ext2 = os.path.splitext(newimg.filepath)
    +                            newimg.filepath = "%s%s%s" % (path2[:-4],'<UDIM>',ext2)
    +                            newimg.name=basename[:-1]
                         else:
                             newimg.tiles.new(tile_number = udim)
    -                    print('  "%s"' % file1)
    +                    print('  "%s"' %  file1)   
                 return newimg
             else:
                 return bpy.data.images.load(newpath)
    @@ -1514,6 +1534,8 @@ class ChangeResolution():
                 newbase = base
             elif len(base) > 5 and base[-5] == "_" and base[-4:].isdigit():
                 newbase = ("%s-res%d%s" % (base[:-5], self.steps, base[-5:]))
    +        elif len(base) > 7 and base[-7:] == '_<UDIM>':
    +            newbase = ("%s-res%d%s" % (base[:-7], self.steps, base[-7:]))
             else:
                 newbase = ("%s-res%d" % (base, self.steps))
             newname = bpy.path.basename(newbase)
    @@ -1542,7 +1564,7 @@ class DAZ_OT_ChangeResolution(DazOperator, ChangeResolution):
    
         def run(self, context):
             self.overwrite = False
    -        paths = self.getAllTextures(context)
    +        paths = self.getAllTextures(context,True)
             self.getFileNames(paths.keys())
             self.replaceTextures(context)
    
    @@ -1570,7 +1592,7 @@ class DAZ_OT_ResizeTextures(DazOperator, ImageFile, MultiFile, ChangeResolution)
    
         def run(self, context):
             if self.resizeAll:
    -            paths = self.getAllTextures(context)
    +            paths = self.getAllTextures(context, False)
             else:
                 paths = self.getMultiFiles(G.theImageExtensions)
             self.getFileNames(paths)
    

  9. Thomas Larsson repo owner

    The patch has been incorporated in the latest commit. I noticed that the behaviour is different now. Before it was enough to do Resize Textures, but now this tool only creates the reduced image files. Change Resolution must be explicitly called to actually replace the textures in the materials. Well, it is not a big deal once you know about it, and it is arguably cleaner if each tool does one thing only.

  10. Uwe Schmidt reporter

    Yes, I should have mentioned this. At least it works.

    “Make UDIM” is still broken, though. Maybe I find time for that at the weekend.

    By the way, is there a way to update the changes to the source code without restarting Blender?

  11. Thomas Larsson repo owner

    I use the Blender 2.7x keymap and hit F8 all the time to reload scripts. However, in the default Blender keymap no key appears to be associated with reloading scripts.

  12. Uwe Schmidt reporter

    Found time. This fixes UDIM creation:

    diff --git a/udim.py b/udim.py
    index 09fb650..099374c 100644
    --- a/udim.py
    +++ b/udim.py
    @@ -134,7 +134,9 @@ class DAZ_OT_UdimizeMaterials(DazPropsOperator, MaterialSelector):
                             img.name = self.makeImageName(basename, atile, img)
                             node.label = basename
                             node.name = basename
    -
    +            if bpy.app.version >= (3, 1, 0):
    +                path2,ext2 = os.path.splitext(anode.image.filepath)
    +                anode.image.filepath = "%s%s%s" % (path2[:-4],'<UDIM>',ext2)
                 img = anode.image
                 tile0 = img.tiles[0]
                 for udim,mname in udims.items():
    @@ -226,6 +228,8 @@ class DAZ_OT_UdimizeMaterials(DazPropsOperator, MaterialSelector):
             src = bpy.path.reduce_dirs([src])[0]
             folder = os.path.dirname(src)
             fname,ext = os.path.splitext(bpy.path.basename(src))
    +        if fname[-6:] == '<UDIM>':
    +            src = os.path.join(folder, "%s_%d%s" % (fname[-6], 1001+udim, ext))
             trg = os.path.join(folder, "%s_%d%s" % (basename, 1001+udim, ext))
             if src != trg and not os.path.exists(trg):
                 print("Copy %s\n => %s" % (src, trg))
    

  13. Log in to comment