Calcul de distance entre 2 obj

Document sans nom

Calcul de distances entre deux objets Blender


Introduction

Nous allons dans ce tutoriel créer un petit script Python/Blender (que l'on surnomme BPY) qui calculera la distance séparant deux objets sélectionnés dans Blender. Nous en profiterons pour aborder les notions de modules (ou librairies), fonctions, listes et GUI, et nous apprendrons (ou reverrons ^^) certaines notions de math QUI SONT TRES SIMPLES (je dis ça pour ceux qui sont déjà partis :-).

Etape 1 : Description du script et cahier des charges

Une fois le script fini, tout se passera dans la vue 3D. L'utilisateur sélectionne deux objets quelconques, puis lance le script depuis la vue 3D toujours (nous verrons comment faire). Un "pop-up" contenant un message d'erreur apparait si il n'y a pas deux objets sélectionnés. Sinon le calcul se fait, et le résultat apparaît dans la console (un pop-up apparaitra pour informer l'utilisateur). Il sera intéressant ensuite d'afficher directement la longueur dans la vue 3D en créant un objet texte.

Quand je fais un script, surtout si il est long, je fonctionne par mé-tho-de pour avancer. Cela permet d'éviter la feuille blanche (ou plutôt l'écran gris ^^) et de bien comprendre ce que j'ai à faire. Donc nous allons étudier le script partie par partie :

1. Pouvoir utiliser les fonctions de Blender et des math: nous verrons ici ce qu'est un module ou library (=bibliothèque), c'est la même chose, et nous apprendrons comment utiliser ce qu'il contient.

2. Récupérer les objets : nous devrons créer deux variables qui contiennent les deux objets sélectionnés.

3. Vérifier le nombre d'objets sélectionnés : un message d'erreur apparaitra si ce nombre n'est pas 2, créé avec le sous-module Draw. Nous verrons ici RAPIDEMENT ce qu'est un GUI (ce ne sera qu'une facette de Draw)

4. Calculer la distance : cette action sera elle-même composée comme ceci :

4-1. Récupérer la position des objets : il faudra deux variables qui contiendront la position respective en x, y, z de chaque objet. Bien sûr nous devrons accéder à ces 3 axes séparément, ainsi nous parlerons des listes.

4-2. Calculer la distance qui les sépare pour chaque axe : nous calculerons la différence des positions en x, en y et en z, pour obtenir 3 longueurs.

4-3. Calculer la distance définitive : nous utiliserons les 3 données d'au-dessus pour appliquer le théorème de Pythagore (non ! ne partez pas encore, je vais l'expliquer dans la partie qui suit).

5. Délivrer le résultat : on écrira dans la console "objet1 --> objet2 : distance" (avec les vrais noms bien sûr ^^) et on informera l'utilisateur avec un pop-up pour qu'il sache qu'il doit aller voir dans la console.

Nous écrirons également quelques lignes qui permettront d'accéder au script depuis la fenêtre de script ("Script Window") et le menu Object. Nous allons créer deux fonctions pour rendre notre code plus clair, nous en créeront une appelée "distance" pour le calcul de la distance, et une autre "main" qui est le programme principal (la fonction "main" utilisera la fonction "distance"). LES FONCTIONS SERONT EXPLIQUEES PLUS TARD, NE VOUS INQUIETEZ PAS SI VOUS NE SAVEZ PAS CE QUE C'EST OU COMMENT CA MARCHE.

Etape 2 : Le théorème de Pythagore

*** Partie facultative pour ceux qui connaissent BIEN ce théorème ***

Pythagore était un mathématicien grec. Comme beaucoup de grecs à cette époque (Euclide, Thalès, etc...), il s'intéressait beaucoup aux lois des mathématiques et cherchait désespérément des "égalités remarquables" qui permettaient de résoudre les équations. Ne partez toujours pas.

Il a trouvé une égalité en examinant les triangles rectangles. 

En effet, en faisant le carré de chaque côté d'un triangle rectangle (ici ABC), il a découvert que dans tout triangle rectangle, le carré de l'hypoténuse est égal à la somme des carrés des deux autres côtés (la célèbre formule...). Rappel : le carré d'un nombre est ce nombre multiplié par lui-même. Donc ici:

Pour ABC rectangle en B, d'après le théorème de Pythagore :

AC² = AB² + BC²

