Importing UV coordinates

I’m currently writing an import script/addon.
The file format is one vertex per line: x, y, z, u, v With 3 lines for each triangle (no quads).
So far I’ve got vertex’s, edges and faces all importing correctly.

Now I just need to import the UV coordinates for each vertex.
However I’m having trouble trying to find a way to do this.
The best result so far was copying code from the UV operator template, which gave me a UV Map, but it puts the whole model at one point.

Any help or pointers in the right direction would be great


UVs are per loop in blender, so create a new uv_texture (which implicitly creates a uv_layer), and assign the uv coords 3x, e.g.

import bpy

me =

uv_tex =
uv_layer = me.uv_layers[-1].data

vert_loops = {}
for l in me.loops:
    vert_loops.setdefault(l.vertex_index, []).append(l.index)
# Assume as vert_uvs = [(0.3, 0.6), (0.1, 0.4), ...]
# with uv coords in order of vertices

for i, coord in enumerate(vert_uvs):
    # For every loop of a vertex
    for li in vert_loops[i]:
        uv_layer[li].uv = coord

Thanks Codeman for that code, is much more readable than what I’ve been doing (taking it from the 3ds import script)

if uv_faces:
    uvl =[:]
    for fidx, pl in enumerate(bmesh.polygons):
        face = myContextMesh_facels[fidx]
        v1, v2, v3 = face

        # eekadoodle
        if v3 == 0:
            v1, v2, v3 = v3, v1, v2

        uvl[pl.loop_start].uv = contextMeshUV[v1 * 2: (v1 * 2) + 2] 
        uvl[pl.loop_start + 1].uv = contextMeshUV[v2 * 2: (v2 * 2) + 2]
        uvl[pl.loop_start + 2].uv = contextMeshUV[v3 * 2: (v3 * 2) + 2]
        # always a tri

I have 2 questions though:

  1. What is that eekadoodle hack? I’ve seen it in many importers but no effect when not using it.

  2. You know why both the above and your method could be slow? In my importer for big scenes (around 40mb) if I add UVs, it takes ~38 seconds, but without UVs, it only takes ~15 seconds!
    Please PM if you are can help investigate that.

some exporters export triangles in a weird vertex order. The ekadoodle hack is used to correct that, by testing the third vertex for equality with 0. If you import and export with your own blender scripts only, it isn’t needed at all.

Not sure why it’s so slow, can you provide the entire script + a that big scene / some sample asset?

The cProfile module is good for profiling, it let’s you easily detect bottlenecks in scripts. But it might be blender internal stuff taking so long.

I wanted to leave here a better method I found which is much faster.
Per Campbell, is always better to use foreach_set. So I did, and the increase in speed with many assets is incredible.
Seems that accessing and setting a value each time for loop is very intensive. It’s not very noticeable in single models though.

# Setting mesh UVs from a source in a per vertex format to blender's per loop (face corner).
# This assumes you have a list of lists, each one containing a uv pair. E.g. [[0.2, 0.1], [0.2, 0.1], [0.2,0.3]] etc.
# me_ob is you mesh from,, etc.

# vertex index : vertex value pair
vi_uv = {i: uv for i, uv in enumerate(vert_uvs)}

#initialize an empty list
per_loop_list = [0.0] * len(me_ob.loops)

for loop in me_ob.loops:
    per_loop_list[loop.index] = vi_uv.get(loop.vertex_index)

# flattening
per_loop_list = [uv for pair in per_loop_list for uv in pair]

# creating the uvs"test")
me_ob.uv_layers[0].data.foreach_set("uv", per_loop_list)  

#  me_ob.uv_layers[0] assumes you had no uv layers before, only the newly created one.

1 Like

Thanks Brachi for the hint on performance!

I tried to compact your code and this is how it looks:

import bpy
from random import random

me =

# Sample data
vert_uvs = [(random(), random()) for i in range(len(me.vertices))]"test")
me.uv_layers[-1].data.foreach_set("uv", [uv for pair in [vert_uvs[l.vertex_index] for l in me.loops] for uv in pair])