Как экспортировать per-vertex UV-координаты в экспорте Blender script

Я пишу экспорт python script из Blender 2.64. Идея заключается в том, что я пытаюсь экспортировать данные сетки в OpenGL VBO-friendly. Поэтому я экспортирую атрибуты вершин в массив макетов структур. Например, для сетки с вершинами, нормалями и одной парой текстурных координат каждый vertexAttribute в VBO будет иметь 8 последовательных поплавков:

vvvnnntt 

Пока все хорошо. Проблема в том, что, когда Blender выполняет UV-отображение, он может фактически назначить разные uvs одной вершине.

То есть, скажем, у вас есть куб: у вас есть 8 вершин, и у вас есть, скажем, 6 граней (квадраты в этом случае). Я бы ожидал, что грань/многоугольник с индексами 0,1,2,3 подразумевается:

vertex 0, normal 0, uvCoord0 
vertex 1, normal 1, uvCoord1 
vertex 2, normal 2, uvCoord2 
vertex 3, normal 3, uvCoord3 

И, следовательно, любое упоминание индекса 0, например, на любой грани всегда будет означать вершина 0, нормальная 0, uvCoord0 кортеж. Ну, получается в Blender, если я правильно понял, что одно лицо может ссылаться на вершину 0 с uvCoord 0, а другая может ссылаться на одну и ту же вершину 0 с другим uVCoord. Таким образом, лицо loop_indices должно фактически использоваться для поиска как вектора, так и uvCoord в общих объектах контейнеров data.vertices и data.uv_layers[].data.

Это позволяет применять uv-карты для лица. Таким образом, у вас может быть куб, в котором каждое лицо имеет другую текстуру uv, и даже если две соседние грани имеют общую вершину, вершина имеет другую координату uv в зависимости от лица.

Тем не менее, моя сетка не должна иметь разные uv для одной вершины, так как я разворачиваюсь на соседние грани. Это означает, что в моей карте UV развернутая сетка представляет собой набор смежных граней (например, например, крестообразную форму, если это куб, состоящий из 6 граней) и между двумя смежными гранями, их общие вершины должны отображаться в одну и ту же точку в карте uv.

Итак, учитывая вышеизложенное, я думал, что этот подход должен работать:

vertexAttributeList = []
    for vertex in mesh.data.vertices:
        vertexAttribute = list(vertex.co)
        vertexAttribute.extend(list(vertex.normal))
        vertexAttributeList.append(vertexAttribute)

    for triangle in mesh.data.polygons:
        for uv_layer in mesh.data.uv_layers:
            for i in triangle.loop_indices:
                lookupIndex = mesh.data.loops[i].vertex_index
                if len(vertexAttributeList[lookupIndex]) == 6:
                    uvCoord = uv_layer.data[i].uv
                    vertexAttributeList[lookupIndex].extend([uvCoord[0], 1 - uvCoord[1]])

Как вы можете видеть, импликация в приведенном выше коде заключается в том, что я буду посещать вершины более одного раза, потому что я выполняю итерацию по граням сетки (в этом случае треугольники), которые разделяют вершины. И каждый раз, когда я посещаю вершину, если у нее еще не назначены ее УФ-координаты, я назначаю их, просматривая их с помощью треугольника loop_indices. В конце концов, мое предположение заключалось в том, что у меня есть, в конце концов, уникальные uv-координаты на вершину.

Приведенный выше код дает ему следующий макет, например (я показываю первые 6 атрибутов вершины сетки):

-1.000000 -1.000000 -1.000000 -0.707083 -0.707083  0.000000  0.076381  0.948520
-1.000000  1.000000 -1.000000 -0.707083  0.707083  0.000000  0.454183  0.948519
 1.000000  1.000000 -1.000000  0.707083  0.707083  0.000000  0.325162  0.948519
 1.000000 -1.000000 -1.000000  0.707083 -0.707083  0.000000  0.205674  0.948519
-1.000000 -1.000000  1.000000 -0.577349 -0.577349  0.577349  0.581634  0.795012
-1.000000  1.000000  1.000000 -0.577349  0.577349  0.577349  0.454183  0.795012
...

Но когда я использую эту информацию плюс грань сетки, которая выглядит так:

4 5 1
5 6 2
6 7 3
7 4 0
...

чтобы отобразить мою модель в моей программе (вроде как двигатель), отображение UV явно перепутано. То есть, модель рендерит тонкие точки с вершинами и нормалями, но текстура uv явно неправильно отображена.

Любые мысли? Я имею в виду, либо я экспортирую право, либо испортил код рендеринга OpenGL в своем приложении, либо я экспортирую неправильное сопоставление между вершинами и координатами uv. (или оба, конечно... но я предполагаю, что теперь я испортил экспорт script).

Последнее, если я изменю приведенный выше код python, чтобы добавить каждый новый uv, который присваивается вершине вместо добавления только в том случае, если uv еще не был назначен, для вершины 1 я получаю:

[-1.0, -1.0, -1.0, -0.7070833444595337, -0.7070833444595337, 0.0, 0.07638061791658401, 0.9485195726156235, 0.5816344618797302, 0.9485194832086563, 0.07638061791658401, 0.9485195726156235]

И в этом примере у вас есть только один слой с uv. Таким образом, Blender присваивал вершинам 1 координаты 2 uv.

Ответ 1

Я думаю, что может быть способ интерполировать или иным образом согласовывать/смешивать UVs, чтобы в итоге получить единственный UV на вершину, если все смежно. Но в то же время, учитывая, что никто не предложил альтернативу, я закончил дублирование вершин с их различными UV-объектами и отказался от попытки экспортировать GL_ELEMENT_ARRAY. Следующий код работает, если рендеринг с одним VBO (с использованием glDrawArrays):

def exportMesh(filepath):

    # Only one mesh per scene
    objList = [object for object in bpy.context.scene.objects if object.type == 'MESH']

    if len(objList) == 0:
        return
    elif len(objList) > 1:
        return
    #raise exepction? dialog box?


    # Process the single mesh object:
    mesh = objList[0]

    # File name is same as the mesh name in Blender
    meshFilePath = filepath[0 : filepath.rindex('/') + 1] + mesh.name + ".mesh"
    file = open(meshFilePath, 'w')

    WorldTransform = Matrix().Identity(4)
    WorldTransform *= Matrix.Rotation(radians(90), 4, "X")
    file.write('World Transform:\n')
    for rcol in WorldTransform_T.row:
        file.write('{:9f} {:9f} {:9f} {:9f}\n'.format(row[0], row[1], row[2], row[3]))
    file.write('\n')

    # Mesh (local) transform matrix
    file.write('Mesh Transform:\n')
    localTransform_T = mesh.matrix_local.copy()
    localTransform_T.transpose()
    for row in localTransform_T.row:
        file.write('{:9f} {:9f} {:9f} {:9f}\n'.format(row[0], row[1], row[2], row[3]))
    file.write('\n')

    vertexAttributeList = []
    for triangle in mesh.data.polygons:
        vertices = list(triangle.vertices)
        i = 0
        for vertex in vertices:
            vertexAttribute = list(mesh.data.vertices[vertex].co)

            if triangle.use_smooth:
                vertexAttribute.extend(list(mesh.data.vertices[vertex].normal))
            else:
                vertexAttribute.extend(list(triangle.normal))

            for uv_layer in mesh.data.uv_layers:
                uvCoord = uv_layer.data[triangle.loop_indices[i]].uv
                vertexAttribute.extend([uvCoord[0], 1 - uvCoord[1]])

            totalVertexWeight = 0
            jointWeights = [group.weight for group in mesh.data.vertices[vertex].groups]
            jointIndices = [group.group for group in mesh.data.vertices[vertex].groups]
            for weight in jointWeights:
                totalVertexWeight += weight

            vgNum = len(mesh.vertex_groups)
            jointWeightsAttribute = []
            jointIndicesAttribute = []
            for vgIndex in range(4):
                if vgIndex < len(jointIndices):
                    jointWeightsAttribute.append(jointWeights[vgIndex] / totalVertexWeight)
                    jointIndicesAttribute.append(jointIndices[vgIndex])
                else:
                    jointWeightsAttribute.append(0)
                    jointIndicesAttribute.append(0)

            vertexAttribute.extend(jointWeightsAttribute)
            vertexAttribute.extend(jointIndicesAttribute)

            vertexAttributeList.append(vertexAttribute)
            i += 1

    # VBO
    vNum = len(vertexAttributeList)
    tNum = len(mesh.data.uv_layers)
    file.write('VBO Length: {:d}\n'.format(vNum))
    for vertexAttribute in vertexAttributeList:
        file.write('{:9f} {:9f} {:9f} {:9f} {:9f} {:9f} {:9f} {:9f} {:9f} {:9f} {:9f} {:9f} {:d} {:d} {:d} {:d}\n'.format(vertexAttribute[0],
                                                                                                                          vertexAttribute[1],
                                                                                                                          vertexAttribute[2],
                                                                                                                          vertexAttribute[3],
                                                                                                                          vertexAttribute[4],
                                                                                                                          vertexAttribute[5],
                                                                                                                          vertexAttribute[6],
                                                                                                                          vertexAttribute[7],
                                                                                                                          vertexAttribute[8],
                                                                                                                          vertexAttribute[9],
                                                                                                                          vertexAttribute[10],
                                                                                                                          vertexAttribute[11],
                                                                                                                          vertexAttribute[12],
                                                                                                                          vertexAttribute[13],
                                                                                                                          vertexAttribute[14],
                                                                                                                          vertexAttribute[15]))
    file.write('\n')


    # Done writing mesh file
    file.close()