Concrètement, ça sert à quoi ? Prenons ce triangle :

Pour MNP rectangle en N, d'après le théorème de Pythagore :

MP² = MN² + NP²
MP² = 4² + 3²
MP² = 16 + 9
MP² = 25

MP = √25

MP = 5 cm

Etape 3 : Les modules

Nous allons ici parler des modules, que l'on appelle aussi "libraries" ("library" au singulier). "Library" est un terme anglais, donc à prononcer "la-ï-bré-ri". De plus, c'est un "faux-ami" : il ne signifie pas "librairie" comme on pourrait le penser, mais "bibliothèque", donc concrètement, une library (ou module) est un script qui contient des définitions de fonctions.

Pour utiliser les fonctions d'un module, il faut l'importer :

import module

Vous voyez, c'est très simple. Il y a aussi une autre façon, qui permet d'obtenir seulement les fonctions ou sous-modules que l'on veut dans le module :

from module import fonction

OU

from module.sousModule import fonction

----------------------------------------------------------------

Pour notre script, nous devons utiliser Blender. Et quelle coïncidence ! il y a un module "Blender". Nous allons importer ses sous-modules qui vont nous intéresser, "Scene" et "Draw" :

from Blender import Scene, Draw

Nous aurons aussi besoin de faire des racines carées ; une fonction "sqrt" existe dans le module "math", importons-la :

from math import sqrt

Et voilà, c'est fini ! On peut maintenant utiliser toutes les fonctions qui nous seront utiles ^^

Etape 4 : Les fonctions

Maintenant que nous pouvons contrôler Blender à partir du script, nous allons pouvoir commencer concrètement. Comme je l'ai déjà dit, il y aura deux fonctions : une qui calcule seulement la distance, et une autre qui s'occupe du GUI et d'appeler la fonction "distance".

Mais d'abord un peu de cours. Une fonction est une série d'instructions qui sont regroupées dans un seul mot. Ce mot finit TOUJOURS par une paire de parenthèses, qui peuvent contenir des valeurs nécessaires à la fonction appelées argument.

En Python, on crée une fonction avec le mot clé "def". On finit les instructions de la fonctions par le mot clé "return" qui l'arrêtera. Le return peut être suivi ou non de la (les) valeur(s) que renvoie la fonction. Par exemple, une fonction multiplier :

def multiplier(var1, var2):
    resultat = var1 * var2
    return resultat

  • La fonction "main()"

Donc nous allons commencer par la fonction "main" (à prononcer me-ï-ne) qui ne prend pas d'argument (on n'oublie pas le def, les parenthèses et les deux points) :

def main():

Pourquoi "main" ? C'est un terme anglais qui signifie "principal", et c'est bien le cas de notre fonction : c'est ici que tout le script s'exécute, grâce à d'autres fonctions d'accord, mais qui sont appelées ici. Donc, par convention, on appelle main la fonction principale du programme.

Commençons la fonction. Nous avons dit qu'il fallait commencer par récupérer les objets sélectionnés. Ceux-ci sont placés dans la scène courante, récupérons celle-ci dans la variable "scn" grâce au sous-module Scene :

scn = Scene.GetCurrent()

Vous avez ici un exemple de fonction ! "GetCurrent()" et une fonction, qui a été créée dans le module Blender, sous-module Scene avec "def GetCurrent():". Vous voyez, on peut faire facilement comme les pros ^^

Pour les objets sélectionnés, on peut les avoir comme ça :

objlist = scn.objects.selected

"objlist" sera une liste de deux objets, et nous allons voir comment y accéder un par un. Mais suivons notre planning, il faut vérifier si il y a deux objets ; pour ça, nous allons utiliser une particularité des listes dans la boucle for :

nbObj = 0
for obj in objlist:
    nbObj += 1
if nbObj != 2:
    Draw.PupMenu("Erreur %t|Vous devez sélectionner 2 objets pour éxecuter le script")
    return

Je crée une variable "nbObj" qui représente le nombre d'objets. Mais la boucle for est bizarre...pas de "in range" mais à la place : une variable ! En fait, objlist est une liste. Elle est composée de plusieurs noms : les objets. Donc la variable "obj" va prendre tour à tour la valeur de chaque membre de la liste, tout en augmentant le nombre d'objets ("nbObj += 1"). Donc si la liste contient 3 valeurs, la boucle tournera 3 fois (une fois pour chaque valeur) et augmentera 3 fois d'1 la variable "nbObj"...qui sera égale à 3.

