Nous allons voir ici
Nous allons du coup devoir voir comment stocker les références de ces objets et comment utiliser les méthodes qui s'y appliquent. Du coup, vous allez réussir à modifier les objets créés par le script Python mais aussi modifier les objets que vous avez créé directement en utilisant l'interface.
Au final, on pourrait faire des animations précises de ce type : (c'est une animation créée pour expliquer le principe de fonctionnement des codeurs pour la robotique : des moteurs qui savent dire combien de tours ils ont fait et dans quel sens !)
On pourrait ainsi créer un script qui uniformise les couleurs des différents objets créés sans avoir à le faire à la main, objet par objet. Avec 200 objets, ça devient vite un must have !
Rappel : Nous allons scripter avec Python dans cette partie.
Parfois, le code plante et il est toujours bon de savoir pourquoi. Or, les erreurs n'apparaissent pas directement dans Blender mais dans la console Système du logiciel Blender. Comment la voir ?
Menu INFO - WINDOW - TOOGLE SYSTEM CONSOLE. Le chemin en image :
AGRANDIR LA ZONE DE TEXTE : Si vous voulez facilement voir tout votre script, vous pouvez maximiser la taille de l'écran en plaçant la souris dessus et en utlisant la combinaison de touches CTRL + ↑.
AFFICHAGE DU SCRIPT : Vous pouvez afficher le menu d'affichage du script en appuyant sur le petit plus (+) juste à sa gauche :
On obtient alors un second menu. Dans ce menu, je vous conseille de sélectionner Line Numbers pour afficher les numéros de lignes, Synthax Highlight pour mettre les éléments synthaxiques en valeur et et de désactiver Live Edit qui va lancer le script dès que vous lui faites subir la moindre modification.
Pour pouvoir accéder à un objet3D depuis un script Python, il suffit en réalité de stocker son 'adresse' dans une variable.
Pour l'instant, vous avez fait des créations d'objets 3D du type :
import bpy # On importe la bibliothèque Blender Python
distance = 2
bpy.ops.mesh.primitive_cube_add( location = (0,0,0))
bpy.ops.mesh.primitive_cube_add( location = (distance,distance,0))
Et cela donne deux cubes situés ainsi :
01° Supprimer le cube initial de Blender puis lancer ce script. Comment se nomme le premier cube créé ? Et le second ? Pour cela, sélectionnez un cube dans la vue 3D et allez voir dans la fenêtre qui liste les objets. Il sera mis en valeur en blanc .
On voit donc que le premier cube se nomme Cube et que le second se nomme Cube.001.
Autant, on garde dans Blender un accés à ce cube, autant dans le script on ne stocke nulle part sa référence. On a placé le nombre 2 dans la variable distance mais nous n'avons pas créé de variables permettant d'accéder aux cubes. Nous parvenons donc à les créer, les modifier et les poser. Mais on ne peut pas les modifier après coup avec le code Python.
C'est ce que nous allons faire.
Pour connaitre la référence de l'objet Blender actuellement sélectionné, il suffit de taper ceci (mais il faut le savoir !) :
objet3DSelectionne = bpy.context.object
Or, lorsqu'on crée un objet, il est automatiquement en mode sélectionné. Si vous voulez mémoriser la référence d'un objet que vous venez de créer avec une instruction Python, il faut donc stocker ce résultat dans une variable.
Si on veut mémoriser les références des deux cubes qu'on vient de créer, on peut donc faire ceci :
import bpy # On importe la bibliothèque Blender Python
bpy.ops.mesh.primitive_cube_add( location = (0,0,0))
monCube1 = bpy.context.object
bpy.ops.mesh.primitive_cube_add( location = (2,2,0))
monCube2 = bpy.context.object
CODE COULEUR : dans la suite des activités Blender-Python, je n'utiliserai plus le bleu pour représenter les variables contenant les références d'un objet 3D : désormais, ces variables particulière seront en orange. Cela permettra plus facilement de comprendre les variables qui sont directement en lien avec des données contenues dans Blender.
02° Supprimer les objets 3D. Placez en mode SCRIPT si ce n'est pas encore fait. Utiliser alors le script suivant :
import bpy # On importe la bibliothèque Blender Python
distance = 2
bpy.ops.mesh.primitive_cube_add( location = (0,0,0))
monCube1 = bpy.context.object
bpy.ops.mesh.primitive_cube_add( location = (distance,distance,0))
monCube2 = bpy.context.object
Ca n'a l'air de rien. Vous devriez arriver à une configuration proche de celle présentée ci-dessous. Vous allez pouvoir voir la puissance de ce qu'on vient de faire.
Maintenant, nous allons passer à un ensemble de commande dans la console Python.
03° Tapez les instructions suivantes. Observez les informations qu'elles transmettent depuis Blender ou les modifications qu'elles engendrent dans l'interface Blender.
>>> bpy.ops.mesh.primitive_cube_add( location = (4,0,0))
>>> monCube3 = bpy.context.object
>>> monCube3
bpy.data.objects['Cube.002']
On voit que la variable monCube3 fait bien référence à un objet 3D contenu dans Blender. On voit son nom dans l'interface : Cube.002
.
04° Tentez maintenant de récupérer des informations géométriques sur votre objet :
>>> monCube3.location
Vector((2.0, 0.0, 0.0))
>>> monCube3.scale
Vector((1.0, 1.0, 1.0))
>>> monCube3.rotation_euler
Euler((0.0, 0.0, 0.0), 'XYZ')
On constate bien que les informations sur la localisation de l'objet sont fournies par groupe de 3 et l'ensemble est fourni entre parenthèses. Il s'agit des coordonnées X,Y,Z mais fournies dans un tuple.
Vector((2.0, 0.0, 0.0))
Pareil pour les informations sur les échelles d'aggrandissement en X,Y et Z ou les inclinaisons de l'objet par rapport aux axes X,Y et Z.
Comment récupérer les informations une à une ? Imaginons qu'on veuille uniquement connaitre la position en X de l'objet, on fait comment ?
05° Utilisez les commandes ci-dessous pour vérifier que l'on obtient bien deux fois la même chose :
1er méthode : on récupère le tuple et on lit son contenu dans ses trois premières 'cases'. Attention, la première porte le numéro 0 !
>>> coordonnees = monCube3.location
>>> coordonnees[0]
2.0
>>> coordonnees[1]
0.0
>>> coordonnees[2]
0.0
2e méthode : on stocke directement les trois valeurs dans 3 variables.
>>> x,y,z = monCube3.location
>>> x
2.0
>>> y
0.0
>>> z
0.0
Vous devriez dans les deux cas obtenir les coordonnées X,Y et Z.
Pour connaitre l'attribut à noter, il suffit d'ouvrir avec N la fenêtre des propriétés dans la vue 3D. En restant immobile avec la souris au dessus de la valeur qu'on veut récupérer, on obtient le code Python permettant de récupérer la donnée : ici, je reste immobile au dessus de la rotation en Y.
06° Utilisez les informations disponibles pour obtenir les changements d'échelle (scale) effectués un objet. Si vous n'avez pas utilisé l'effet Scale sur l'objet, vous devriez obtenir 1.0 dans les trois directions XYZ.
...CORRECTION...
En laissant la souris au dessus des 3 valeurs Scale, on obtient scale[0], scale[1] et scale[2].
On obtient donc deux façons d'obtenir ces valeurs :
>>> changementEchelle = monCube3.scale
>>> changementEchelle[0]
1.0
>>> changementEchelle[1]
1.0
>>> changementEchelle[2]
1.0
>>> ex,ey,ez = monCube3.scale
>>> ex
1.0
>>> ey
1.0
>>> ez
1.0
Comme vous l'avez vu, les noms des attributs ne sont pas choisi non plus totalement au hasard. En programmation, on tente toujours de donner des noms qui aient un sens immédiatement.
Pour trouver les coordonnées, on utilise donc monObjet3D.location
.
Pour trouver les modifications d'échelle, on utilise donc
monObjet3D.scale
Cette fois, il suffit d'utiliser l'attribut dimensions.
monObjet3D.dimensions
Comme un cube est de largeur 2 par défaut, on pourra obtenir ceci si vous n'avez pas modifié votre cube :
>>> monCube3.dimensions
Vector((2.0, 2.0, 2.0))
>>> dimX,dimY,dimZ = monCube3.dimensions
>>> dimX
2.0
Encore une fois, en se renseignant dans la vue 3D, on voit que l'attribut se nomme rotation_euler.
monObjet3D.rotation_euler
Pour le cube précédent, si vous n'avez pas fait tourner :
>>> monCube3.rotation_euler
Euler( (0.0, 0.0, 0.0), 'XYZ' )
>>> rx,ry,rz = monCube3.rotation_euler
>>> rx
0.0
Pour l'instant, nous travaillons avec la console et en travaillant sur une variable obtenu à partir de l'objet sélectionné à un moment :
>>> monCube3 = bpy.context.object
Mais on peut faire bien mieux. Vous pouvez créer des objets avec l'interface, ou les importer depuis un fichier. Et ensuite, vous pourrez créer des variables permettant de les manipuler, pourvu que vous connaissiez leurs noms dans l'interface.
07° Créez deux cylindres avec l'interface. Normalement, ils devraient se nommer Cylinder et Cylinder.001. Déplacer les cylindres, faites leur subir une rotation ect ... Bref, modifiez les.
Maintenant, nous voudrions modifiez ces deux objets à l'aide d'un script. Il va donc falloir récupérer leurs références. Comment ? Comme ça (attention, il s'agit d'un script, pas de commandes à taper directement dans la console :
import bpy # On importe la bibliothèque Blender Python
monObjet1 = bpy.data.objects['Cylinder']
monObjet2 = bpy.data.objects['Cylinder.001']
Comme vous le voyez, on va chercher dans le module bpy.data
pour y chercher un objet objects, avec un s attention. Cette objet est en réalité une collection de toutes les références des objets contenus dans votre fichier 3D. Pour y récupérer la référence d'un objet en particulier, il faut ouvrir et fermer des crochets et fournir à l'intérieur le string contenant le nom de l'objet voulu. J'aurai pu lui garder une couleur bleue : il s'agit d'une variable. Mais elle est un peu particulière : on a pas besoin de la créer. C'est Blender qui la remplit tout seul. D'où sa couleur marron.
Maintenant, nous allons pouvoir récupérer les informations de nos deux objets à l'aide des variables-objets monObjet1 et monObjet2.
08° Rajouter des lignes à ce script pour qu'il affiche les coordonnées du premier objets et ses dimensions.
...CORRECTION...
import bpy # On importe la bibliothèque Blender Python
# Recherche des références aux objets
monObjet1 = bpy.data.objects['Cylinder']
monObjet2 = bpy.data.objects['Cylinder.001']
# Affichage des coordonnées
print('Coordonnées :')
for x in range(3) :
print(monObjet1.location[x])
# Affichage des dimensions
print('Dimensions :')
for x in range(3) :
print(monObjet1.dimensions[x])
Console : n'oubliez pas qu'un print affiche le résultat dans la console Système, pas la console Python intégrée à Blender.
Pour finir, voyons encore quelques dernières propriétés :
09° Tapez ceci dans la console Python de façon à mémoriser le premier cylindre puis tapez le reste de l'exemple :
>>> monObjet1 = bpy.data.objects['Cylinder']
Pour voir si l'objet est caché ou visible, vous pouvez chercher l'attribut hide
Si l'objet est visible (le petit oeil de l'interface à côté de son nom est sélectionné), cela va renvoyer False car l'objet n'est pas caché :
>>> monObjet1.hide
False
10° Cacher l'objet à l'aide de l'oeil dans le menu de droite et retaper les mêmes instructions. Vous devriez obtenir une réponse True cette fois : l'objet est caché.
>>> monObjet1.hide
True
Nous allons voir deux derniers attributs assez pratiques :
>>> monObjet1.type
'MESH'
Le type vous permet de savoir que vous avez bien affaire à un objet fait avec des vertex, pas une caméra ou une lumière.
Voici la commande à taper pour obtenir le type de l'objet sélectionné :
>>> bpy.context.object.type
11° Sélectionner différents objets et faire un essai avec l'instruction à chaque fois. Sélectionnez des objets, la caméra, une lumière ect... CTRL C et CTRL V sont vos amis.
Quelques exemples de réponses. A vous de deviner ce que j'ai sélectionné à chaque fois !
'CAMERA'
'LAMP'
Et ça sert à quoi ? Nous verrons plus tard qu'on peut tester une valeur. On pourra ainsi éviter de tenter d'appliquer un matériau à la caméra par exemple !
Dernière information pratique à récupérer parfois : le nom de l'objet. C'est facile, c'est l'attribut name.
Exemple :
>>> monObjet1.name
'Cylinder'
Un test sur cette valeur peut notamment vous permettre de savoir si l'objet est un Cube, une Sphere ... Nous verrons cela lors de l'activité sur les tests IF.
Passons aux choses sérieuses : nous allons pouvoir changer les paramètres d'un objet. Par exemple, nous pourrions décider de créer un script qui éloigne l'objet actif en cours du centre du plateau. On veut multiplier chaque coordonnée par 2 par exemple. Et lui faire subir une rotation de 30° selon l'axe z au passage tiens.
Et comment fait-on cela ? C'est simple, on utilise l'affectation.
Voici des exemples sur un objet qu'on nommera ici monObjet :
Agit sur | Attribut | Exempe |
---|---|---|
Coordonnées | location |
monObjet.location = (1,2,3) Déplace l'objet aux coordonnées X=1, Y=2 et Z = 3. |
Echelle | scale |
monObjet.scale = (2,3,4) Multiplie la taille de l'objet par 2 sur X, 3 sur Y et 4 sur Z. |
Dimensions | dimensions |
monObjet.dimensions = (2,3,4) L'objet est large de 2 selon X, 3 selon Y et 4 selon Z. |
Rotation | rotation_euler |
monObjet.rotation_euler = (2,3,4) On fait tourner l'objet de 2 rad sur l'axe X, 3 rad sur l'axe Y ect ... L'objet tourne de un tour avec 2 pi radians (soit 360°). Cela correspond en gros à
Nous reprendrons plus bas l'exercice permettant de convertir proprement les degrés en radians. |
Visible ou non | hide |
monObjet.hide = True Dissimule l'objet à la vue monObjet.hide = False Reactive la visibilté de l'objet |
Nom Blender | name |
monObjet.name = "nouveauNom" Change le nom de l'objet dans l'interface |
A vous de jouer.
12° Créer un script qui multiplie la dimension d'un objet par 1.1 mais qui l'éloigne du centre en multipliant les distances x,y et z par 1,3. On rajoutera une rotation de 30° selon l'axe X à chaque fois. Vérifier le fonctionnement en sélectionnant différents objets.
...CORRECTION...
import bpy # On importe la bibliothèque Blender Python
# Recupération de l'objet sélectionné
monObjet = bpy.context.object
# Récupération et modification des coordonnées
x,y,z = monObjet.location
monObjet.location = (x*1.3,y*1.3,z*1.3)
# Récupération et modification des dimensions
dx,dy,dz = monObjet.dimensions
monObjet.dimensions = (dx*1.1,dy*1.1,dz*1.1)
# Récupération et modification de la rotation selon Z
rx,ry,rz = monObjet.rotation_euler
monObjet.rotation_euler = (rx+0.52,0,0)
Voici l'exemple de la rotation de l'une des activités précédentes.
import bpy # On importe la bibliothèque Blender Python
import math # On importe la bibliothèque math pour calculer les cos...
rayon = 10
for angle_degre in range(-0,360,36):
angle = math.radians(angle_degre) # math.radians permet de convertir en radians
x = rayon*math.cos(angle)
y = rayon*math.sin(angle)
bpy.ops.mesh.primitive_cube_add(location=(x,y,0))
13° Question : pourriez-vous parvenir à créer un script qui fait subir à l'objet actif une rotation de 10° autour de sa position actuelle à chaque fois qu'on lance le script ?
Remarque super pratique : si la souris est sur la fenêtre des scripts, vous pouvez utilisez ALT+P (comme python) pour lancer automatiquement le script en cours. Sympa non ? Ca évite de taper sur RUN SCRIPT.
...CORRECTION...
import bpy # On importe la bibliothèque Blender Python
import math # On importe la bibliothèque math pour calculer les cos...
# Recherche de 10° en radians
angle = math.radians(10) # math.radians permet de convertir en radians
# Recupération de l'objet sélectionné
monObjet = bpy.context.object
# Récupération et modification de la rotation selon Z
rx,ry,rz = monObjet.rotation_euler
monObjet.rotation_euler = (rx+angle,0,0)
Comment créer un cone et modifier sa taille ? Il suffit d'aller voir la documentation : aller sur le menu CREATION de Blender, faire un clic-droit et sélectionner la documentation Python en ligne :
bpy.ops.mesh.primitive_uv_sphere_add(segments=32, ring_count=16, size=1.0, calc_uvs=False, view_align=False, enter_editmode=False, location=(0.0, 0.0, 0.0), rotation=(0.0, 0.0, 0.0), layers=(False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False))
Construct a UV sphere mesh.
Parameters:
bpy.ops.mesh.primitive_cube_add(radius=1.0, calc_uvs=False, view_align=False, enter_editmode=False, location=(0.0, 0.0, 0.0), rotation=(0.0, 0.0, 0.0), layers=(False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False))
Construct a cube mesh.
Parameters:
bpy.ops.mesh.primitive_cone_add(vertices=32, radius1=1.0, radius2=0.0, depth=2.0, end_fill_type='NGON', calc_uvs=False, view_align=False, enter_editmode=False, location=(0.0, 0.0, 0.0), rotation=(0.0, 0.0, 0.0), layers=(False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False))
Construct a conic mesh.
Parameters:
14° Créer un script agit sur une sphère et un cone créé à l'aide de l'interface. Lorsqu'on lance le script, la taille du cube doit doubler, la taille du cone doit tripler.
Voici une première version du code que vous devriez tenter de faire. Attention aux noms de vos objets. Je vous rassure, ça ne fonctionne pas. C'est normal !
...CORRECTION...
import bpy # On importe la bibliothèque Blender Python
# Recupération des objets sélectionnés
maSphere = bpy.data.objects['Sphere']
monCone = bpy.data.objects['Cone']
# Récupération et modification de la taille de la sphère
rayon = maSphere.size
maSphere.size = rayon*2
# Récupération et modification du cone
rayon1 = monCone.radius1
rayon2 = monCone.radius2
hauteur = monCone.depth
monCone.radius1 = rayon1*2
monCone.radius2 = rayon2*2
monCone.depth = hauteur*2
Si vous lancez le script, vous pourrez voir une jolie erreur apparaitre dans la console système :
AttributeError: 'Object' object has no attribute 'size'
Error: Python script fail, look in the console for now...
D'où vient le problème ? Pas de la documentation mais de son utilisation. La méthode présentée est une méthode dite constructeur. Elle sert à créer l'objet 3D en lui fournissant certains paramètres.
Mais une fois l'objet créé, il réagit comme n'importe quel objet : c'est juste un ensemble de vertex. Pour l'agrandir, il va donc falloir utiliser soit scale, soit dimensions.
15° Créer un script qui fonctionne cette fois.
...CORRECTION...
import bpy # On importe la bibliothèque Blender Python
# Recupération des objets sélectionnés
maSphere = bpy.data.objects['Sphere']
monCone = bpy.data.objects['Cone']
# Récupération et modification de la sphère
maSphere.dimensions = (2*maSphere.dimensions[0], 2*maSphere.dimensions[1], 2*maSphere.dimensions[2])
# Récupération et modification du cone
monCone.dimensions = (3*monCone.dimensions[0], 3*monCone.dimensions[1], 3*monCone.dimensions[2])
Attention, un code qui tenterai de modifier un à un le conteun du tuple ne fonctionnera pas :
import bpy # On importe la bibliothèque Blender Python
# Recupération des objets sélectionnés
maSphere = bpy.data.objects['Sphere']
monCone = bpy.data.objects['Cone']
# Récupération et modification de la sphère
maSphere.dimensions[0] = maSphere.dimensions[0]*2
maSphere.dimensions[1] = maSphere.dimensions[1]*2
maSphere.dimensions[2] = maSphere.dimensions[2]*2
# Récupération et modification du cone
monCone.dimensions[0] = monCone.dimensions[0]*3
monCone.dimensions[1] = monCone.dimensions[1]*3
monCone.dimensions[2] = monCone.dimensions[2]*3
Pourquoi ? Parce qu'on ne peut pas modifier les éléments d'un tuple. Il faut recréer l'intégralité du tuple et le réaffecter à dimensions. Une fois créé, le contenu d'un tuple est immuable. Si vous voulez le modifier, il faut récréer un autre tuple. Les strings réagissent de la même manière.
A cause de cela, ce n'est pas facile de manipuler un tuple avec un boucle for par exemple.
Comme vous le voyez, modifier des tuples, c'est un peu long. Heureusement, les concepteurs de ce objet de stockage d'informations ont pensé à certaines fonctionnalités bien pratiques. Ainsi le tuple supporte par exemple la multiplication. On pourra se contenter d'écrire ceci :
import bpy # On importe la bibliothèque Blender Python
# Recupération des objets sélectionnés
maSphere = bpy.data.objects['Sphere']
monCone = bpy.data.objects['Cone']
# Récupération et modification de la sphère
maSphere.dimensions = 2*maSphere.dimensions
# Récupération et modification du cone
monCone.dimensions = 3*monCone.dimensions
Vous savez maintenant créer des objets 3D de base avec Python.
Vous savez mettre les références en mémoire et les utiliser pour déplacer ou déformer ces objets.
Nous allons continuer à utiliser dans le cadre de la création et gestion des matériaux avec Blender et Python.
Commençons par apprendre à créer les matériaux avec du code
La ligne de code suivante revient à sélectionner le menu Material(1), cliquer sur l'icone permettant de créer un nouveau matériau (2) puis à lui donner un nom (3) :
# Création du matériau-objet "couleur_1
bpy.data.materials.new( 'couleur_1' )
Pourquoi avoir mis materials en marron ? Car il s'agit d'une collection des références aux différents matériaux. Comme objects. En cliquant sur la collection des matériaux disponibles, vous devriez voir celui que nous venons de créer en Python :
On remarquera également que new est en rouge foncée : il s'agit d'une méthode intégrée à materials. Nous verrons plus tard qu'on fournit des informations aux méthodes à l'aide des parenthèses.
Comme vous pouvez le voir, nous avons bien créer un nouveau matériau. Par contre, il n'est pas affecté à notre objet en cours (on voit 0 devant son nom) et les couleurs ... sont celles par défaut.
Une fois le matériau créé, il faut donc définir sa couleur "diffuse" par exemple : (rouge, vert et bleu devront être remplacés par des valeurs ou des variables contenant les valeurs voulues.
Pour accéder à l'un des éléments d'une collection de données, il faut utiliser les crochets [ et ] et fournir via un STRING le nom de l'élément qu'on veut récupérer.
Voici le code pour modifier la couleur diffuse d'un matériau existant nommé 'couleur_1' :
# Affectation des couleurs "diffuse" en format RGB
bpy.data.materials['couleur_1'].diffuse_color = (rouge, vert, bleu)
Ainsi, si on tape la ligne suivante, cela revient à règler manuellement les trois couleurs à la valeur de 0.8 :
bpy.data.materials['couleur_1'].diffuse_color = (0.8, 0.8, 0.8)
Et comment je sais qu'il faut utiliser diffuse_color ? Grace à la documentation : cliquez droit sur le diffuse de l'interface et sélectionnez Online Python References. Vous y trouverez le nom de l'attribut à utiliser sur la référence de votre matériau : diffuse_color.
16° Créer un script qui crée un matériau nommé couleur_1 dont la couleur diffuse RGB est réglée sur (1,0.5 et 0.25). Cela veut dire que l'intensité rouge est reglée à 1, le vert à 0.5 et le bleu à 0.25.
Attention : si vous lancez le script plusieurs fois, vous allez créer un premier matériau nommé couleur_1, puis un matériau nommé couleur_1_001 puis couleur_1_002. Il faudra donc être vigilant et ne pas confondre la création et la modification. Nous n'avons pas encore vu les tests, le mieux est encore de simplement créer les objets et matériaux via l'interface et les modifier via Python.
Première solution : on ne stocke pas la référence du matériau. On va juste le chercher à chaque fois dans le menu Material de Blender.
...CORRECTION SANS STOCKAGE...
import bpy # On importe la bibliothèque Blender Python
# Création du matériau-objet "couleur_1
bpy.data.materials.new( 'couleur_1' )
bpy.data.materials['couleur_1'].diffuse_color = (0.8, 0.8, 0.8)
Deuxième solution : on stocke la référence du matériau. Cela permettra d'y faire référence plus facilement.
...CORRECTION AVEC STOCKAGE...
import bpy # On importe la bibliothèque Blender Python
# Création et stockage du matériau-objet "test_1" dans Blender, monMaterial dans Python
bpy.data.materials.new( 'couleur_1' )
monMaterial = bpy.data.materials['couleur_1']
# Modification de monMaterial dans Python
monMaterial.diffuse_color = (0.8, 0.8, 0.8)
Néanmoins, ce matériau n'est relié à aucun de nos objets.
Pour cela, nous allons aujourd'hui apprendre à le faire sur notre objet ACTIF, l'objet sélectionné par un clic droit sur l'interface 3D :
bpy.context.object.active_material = bpy.data.materials[ 'couleur_1' ]
Ou encore à le relier à un objet Blender dont on connait le nom dans l'interface :
bpy.data.objects[ 'Sphere' ].active_material = bpy.data.materials[ 'couleur_1' ]
Ou simplement à le relier à un objet Blender dont on connait la référence dans Python :
monObjet.active_material = bpy.data.materials[ 'couleur_1' ]
Comme vous pouvez le voir, il suffit d'utiliser l'attribut active_material.
Cela permet d'aillers de l'imposer (avec un =) ou simplement de lire ce qu'il contient.
Comme vous pouvez le voir sur l'image, notre matériau a bien été affecté à l'objet en cours de sélection qu'on obtient à l'aide de bpy.context.object
. J'ai encore utilisé de l'orange pour le dernier car il s'agit de l'objet que vous venez de sélectionner. On peut donc interagir avec lui depuis l'interface très facilement.
Et en quoi c'est si bien ce truc ?
Vous devez voir souvenir du script qui permet de créer des cubes placé en cercle en fonction de l'altitude :
import bpy # On importe la bibliothèque Blender Python
import math # On importe la bibliothèque math pour calculer les cos...
rayon = 10
for rayon in range (6,23,4):
for angle_degre in range(0,360,int(36*6/rayon)):
angle = math.radians(angle_degre) # math.radians permet de convertir en radians
x = rayon*math.cos(angle)
y = rayon*math.sin(angle)
altitude = -rayon//2
bpy.ops.mesh.primitive_cube_add( location=(x,y,altitude) )
Sans couleur, on obtient ceci :
17° Modifier le script pour que chaque cube possède le matériau que nous avons déjà concu, couleur_1. Il suffit de rajouter une ligne au bon endroit !
Et maintenant, on obtient ceci :
Vous imaginez si vous aviez dû affecter les matériaux un à un !
...CORRECTION...
import bpy # On importe la bibliothèque Blender Python
import math # On importe la bibliothèque math pour calculer les cos...
rayon = 10
for rayon in range (6,23,4):
for angle_degre in range(0,360,int(36*6/rayon)):
angle = math.radians(angle_degre) # math.radians permet de convertir en radians
x = rayon*math.cos(angle)
y = rayon*math.sin(angle)
altitude = -rayon//2
bpy.ops.mesh.primitive_cube_add( location=(x,y,altitude) )
bpy.context.object.active_material = bpy.data.materials[ 'couleur_1' ]
Finissons cette partie par deux simples remarques :
Obtention de la référence d'un matériau : comment obtenir l'accés à la référence d'un matériau d'un objet monObjet ? C'est facile :
leMateriau = monObjet.active_material
Obtention du nom du matériau : comment connaitre le nom d'un matériau affecté à un objet ? C'est facile :
nomMateriau = monObjet.active_material.name
Pour finir, nous allons voir rapidement comment créer des keyframes avec Python. Nous n'allons faire que survoler cette fonction car la gestion complète va nécessiter une activité entière.
Première chose à faire : transformer la console Python du bas en gestionnaire de timeline : activez le menu Timeline comme indiqué sur l'image :
Résumé des épisodes précédents :
Nous avons vu comment créer un nouveau objet :
bpy.ops.mesh.primitive_uv_sphere_add(location=(x,y,z))
Nous avons vu comment créer un matériau et lui imposer une couleur :
bpy.data.materials.new(nom)
bpy.data.materials[nom].diffuse_color = (couleur, 0.04, 0.04)
Nous avons égalememt vu deux collections : la collection des objets 3D et la collection des matériaux.
bpy.data.objects
bpy.data.materials
Nous avons vu également qu'on pouvait récuper l'objet sélectionné actuellement et en profiter pour changer des choses, son nom par exemple ou lui fournir un nouveau matériau :
bpy.context.object.name
bpy.context.object.active_material = bpy.data.materials[nom]
Il reste un nombre important de commandes bien entendu. Nous en rencontrerons beaucoup dans les activités Blender Python suivantes.
Pour l'instant, prenons un cas compliqué mais intéressant demandant de gérer beaucoup de keyframes sur beaucoup d'objets : la rotation des cubes précédents par exemple.
Pour placer le curseur temporel des animations à une certaine position, nous allons utiliser bpy.context.scene.frame_set(numero_de_la_frame_voulue)
.
Si vous tapez bpy.context.scene.frame_set(20)
, cela revient à vous placer sur 20 sur l'interface Timeline :
Un exemple en image :
Et si on veut aller à la 40 ?
Ensuite, il faut modifier l'attribut de l'objet désiré pour le fixer à la valeur voulue. Ensuite, on insère une keyframe avec la méthode keyframe_insert().
Au vu de la documentation monstrueuse de Blender, vous devriez commencer à vous dire "mais comment il sait tout ça lui ! Il passe ses nuits à lire la documentation ou quoi ?!?". Et la réponse est ... non. Il suffit d'ouvrir la zone du haut en info et d'étendre la zone :
Et voilà. Si vous cherchez la façon de réaliser une action, il suffit
Le problème de la première méthode, c'est qu'il s'agit des instructions pour passer par le menu. Mais ça donne au moins une première idée de ce qu'il faut faire.
Reprenons :
Pour fixer à la frame 10 (reglée par frameset) une keyframe de position sur un objet 3D dont la référence est stockée dans une variable monObjet, on tape par exemple :
bpy.context.scene.frame_set(10)
monObjet.keyframe_insert(data_path="location")
On peut même faire plus simple :
monObjet.keyframe_insert(data_path="location", frame=10)
Pour enregistrer les grandeurs de rotation sur la frame en cours :
monObjet.keyframe_insert(data_path="rotation_euler")
Pour enregistrer les grandeurs de scale sur la frame 50 :
monObjet.keyframe_insert(data_path="scale", frame=50)
Et voilà, c'est tout.
18° Commencons par un petit exercice : tentons de créer un script qui permet de créer une animation : l'objet sélectionné par l'utilisateur (l'objet actif donc) doit subir plusieurs keyframe :
A vous de jouer.
A titre d'exemple, voici ce que cela donne sur l'un des cubes précédent :
...CORRECTION...
import bpy # On importe la bibliothèque Blender Python
# Recherche de la référence et enregistrement de sa position au moment du départ du script
monObjet = bpy.context.object
x, y ,z = monObjet.location
# Création des keyframes et déplacement de l'objet
monObjet.keyframe_insert(data_path="location", frame=0)
monObjet.location = ( x, y, z+3 )
monObjet.keyframe_insert(data_path="location", frame=40)
monObjet.location = ( x, y, z-2.5 )
monObjet.keyframe_insert(data_path="location", frame=80)
monObjet.location = ( x, y, z+2 )
monObjet.keyframe_insert(data_path="location", frame=120)
monObjet.location = ( x, y, z-1.5 )
monObjet.keyframe_insert(data_path="location", frame=160)
monObjet.location = ( x, y, z+1 )
monObjet.keyframe_insert(data_path="location", frame=200)
monObjet.location = ( x, y, z-0.5 )
monObjet.keyframe_insert(data_path="location", frame=240)
monObjet.location = ( x, y, z )
monObjet.keyframe_insert(data_path="location", frame=280)
19° Il ne reste plus qu'à appliquer ceci à tous les cubes de notre pyramide des cubes.
20° Dernière subtilité : je voudrais que le sens de déplacement varie d'un cercle de rayon sur l'autre : l'un commence par le haut, l'autre par le bas.
Le plus facile est certainement de créer une variable sens hors de la boucle.
On pourrait changer son signe en la multipliant par 1 dans la première bouche.
Si elle est positive, on doit commencer par monter.
Si elle est négative, on doit commencer par descendre.
import bpy # On importe la bibliothèque Blender Python
import math # On importe la bibliothèque math pour calculer les cos...
rayon = 10
sens = 1
for rayon in range (6,23,4):
sens = sens*(-1)
for angle_degre in range(0,360,int(36*6/rayon)):
angle = math.radians(angle_degre) # math.radians permet de convertir en radians
x = rayon*math.cos(angle)
y = rayon*math.sin(angle)
altitude = -rayon//2
bpy.ops.mesh.primitive_cube_add( location=(x,y,altitude) )
bpy.context.object.active_material = bpy.data.materials[ 'couleur_1' ]
# A VOUS DE CODER ICI !
Cela pourrait donner ceci :
...CORRECTION ...
import bpy # On importe la bibliothèque Blender Python
import math # On importe la bibliothèque math pour calculer les cos...
rayon = 10
sens = 1
for rayon in range (6,23,4):
sens = sens*(-1)
for angle_degre in range(0,360,int(36*6/rayon)):
angle = math.radians(angle_degre) # math.radians permet de convertir en radians
x = rayon*math.cos(angle)
y = rayon*math.sin(angle)
altitude = -rayon//2
bpy.ops.mesh.primitive_cube_add( location=(x,y,altitude) )
bpy.context.object.active_material = bpy.data.materials[ 'couleur_1' ]
# A VOUS DE CODER ICI !
monObjet = bpy.context.object
x, y ,z = monObjet.location
# Création des keyframes et déplacement de l'objet
monObjet.keyframe_insert(data_path="location", frame=0)
monObjet.location = ( x, y, z+3*sens )
monObjet.keyframe_insert(data_path="location", frame=40)
monObjet.location = ( x, y, z-2.5*sens )
monObjet.keyframe_insert(data_path="location", frame=80)
monObjet.location = ( x, y, z+2*sens )
monObjet.keyframe_insert(data_path="location", frame=120)
monObjet.location = ( x, y, z-1.5*sens )
monObjet.keyframe_insert(data_path="location", frame=160)
monObjet.location = ( x, y, z+1*sens )
monObjet.keyframe_insert(data_path="location", frame=200)
monObjet.location = ( x, y, z-0.5*sens )
monObjet.keyframe_insert(data_path="location", frame=240)
monObjet.location = ( x, y, z )
monObjet.keyframe_insert(data_path="location", frame=280)
Comme vous pouvez le constater, faire ceci avec juste l'interface risque d'être long. On peut toujours travailler avec les armatures mais travailler avec le code est bien pratique aussi.
Dans les prochaines activités, nous allons découvrir encore d'autres outils qui vont décupler ce que vous venez de découvrir :
Et avec tout cela, les possibilités d'animation sont infinies...
Question : comment faire pour connaitre les noms des attributs ?
Pour aller plus loin, la documentation de Blender est fournie intégralement sur Internet, même si elle est massive :
VERS LA DOC DE BLENDER SUR LES OBJECTS
Ainsi, on pourrait se demander comment je sais que le nom de l'objet est stocké dans l'attribut nommé name et donc accessible avec un code du type monObjet.name
?
Le plus simple est encore d'aller chercher la chose qui vous interesse dans l'interface et d'y faire un clic droit : en cliquant sur Online Python References, vous pourrez accéder à la page comportant des informations sur la donnée recherchée.
Voici une vidéo vous montrant comment trouver l'attribut correspondant au nom et à la localisation de votre objet ?
On voit ainsi qu'on doit taper monObjet.name
ou monObjet.location
.