0
$\begingroup$

BACKGROUND I have code (below) that scales a texture on the Y-axis to eliminate distortion from unwrapping a very deep cylinder.

The code performs successfully on a standalone basis (as a test), but when I drop the code into a bigger app it throws an error because setting an area to IMAGE_EDITOR does not behave the same way as in the test.

Stand alone performance: the texture image appears correctly when I set an area to IMAGE_EDITOR; from there the code scales the UV map correctly by selecting all the uv vertices and scaling.

Within big app performance: a blank appears when I set an area to IMAGE_EDITOR. The code throws an error because there are no uv vertices to select. However, the image/uvmap exists and can be found when I switch the workspace from "layout" to "uv editing"

QUESTIONS ON HOW I CAN FIX THE CODE SO THAT IT WORKS IN THE BIG APP

(a) If the correct texture image/uvmap appears in IMAGE_EDITOR, can I assume the code will work as well when included in big app as in the test?

(b) How can I force IMAGE_EDITOR to perform in the big app the same way it performs as a standalone?

SCREENSHOTS AND CODE

enter image description here

On a standalone basis, the code successfully resizes the UV map and eliminates the texture distortion: enter image description here

import bpy

bpy.ops.mesh.primitive_cylinder_add(vertices=32, radius=0.2, depth=30, enter_editmode=False, location=(0,0,0))
TextureFolder='G:\\Blender\\Textures\\'
TextureFilename='wildtextures-zinc-galvanized-metal-sheet.jpg'

def obj_material_texture_UVscale(objname,filename,Xscale,Yscale,Zscale):
    path=TextureFolder+filename
    materials = bpy.data.materials
    material = materials.get('mat')
    if not material:
        material = materials.new('mat')
    material.use_nodes = True
    if material.node_tree:
        material.node_tree.links.clear()
        material.node_tree.nodes.clear()
    nodes = material.node_tree.nodes
    links = material.node_tree.links
    output = nodes.new(type = 'ShaderNodeOutputMaterial' )
    diffuse = nodes.new(type = 'ShaderNodeBsdfDiffuse' )
    imgTex = bpy.data.images.load(path)
    node_texture = nodes.new(type='ShaderNodeTexImage')
    node_texture.image = imgTex
    node_texture.location = 0,200    
    tex_coord = nodes.new(type = 'ShaderNodeTexCoord')#NEW 
    links = material.node_tree.links
    link=links.new(tex_coord.outputs["UV"], node_texture.inputs["Vector"]) #NEW
    link = links.new(node_texture.outputs[0], nodes.get('Diffuse BSDF').inputs[0])
    link = links.new( diffuse.outputs['BSDF'], output.inputs['Surface'] )
    obj=bpy.data.objects[objname]
    obj.active_material=material
    bpy.ops.object.select_all(action='DESELECT')
    bpy.data.objects[objname].select_set(True)
    bpy.ops.object.mode_set(mode='EDIT')
    bpy.ops.mesh.select_all(action='SELECT')
    bpy.ops.uv.smart_project()

    original_area = bpy.context.area.type

    scene = bpy.context.scene
    for count, ob in enumerate(bpy.context.selected_objects):
        if ob.type == 'MESH':
            #scene.objects.active = ob
            bpy.context.view_layer.objects.active=ob
            #+ switch to the image editor to perform transforms etc
            bpy.context.area.type = 'IMAGE_EDITOR'
            bpy.ops.object.mode_set(mode='EDIT', toggle=False)

            bpy.ops.mesh.reveal()
            bpy.ops.mesh.select_all(action='SELECT')
            #+ select the uvs
            bpy.ops.uv.select_all(action='SELECT')

            bpy.ops.transform.translate(value=(0, 0, 0), constraint_axis=(True, False, False))
            bpy.ops.transform.resize(value=(Xscale,Yscale,Zscale))

            #+ return to the original mode where the script was run
            bpy.context.area.type = original_area
            bpy.ops.object.mode_set(mode='OBJECT', toggle=False)

obj_material_texture_UVscale('Cylinder',TextureFilename,1,30,1)
$\endgroup$
2
$\begingroup$

You do not need to mess around with the UI or change the layout at all if all you want to do is update a piece of data within an object.

You can dig down into any variable's type interactively in the scripting console without even having to look up the documentation. Autocomplete via ctrl-space, or calling dir(obj) and type(foo) describes just about anything.

So all you need to do is dig down to find where the UV coordinates are stored, and multiply them directly.

Here's how that would look for your script.

import bpy

bpy.ops.mesh.primitive_cylinder_add(vertices=32, radius=0.2, depth=30, enter_editmode=False, location=(0,0,0))
TextureFolder = 'G:\\Blender\\Textures\\'
TextureFilename = 'wildtextures-zinc-galvanized-metal-sheet.jpg'


def obj_material_texture_UVscale(objname, filename, Xscale, Yscale, Zscale):
    path = TextureFolder+filename
    materials = bpy.data.materials
    material = materials.get('mat')
    if not material:
        material = materials.new('mat')
    material.use_nodes = True
    if material.node_tree:
        material.node_tree.links.clear()
        material.node_tree.nodes.clear()
    nodes = material.node_tree.nodes
    links = material.node_tree.links
    output = nodes.new(type = 'ShaderNodeOutputMaterial' )
    diffuse = nodes.new(type = 'ShaderNodeBsdfDiffuse' )
    imgTex = bpy.data.images.load(path)
    node_texture = nodes.new(type='ShaderNodeTexImage')
    node_texture.image = imgTex
    node_texture.location = 0,200    
    tex_coord = nodes.new(type = 'ShaderNodeTexCoord')#NEW 
    links = material.node_tree.links
    link = links.new(tex_coord.outputs["UV"], node_texture.inputs["Vector"]) #NEW
    link = links.new(node_texture.outputs[0], nodes.get('Diffuse BSDF').inputs[0])
    link = links.new( diffuse.outputs['BSDF'], output.inputs['Surface'] )
    obj = bpy.data.objects[objname]
    obj.active_material = material
    bpy.ops.object.select_all(action='DESELECT')
    bpy.data.objects[objname].select_set(True)
    bpy.ops.object.mode_set(mode='EDIT')
    bpy.ops.mesh.select_all(action='SELECT')
    bpy.ops.uv.smart_project()
    bpy.ops.object.mode_set(mode='OBJECT')

    # Just directly scale the UVs!
    cyluvs = obj.data.uv_layers[0].data
    for cuv in cyluvs: # each is a bpy.types.MeshUVLoop
        cuv.uv[0] *= Xscale
        cuv.uv[1] *= Yscale

# len(o.data.uv_layers['UVMap'].data) -> 192
# Each of these is a bpy.types.MeshUVLoop
# which has .uv, .select, .select_edge, .pin_uv.
# item.uv is a Vector. We can just scale its Y 30 times!

obj_material_texture_UVscale('Cylinder',TextureFilename,1,30,1)
$\endgroup$
  • $\begingroup$ that works really well. I can't thank you enough after me going down rabbit holes for 10+ hours. $\endgroup$ – vndep Aug 25 at 12:15

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.