La suite est plus simple, on teste si "nbObj" est différent de 2, et si oui, on...Quoi ? C'est quoi ce truc ? Aha ! Voilà votre première rencontre avec le sous-module Draw. Il gère l'interface graphique, que l'on appelle GUI (Graphical User's Interface, autrement dit Interface Graphique de l'Utilisateur).

La fonction "PupMenu()" (encore une fonction !) crée une fenêtre pop-up ; c'est le message qui apparait dans Blender juste sous votre souris pour certaines touches à plusieurs fonctions (W, Alt+P, etc...) ou quand il y a une erreur (dans les scripts par exemple, "Python script error, check console"). Elle prend un argument...qui en contient en fait 2. On sépare en fait chaque ligne du pop-up par le signe "|" (Alt Gr+6 sur le clavier français). Celle qui est en titre (partie grisée) finit par "%t", les autres par "%x" + le numéro de la ligne (%x1, %x2, etc...) Ici pas besoin des "%x#" car il n'y a qu'une ligne ^^

Le "return" sert juste à arrêter la fonction, et il ne renvoie aucune valeur puisqu'il ne s'est rien passé.

Bon, on a testé les erreurs, il est maintenant temps de calculer la distance, et pour cela nous allons utiliser la fonction que nous créerons bientôt : "distance(obj0, obj1)". Vous voyez qu'elle prend deux arguments, qui sont les deux objets, donc il faut les obtenir séparément, dans deux variables que nous appellerons "ob0" et "ob1". Pour cela, petit cours sur les listes...

Les listes

Déjà, je vous le dis tout de suite pour que ça soit une habitude directement, en informatique on commence toujours à compter depuis 0 ! C'est pour ça qu'ici j'ai mes variables 0 et 1 et non pas 1 et 2 ^^ (et si vous n'avez pas envie de faire comme ça, ben de toute façon, vous serez obligés :-p...).

Une liste en Python est faite comme ça :

liste = [var1, var2, var3, var4, etc]

Chaque élément entre crochets est séparé des autres par une virgule, donc en principe, le nombre de membres d'une liste est égal au nombre de virgules moins 1 (on s'en fout mais c'est pas grave). Et pour accéder séparément à chacun d'eux, on écrit :

liste[numeroDuMembre]

Donc, par exemple, pour accéder au troisième élément de la liste "liste", on écrira "liste[3]"... Et non ! Qu'est-ce que j'ai dit tout à l'heure ? On commence à 0 ! Donc le premier élément de la liste est le numéro 0, le troisième le numéro 2, le dixième le numéro 9, etc... Vous ne pourrez pas échapper à cette habitude informatique, désolé :-p !

----------------------------------------------------------

Retour au script. Il faut obtenir les deux objets de la liste :

ob0 = objlist[0]
ob1 = objlist[1]

On peut maintenant appeler notre fonction avec ces deux variables en argument, et afficher un pop-up pour dire que le calcul est fait et qu'il faut aller voir la console (on oublie pas les guillements, le "%t" et le "|" dans le pop-up) :

distance(ob0, ob1)
Draw.PupMenu("Fini %t|Calcul effectué avec succès, vérifiez la console")

Remarque : ce n'est pas grave si les noms des variables de la définition de fonction et ceux de l'appel de fonction ne sont pas les mêmes.

Résumé de la fonction "main()" :

def main():
    scn = Scene.GetCurrent()
    objlist = scn.objects.selected
   
    nbObj = 0
    for obj in objlist:
        nbObj += 1
    if nbObj != 2:
        Draw.PupMenu("Erreur %t|Vous devez sélectionner 2 objets pour éxecuter le script")
        return

    ob0 = objlist[0]
    ob1 = objlist[1]
    distance(ob0, ob1)
    Draw.PupMenu("Fini %t|Calcul effectué avec succès, vérifiez la console")

  • La fonction "distance(obj0, obj1)"
Attaquons enfin le vif du sujet : le calcul de la distance. La fonction que nous allons créer sera à placer AVANT le main, car on l'appelle dans cette dernière, et il faut que Python connaisse ce nom. Ainsi, votre programme ressemblera à ça :



Ecrivez la ligne de définition maintenant, en vérifiant sa place :

def distance(obj0, obj1):

