Fiche technique de l'accès aux données Blender Python Mesh

Nous allons résumer comment accéder aux données de maillage avec Blender Python. Ce n'est pas si difficile de chercher, mais je n'ai pas beaucoup d'informations, je vais donc les rassembler pour qu'elles puissent être répertoriées comme aide-mémoire. Il n'y a aucune garantie que les méthodes décrites ici soient optimales en termes de vitesse. S'il vous plaît laissez-moi savoir s'il existe une autre meilleure façon.

Ci-dessous, je voudrais résumer l'exemple de code qui crée des cubes et les définit avec UV, Vertex Color, Shape key et Skin Weight (Vertex Group). Il contient également le code et le journal pour imprimer l'ensemble de données qu'ils contiennent.

Postscript (03/04/2019): La version de Blender est devenue 2.80 et l'API a été partiellement modifiée et a cessé de fonctionner, alors je l'ai corrigée. Fondamentalement, les 4 points suivants.

  1. Lorsque de nouvelles données telles que Object, Mesh, etc., le mot-clé name de l'argument ne peut plus être omis, il est donc spécifié. Aussi, object_data.
  2. En raison du concept accru de collection, j'ai modifié «bpy.context.scene.objects» en «bpy.context.scene.collection.objects».
  3. Puisque mesh.uv_textures ne peut plus être utilisé, je l'ai changé en uv_layers.
  4. Puisque alpha ne peut pas être omis lors de l'initialisation de la classe de couleur, il a été modifié pour que alpha soit également spécifié. J'ai modifié le code source ci-dessous et laissé le code jusqu'à présent dans les commentaires.

Créer un maillage

Vous pouvez également créer un maillage en entrant en mode édition et en le créant en tant que BMesh, mais voici comment le créer en mode objet.

create_mesh.py


import bpy

verts = [[-1.0, -1.0,  1.0], [1.0, -1.0,  1.0], [1.0, 1.0,  1.0], [-1.0, 1.0,  1.0],
         [-1.0, -1.0, -1.0], [1.0, -1.0, -1.0], [1.0, 1.0, -1.0], [-1.0, 1.0, -1.0], ]
faces = [[0,1,2,3], [0,4,5,1], [1,5,6,2], [2,6,7,3], [0,3,7,4], [4,7,6,5]]

msh = bpy.data.meshes.new(name="cubemesh")
#msh = bpy.data.meshes.new("cubemesh")
msh.from_pydata(verts, [], faces)
msh.update()
obj = bpy.data.objects.new(name="cube", object_data=msh)
#obj = bpy.data.objects.new("cube", msh)
scene = bpy.context.scene
#scene.objects.link(obj)
scene.collection.objects.link(obj)

