#!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
"""
#=========================================================
# Personal functions to avoid using the complete version
# of the python. Valid on: win32, linux, irix and 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
#=========================================================
# ... end of the additional fonctions
#=========================================================
#=========================================================
# It can be difficult to charge the math module on certain
# linux installation . Generally, the installation of a
pack python
# for developers is necessary. The problem was signaled to
# the Blender python API developers , which have,
# initially, added the math functions to withdraw them
# thereafter under the pretext that a module math in the Blender
# module could be misleading .
#--
# Under win32 (win9x, xp, NT, 2000) the module math is
# delivered with the DLL python
# which accompanies Blender.
#=========================================================
import math
from math import pi,cos,sin
#=========================================================
# Module Python Blender
#=========================================================
import Blender
#=========================================================
# Sub module for MESHES management
#=========================================================
#from Blender import NMesh
#=========================================================
# Sub module for graphic management interface
#=========================================================
#from Blender import Draw
#=========================================================
# Sub module for graphic management interface d'interface
avec les fonctions OpenGL
#=========================================================
#from Blender import BGL
#=========================================================
# Sub module for MATERIAL management
#=========================================================
#from Blender import Material
#=========================================================
# Sub module for WINDOW management
#=========================================================
#from Blender import Window
#=========================================================
# Sub module for IPO animation curves management
#=========================================================
#from Blender import Ipo
#=========================================================
# Sub module for SCENE management
# (with so numerous functions that it was separated in
two
# and there is now a sub module Scene.Render )
#=========================================================
from Blender import Scene
SCENE=Blender.Scene.GetCurrent()
#=========================================================
# Python's sys module to read the current directory and
# add momentarily a path research to the 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)
#=========================================================
# Some global variables
#=========================================================
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..."
#=========================================================
# Dictionary to call a frame by its number
#=========================================================
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 |