Première instruction de la fonction : récupérer la position de chaque objet ; nous utiliserons la fonction "getLocation()", qui signifie "obtenir" (get) "position" (location). Vous remarquerez qu'encore une fois, on commence à compter à partir de 0 :

pos0 = obj0.getLocation()
pos1 = obj1.getLocation()

"pos1" et "pos0" sont maintenant des listes de 3 nombres à virgule, qui réprésentent la position en x, y et z des objets ; nous pourrons donc nous servir de ce que nous avons appris sur les listes pour récupérer individuellement ces 3 nombres.

En suivant le cahier des charges, il faut maintenant calculer la distance entre les deux objets pour chaque axe. Mais pourquoi ? Explication.


*** Pour ceux qui n'auraient pas compris ***

Nous voulons calculer la distance entre deux points placés selon des axes. Je vais prendre un exemple ici avec seulement les axes x et y.

Si vous avez un tant soit peu de logique, il est évident que pour trouver l'écart entre deux nombres, il faut les soustraire ; mais ici, nous en avons 4, pour trouver un résultat ! Seul solution : soustraire les x entre eux, et les y entre eux. En faisant ça, on obtient deux valeurs, et il faut trouver comment les rassembler en une. Avec un dessin, c'est presque évident :



En soustrayant les x, on obtient 4 - 1 = 3
En soustrayant les y, on obtient 6 - 2 = 4

En regardant bien, ces longueurs 3 et 4 forment deux segments [AB] et [BC] (pointillés), qui sont perpendiculaires et se joignent en C. En traçant un segment entre les deux points, on obtient un triangle ABC ; et celui-ci est rectangle en B, car [AB] et [BC] sont perpendiculaires. On peut donc utiliser le théorème de Pythagore pour trouver l'écart entre les deux points !

Pour 3 axes, c'est aussi facile : on se sert des x et y pour calculer une hypoténuse au carré, puis on se sert de celle-ci pour calculer la distance finale.



On connaît les longueurs en orange (grâce à la soustraction). On se sert des deux horizontales pour calculer leur hypoténuse, qui nous servira à calculer la diagonale (qui sera la distance).

--------------------------------------------------------------------

Donc, en suivant ceque l'on a dit plus haut, il faut faire une soustraction pour chaque axe (on commence encore une fois à 0 pour compter) :

cotex = +(pos0[0] - pos1[0])
cotey = +(pos0[1] - pos1[1])
cotez = +(pos0[2] - pos1[2])

N'oubliez pas le + suivi des parenthèses devant la soustraction. En effet, d'habitude, on soustrait le plus petit nombre du plus grand (pour obtenir un résultat positif) ; mais qui nous dit que l'objet 1 n'a pas une position en x inférieure à l'objet 2 ? Il faut pourtant que le résultat soit positif, donc on met la soustraction entre parenthèses pour lui appliquer le +.

Calculons maintenant le premier hypoténuse avec Pythagore :

hypCarre = cotex**2 + cotey**2

Le double signe "multiplier" (**) sert à utiliser les puissances. On écrit "nombre**puissance", par exemple 4 puissance 5 s'écrira "4**5"

Pas besoin de faire la racine carée, nous avons besoin ensuite du carré :

dist = sqrt(hypCarre + cotez**2)

La fonction "sqrt()" que nous avons importé depuis le module math sert à faire les racines carées ("SQuare RaTe", autrement dit "racine carrée").

Nous pouvons réduire notre code ; en effet, "hypCarre" est une addition que l'on ajoute à un nombre : on peut tout regrouper dans la racine carrée (supprimez la ligne "hypCarre")

dist = sqrt(cotex**2 + cotey**2 + cotez**2)

On peut maintenant afficher le résultat dans la console et arrêter la fonction en renvoyant la distance :

print obj0.name, "-->", obj1.name, ":", dist
return dist

J'utilise la sous-variable "name" pour obtenir le nom des objets.

Résumé de la fonction "distance(obj0, obj1)" :

def distance(obj0, obj1):
    pos0 = obj0.getLocation()
    pos1 = obj1.getLocation()
   
    cotex = +(pos0[0] - pos1[0])
    cotey = +(pos0[1] - pos1[1])
    cotez = +(pos0[2] - pos1[2])
   
    dist = sqrt(cotex**2 + cotey**2 + cotez**2)
   
    print obj0.name, "-->", obj1.name, ":", dist
   
    return dist

