Blender (jusqu'à 2.49)
Python
Importer l'animation des 
créatures de Framstick

(english version)

 
    Début   Index
précédentScript LOD (level of details)
Script div segment Suivant

(Transcription html d'un article  paru dans le magazine Pixel à l'automne 2004)

Certaines fonctions spécialisées ne sont pas toujours disponibles dans un logiciel. La possibilité de les écrire soi-même offre une grande souplesse de travail . Surtout si le langage de programmation est simple et facile à mettre en œuvre .
 

Le format Povray
Sur la piste des macros
L’éditeur de script
Les sous-modules
Les fonctions
Collecte des informations
En guise de conclusion
Le script 

Le Python, le langage libre développé à l’origine par Guido Van Rossum, est un interpréteur qui peut être intégré dans n’importe quel logiciel. Il est associé à la suite Blender 3D depuis 1999. Pour le découvrir un peu, nous vous proposons de survoler la création d’un script de chargement d’une animation Framsticks, un logiciel de vie artificielle 


[figure1] 

Le format Povray.
Framsticks permet d’exporter les mouvements des créatures sous le format povray dont la syntaxe est idéale  pour une importation d’animation car les informations qui décrivent la forme et le  positionnement sont séparées. Cette méthode correspond a ce que l’on fait dans  Blender quand on réalise une animation linéaire  :  on crée un objet, on le déplace et on lui attribue une clef d’animation. 

Sur la piste des macros
L’animation se présente sous la forme d’un ensemble de fichiers clairement numérotés. L’ordre dans lequel ils doivent être traités est évident. En étudiant le contenu d’un fichier, on trouve au milieu d’un très grand nombre de variables, l’expression BeginJoint(n,n1)  qui correspond à une macro Povray située dans le fichier framstick.inc. Elle ne contient que quelques déclarations de variables et aucune description d’objet. Les deux macros suivantes JointGeometry(Location1,Location2,Rotation) et EndJoint()


[figure 2]

fournissent des informations concrètes ainsi qu’un appel à une autre macro, CreatureStick(StickLoc1,StickLoc2,StickRot) qui contient  au moins une référence significative à une forme de type cylindre, ligne 556. Le reste de la macro peut être éliminé puisqu’il s’agit d’expressions conditionnelles #if #end. En continuant le travail d ‘élagage des conditions, on s’aperçoit que les informations de positionnement (scale, rotate, translate) se trouvent à la ligne 645 


[figure 3] 

BeginJoint(), EndJoint() et JointGeometry() sont donc les expressions qu’un script doit identifier et utiliser pour savoir où en est l’animation.

L’éditeur de script 
On accède simplement au python par un éditeur de texte intégré qui apparaît dans la fenêtre active, (celle dont le cadre est plus clair) lorsqu’on utilise le combo de touche shift-F11Alt-N  ouvre un nouveau fichier . La ligne : 
 

import Blender

est la clef indispensable pour obtenir le chargement du module  principal qui fait interface avec une grande partie des fonctions du logiciel.  Ces fonctions sont elles-mêmes réparties dans des sous-modules de manière thématique . 

Les sous-modules 
Plusieurs sont sollicités : 

Draw qui regroupent les fonctions de gestion de l’interface graphique de l’API python, les différents boutons et la gestion des événements ; 
BGL qui regroupe toutes fonctions d’interface avec l’affichage OpenGL (Blender est entièrement écrit et compatible avec OpenGL) ; 
NMesh qui regroupe les fonctions de traitement des données meshes (les réseaux de polygones) ; Material qui permet de créer  ou de lire les materiaux ;
Ipo qui gère toutes les interactions du python avec les courbes d’animation ; 
Window qui permet de réaliser quelques opérations d’affichages plus générales que celles de la fenêtre python ; 
Scene qui permet de gérer les relations entre les objets et l’image qui sera ensuite rendue, 
Render, enfin, qui permet d’accéder à certaines données d’affichage comme la taille de l’animation en déterminant, par exemple, l’image de départ et celle d’arrivée.


Les fonctions
En python les fonctions sont identifiée par un mot clef  def qui les précède et sont terminées par des parenthèses suivies de deux points: .  Les parenthèses sont fondamentales, sans elles le nom de la fonction est considérée comme un simple identifiant faisant référence à une variable 


[figure 4]

 La première opération d’importation consiste à pouvoir choisir intuitivement le fichier que l’on souhaite traiter. Pour cela, il est possible de créer une interface graphique en passant par les modules Blender.Draw et Blender.BGL. Draw connecte trois fonctions au moteur principal de Blender au travers de la fonction Register(). Le nom de ces fonction est sans importance. Mais pour la lisibilité du script, il est préférable de donner des noms significatifs : GUI_EVENEMENT(event) et GUI_DESSIN(). La dernière affiche les boutons et ne doit pas comporter de variable entre ses parenthèses. La première gère les événements. Le bouton titré "Sélectionne l'import" 


[figure 5]

permet d’exécuter la fonction Blender.Window.FileSelector pour repérer le fichier scene_0001.pov 
 
 


[figure 6]

et extraire de son nom complet, l’adresse du répertoire dans lequel sont stockés les fichiers .pov. 

Collecte des informations
Elle se fait en deux temps. Le script relève d’abord tous les noms de fichiers Scène dont il extrait les numéros (scene_0700.pov donne 700) pour les ranger dans un dictionnaire (une liste d’objet dont chaque élément porte un nom, (700 :’scene_0700.pov’). Il devient alors simple de créer la liste des clefs (700 est une clef) et de les ordonner de manière croissante (ligne 328 et 363 du script) .  Le script définit aussi deux variables : PREMIERE et DERNIERE qui sont les limites extrêmes de l’animation. Elles sont utilisées pour modifier la timeline de Blender en modifiant les valeurs Sta et End


[figure 7]

En second lieu, et seulement sur la première scène, il recherche toutes les créatures et tous les éléments ‘joints’ qui les constituent ainsi que le numéro de la ligne sur laquelle est donnée l’information pour éviter de perdre du temps à filtrer toutes les lignes les fichiers suivants, et  les crée à la volée sous la forme de cylindre Mesh


[figure 8]



Ces objets sont amenés à leur  place par les courbes IPO qui leur sont attribuées : LocX, LocY, LocZ, RotX, RotY, RotZ, SizeX, SizeY, SizeZ. Les courbes IPOs assurent  la  mise en forme réel de  l’animal virtuel en même temps que ses mouvements. Les emplacements successifs sont ensuite lus dans les autres fichiers scènes disponibles et ajoutés à chaque courbes sous la forme de poignets de contrôle 
 
 


[figure 9]

En guise de conclusion
Obtenir un résultat est l’objectif principal visé par ce script en laissant, cependant, un maximum de lisibilité  pour que l’on puisse en étudier le contenu facilement. La documentation n’a pas été économisée et on y trouvera beaucoup d’autres explications qui ne peuvent être données ici faute de place. 

#!BPY

""" Registration info for Blender menus: <- these words are ignored
Name: 'framstick'
Blender: 232
Group: 'import'
Tip: 'import framstick animation from povray .inc IMAGEs'
"""
"""
(c) jm soler 2004
"""

#=========================================================
# fonctions personnelles pour eviter d'utiliser la
# version complète du python. Valides sur : win32, linux,
# irix et macosX...
#=========================================================
 

try:
    import nt
    os=nt
    os.sep='\\'

except: 
    import posix
    os=posix
    os.sep='/'

def isdir(path):
    try:
        st = os.stat(path)
        return 1 
    except:
        return 0

def split(pathname):
         if pathname.find(os.sep)!=-1:
             k0=pathname.split(os.sep)
         else:
            if os.sep=='/':
                k0=pathname.split('\\')
            else:
                k0=pathname.split('/') 

         directory=pathname.replace(k0[len(k0)-1],'')
         Name=k0[len(k0)-1]
         return directory, Name

def join(l0,l1): 
     return  l0+os.sep+l1

os.isdir=isdir
os.split=split
os.join=join
#=========================================================
# ... fin des fonctions supplémentaires
#=========================================================
#=========================================================
# Le module math peut être difficile à charger sur certaines
# version de linux. Le plus souvent, l'installation d'un pack
# python devloppeur est necessaire.
# Le problème a été signalé aux developpeurs de l'API python
# de Blender, qui ont, dans un premier temps ont ajouté les
# fonctions supplémentaires pour les retirer par la suite
# sous le prétexte qu'un module math dans le module Blender
# pourrait préter à confusion.
# --
# Sous win32 (win9x, xp, nt, 2000) le module math est livré
# avec la dll python qui accompagne Blender.
#=========================================================
import math
from math import pi,cos,sin
#=========================================================
# Module Python Blender 
#=========================================================
import Blender

#=========================================================
# Sous module de gestions des MESHES
#=========================================================
#from Blender import NMesh
#=========================================================
# Sous module de gestions de l'interface graphique
#=========================================================
#from Blender import Draw
#=========================================================
# Sous module d'interface avec les fonctions OpenGL
#=========================================================
#from Blender import BGL
#=========================================================
# Sous module d'interface avec les Materiaux
#=========================================================
#from Blender import Material
#=========================================================
# Sous module d'interface avec l'affichage des fenetres
#=========================================================
#from Blender import Window
#=========================================================
# Sous module d'interface avec les courbes d'animations
#=========================================================
#from Blender import Ipo
#=========================================================
# Sous module d'interface avec les fonctions et données de
# la scene (si compliqué qu'elle a été divisé en deux
# et qu'il y a maintenant un sous module Scene.Render
#
#=========================================================
from Blender import Scene
SCENE=Blender.Scene.GetCurrent()

#=========================================================
# Module sys pour lire le répertoire courant et
# ajouter momentanément au chemin de recherche
# du python.
#
#=========================================================
import sys
path=Blender.Get('filename')
dirname=Blender.sys.dirname(path)
oldpath=sys.path
if dirname not in sys.path: sys.path.append(dirname)

#=========================================================
# Quelques variables globales
#=========================================================
PROBLEME =             0
REPERTOIRE     =    ""
NOM            =    "e:\\zi^p\\3dgrx\\blender\\reserve_python\\importanimpov\\scene_0700.pov"
FICHIERSELECTIONNE=   -1
DECALE=                0
FRAMESCENE=           ""

E_CHANGECHEMIN=        1
E_IMPORTE=             2
E_QUITTE=              8
E_FAIT=               11
E_IMPORTE_SELECT=     12
E_IMPORTE_CONTR=      13
A_RECALE=             14
A_REDOCU=             15
A_REDESS=             16
M_PARENT=             17
E_CONTINUE=           18 
 

PROBLEME_TEXTE =    {E_IMPORTE_SELECT:' selection de fichier.',
                            E_IMPORTE:' d\' import.',
                            3:' Ce fichier n\'est peut-être pas un fichier framestick.',}

ETAT="En attente..."

#=========================================================
# dictionnaire qui permettra d'appeler les images par
# leur numero
#=========================================================
LISTEFICHIER={}

#=========================================================
# Quelques variables globales pour gerer la progression
# de la lecture de l'animation
#=========================================================
PREMIERE=-1
DERNIERE=-1
IMAGE=-1
#=========================================================
# Dictionnaire contenant les informations relatives
# aux créatures du fichier importe' 
#=========================================================
CREATURES={}

BOUTONS={
#=========================================================
# Bouton utilise dans l'interface graphique pour contenir
# l'adresse du fichier selectionne
#=========================================================
'NOMDEFICHIER':[ Blender.Draw.Create(NOM),"> ",
       "Interface graphique de selection de fichier." +
       "Pointez de preference le premier fichier de la liste des scenes."], 
#=========================================================
# Un bouton de type booleen pour déterminer si le script
# doit faire démarrer l'animation sur l'image 1 ou sur
# le numéro de la premier scene.
#=========================================================
'RecaleANIM' :[ Blender.Draw.Create(0),'RecaleANIM',
                "repositionne toutes les IMAGEs à partir de l\'IMAGE 0"+
                 " de la TimeLine de Blender"],
'ReDocANIM': [ Blender.Draw.Create(0),'ReDocANIM',
               "Met à jour les parametres d'animation interne pour ne calculer que les"+
               " images située entre le début et la fin de cette séquence framsticks."""],

#=========================================================
# Un bouton de type booleen pour déterminer si le script
# doit réafficher l'écran. A desactiver pour des scenes
# lourdes
#=========================================================
'ReDESSINEANIM' : [ Blender.Draw.Create(0),'ReDESSINE',
                "reaffiche toutes les fenêtres ouvertes à l'écran. Peut être source de "+
               "longueurs indésiarables dans le traitement de très gros  ou très"+
                " nombreux fichiers"],

#=========================================================
# Un bouton de type booleen pour déterminer si le script
# doit réafficher l'écran. A desactiver pour des scenes
# lourdes
#=========================================================
'PARENT' : [ Blender.Draw.Create(1),'PARENT',
                "lie l'ensemble des cylindres à  un objet parent unique pour faciliter "+
                "la manipulation."]}

#=========================================================
# Comme  il n'existe pas de forme prédéfinie que l'on puisse
# appeler par l'API python de Blender il faut en creer une
# qui corresponde à l'objet cylindre de povray.
# --
# L'axe de ce cylindre coincide avec l'axe x du monde 3D
# Cette position evite quelques calculs quand on doit
# imiter la macro povray CreatureStick()
#=========================================================
class cylindre:
    def __init__(self,nom='cylindreModele',type='cyl',r1=0.1, r2=0.1,h=1.0, n=8,smooth=1):
          #====================================
          # creation d'un objet mesh vide
          #====================================
          me=Blender.NMesh.GetRaw()
          #====================================
          # creation de la liste des sommets
          #====================================
          # 1/ les rayons sont stocké dans une
          #    liste
          #====================================
          r=[r1,r2]
          #====================================
          # 2/ création des sommets qui constituent
          #    les extremites.
          #    l'utilisation de range plutot
          #    que de la liste r elle-même
          #    permet de localiser la hauteur
          #    du premier disque en 0 et le second
          #    en h 
          #====================================
          for i in range(0,2):
              for j in range(0,n):
                  z=sin(j*pi*2/(n))*r[i]
                  y=cos(j*pi*2/(n))*r[i]
                  x=float(i)*h
                  v=Blender.NMesh.Vert(x,y,z)
                  me.verts.append(v)

          #==================================== 
          # petit tour de passe-passe avec les listes
          # pour traiter tous les segments d'une facettte
          #====================================
          vlist=[v for v in range(n)]
          vlist.append(0)
          #====================================
          # creation de la liste des faces
          #====================================
          for i in range(n):
              f=Blender.NMesh.Face() 
              f.v.append(me.verts[vlist[i]]) 
              f.v.append(me.verts[vlist[i+1]])
              f.v.append(me.verts[vlist[i+1]+n]) 
              f.v.append(me.verts[vlist[i]+n]) 
              me.faces.append(f) 
              f.smooth=smooth
          #====================================
          #  le type 'cyl' permet de fermet
          # les extremites du cylindre en
          # construisant les faces qui les
          # constituent sinon le tube reste
          # ouvert
          #====================================
          if type=='cyl':
              #====================================
              # creation d'un sommet supplementaire
              # au centre de chaque disque
              #====================================
              pos=[[0.0,0.0,0.0],[0.0,0.0,h]]
              V0=Blender.NMesh.Vert(pos[0][0],pos[0][1],pos[0][2])
              V1=Blender.NMesh.Vert(pos[1][0],pos[1][1],pos[1][2])
              me.verts.append(V0)
              me.verts.append(V1)

              for i in range(n):
                  f=Blender.NMesh.Face()
                  f.v.append(me.verts[vlist[i]])
                  f.v.append(me.verts[vlist[i+1]])
                  f.v.append(me.verts[-2])
                  me.faces.append(f) 
                  f.smooth=smooth

                  f=NMesh.Face()
                  f.v.append(me.verts[vlist[i+1]+n]) 
                  f.v.append(me.verts[vlist[i]+n]) 
                  f.v.append(me.verts[-1])
                  me.faces.append(f) 
                  f.smooth=smooth

          self.objet=Blender.NMesh.PutRaw(me,nom,1)
          #print nom, me.name 
          if self.objet==None:
              OBJETS=[]
              for O in Blender.Object.Get():
                  if O.getType()=='Mesh':
                     if nom==O.getData().name: 
                        self.objet=O
#========================================================= 
# une sorte de contournement qui permet d'utiliser la fonction
# et de documenter les variables Window.FileSelector
#=========================================================
def fonctionSELECT(nom):
             global BOUTONS
             print BOUTONS['NOMDEFICHIER'] 
             BOUTONS['NOMDEFICHIER'][0].val=nom
             scan_DIR(nom)

#========================================================= 
# Pour filtrer le répertoire pointé par 
# BOUTONS['NOMDEFICHIER'][0].val, on liste
# tous les fichier pov qui auraient une partie
# de nom
#=========================================================
def scan_DIR(dir):
    global LISTEFICHIER, REPERTOIRE, NOM, DECALE, PROBLEME
    global PREMIERE, DERNIERE, FICHIERSELECTIONNE

    REPERTOIRE, NOM=split(dir)

    if NOM.find("_")==-1:
        PROBLEME=3

    FICHIERSELECTIONNE=1

    if NOM.find(".pov")!=-1 and PROBLEME==0:
       LISTEFICHIER={} 
       for f in os.listdir(REPERTOIRE):
             if f.find('.pov')!=-1:
               try: 
                 IMAGE=int(f[f.find('_')+1:f.find('.')]) 
                 LISTEFICHIER[IMAGE]=f
                 if DERNIERE==-1:
                      DERNIERE=IMAGE
                 elif IMAGE>DERNIERE:
                      DERNIERE=IMAGE
                 if PREMIERE==-1:
                      PREMIERE=IMAGE
                 elif IMAGE<PREMIERE:
                      PREMIERE=IMAGE
               except:
                 pass 
             else:
               print f
       if PREMIERE==0:
              DECALE=1
    else:
        print "Probleme : ce n'est pas un fichier framsticks"

def fonctionIMPORT():
       global  PREMIERE, DERNIERE,  FICHIERSELECTIONNE
       global  REPERTOIRE, NOM, ETAT, BOUTONS, SCENE
       global  CREATURES

       if FICHIERSELECTIONNE==-1:
           REPERTOIRE,NOM=split(NOM)
           scan_DIR(REPERTOIRE+NOM)

       if BOUTONS['ReDocANIM'][0].val:
           RenderContext=Blender.SCENE.getRenderingContext()
           RenderContext.startFrame(1) 
           RenderContext.endFrame(DERNIERE-PREMIERE+1) 
       else:
           print PREMIERE, DERNIERE
           RenderContext=SCENE.getRenderingContext()
           RenderContext.startFrame(PREMIERE+DECALE) 
           RenderContext.endFrame(DERNIERE+DECALE) 

       key=LISTEFICHIER.keys()
       key.sort()
       for k in key:
           ETAT='Analyse de l\'IMAGE : '+ str(k) + '   Patience...'
           AnalyseSCENE(k)
           if BOUTONS['ReDESSINEANIM'][0].val:

               Blender.Window.RedrawAll()
               Blender.Set('curframe',k)
           print ETAT
 

       for c in CREATURES.keys():
           if BOUTONS['PARENT'][0].val==1 : 
                    CREATURES[c]['parent']=Blender.Object.New('Empty',c)

           SCENE.link(CREATURES[c]['parent'])
           ENFANTS=[]
           for j in CREATURES[c]['Joints'].keys():
                o= CREATURES[c]['Joints'][j]['objet']
                ENFANTS.append(o)
           if BOUTONS['PARENT'][0].val==1 : 
                   CREATURES[c]['parent'].makeParent(ENFANTS,1)

#=========================================
# Deux possibilités, soit on traite le premier
# fichier décrivant une scene soit on travaille sur
# les données d'animation exclusivement,
# la première analyse ayant pointé tous
# les numeros de liges où il faut chercher
# les informations, le traitement devrait
# en être plus rapide dan sla mesure où il n'est
# plus nécessaire de controler le contenu de chaque
# ligne.
#==========================================
def AnalyseSCENE(image):
    global IMAGE, PREMIERE, DERNIERE, CREATURES
    IMAGE=image
    IMAGEFile=open(REPERTOIRE+'/'+LISTEFICHIER[IMAGE],'r')
    IMAGElignes=IMAGEFile.readlines()
    IMAGEFile.close()
    if image==PREMIERE:
       CREATURES= EtatCREATURES(IMAGElignes,CREATURES)
    else:
       CREATURES=AnimationCREATURES(IMAGElignes,CREATURES)

#=========================================
#  Pour récuperer les infos qui sont au format
#  texte  dans un format plus comprehensible
#  par le script
#=========================================
def extrait_valeur(l,n0,n1):
    val=l[l.find(n0)+1:l.find(n1)]
    #print val
    if val.find('<')!=-1:
       #=============
       # un vecteurs compose de 3 valeurs décimales
       #=============
       val=val.replace('<','[')
       val=val.replace('>',']')
       exec'val=[%s]'%val 
       return   val
    elif val.find('.')!=-1:
       #=============
       # un seul nombre décimal
       #=============
        return float(val)
    elif val.find('"')!=-1:
       #=============
       # du texte !  débarrassé des fioritures
       #=============
        return val[1:-2]
    elif val.find(',')!=-1:
       #=============
       # liste de deux entiers, ou de quoi que
       # ce soit séparer par une virgule
       #============= 
        exec'val=[%s]'%val 
        return   val
    else:
       #=============
       # Dans tous les autres cas
       # un entier
       #=============
        return int(val)

#=========================================
# pour faciliter la copie de dictionnaire
#=========================================
def copie(d1,d2):
     for k in d2.keys():
         d1[k]=d2[k]

#=========================================
# utilise pour l'echelle des elements 'joints'
#========================================= 
def longueur_vect(p1,p2):
    p2[0]=p2[0]-p1[0]
    p2[1]=p2[1]-p1[1]
    p2[2]=p2[2]-p1[2]
    return [abs(p2[0]**2+p2[1]**2+p2[2]**2)**0.5,1.0,1.0]
 

#========================================= 
# Documente le dictionnaire des CREATURES
# en indiquant :
#  1/ le nom de l'animal
#  2/ tous les batonnets qui la constituent
#  3/ pour chaque batonnet quelle ligne doit être lue
#     dans le prochain fichier
#  4/ pour chaque batonnet, le bloc IPO
#     qui doit etre modifié au prochain
#     fichier.
#=========================================
def EtatCREATURES(IMAGElignes,CREATURES):
    global IMAGE,  PREMIERE, DERNIERE, SCENE
    for l in range(len(IMAGElignes)):
       if IMAGElignes[l].find('field_Creature_name')!=-1:
          joints=0
          CREATURENAME=IMAGElignes[l][IMAGElignes[l].find('"')+1:-2]
          CREATURES[CREATURENAME]={}
          CREATURES[CREATURENAME]['Joints']={} # definis plus tard dans le script 
          jointNUMERO=-1 
          while IMAGElignes[l].find('EndObject()')==-1:
              if IMAGElignes[l].find('field_Creature_numjoints')!=-1:
                  joints=extrait_valeur(IMAGElignes[l],'=',';') 
              if IMAGElignes[l].find('BeginJoint')!=-1:
                 JOINT=extrait_valeur(IMAGElignes[l],'(',')')
                 jointNUMERO+=1
                 CREATURES[CREATURENAME]['Joints'][jointNUMERO]={}
                 mouvements=extrait_valeur(IMAGElignes[l+1],'(',')')
                 CREATURES[CREATURENAME]['Joints'][jointNUMERO]['ligne']=l+1 
                 CREATURES[CREATURENAME]['Joints'][jointNUMERO
                                     ]['StickLoc1']=mouvements[0]
                 CREATURES[CREATURENAME]['Joints'][jointNUMERO
                                     ]['StickSize']=longueur_vect(mouvements[0],mouvements[1])
                 CREATURES[CREATURENAME]['Joints'][jointNUMERO
                                      ]['StickRot']=[mouvements[2][0]*5.7,
                                                            mouvements[2][1]*5.7,mouvements[2][2]*5.7]
              if IMAGElignes[l].find('EndJoint')!=-1:
                     CREATURES[CREATURENAME]['Joints'][jointNUMERO
                            ]['objet'], CREATURES[CREATURENAME]['Joints'
                            ][jointNUMERO]['ipo']=CreatureStick(CREATURENAME,
                             jointNUMERO)
              l+=1
    return CREATURES

#========================================= 
# Dans le ficheir actuellement lu, pour chaque joint 
# de chaque creature, va lire la ligne numero n associee
# a cet objet pour en retirer les informations
# de positionnement. 
#=========================================
def AnimationCREATURES(IMAGElignes,CREATURES):
    global IMAGE, PREMIERE, DERNIERE, SCENE
    for creature in CREATURES.keys():
          for joint in CREATURES[creature]['Joints'].keys():
                 l=CREATURES[creature]['Joints'][joint]['ligne'] 
                 if IMAGElignes[l].find('JointGeometry')!=-1: 
                    mouvements=extrait_valeur(IMAGElignes[l],'(',')')
                    CREATURES[creature]['Joints'][joint]['StickLoc1']=mouvements[0]
                    CREATURES[creature]['Joints'][joint]['StickSize'
                                   ]=longueur_vect(mouvements[0],mouvements[1])
                    CREATURES[creature]['Joints'][joint]['StickRot'
                                   ]=[mouvements[2][0]*5.7,mouvements[2][1]*5.7,mouvements[2][2]*5.7]
                    CreatureStick(creature,joint)
    return CREATURES

#================================
# def doc_COURBESIPO(CREAT,joint,IP,IDENT):
#   pour la creature CREAT ajoute les infos
#   de type  IDENT sur la courbe viser par
#   IP
#================================
def doc_COURBESIPO(CREAT,joint,IP,IDENT): 
        global CREATURES, IMAGE, PREMIERE, DECALE, BOUTONS
        if IMAGE==PREMIERE:
            Lx=IP.addCurve(IDENT[0])
        else: 
            Lx=IP.getCurve(IDENT[0])
        if BOUTONS['RecaleANIM'][0].val:
           Lx.addBezier((float(IMAGE-PREMIERE+DECALE)+1.0,
                          CREATURES[CREAT]['Joints'][joint][IDENT[1]][IDENT[2]]))
        else:
           Lx.addBezier((float(IMAGE+DECALE)+1.0,
                         CREATURES[CREAT]['Joints'][joint][IDENT[1]][IDENT[2]])) 
        Lx.Recalc()
        Lx.update()

#================================
# def CreatureStick(creature,joint):
# à la premiere IMAGE, crée le bloc ipo
# qui contient toutes les courbes d'animations
# aux suivants ajoute simplement les positions
# pour chaque courbes identifiée dans la liste
# identiteIPO
#================================
def CreatureStick(creature,joint): 
    global CREATURES, IMAGE, PREMIERE
    identiteIPO=[['LocX','StickLoc1',0],
             ['LocY','StickLoc1',1],
             ['LocZ','StickLoc1',2],
             ['RotX','StickRot',0],
             ['RotY','StickRot',1],
             ['RotZ','StickRot',2],
             ['SizeX','StickSize',0],
             ['SizeY','StickSize',1],
             ['SizeZ','StickSize',2]]
    if IMAGE==PREMIERE :
        cyl=cylindre(creature.replace(' ','_')+'join_'+str(joint),'tube',0.1,0.1,1.0,6)
        i=Blender.Ipo.New('Object',creature[:6]+'L1j'+str(joint))
        cyl.objet.setIpo(i)
        for ident in identiteIPO:
           doc_COURBESIPO(creature,joint,i,ident)
        return cyl.objet,i
    else:
        i=CREATURES[creature]['Joints'][joint]['ipo']
        for ident in identiteIPO:
           doc_COURBESIPO(creature,joint,i,ident)

#================================
# def GUI_SORTIE ():
#================================
def GUI_SORTIE ():
    Blender.Draw.Exit()

#================================
# def GUI_EVENEMENT (event):
#================================
def GUI_EVENEMENT (event):
      global NOM, REPERTOIRE, PROBLEME, BOUTONS

      if (event==E_IMPORTE):
         fonctionIMPORT()

      elif (event==E_IMPORTE_SELECT): 
         try: 
            Blender.Window.FileSelector (fonctionSELECT, 'SELECTIONNER LE FICHIER .INC')
            REPERTOIRE, NOM=split(BOUTONS['NOMDEFICHIER'][0].val)
         except:
           PROBLEME=E_IMPORTE_SELECT

      elif (event==E_QUITTE):
         GUI_SORTIE()

      elif (event==A_REDOCU):
            pass

      elif (event==E_CONTINUE):
            PROBLEME=0
            Blender.Redraw()

#================================
def GUI_DESSIN():
#================================
      global operation, NOM, REPERTOIRE, ETAT
      global  PROBLEME, PROBLEME_TEXTE
      global PREMIERE , DERNIERE, BOUTONS

      Blender.BGL.glColor3f(0.7, 0.7, 0.7)
      Blender.BGL.glClear(Blender.BGL.GL_COLOR_BUFFER_BIT)
      Blender.BGL.glColor3f(0.1, 0.1, 0.15)

      size=Blender.BGL.Buffer(Blender.BGL.GL_FLOAT, 4)
      Blender.BGL.glGetFloatv(Blender.BGL.GL_SCISSOR_BOX, size)
      size= size.list

      for s in [0,1,2,3]: size[s]=int(size[s])
      ligne=20
      Blender.BGL.glRasterPos2f(20, size[3]-ligne+4)
      Blender.Draw.Text("FramStick ANIM Importeur, (c) jm soler 2004")

      Blender.BGL.glRasterPos2f(20, size[3]-ligne*2+6)
      Blender.Draw.Text("IMAGE de depart :"+str(PREMIERE))

      Blender.BGL.glRasterPos2f(
             20+Blender.Draw.GetStringWidth("IMAGE de depart :"+
             str(PREMIERE))+8, size[3]-ligne*2+6)
      Blender.Draw.Text(" IMAGE d'arrivee : "+str(DERNIERE))

      if PROBLEME==0:
        Blender.Draw.Button ("Selectionne l'import",E_IMPORTE_SELECT,20,
                     size[3]-ligne*3+2,
                     Blender.Draw.GetStringWidth("Selectionne l'import")+8,ligne,"")
        BOUTONS['NOMDEFICHIER'][0]=Blender.Draw.String (BOUTONS['NOMDEFICHIER'][1],
                    E_CHANGECHEMIN,
                    Blender.Draw.GetStringWidth("Selectionne l'import")+30,size[3]-ligne*3+2,
                    Blender.Draw.GetStringWidth(BOUTONS['NOMDEFICHIER'][0].val)+
                    Blender.Draw.GetStringWidth(BOUTONS['NOMDEFICHIER'][1])+4 ,
                    ligne, BOUTONS['NOMDEFICHIER'][0].val,320,
                    BOUTONS['NOMDEFICHIER'][2])
        Blender.Draw.Button ("Import",E_IMPORTE,20,size[3]-ligne*4,80,ligne-2,"")
        Blender.Draw.Button ("Exit",E_QUITTE,20,size[3]-ligne*5,80,ligne-2,"")
        BOUTONS['RecaleANIM'][0]=Blender.Draw.Toggle(BOUTONS['RecaleANIM'][1],
              A_RECALE,100+2,size[3]-ligne*4,80,
              ligne-2, BOUTONS['RecaleANIM'][0].val, BOUTONS['RecaleANIM'][2])
        BOUTONS['ReDocANIM'][0]=Blender.Draw.Toggle(BOUTONS['ReDocANIM'][1],
              A_REDOCU,100+2,size[3]-ligne*5,80,
              ligne-2, BOUTONS['ReDocANIM'][0].val,BOUTONS['ReDocANIM'][2])
        BOUTONS['ReDESSINEANIM'][0]=Blender.Draw.Toggle(BOUTONS['ReDESSINEANIM'][1],
              A_REDESS,100+2,size[3]-ligne*6,80,
              ligne-2, BOUTONS['ReDESSINEANIM'][0].val,BOUTONS['ReDESSINEANIM'][2])
        BOUTONS['PARENT'][0]=Blender.Draw.Toggle(BOUTONS['PARENT'][1],
              A_REDESS,100+2,size[3]-ligne*7,80,
              ligne-2, BOUTONS['PARENT'][0].val,BOUTONS['PARENT'][2])

        Blender.BGL.glRasterPos2f(20,size[3]-ligne*9)
        Blender.Draw.Text(ETAT)
 

      else:
        Blender.BGL.glRasterPos2f(20, size[3]-ligne*3+8)
        Blender.Draw.Text('Desolé : probleme dans la fonction de '+PROBLEME_TEXTE[PROBLEME])
        Blender.Draw.Button ("Continue",E_CONTINUE,20,size[3]-ligne*4,100,ligne)

#================================
def GUI_ENREGISTRE ():
#================================
    Blender.Draw.Register (GUI_DESSIN,None,GUI_EVENEMENT)

#================================
# PARTIE PRINCIPALE DU SCRIPT
#================================
GUI_ENREGISTRE ()

sys.path=oldpath

précédentScript LOD (level of details)
 Script div segment Suivant
Vers le  Haut de page

Les questions concernant cette page  peuvent être posées sur  :
 news://news.zoo-logique.org/3D.Blender

Livre en français
Blender : apprenez, pratiquez, Créez, livre, Ed. Campus Press, coll. Starter Kit
Blender Starter Kit

Forum
FAQ
Lexique
Didacticiels
Compilations
Blender2KT
Débuter
Modelage
Blender python
Materiaux
Lumière
Animation
API python (eng)
Archives nzn
Statistiques
Doc flash Sculptris
Galerie Sculptris

mon site de démos sur youtube