En tant que flux,

  1. Créez une instance de données de maillage avec meshes.new
  2. Créez un maillage en enregistrant le tableau de coordonnées de vertex et l'indice de sommet de chaque face avec from_pydata
  3. Reflétez les changements de maillage avec msh.update (). Cela peut ne pas être nécessaire si vous appelez from_pydata.
  4. Créez un objet avec ʻobjects.new`, mais passez les données de maillage créées précédemment comme argument.
  5. Enfin, n'oubliez pas de lier les objets créés dans scene.objects.link à la scène.

La partie [] de l'argument central de from_pydata définit les informations de bord. Sinon, il sera calculé automatiquement à partir des informations Face, donc si vous souhaitez créer un maillage qui génère des faces, il n'y a pas de problème avec un tableau vide. Blender peut également créer un maillage sans faces, auquel cas vous pouvez spécifier l'arête et laisser la spécification Face Index vide. Vous pouvez créer un maillage sans faces avec un script, tel qu'un maillage pour un contrôleur de rig.

Accès aux données de sommets

--vertices propriété --index: Index des sommets --co: Coordonnées des sommets --normal: sommet normal

#Énumération des informations sur les sommets
print("num of vertices:", len(msh.vertices))
for vt in msh.vertices:
    print("vertex index:{0:2} co:{1} normal:{2}".format(vt.index, vt.co, vt.normal))

production


num of vertices: 8
vertex index: 0 co:<Vector (-1.0000, -1.0000, 1.0000)> normal:<Vector (-0.5773, -0.5773, 0.5773)>
vertex index: 1 co:<Vector (1.0000, -1.0000, 1.0000)> normal:<Vector (0.5773, -0.5773, 0.5773)>
vertex index: 2 co:<Vector (1.0000, 1.0000, 1.0000)> normal:<Vector (0.5773, 0.5773, 0.5773)>
vertex index: 3 co:<Vector (-1.0000, 1.0000, 1.0000)> normal:<Vector (-0.5773, 0.5773, 0.5773)>
vertex index: 4 co:<Vector (-1.0000, -1.0000, -1.0000)> normal:<Vector (-0.5773, -0.5773, -0.5773)>
vertex index: 5 co:<Vector (1.0000, -1.0000, -1.0000)> normal:<Vector (0.5773, -0.5773, -0.5773)>
vertex index: 6 co:<Vector (1.0000, 1.0000, -1.0000)> normal:<Vector (0.5773, 0.5773, -0.5773)>
vertex index: 7 co:<Vector (-1.0000, 1.0000, -1.0000)> normal:<Vector (-0.5773, 0.5773, -0.5773)>

Accès au bord

print("num of edges:", len(msh.edges))
for ed in msh.edges:
    print("edge index:{0: 2} v0:{0} v1:{1}".format(ed.index, ed.vertices[0], ed.vertices[1]))

production


num of edges: 12
edge index: 0 v0: 4 v1: 5
edge index: 1 v0: 3 v1: 7
edge index: 2 v0: 6 v1: 7
edge index: 3 v0: 2 v1: 6
edge index: 4 v0: 5 v1: 6
edge index: 5 v0: 0 v1: 1
edge index: 6 v0: 4 v1: 7
edge index: 7 v0: 1 v1: 2
edge index: 8 v0: 1 v1: 5
edge index: 9 v0: 2 v1: 3
edge index:10 v0: 0 v1: 3
edge index:11 v0: 0 v1: 4

Accès au visage

La propriété ** polygones **, pas les faces. Il y a plus de logiciels avec les faces de nom de propriété, et j'essaye juste d'y accéder avec des visages? Je le souligne parce que ça devient.

--polygons, propriété --index: Index du polygone --vertices: Index des sommets adjacents --loop_start: Index des données de boucle de début pour itérer les arêtes et les sommets de ce polygone (le maillage a une propriété de boucles et un index dessus) --loop_total: nombre de boucles, réel, nombre de sommets de polygone --loop_indices: Liste des index de boucle

Avoir


print("num of polygons:", len(msh.polygons))
for pl in msh.polygons:
    print("polygon index:{0:2} ".format(pl.index), end="")
    print("vertices:", end="")
    for vi in pl.vertices:
        print("{0:2}, ".format(vi), end="")
    print("")

for pl in msh.polygons:
    print("polygon index:{0:2} ".format(pl.index))
    print(" > loops:", end="")
    print(" total:", pl.loop_total, end="")
    print(" start:", pl.loop_start, end="")
    print(" indices:", end="")
    for lp in pl.loop_indices:
        print("{0:2}, ".format(lp), end="")
    print("")

production


num of polygons: 6
polygon index: 0 vertices: 0,  1,  2,  3,
polygon index: 1 vertices: 0,  4,  5,  1,
polygon index: 2 vertices: 1,  5,  6,  2,
polygon index: 3 vertices: 2,  6,  7,  3,
polygon index: 4 vertices: 0,  3,  7,  4,
polygon index: 5 vertices: 4,  7,  6,  5,
polygon index: 0
 > loops: total: 4 start: 0 indices: 0,  1,  2,  3,
polygon index: 1
 > loops: total: 4 start: 4 indices: 4,  5,  6,  7,
polygon index: 2
 > loops: total: 4 start: 8 indices: 8,  9, 10, 11,
polygon index: 3
 > loops: total: 4 start: 12 indices:12, 13, 14, 15,
polygon index: 4
 > loops: total: 4 start: 16 indices:16, 17, 18, 19,
polygon index: 5
 > loops: total: 4 start: 20 indices:20, 21, 22, 23,

À propos de Loop

Données qui gèrent l'index des sommets et des arêtes. Il peut être utilisé lorsque vous souhaitez suivre les arêtes autour du polygone. Cependant, si vous souhaitez tracer correctement la contiguïté des sommets, des arêtes et des polygones, il est préférable de passer en mode Edition et d'utiliser Bmesh, donc je pense qu'il existe une telle propriété.

print("num of loops:", len(msh.loops))
for lp in msh.loops:
    print("loop index:{0:2} vertex index:{1:2} edge index:{2:2}".format(lp.index, lp.vertex_index, lp.edge_index))

production


num of loops: 24
loop index: 0 vertex index: 0 edge index: 5
loop index: 1 vertex index: 1 edge index: 7
loop index: 2 vertex index: 2 edge index: 9
loop index: 3 vertex index: 3 edge index:10
loop index: 4 vertex index: 0 edge index:11
loop index: 5 vertex index: 4 edge index: 0
loop index: 6 vertex index: 5 edge index: 8
loop index: 7 vertex index: 1 edge index: 5
loop index: 8 vertex index: 1 edge index: 8
loop index: 9 vertex index: 5 edge index: 4
loop index:10 vertex index: 6 edge index: 3
loop index:11 vertex index: 2 edge index: 7
loop index:12 vertex index: 2 edge index: 3
loop index:13 vertex index: 6 edge index: 2
loop index:14 vertex index: 7 edge index: 1
loop index:15 vertex index: 3 edge index: 9
loop index:16 vertex index: 0 edge index:10
loop index:17 vertex index: 3 edge index: 1
loop index:18 vertex index: 7 edge index: 6
loop index:19 vertex index: 4 edge index:11
loop index:20 vertex index: 4 edge index: 6
loop index:21 vertex index: 7 edge index: 2
loop index:22 vertex index: 6 edge index: 4
loop index:23 vertex index: 5 edge index: 0

Paramètres UV

Définir et obtenir des UV est aussi simple que d'ajouter un canal UV et d'utiliser ce canal comme clé pour accéder à la matrice UV. Notez que les canaux affichés dans les UV Maps de l'interface graphique sont gérés par la propriété appelée uv_textures de Mesh, mais les informations de coordonnées UV réelles sont conservées par la propriété appelée uv_layers.

Il ne conserve pas une topologie différente pour chaque canal UV comme 3dsMax. Par conséquent, le nombre de coordonnées UV contenues dans chaque couche est toujours la somme du nombre de sommets de tous les polygones. Blender ne semble pas avoir de concepts tels que la soudure et le soudage dans les données.

>>> len(msh.uv_layers[channel_name].data.items())
24
#Réglage UV
tmp = [[0.0, 0.0], [0.5, 0.0], [0.5, 0.5], [0.0, 0.5]]
uvs = tmp * 6 #Initialiser les coordonnées UV à définir
channel_name = "uv0" #Nom du canal UV
msh.uv_layers.new(name=channel_name) #Création d'un canal UV
#msh.uv_textures.new(channel_name) #Création d'un canal UV
for idx, dat in enumerate(msh.uv_layers[channel_name].data): #Itérer avec un tableau de couches UV
    dat.uv = uvs[idx]

Confirmation des données


print("num of uv layers:", len(msh.uv_layers))
#print("num of uv layers:", len(msh.uv_textures))
for ly in msh.uv_layerss:
    #for ly in msh.uv_textures:
    print(ly.name)
    for idx, dat in enumerate(msh.uv_layers[ly.name].data):
        print("  {0}:{1}".format(idx, dat.uv))
    print("")

production


uv0
  0:<Vector (0.0000, 0.0000)>
  1:<Vector (0.5000, 0.0000)>
  2:<Vector (0.5000, 0.5000)>
  3:<Vector (0.0000, 0.5000)>
  4:<Vector (0.0000, 0.0000)>
  5:<Vector (0.5000, 0.0000)>
  6:<Vector (0.5000, 0.5000)>
  7:<Vector (0.0000, 0.5000)>
  8:<Vector (0.0000, 0.0000)>
  9:<Vector (0.5000, 0.0000)>
  10:<Vector (0.5000, 0.5000)>
  11:<Vector (0.0000, 0.5000)>
  12:<Vector (0.0000, 0.0000)>
  13:<Vector (0.5000, 0.0000)>
  14:<Vector (0.5000, 0.5000)>
  15:<Vector (0.0000, 0.5000)>
  16:<Vector (0.0000, 0.0000)>
  17:<Vector (0.5000, 0.0000)>
  18:<Vector (0.5000, 0.5000)>
  19:<Vector (0.0000, 0.5000)>
  20:<Vector (0.0000, 0.0000)>
  21:<Vector (0.5000, 0.0000)>
  22:<Vector (0.5000, 0.5000)>
  23:<Vector (0.0000, 0.5000)>

Vertex Color

Il est facilement accessible via la propriété vertex_colors.

Réglage


#6 côtés rouge, Green, Blue, Cyan, Magenta,Essayez de peindre avec du jaune
#colormaps = [[1.0,0.0,0.0]]*4+[[0.0,1.0,0.0]]*4+[[0.0,0.0,1.0]]*4+[[0.0,1.0,1.0]]*4+[[1.0,0.0,1.0]]*4+[[1.0,1.0,0.0]]*4
colormaps = [[1.0,0.0,0.0,1.0]]*4+[[0.0,1.0,0.0,1.0]]*4+[[0.0,0.0,1.0,1.0]]*4+[[0.0,1.0,1.0,1.0]]*4+[[1.0,0.0,1.0,1.0]]*4+[[1.0,1.0,0.0,1.0]]*4

print("colormaps:", colormaps)
msh.vertex_colors.new(name='col')
# msh.vertex_colors.new('col')
for idx, vc in enumerate(msh.vertex_colors['col'].data):
    vc.color = colormaps[idx]

Obtenez des données


#Affichage de la couleur du sommet
print("num of vertex color layers:", len(msh.vertex_colors))
for ly in msh.vertex_colors:
    print(ly.name)
    for idx, vc in enumerate(msh.vertex_colors['col'].data):
        print("  {0:2}:{1}".format(idx,vc.color))

production


num of vertex color layers: 1
col
   0:<Color (r=1.0000, g=0.0000, b=0.0000)>
   1:<Color (r=1.0000, g=0.0000, b=0.0000)>
   2:<Color (r=1.0000, g=0.0000, b=0.0000)>
   3:<Color (r=1.0000, g=0.0000, b=0.0000)>
   4:<Color (r=0.0000, g=1.0000, b=0.0000)>
   5:<Color (r=0.0000, g=1.0000, b=0.0000)>
   6:<Color (r=0.0000, g=1.0000, b=0.0000)>
   7:<Color (r=0.0000, g=1.0000, b=0.0000)>
   8:<Color (r=0.0000, g=0.0000, b=1.0000)>
   9:<Color (r=0.0000, g=0.0000, b=1.0000)>
  10:<Color (r=0.0000, g=0.0000, b=1.0000)>
  11:<Color (r=0.0000, g=0.0000, b=1.0000)>
  12:<Color (r=0.0000, g=1.0000, b=1.0000)>
  13:<Color (r=0.0000, g=1.0000, b=1.0000)>
  14:<Color (r=0.0000, g=1.0000, b=1.0000)>
  15:<Color (r=0.0000, g=1.0000, b=1.0000)>
  16:<Color (r=1.0000, g=0.0000, b=1.0000)>
  17:<Color (r=1.0000, g=0.0000, b=1.0000)>
  18:<Color (r=1.0000, g=0.0000, b=1.0000)>
  19:<Color (r=1.0000, g=0.0000, b=1.0000)>
  20:<Color (r=1.0000, g=1.0000, b=0.0000)>
  21:<Color (r=1.0000, g=1.0000, b=0.0000)>
  22:<Color (r=1.0000, g=1.0000, b=0.0000)>
  23:<Color (r=1.0000, g=1.0000, b=0.0000)>

Shape Key

C'est fondamentalement le même que le réglage UV. Contrairement à UV, la propriété shape_keys du maillage est responsable à la fois de la gestion des clés de forme et de la gestion des données des sommets. C'est la propriété key_blocks de la propriété shape_keys qui contient réellement les données.

Réglage



 #Dans la variable verts définie lors de la création du cube(0,0,1), (0,0,-1)MoveUp chacun,Utilisez la touche Descendre
shapemaps = {'MoveUp':[[v[0],v[1],v[2]+1.0] for v in verts],
             'MoveDown':[[v[0],v[1],v[2]-1.0] for v in verts],}

#Créer une clé de base s'il n'y a pas de forme
obj.shape_key_add()
msh.shape_keys.key_blocks[-1].name = "Basis"

for sname in shapemaps:
    lst = shapemaps[sname]
    obj.shape_key_add()
    kb = msh.shape_keys.key_blocks[-1]
    kb.name = sname
    for idx, co in enumerate(lst):
        kb.data[idx].co = co

Obtenez des données


#Obtenir la clé de forme
print("num of Shape Keys:", len(msh.shape_keys.key_blocks))
for kb in msh.shape_keys.key_blocks:
    print("  Key Block:", kb.name)
    for idx, dat in enumerate(kb.data):
        print("    {0}:{1}".format(idx, dat.co))

production


num of Shape Keys: 3
  Key Block: Basis
    0:<Vector (-1.0000, -1.0000, 1.0000)>
    1:<Vector (1.0000, -1.0000, 1.0000)>
    2:<Vector (1.0000, 1.0000, 1.0000)>
    3:<Vector (-1.0000, 1.0000, 1.0000)>
    4:<Vector (-1.0000, -1.0000, -1.0000)>
    5:<Vector (1.0000, -1.0000, -1.0000)>
    6:<Vector (1.0000, 1.0000, -1.0000)>
    7:<Vector (-1.0000, 1.0000, -1.0000)>
  Key Block: MoveDown
    0:<Vector (-1.0000, -1.0000, 0.0000)>
    1:<Vector (1.0000, -1.0000, 0.0000)>
    2:<Vector (1.0000, 1.0000, 0.0000)>
    3:<Vector (-1.0000, 1.0000, 0.0000)>
    4:<Vector (-1.0000, -1.0000, -2.0000)>
    5:<Vector (1.0000, -1.0000, -2.0000)>
    6:<Vector (1.0000, 1.0000, -2.0000)>
    7:<Vector (-1.0000, 1.0000, -2.0000)>
  Key Block: MoveUp
    0:<Vector (-1.0000, -1.0000, 2.0000)>
    1:<Vector (1.0000, -1.0000, 2.0000)>
    2:<Vector (1.0000, 1.0000, 2.0000)>
    3:<Vector (-1.0000, 1.0000, 2.0000)>
    4:<Vector (-1.0000, -1.0000, 0.0000)>
    5:<Vector (1.0000, -1.0000, 0.0000)>
    6:<Vector (1.0000, 1.0000, 0.0000)>
    7:<Vector (-1.0000, 1.0000, 0.0000)>

Vertex Group

Je pense que ce sont les données les plus simples, mais c'est la structure de données la plus gênante. Blender détient le poids de la peau dans ce groupe Vertex. Par conséquent, les données sont conservées sous forme de tableau associatif avec l'indice du groupe de sommets comme clé et le poids comme valeur pour chaque sommet. Une fonction pour définir le poids des sommets est fournie dans la structure du groupe de sommets afin qu'il puisse être défini comme s'il s'agissait d'UV. Je ne pense pas que la vitesse de traitement soit rapide.

Réglage


bone_list = ["bone1", "bone2"]
weight_map = {"bone1":[1.0]*4+[0.0]*4, # [1.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0],
              "bone2":[0.0]*4+[1.0]*4,
              }

vg_list = [] #Liste des VertexGroup
#Créer un groupe de sommets
for bname in bone_list:
    obj.vertex_groups.new(name=bname)
    #obj.vertex_groups.new(bname)
    vg_list.append(obj.vertex_groups[-1])

#Paramètres via Vertex Group
for vg in vg_list:
    weights = weight_map[vg.name]
    for vidx, w in enumerate(weights):
        if w != 0.0:
            vg.add([vidx], w, 'REPLACE')

Essayez d'obtenir les données via les propriétés des sommets.

Obtenez des données


#Obtenir du poids via la propriété groupes de Vertex
msh = obj.data
for v in msh.vertices:
    for vge in v.groups:
        print("vindex:{0} group index:{1} weight:{2}".format(v.index, vge.group, vge.weight))

production


vindex:0 group index:0 weight:1.0
vindex:1 group index:0 weight:1.0
vindex:2 group index:0 weight:1.0
vindex:3 group index:0 weight:1.0
vindex:4 group index:1 weight:1.0
vindex:5 group index:1 weight:1.0
vindex:6 group index:1 weight:1.0
vindex:7 group index:1 weight:1.0

Bonus: à propos de foreach_set

Les données de collection telles que la propriété data de uv_layer ont une fonction appelée foreach_set, et vous pouvez définir la valeur en une seule fois avec un tableau. Il n'a pas été vérifié s'il est plus rapide que d'itérer les séquences normalement et de les affecter une par une. Notez que cette méthode passera un tableau ordinaire qui est aplati au lieu du tableau du tableau ci-dessus.

python


tmp = [0.0, 0.0, 0.5, 0.0, 0.5, 0.5, 0.0, 0.5]
uvs = tmp * 6 #Initialiser les coordonnées UV à définir
channel_name = "uv0"
msh.uv_textures.new(channel_name)
msh.uv_layers[channel_name].data.foreach_set("uv", uvlist)

python


shapemaps = {'MoveUp':[[v[0],v[1],v[2]+1.0] for v in verts],
             'MoveDown':[[v[0],v[1],v[2]-1.0] for v in verts],}
for sname in shapemaps:
    lst = shapemaps[sname]
    
    lst2 = list(chain.from_iterable(lst)) #Cela semble être plus rapide avec la méthode d'aplatissement du tableau de tableaux
    obj.shape_key_add()
    sk = msh.shape_keys.key_blocks[-1]
    sk.name = sname
    sk.data.foreach_set("co", lst2)

Recommended Posts

Fiche technique de l'accès aux données Blender Python Mesh
Fiche technique de la science des données (Python)
Aide-mémoire Python3 (basique)
Fiche technique PySpark [Python]
Feuille de triche de tri Python
[Python3] Entrée standard [Cheet sheet]
Fiche technique du didacticiel Python Django
Apache Beam Cheet Sheet [Python]
Aide-mémoire Python (pour les expérimentés C ++)
Accéder aux nœuds de shader de Blender depuis Python
Fiche de triche AtCoder en python (pour moi-même)
Feuille de calcul du modélisateur d'optimisation mathématique (PuLP) (Python)
Analyse de données python
Extrusion Python Extrude de Blender 2.9
[Mise à jour] Aide-mémoire de la syntaxe Python pour la boutique Java
Aide-mémoire au curry
Aide-mémoire SQLite3
feuille de triche pyenv
[python] Lecture de données
[Pour la science des données] Aide-mémoire Oreore Jupyter [Jupyter Notebook / Lab]
Obtenir des données de la base de données via ODBC avec Python (Access)
Analyse de données avec python 2
feuille de triche de commande conda
Aide-mémoire PIL / Pillow
Accéder à bitcoind depuis python
Présentation de l'analyse de données python
mixeur, python, escalier en colimaçon
feuille de triche de commande ps
Nettoyage des données à l'aide de Python
Aide-mémoire de l'API Spark
mélangeur, python, comportement de la sphère
Blender 2.8, mise à l'échelle du cube Python
Faire fonctionner Blender avec Python
Modèle d'analyse de données Python
[Tutoriel Python] Structure des données
[Python] Tri des données Numpy
Feuilles de triche PDF basées sur Python
Analyse de données avec Python