Etape 5 : Script dans le menu

Il nous reste encore une chose à faire : pour l'instant, notre script n'est utilisable que depuis l'éditeur de texte ; mais ce n'est pas pratique, car il faut sélectionner des objets dans la vue 3D ! Rajoutez ces lignes au tout début de votre script, avant l'import de modules :

#!BPY

"""
Name: 'Distance entre objets'
Blender: 240
Group: 'Object'
Tip: 'Calcule la distance entre deux objets'
"""


Je n'explique pas en détail, mais le commentaire "!BPY" indique que le script s'utilise dans Blender, et les lignes suivantes QUI SONT AUSSI DES COMMENTAIRES, disent à Blender le nom du script, le menu où il doit être placé, la version de Blender requise, et sa bulle-info.

Et maintenant, nous avons fini ! Sélectionnez deux objets et lancez le script depuis le menu Object (il faut sauvegarder le script avant) et...ça fait rien !? C'est quoi cette arnaque !? Allons allons, c'est normal ! Nous avons créé une fonction "main()", mais nous n'avons pas dit à Python qu'il fallait l'utiliser. Donc rajoutez une dernière ligne à votre code à la suite des fonctions (mais pas à l'intérieur) :

main()

Et voilà, ça marche maintenant ^^

Voici le résumé du code (avec des commentaires) :

#!BPY

"""Configuration des infos du script pour le menu <--cette ligne est ignorée
Name: 'Distance entre objets'
Blender: 240
Group: 'Object'
Tip: 'Calcule la distance entre deux objets'
"""

##### Import des modules nécessaires #####

from math import sqrt
from Blender import Scene, Draw


##### Définition des fonctions #####

def distance(obj0, obj1):
    pos0 = obj0.getLocation()
    pos1 = obj1.getLocation()
   
    cotex = +(pos0[0] - pos1[0])
    cotey = +(pos0[1] - pos1[1])
    cotez = +(pos0[2] - pos1[2])
   
    dist = sqrt(cotex**2 + cotey**2 + cotez**2)    # utilisation de la fonction sqrt du module math pour faire la racine carée
   
    print obj0.name, "-->", obj1.name, ":", dist
   
    return dist    # la fonction s'arrête là grâce au return, qui renvoie la distance


def main():
    scn = Scene.GetCurrent()
    objlist = scn.objects.selected
   
    nbObj = 0
    for obj in objlist:    # boucle for parcourant la liste d'objets
        nbObj += 1
    if nbObj != 2:
        Draw.PupMenu("Erreur %t|Vous devez sélectionner 2 objets pour éxecuter le script")
        return    # le return ne renvoie rien, il sert jute à arrêter la fonction

    ob0 = objlist[0]
    ob1 = objlist[1]
    distance(ob0, ob1)
    Draw.PupMenu("Fini %t|Calcul effectué avec succès, vérifiez la console")



##### PROGRAMME #####

main()   
# on exécute la fonction main() qui appelle elle-même la fonction distance()

Conclusion

Ca y est, le tutoriel est terminé...

Bien qu'il nécessite un certain nombre de bases en Python, il s'adressait quand même aux débutants car j'y est enseigné les modules, les fonctions et les listes, qui sont des notions fondamentales dans n'importe quel langage de programmation (j'ai même parlé de Pythagore, alors...). En plus de montrer comment s'y prendre pour créer un programme, c'étaient les seules utilités qu'avait le tutoriel (cette fonction existe déjà je crois, et je ne dois pas être le premier à y avoir pensé :-D)

Vous pouvez toujours améliorer le script ; voici quelques idées :
    - créez d'autres fonctions "distance" (avec des noms différents bien sûr ^^) pour faire d'autres calculs (vertex à vertex, objet à curseur)
    - pourquoi ne pas indiquer directement la distance dans un pop-up (ou, si vous vous y connaissez bien, créer un objet "Text" qui l'indiquera) ?
    - vous pourrez également créer un vrai GUI, qui rendra le programme plus agréable : on pourra y choisir la fonction, le mode d'affichage du résultat (console, objet texte ou carrément dans le GUI), si on veut arrondir le résultat, à combien l'arrondir, etc...

Si vous bloquez ou avez un problème technique (fonction, syntaxe...), n'hésitez pas à poser votre question sur le forum (mais faites une recherche avant :-p). Vous pouvez aussi aller voir la documentation officielle de l'API Blender/Python (BPY).

Si jamais vous ne comprenez pas une partie du tuto, ce qui est fort possible (j'ai que 13 ans !), envoyez-moi un mail à robin.gullo@laposte.net (ou un MP si vous êtes inscrits sur le site). N'ayez pas peur de le faire, déjà parce que ne vais pas vous manger (par mail ça risque d'être dur :-D), mais plus sérieusement parce que si vous, vous ne comprenez pas une étape, il est très trop propable que d'autres aussi, alors n'hésitez pas à critiquer ;-)

tutoriel rédigé pour le Blender Clan par granarc, 7 avril 2008

    


Les commentaires appartiennent à leurs auteurs. Nous ne sommes pas responsables de leur contenu.

Sujets récemment répondus
Forums Sujets Réponses Lus Dernières contributions
Questions & Réponses en sculpt mode 5 89 Hier 18:21:10
prometheusx 
Questions & Réponses [résolu] Array circulaire 2 83 Hier 15:22:46
Redstar 
[WIP] et travaux terminés [WIP] Teeny Tiny - Story    [1][2][3]...[5] 40 3720 Hier 07:18:00
Bibi09 
Moteur de jeu GameBlender et alternatives [WIP] DeadSigns FPS horreur - Première version disponible !    [1][2][3]...[58] 576 97588 03/08 11:11:32
Hook 
Questions & Réponses Addon pour textures procedurales 2 91 02/08 20:11:35
Crocodile_13 
Questions & Réponses avis animation marche :)    [1][2][3][4] 34 520 02/08 20:03:14
prometheusx 
[WIP] et travaux terminés [terminé] court métrage 2020    [1][2] 13 766 02/08 15:28:29
flahaut 
Questions & Réponses [résolu] Générique, sous-titres etc 3 97 02/08 09:18:02
Corpia 
Questions & Réponses [résolu] Scène prend la couleur du fond    [1][2] 11 186 02/08 08:20:32
Arielboid 
Questions & Réponses [non résolu] Problele d'addon 0 70 02/08 01:52:30
nayther 
Questions & Réponses Addon pour textures procedurales 1 63 01/08 12:22:52
moonboots 
Questions & Réponses hair densité sélection de polys 9 197 30/07 23:18:21
lenogre 
Questions & Réponses [résolu] graph editor et location key frame 7 152 30/07 16:41:18
math_ 
Questions & Réponses les graphs editor :/ 4 140 30/07 11:19:07
prometheusx 
[WIP] et travaux terminés [WIP] Tutoriel vidéo et création de mon prochain court métrage 2 142 29/07 20:32:46
Bibi09 
Le coin des geeks pc bas prix    [1][2][3][4] 38 1716 28/07 21:14:56
Bibi09 
Questions & Réponses mesh pas assez smooth 3 172 28/07 15:10:12
Zear 
Questions & Réponses Clignotement de l'interface de blender 8 765 28/07 12:15:27
zezer 
Le coin des geeks carte graphique Intel(R) HD Graphics et OpenGL    [1][2][3][4] 31 2401 28/07 10:35:13
gedru 
Questions & Réponses Problème perso qui bouge des 2 côtés qu'avec 1 coté de l'armature et pas de l'autre    [1][2] 10 1399 27/07 19:14:45
Rimpotche 

Qui est en ligne
163 utilisateur(s) en ligne (dont 1 sur Tutoriels)

Membre(s): 1
Invité(s): 162


SusieArria, plus...
Nouveaux membres
LornaC1171 4/8/2020
JonathanDu 4/8/2020
EileenCayt 4/8/2020
ConcettaPr 4/8/2020
LethaV7387 4/8/2020
RosauraOrd 4/8/2020
SusieArria 4/8/2020
AlexPress1 4/8/2020
BaileyBarc 4/8/2020
nayther 2/8/2020
Dernier Ajout
mirages-preview~0.jpg

Evènements à venir
Aou 7
anniversaire Cem47
Aou 13
Anniversaire Siskas
Sep 8
Anniversaire de ang
plus 267 plus d'élément(s)
 Par Mickaël Guédon [ebrain] © 2003-2020 The Blender Clan - hébergé par TuxFamily - Site déclaré à la CNIL sous le numéro 1155445