1
$\begingroup$

I have a game model format which stores the UV coordinates for each vertex. To implement seams in the UV mapping, a vertex just appears twice, each having a different UV coordinate (it solves the lighting issues introduced with that by using custom normals stored for each vertex, but sadly I can't teach Blender custom normals correctly yet as I learned... another story).

Basically, my vertex data looks like this (made-up data):

Index   X       Y       Z       U       V
0       1.4235  0.2434  -0.322  0.0     0.5
1       2.3242  0.231   0.323   0.5     0.0
2       1.2242  0.531   -0.323  0.5     0.5
...

So I started writing an importer for this format, and thought it's easy enough to just set an uv property of a BMVert instance - just to find out that BMVert does not have a property for UV mapping at all.

From looking at the samples, it (logically) looks like I have to set the UV coordinates by iterating through loops rather than the vertices. But I'm a little stumped for an answer on how to find out the index of the vertex having the required UV coordinate when iterating through the loops.

The answer here UV Vertex using Python? was suggested, but it does not show how to retrieve the corresponding index of a vertex.

My current code to import the XYZ vertex positions (and normals) is as follows:

# Get the vertices and indices of the most detailled LoD model.
vertices = get_vertices(...)
indices = get_indices(...)
# Create a mesh and a bmesh to represent the polygon.
mesh = bpy.data.meshes.new(fshp.header.name_offset.name)
bm = bmesh.new()
bm.from_mesh(mesh)
# Go through the vertices and add them to the bmesh.
for vertex in vertices:
    bm_vert = bm.verts.new(vertex.p0)
    #bm_vert.normal = vertex.n0 # TODO: Blender does not really support custom normals yet, and they look weird.
bm.verts.ensure_lookup_table() # Required after adding / removing vertices and before accessing them by index.
# Connect the faces, they are organized as a triangle list.
for i in range(0, len(indices), 3):
    bm.faces.new(bm.verts[j] for j in indices[i:i + 3])
# TODO: Set the UV coordinates??
uv_layer = bm.loops.layers.uv.active
for face in bm.faces:
    for loop in face.loops:
        loop[uv_layer].uv = #..?
# Write the bmesh data back to the mesh.
bm.to_mesh(mesh)
bm.free()
mesh_obj = bpy.data.objects.new(mesh.name, mesh)
bpy.context.scene.objects.link(mesh_obj)
$\endgroup$
6
  • 1
    $\begingroup$ Possible duplicate of UV Vertex using Python? $\endgroup$ Commented May 29, 2016 at 22:26
  • $\begingroup$ Also related, but not using Bmesh: blender.stackexchange.com/questions/30677/… $\endgroup$ Commented May 29, 2016 at 22:27
  • $\begingroup$ There is a shortcoming in your idea: a vertex can have as many UV coordinates as the number of faces it is connected to. The right approach is to work with faces and loops and go down to vertex level from there. $\endgroup$
    – kheetor
    Commented May 31, 2016 at 10:05
  • $\begingroup$ @kheetor: I know that, but that's how the format works (I cannot change it). It has two vertices at UV seam points to make the seams possible. It's basically like a "compiled" version of a model, prepared to be sent to the GPU. $\endgroup$
    – Ray
    Commented May 31, 2016 at 12:14
  • $\begingroup$ Yes and that's fine, that is how many formats handle vertices with split normals and UVs. But IMO in the beginning you need to start compiling the initial vertice list by going through the loops, not the bmverts. $\endgroup$
    – kheetor
    Commented May 31, 2016 at 12:20

2 Answers 2

2
$\begingroup$

It took me some time to realize I could just ask the vert property of a BMLoop when iterating through them, and then ask the index property of the retrieved BMVert to find my index.

It required to call bm.verts.index_update() before, or all the retrieved indices would've been -1.

This is my working code:

vertices = model.get_vertices()
# Add the vertices to the model.
for vertex in vertices:
    bm_vert = bm.verts.new(vertex.p0)
    #bm_vert.normal = vertex.n0 # TODO: Blender does not really support custom normals yet, and they look weird.
bm.verts.ensure_lookup_table() # Required after adding / removing vertices and before accessing them by index.
bm.verts.index_update()  # Required to actually retrieve the indices later on (or they stay -1).
# Connect the faces, they are organized as a triangle list.
for i in range(0, len(indices), 3):
    bm.faces.new(bm.verts[j] for j in indices[i:i + 3])
# Set the UV coordinates by iterating through the face loops.
uv_layer = bm.loops.layers.uv.new()
for face in bm.faces:
    for loop in face.loops:
        # Get the index of the vertex the loop contains.
        loop[uv_layer].uv = vertices[loop.vert.index].u0
$\endgroup$
2
  • $\begingroup$ This doesn't look like working code, vertices[]: is an error. Please show a full working example (if this is to be considered a good answer). $\endgroup$
    – ideasman42
    Commented May 31, 2016 at 9:55
  • $\begingroup$ @ideasman42: Whoops, there was a concept of visibility groups in that format, only requiring a slice of the vertices. I left it out for readability because it's unimportant for the problem, but forgot to remove the braces. Thanks! $\endgroup$
    – Ray
    Commented May 31, 2016 at 12:14
-1
$\begingroup$

You do the mapping like this:

for face in bm.faces:
    for vert, loop in zip(face.verts, face.loops):
        loop[uv_layer].uv = get_uvs(vert.index, face.index,...)

The last line depends on your file format and how are the UVs stored. You should be able to retrieve the appropriate UV coords from vert.index and face.index info.

$\endgroup$
2
  • $\begingroup$ @RayKoopa if there can be only 1uv per physical vertex then you cannot have any seams and so there is only support for planar meshes. Your function is then very simple (something like get_uvs(vert.index, file_path)). $\endgroup$ Commented May 30, 2016 at 21:22
  • $\begingroup$ Yeah, as said, the model file format splits vertices to realize seams; it's like a model version readily compiled for use by the game engine so the game doesn't have to split them before sending them to the GPU, I guess. I found a solution; maybe it makes my question more clear ='3 $\endgroup$
    – Ray
    Commented May 31, 2016 at 8:17

You must log in to answer this question.

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