>>как мне ПРОГРАМНО посчитать количество мэшей в сцене
... тебя интересует конкретная сцена и все меши? ...
import bpy
c=len(bpy.data.meshes)
print(c)
>> вижу что раздел называется Vertices of the mesh но не понимаю как мне до него достучаться.
... поясню, поскольку для эффективной работы желательно пользоваться описанием blender api ...
... по указанной ссылке мы видим описание свойства vertices блока данных mesh ...
vertices
Vertices of the mesh
Type: MeshVertices bpy_prop_collection of MeshVertex, (readonly)
... из типа bpy_prop_collection of MeshVertex ясно, что это коллекция вершин ...
... если кликнуть на MeshVertex, то мы получим описание блока данных вершин ...
... например координаты хранятся в co ...
https://www.blender.org/api/blender_python_api_2_78a_release/bpy.types.MeshVertex.html#bpy.types.MeshVertex.co
... отсюда получаем требуемый код ...
mesh = bpy.data.meshes["Cube"]
for vert in mesh.vertices:
print(vert.co)
Спасибо, с вершинами разобрался. Но возник другой вопрос. Мне не понятно как в Python узнать в каком из полигонов участвует конкретная вершина. В DX11 это называется индексным буфером, суть го в том что это просто массив типа int который указывает в каком порядке соединяются вершины(формируются полигоны). Желательно получить из Blender этот индексный буфер в таком же формате.
me = ob.to_mesh(scene, True, 'RENDER',calc_tessface=True)
me_faces = me.tessfaces
faces_verts = [f.vertices[:] for f in me_faces]
LanuHum я не могу разобраться в коде предложенном вами.
В строке :
>> me = ob.to_mesh(scene, True, 'RENDER',calc_tessface=True)
я не понимаю тут что за структура "ob"
Предположу, что LanuHum выложил "кастрированный" код...
Видимо, начало должно выглядеть так:
import bpy
ob=bpy.context.object
Остального не знаю... И мне непонятно с чего ты взял, что координаты вершины должны быть целыми переменными (int), а не вещественными (float) ?
Опять же таки при экспорте нет необходимости портить свой меш, превращая четырехугольники в треугольники.
Пишем функцию:
def mesh_triangulate(me):
import bmesh
bm = bmesh.new()
bm.from_mesh(me)
bmesh.ops.triangulate(bm, faces=bm.faces)
bm.to_mesh(me)
bm.free()
Затем используем её по назначению:
me = ob.to_mesh(scene, True, 'RENDER',calc_tessface=True)
mesh_triangulate(me)
И, не забываем после экспорта объекта удалить me
bpy.data.meshes.remove(me)
Извините, не подумал вот :
Нужно вынести функцию за пределы класса:
import bmesh
def mesh_triangulate(me):
bm = bmesh.new()
bm.from_mesh(me)
bmesh.ops.triangulate(bm, faces=bm.faces)
bm.to_mesh(me)
bm.free()
class KfoExport(bpy.types.Operator):
Спасибо sungreen, все работает!
Ну, если тебе всё же хочется, чтобы после экспорта сетка оригинала состояла из треугольников, то нормально, но, я всё же писал бы так:
File = open(self.filepath + ".kfo", 'wb')
meshesCout = len(bpy.data.meshes)# тут я узнаю сколько у меня мэшей
File.write(np.int32(meshesCout)) # сохраняю количество мэшей
for ob in bpy.data.objects:
if ob.type == 'MESH':
mesh = ob.to_mesh(scene, True, 'RENDER',calc_tessface=False)
mesh_triangulate(mesh)
File.write(np.int32(len(mesh.vertices)))
for vert in mesh.vertices:
File.write(np.float32(vert.co.x)) # Х кордината вертекса
File.write(np.float32(vert.co.y)) # Y кордината вертекса
File.write(np.float32(vert.co.z)) # Z кордината вертекса
File.write(np.float32(0)) # значение падинг для уравнения в передачи вшейдер
File.write(np.float32(0)) # X кордината текстуры
File.write(np.float32(0)) # Y кордината текстуры
File.write(np.float32(0)) # X кордината нормали
File.write(np.float32(0)) # Y кордината нормали
File.write(np.float32(0)) # Z кордината нормали
File.write(np.float32(0)) # X кордината тангента
File.write(np.float32(0)) # Y кордината тангента
File.write(np.float32(0)) # Z кордината тангента
size = len(mesh.polygons) * 3
File.write(np.int32(size))
for face in mesh.polygons:
File.write(np.int32(face.vertices[2]))
File.write(np.int32(face.vertices[1]))
File.write(np.int32(face.vertices[0]))
bpy.data.meshes.remove(mesh)
File.close()
При такой записи после экспорта твой меш останется таким, каким и был до экспорта, ты можешь дальше продолжать над ним работать...
Пробовал создать развертку. Не помогло... Переглядел еще тучу примеров не один не работает, или я что то не так делаю или версия Blender не та. Есть ли способ просто взять для каждой вершины эту uv координату? Документацию смотрел со всех сторон везде каккието косвенно связанные параметры и функции но нет конкретного рабочего примера.
При созданной развёртке:
me = ob.to_mesh(scene, True, 'RENDER',calc_tessface=True)
mesh_triangulate(me)
me_faces = me.tessfaces
me_verts = me.vertices
lenverts = len(me_verts)
lenfaces = len(me_faces)
uv_textures = me.tessface_uv_textures
if len(uv_textures) > 0:
if me.uv_textures.active and uv_textures.active.data:
uv_layer = uv_textures.active.data
else:
uv_layer = None
for face in me_faces:
if uv_layer:
uv = uv_layer[face.index]
uv1 = "<%.6g,%.6g>"%uv.uv1[:]
uv2 = "<%.6g,%.6g>"%uv.uv2[:]
uv3 = "<%.6g,%.6g>"%uv.uv3[:]
>> Но как я понимаю тут код по получению текстурных координат из полигонов. А специфика моего движка такова что мне нужна эта координата для каждой вершины в порядке их индексации.
... вопрос такой - у нас есть два полигона 6 вершин и есть uv развёртка с 8 точками для этих полигонов ...
... как должна выглядеть твоя таблица? ...
Да, видимо в Blender все так и и устроено, но в DX11 (как я понимаю) для каждой точки есть конкретная одна текстурная координата. Так как я раньше работал с Obj форматом то знаю что Blender может сохранить этот параметр так как мне надо (ведь в Obj все именно так реализовано). Пробовал в папке с аддонами найти экспортер в Obj но не нашел.
экспортёр здесь: /scripts/addons/io_scene_obj
Только, не надо нас вводить в заблуждение. :)
Вот файл obj. Что мы видим? 8 вершин и 24 текстурные координаты. Ещё, что мы видим? 12 фейсов, которые берут данные из трёх списков: списка вершин, списка текстурных координат и списка нормалей. Нет здесь нигде понятия о том, что у одной вершины - только одна текстурная координата.
o Cube
v -1.000000 -1.000000 1.000000
v -1.000000 1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v -1.000000 1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v 1.000000 1.000000 1.000000
v 1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -1.000000
vt 0.6312 0.3438
vt 0.3812 0.0939
vt 0.6312 0.0939
vt 0.5938 0.6563
vt 0.3438 0.4064
vt 0.5938 0.4064
vt 0.3105 0.3542
vt 0.0605 0.1043
vt 0.3105 0.1043
vt 0.9457 0.3626
vt 0.6958 0.1126
vt 0.9457 0.1126
vt 0.6419 0.9294
vt 0.8919 0.6794
vt 0.8919 0.9294
vt 0.0626 0.9335
vt 0.3126 0.6836
vt 0.3126 0.9335
vt 0.3812 0.3438
vt 0.3438 0.6563
vt 0.0605 0.3542
vt 0.6958 0.3626
vt 0.6419 0.6794
vt 0.0626 0.6836
vn -1.0000 0.0000 0.0000
vn 0.0000 0.0000 -1.0000
vn 1.0000 0.0000 0.0000
vn 0.0000 0.0000 1.0000
vn 0.0000 -1.0000 0.0000
vn 0.0000 1.0000 0.0000
usemtl Material.001
s off
f 2/1/1 3/2/1 1/3/1
f 4/4/2 7/5/2 3/6/2
f 8/7/3 5/8/3 7/9/3
f 6/10/4 1/11/4 5/12/4
f 7/13/5 1/14/5 3/15/5
f 4/16/6 6/17/6 8/18/6
f 2/1/1 4/19/1 3/2/1
f 4/4/2 8/20/2 7/5/2
f 8/7/3 6/21/3 5/8/3
f 6/10/4 2/22/4 1/11/4
f 7/13/5 5/23/5 1/14/5
f 4/16/6 2/24/6 6/17/6
Вот для этой картинки:
Выцепил из стороннего кода работу с uv развертками
for face in mesh.polygons:
IndexStruct[counter].append(np.int32(face.vertices[2]))
IndexStruct[counter].append(np.int32(face.vertices[1]))
IndexStruct[counter].append(np.int32(face.vertices[0]))
for loop in face.loop_indices:
for item in (ob.data.uv_layers.active.data[loop].uv if ob.data.uv_layers.active!=None else (0,0)):#uv
print (item)
И смог вывести в консоль числа от 0 до 1 (я так понимаю это и есть информация о развертке). Мне в этом коде не понятны некоторые моменты,:
1) face.loop_indices - что это
2) и в каком порядке идут Uv координаты (в порядке как у меня мэш сформирован или в какой другой закономерности) в соответствии с этим кодом
Попробовал поэкспериментировать с единственной костью в арматуре, задал ей пару ключевых кадров:
>>> ob=bpy.data.objects['Armature']
>>> action = ob.animation_data.action
>>> fcu=action.fcurves
>>> for fc in fcu:
... print(fc.data_path, "index=", fc.array_index)
...
pose.bones["Bone"].location index= 0
pose.bones["Bone"].location index= 1
pose.bones["Bone"].location index= 2
pose.bones["Bone"].rotation_quaternion index= 0
pose.bones["Bone"].rotation_quaternion index= 1
pose.bones["Bone"].rotation_quaternion index= 2
pose.bones["Bone"].rotation_quaternion index= 3
pose.bones["Bone"].scale index= 0
pose.bones["Bone"].scale index= 1
pose.bones["Bone"].scale index= 2
Соотвествие индексов: 0 - x, 1 - y, 2 - z
Численные значения координат:
>>> for frame in range(10):
... print ("кадр=", frame, "x=", fcu[0].evaluate(frame))
...
кадр= 0 x= 0.0
кадр= 1 x= 0.0
кадр= 2 x= 0.0018280739895999432
кадр= 3 x= 0.007335450500249863
кадр= 4 x= 0.016550853848457336
кадр= 5 x= 0.029494034126400948
кадр= 6 x= 0.046174466609954834
кадр= 7 x= 0.06658995151519775
кадр= 8 x= 0.09072526544332504
кадр= 9 x= 0.1185506209731102
1) Перебирать в цикле все объекты.
Я думал, что с этим-то ты разобрался
for ob in bpy.data.objects:
что-то сделать с ob
Хотя я, если когда-нибудь буду делать экспортёр, не стану перебирать в цикле все объекты, буду работать только с одним, активным объектом, мне кажется, так правильно.
Узнавать, есть ли в них арматура.
for mod in ob.modifiers.values():
if mod.type == 'ARMATURE':
значит mod - это арматура, что-то с ней сделать
3) Если есть, узнать общее количество кадров для этой арматуры, узнать какие из них ключевые.
# amt - объект арматуры, его получение см п.2
act=amt.animation_data.action # действия арматуры
# в act.frame_range находится 1 и последний кадры этой арматуры
как найти ключевые кадры, я так и не понял
5) Перебирать в цикле все кости арматуры.
#данные по костям в режиме редактирования:
# amt - объект арматуры, его получение см п.2
amtd=amt.data # данные арматуры
for bone in amtd.bones:
что-то делать с костью bone
#данные по костям в режиме позы:
for bone in amt.pose.bones:
что-то делать с костью bone
получение координат в режиме позы я описал вчера:
fcu[n] - кривая анимации номер n у арматуры
список кривых такой же, как в окне Экспозиционного листа
всего кривых - len(fcu)
для кривой №0:
fcu[0].data_path - название этой кривой (внутри есть название кости, его можно выцепить оттуда)
fcu[0].array_index - индекс этой кривой: для location, scale 0-x, 1-y, 2-z; для rotation_quaternion 0-w, 1-x, 2-y, 3-z
fcu[0].range() - 1 и последний кадры этой кривой
fcu[0].keyframe_points - список ключевых кадров этой кривой
fcu[0].keyframe_points[0].co - данные по 1 ключевому кадру этой кривой
Striver, извиняюсь за глупый вопрос, но не разобрался что вы имели ввиду под "# amt - объект арматуры, его получение см п.2" . Если п.2 - это пункт 2 , то как связаны выражение :
for mod in ob.modifiers.values():
if mod.type == 'ARMATURE':
и amt?
Ну да, я не связал их, извини
если mod - модификатор арматуры у меш-объекта, то его объект-арматура получается так:
или, если объединить со 2 пунктом:
for mod in ob.modifiers.values():
if mod.type == 'ARMATURE':
amt=mod.object
Как получить массив аналогичных индексов но для всех дочерних костей?
Так тут же (по приведённой тобой ссылке) есть свойство children. Там коллекция дочерних костей.
вот что в консоли видно на эту тему:
>>> ob.data.bones[0]
bpy.data.armatures['Armature'].bones["Bone"]
>>> ob.data.bones[0].children
bpy.data.armatures['Armature'].bones["Bone"].children
>>> list(ob.data.bones[0].children)
[bpy.data.armatures['Armature'].bones["Bone.001"], bpy.data.armatures['Armature'].bones["Bone.002"]]
Вы не много не поняли. Мне ненужен доступ к данным о дочерней кости, мне нужен конкретно ее индекс в общем список. Это нужно чтоб я просто сохранил целиком список всех костей, а уже затем в своем движке по индексам в этом списке смог получить информацию о дочерних костях из общего списка.
import bpy
for key,value in enumerate(bpy.context.object.data.bones):
print(key,value.name)
Очередная загадочная ошибка. Код:
Vertex_Counter = 0
while Vertex_Counter < (MeshSize[counter] / 12) :
GroupsCounter = 0
File.write(np.int32(VertexGroupsCount[counter][Vertex_Counter])) # записываю количесто групп для вершин
while GroupsCounter < VertexGroupsCount[counter][Vertex_Counter]:
File.write(np.int32(Weightes_Indexes[counter][Vertex_Counter][GroupsCounter *2])) # индекс вертекс группы
File.write(np.float32(Weightes_Indexes[counter][Vertex_Counter][(GroupsCounter *2) + 1])) # вес
GroupsCounter += 1
Veretex_Counter = Veretex_Counter + 1
Что в вашем понимании анимация номер n? Это изменения кости номер n?
Нет, не кости номер n. У костей вообще нет постоянных номеров, есть имена, коллекция костей эмулирует словарь, это я тоже уже писал.
повторю то, что писал две страницы назад:
>>> ob=bpy.data.objects['Armature']
>>> action = ob.animation_data.action
>>> fcu=action.fcurves
>>> for fc in fcu:
... print(fc.data_path, "index=", fc.array_index)
...
pose.bones["Bone"].location index= 0
pose.bones["Bone"].location index= 1
pose.bones["Bone"].location index= 2
pose.bones["Bone"].rotation_quaternion index= 0
pose.bones["Bone"].rotation_quaternion index= 1
pose.bones["Bone"].rotation_quaternion index= 2
pose.bones["Bone"].rotation_quaternion index= 3
pose.bones["Bone"].scale index= 0
pose.bones["Bone"].scale index= 1
pose.bones["Bone"].scale index= 2
pose.bones["Bone.001"].rotation_quaternion index= 0
pose.bones["Bone.001"].rotation_quaternion index= 1
pose.bones["Bone.001"].rotation_quaternion index= 2
pose.bones["Bone.001"].rotation_quaternion index= 3
В данном случае у меня 2 кости. У кости "Bone" ключи определены для перемещения, масштаба и поворота, поэтому у неё 10 кривых. Для кости "Bone.001" определены только повороты, у неё 4 кривые. Всего у этой арматуры 14 кривых.
Посмотреть, за что отвечает кривая номер n, можно вызвав fcu[n].data_path и fcu[n].array_index (в своём коде я вызываю это в цикле).
надо просто выцеплять это из строки?
Ну я таки вас умоляю, Питон - один из удобнейших языков для работы со строковыми данными. А тут и работы-то никакой не нужно, весь разбор в трёх строчках кода.
Вот пример для 0-й кривой, в цикле можно аналогично для энной сделать:
dp=fcu[0].data_path
skobka2=dp.find('"]')
bone=dp[12:skobka2]
napravlenie=dp[skobka2+3:]
print("кость=", bone, " направление=", napravlenie)
у меня выдало
кость= Bone направление= location
Есть другой метод понять что это за данные?
Может, и есть, но мне про него неизвестно.
Можно ли узнать из fcu[0].keyframe_points[0] какой это кадр по номеру?
у этого объекта есть свойство .co, которое выдаёт вектор (кадр, координата)
>>> kf0=fcu[0].keyframe_points[0]
>>> kf0.co
Vector((1.0, 0.0))
Честно говоря, мне было бы лень разбираться с ключевыми кадрами. Тут кроме координат и номеров кадров нужно выяснять тип интерполяции, а потом в движке эту интерполяцию воспроизводить. Ведь кривая может быть линейной, скруглённой, или ступенчатой.
Проще тупо взять все координаты всех кадров (не думая, какие из них ключевые) с помощью fcu[n].evaluate(frame)) . Но так, конечно же, размер экспортированного файла вырастет.
Дело видимо в том, что я в DX пользуюсь системой координат отличной от Blender
Ну, если ты пишешь свой собственный движок, то, по идее, должен в таких вещах разбираться... Помнится, в Ogre3D я запарился, но разобрался с этими кватернионами. Не знаю, отличается ли это от DX (про него ничего не знаю).
В Огре оси поставлены так:
x - слева-вправо
y - снизу-вверх
z - от камеры (от пользователя) - вглубь экрана
При такой постановке осей в экспортёре Огра сделано так:
mathutils.Quaternion( [ vec.w, vec.x, vec.z, -vec.y] )
Не, знаю, поможет ли тебе чем-то, но это всё, что я сейчас про это могу сказать. Может, кто-нибудь тут ещё может ответить...
... посмотри пример вот в этой темке, там углы Эйлера ...
http://blender-3d.ru/forum/index.php/topic,1756.msg25181.html#msg25181
def rot_bone(name,bone_name,axis,angle):
ob = bpy.data.objects[name]
bone = ob.pose.bones[bone_name]
bone.rotation_mode = 'XYZ'
bone.rotation_euler.rotate_axis(axis, math.radians(angle))
... имеется ввиду прочитать или записать углы? ...
... там во втором примере код ...
def rot_bone(name,bone_name,xyz_euler_angle):
ob = bpy.data.objects[name]
bone = ob.pose.bones[bone_name]
bone.rotation_euler = xyz_euler_angle
... то есть если записать, то bone.rotation_euler = xyz_euler_angle , а если читать - xyz_euler_angle = bone.rotation_euler ...