Move uv map via python?

Greetings. I’m trying to wrap my head around this but I still am not getting it to work.
As on below, I’m looping through vertices and getting the uv coordinates of each vertex.

What I am trying to do, is to manually control the position of the uv map. This is for item thumbnail images.
Lets say I have 320x320 pixels size texture that contains item thumbnails of size 32x32.
I know UV coordinate position scales from 0.0 to 1.0.

But what I don’t know, is in which order I have to manipulate each vertex to get the uv positioning to work in Python.
for starter: How would I nudge the current UV map 32 pixels to right?


mesh = thumbnail.meshes[0]
array = mesh.getVertexArrayLength(0)

for v in range(0, array):
    vertex = mesh.getVertex(0, v)
    uv = vertex.getUV()
    
    #Possible solution goes here
    
    vertex.setUV(uv)

I strongly recommend that you would use mesh.transformUV() instead of changing the UV position for every vertex separately (with vertex.uv). Simple reason because transformUV() is so much faster. Here’s an example of rotating the UV coordinates. UVTransform_00.blend (99 KB).

If you would like to move to the right by an offset, replace the code with this:

from mathutils import Matrix

offset = 0.05, 0.0

matrix = Matrix.Translation((-offset[0], -offset[1], 0.0))

def translate(cont):
    if not cont.sensors[0].positive:
        return
    mesh = cont.owner.meshes[0]
    for mat_id in range(mesh.numMaterials):
        mesh.transformUV(mat_id, matrix, 0)

Don’t forget to change the module name in the Python controller field.

Lets say I have 320x320 pixels size texture that contains item thumbnails of size 32x32.I know UV coordinate position scales from 0.0 to 1.0.

32 pixels out of 320 pixels would be 320/32 = 0.1. You can induce this calculation in your script so you don’t have to switch some magic numbers around if you change texture or tile sizes.


GetUVIncrement():
    AtlasSize = 320
    ThumbSize = 32

    return ThumbSize/AtlasSize

Depending on the complexity of your UV mapped polygons, you might want to deal only with setting the UV rather than nudging. So you wouldn’t have flow like this:

check for if you can move that direction
get where you are
set your position to that - something
(check if you are there yet)
(repeat if not)

You could have a function that simply sets the position according to which cell you want. Like if you have a 4-point polygon (that you have in order):


def Get4PointUVPositionForTile(xcell,ycell):
    i = GetUVIncrement()
    x = xcell * i
    y = ycell * i

    a = [x,y]
    b = [x + i, y]
    c = [x, y + i]
    d = [y+ i, x + i]

return [a, b, c, d]

Then use that list to set your vertex positions

Kheetor said some relevant stuff already, but I thought of giving an example on transformUV(). Three functions that change the cell id through scrolling or selection. Left mouse to scroll, space to select property value, and 1-9 keys to select cells with indexes from 0 to 8. You’ll notice that the UV texture image doesn’t have to be an image with equal amount of cells for the width and height necessarily. Also the mesh may have more than one material. To define which material is affected by the uv transform functions there’s an identifier ‘_UV’. Hope this helps. By the way, if you only need to ‘scroll’ you can remove functions ‘select1’ and ‘select2’. It are just examples.

Attachments

UVTransform_01.blend (97.8 KB)

I have a few question. Does this allow me to have a one object on inactive layer that I make copies of (Using addObject())?
I tested this method and it seems great, however so far every object ends up having the same offset, instead of each addObject() object having own transformed UV coord.

Another issue I’m having is that sometimes they show as plain black (If I click toggle inventory a few times it clears out and works well but again bugs often when I keep toggle it again).

I know I’m doing somethign wrong so, here’s my current script:


def inventory_update(cont):
    scene = bge.logic.getCurrentScene()
    
    #This clears all old inventory items
    for child in cont.owner.children:
        child.endObject()

    pos_y = 0.375
    uv_multiplier = 1

    for line in bge.logic.globalDict['Player info'].get('Inventory'):
        pos_x = -0.375

        for item in line:
            thumbnail = scene.addObject('4Ui_inventory_thumbnail', cont.owner, 0)
            thumbnail.setParent(cont.owner)
            thumbnail.localPosition = [pos_x, pos_y, 0.001]
            
            print (item)
            
            matrix = mathutils.Matrix.Translation((0.1, 0.0, 0.0))

            mesh = thumbnail.meshes[0]

            for mat_id in range(mesh.numMaterials):
                mesh.transformUV(mat_id, matrix, 0)

            pos_x += 0.25

        pos_y -= 0.25

UV’s are attached to mesh-data, not to object instances.
Or in plain english: added objects are duplicates, and if you change something on one (eg vertex position, UV map) it will change for all of them.

Solutions:

  • Use libnew to make sure added objects are unique in-game (complex)
  • Have more objects than you’ll ever need to display at once, and cycle through them/reset them. (still complex)
  • Think of a different way to tiled-animations. (Simple)

My suggestion is to not use a tiled/scrolling image system, but rather to have lots of meshes (one for each frame) and change between them using replaceMesh.

Why are you wanting to display lot’s of different images? Why are they added objects. If they’re part of an animation (like an explosion texture), then I can understand it, but as part of an inventory it would make more sense to have different objects for each wouldn’t it?

Well originally, since an inventory item only represents the item and not being the actual item, I tried to make an inventory item to be just a rectangle containing the item ID (a pointer to an actual item dictionary). For example 4x4 inventory could be:

 inventory = [[129,3,2,0],[0,0,0,0],[1,78,993,23],[34,23,65,2]] 

EDIT: and then I would have an image sheet (one image) containing all the thumbnails and I would just move the UV to fetch the matching thumbnail for each object.

But yes, I guess you are right sdfgeoff. I guess it’s best to just create each thumbnail a own mesh and replace them to match these.
Thank you all for these transform tips though, I’m sure I can use them elsewhere at some point :slight_smile:

You don’t even have to use replacemesh. How I have my inventory set up is that inventory slots are just empties and I addObject() and parent the items to them. I can get “reserved slots” by checking if the slot has any children. Hiding the inventory sets the items .visible = False.

Just an example, something to consider :slight_smile: