Как экспортировать UV-координаты для каждой вершины в скрипте экспорта Blender
Я пишу скрипт экспорта Python из Blender 2.64. Идея состоит в том, что я пытаюсь экспортировать данные меша в OpenGL VBO-дружественном виде. Поэтому я экспортирую атрибуты вершин в макет массива структур. Например, для сетки с вершинами, нормалями и 1 парой координат текстуры, каждая 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 развернутая сетка представляет собой набор смежных граней (например, крестообразную форму, если это был куб, состоящий из 6 граней), а между двумя смежными гранями их общие вершины должны отображаться в одну и ту же точку. на ультрафиолетовой карте.
Поэтому, учитывая вышесказанное, я подумал, что этот подход должен работать:
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]])
Как вы можете видеть, смысл приведенного выше кода состоит в том, что я буду посещать вершины более одного раза, потому что я перебираю грани сетки (в данном случае треугольники), которые имеют общие вершины. И каждый раз, когда я посещаю вершину, если ей еще не назначены координаты UV, я назначаю их, просматривая их с помощью треугольника 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
...
Чтобы отобразить мою модель в моей программе (своего рода движок), ультрафиолетовое отображение явно испорчено. То есть модель хорошо отрисовывается с точки зрения вершин и нормалей, но уф-текстура явно не правильно отображается.
Какие-нибудь мысли? Я имею в виду, что я либо экспортирую правильно, и испортил код рендеринга OpenGL в своем приложении, либо я экспортирую неправильное отображение между вершинами и координатами uv. (или оба, конечно.. но я предполагаю, что сейчас я испортил сценарий экспорта).
И последнее: если я изменю приведенный выше код 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]
И обратите внимание, в этом примере только один ультрафиолетовый слой. Итак, ясно, что Блендер действительно назначил координаты 2 uv вершине 1.
1 ответ
Я думаю, что мог бы быть способ интерполировать или иным образом согласовывать / смешивать UV, чтобы в конечном итоге с одним 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's 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()