0
$\begingroup$

I'm writing an addon that bakes various passes to textures using cycles bake.

The script checks the geometry and works out which UDIM tiles the uvs on the mesh are occupying, creates a new image and then adds those tiles to the image. So far I've found two different ways to add tiles to the image:

Method 1:

image_name = obj.name + '_BakedTexture'
img = bpy.data.images.new(name=image_name, width=1024, height=1024, tiled=True,float_buffer=True, alpha=False)
new_tile = img.tiles.new(tile_number=1002)

This is my preferred method, since it avoids using bpy.ops, but I cant for the life of me figure out how to fill the UDIM tile.

Method 2:

image_name = obj.name + '_BakedTexture'
img = bpy.data.images.new(name=image_name, width=1024, height=1024, tiled=True,float_buffer=True, alpha=False)
bpy.context.area.ui_type = 'IMAGE_EDITOR'
bpy.context.space_data.image = img
bpy.ops.image.tile_add(number = 1002, generated_type = "UV_GRID", alpha=False, float=True)

Method 2 does everything I need it to do, since bpy.ops.image.tile_add has the added bonus of filling the tile, and this script works fine when run from the Text Editor in blender, but I get the following error when running it from a panel in the 3D View:

bpy.context.area.ui_type = 'IMAGE_EDITOR'
AttributeError: 'NoneType' object has no attribute 'ui_type'

I've looked at these two posts, which is how I got to this point:

UDIM tiles not importing properly image texture shader

Fill a UDIM tile with python API

But neither of them solves the above issue.

Thanks in advance!

$\endgroup$

1 Answer 1

0
$\begingroup$

My Solution

Hi, solution that worked for me after days of trying (context overriding):

    image = bpy.data.images[0] # your image
    # for my addon i use this kind of range specification,
    # but for you this might be just a simple **count = 10** variable
    start_tile_index = 1001 # start tile number
    end_tile_index = 1012 # end tile number
    
    image_area = None
    image_screen = None
    image_region = None
    old_area_type = None
    
    # NOTE: it is possible to find image/uv editor and execute fill there
    # but for some reason **context incorrect** error appears every time
    # finding image_editor area to exec image OTs
    # for screen in bpy.data.screens:
    #     for area in screen.areas:
    #         if area.ui_type in ['IMAGE_EDITOR', 'UV']:
    #             image_area = area
    #             for region in area.regions:
    #                 print(region.height, region.type)
    #                 if region.type == 'WINDOW':
    #                     image_region = region
    #             # XXX don't know if there's always a WINDOW region
    #             if image_region is None:
    #                 image_region = area.regions[0]
    #             image_screen = screen
    #             break
    
    # NOTE: but looks like it works with just changing current area
    # if not found: change context.area ui_type
    if any([image_area is None, image_screen is None]):
        old_area_type = context.area.ui_type
        context.area.ui_type = 'UV'
        image_area = context.area
        for region in image_area.regions:
            if region.type == 'WINDOW':
                image_region = region
        # XXX don't know if there's always a WINDOW region
        if image_region is None:
            image_region = area.regions[0]
        image_screen = context.screen

    # set image_editor active image
    old_active_image = image_area.spaces.active.image
    image_area.spaces.active.image = image

    # overriding context to avoid RuntimeError
    context_overridden = context.copy()
    context_overridden['area'] = image_area
    context_overridden['screen'] = image_screen
    context_overridden['region'] = image_region

    # filling existing tiles to get the ability to add new ones
    # i guess all tiles should be filled
    for tile_index, tile in enumerate(image.tiles):
        # setting active tile and filling it
        image.tiles.active_index = tile_index
        bpy.ops.image.tile_fill(context_overridden,
                                # your args #
                                # color=color,
                                # generated_type=generated_type,
                                # width=width,
                                # height=height,
                                # float=float,
                                # alpha=alpha
                                # your args #)

    # adding tiles of count: udim baking range
    # and filling
    bpy.ops.image.tile_add(context_overridden,
                           number=start_tile_index,
                           count=(end_tile_index - start_tile_index + 1),
                           label='',
                           fill=True, 
                           # your args #
                           # color=color,
                           # generated_type=generated_type,
                           # width=width,
                           # height=height,
                           # float=float,
                           # alpha=alpha
                           # your args #)

    # restore old active image in the image_editor
    if old_active_image is not None:
        image_area.spaces.active.image = old_active_image
    # if context.area changed, restore back   
    if context.area.ui_type != old_area_type and old_area_type is not None:
        context.area.ui_type = old_area_type

More

here is some additional information i found on my way to solution:

context regions

file_tile poll() and source code

# NOTE:
# from what i understood, it checks if image is not None
# (bpy.data.images[0] is not None)
# image source is tiled (bpy.data.images[0].source will give you that)
# there is a comment about filling (basically that's why i loop over
# and fill all tiles before adding new ones)

# C CODE
# 4117 static bool tile_fill_poll(bContext *C)
# 4118 {
# 4119   Image *ima = CTX_data_edit_image(C);
# 4120 
# 4121   if (ima != NULL && ima->source == IMA_SRC_TILED) {
# 4122     /* Filling secondary tiles is only allowed if the primary tile exists. */
# 4123     return (ima->active_tile_index == 0) || BKE_image_has_ibuf(ima, NULL);
# 4124   }
# 4125   return false;
# 4126 }

# it also checks for created image buffer (BKE_image_has_ibuf)
# i suspect this is the error i get when trying to find existing image editor
# below is the source code for it i also found:

# C CODE
# /* checks whether there's an image buffer for given image and user */
#bool BKE_image_has_ibuf(Image *ima, ImageUser *iuser)
#{
#   ImBuf *ibuf;
#
#   /* quick reject tests */
#   if (!image_quick_test(ima, iuser))
#       return false;
#
#   BLI_spin_lock(&image_spin);
#
#   ibuf = image_get_cached_ibuf(ima, iuser, NULL, NULL);
#
#   if (!ibuf)
#       ibuf = image_acquire_ibuf(ima, iuser, NULL);
#
#   BLI_spin_unlock(&image_spin);
#
#   IMB_freeImBuf(ibuf);
#
#   return ibuf != NULL;
# }

image c headers

image c code

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .