use f.area where possible over python function and use len(mface) over len(mface.v)
[blender.git] / release / scripts / tex2uvbaker.py
1 #!BPY
2
3 """ Registration info for Blender menus:
4 Name: 'Texture Baker'
5 Blender: 239
6 Group: 'UV'
7 Tooltip: 'Procedural to uvmapped texture baker'
8 """
9
10 __author__ = "Jean-Michel Soler (jms)"
11 __url__ = ("blender", "elysiun",
12 "Official Page, http://jmsoler.free.fr/didacticiel/blender/tutor/cpl_mesh3d2uv2d_en.htm",
13 "Communicate problems and errors, http://www.zoo-logique.org/3D.Blender/newsportal/thread.php?group=3D.Blender")
14 __version__ = "0.3.2 2005/12/28"
15
16 __bpydoc__ = """\
17 This script "bakes" Blender procedural materials (including textures): it saves
18 them as 2d uv-mapped images.
19
20 This script saves an uv texture layout of the chosen mesh, that can be used as
21 an uv map for it.  It is a way to export procedurals from Blender as normal
22 image textures that can be edited with a 2d image manipulation program or used
23 with the mesh in games and other 3d applications.
24
25 Usage:
26
27 a) Enter face mode and define uv coordinates for your mesh;<br>
28 b) Define its materials and textures ;
29 c) Run this script and check the console.
30
31 Global variables 
32
33 a)  FRAME  integer, the last frame of the animation, autodocumented .
34 b)  LIMIT  integer, 0 or 1, uvcoords may exceed limits 0.0 to 1.0 , this variable
35 obliges the script to do a complete framing of the uvcoord . 
36
37 Notes:<br>
38    This script was based on a suggestion by Martin (Theeth) Poirier;<br>
39 """
40
41 #---------------------------------------------
42 # Last release : 0.3.1 ,  2005/10/21 , 20h23
43 #---------------------------------------------
44 #---------------------------------------------
45 # (c) jm soler  07/2004 : 'Procedural Texture Baker'
46 #     Based on a Martin 'Theeth' Poirier's really
47 #     good idea : makes a rvk mesh with uv coords
48 #     of the original mesh.
49 #
50 #     Released under Blender Artistic Licence
51 #
52 #   0.3.2
53 #     blender 2.40 update to deal with the new shape  
54 #     key system . 
55 #
56 #   0.3.1 
57 #     stupid bug correction    
58 #     
59 #   0.3.0
60 #      TAILLEIMAGE variable 
61
62 #   0.2.9
63 #      -- little probleme with the KEEPRENDERWINDOW variable .
64 #      removed . script seems to works correctly now .
65
66 #   0.2.8
67 #       -- added the forgotten image property in face
68 #       data. a little longer but better.
69 #       ( a remove double in the resulting mesh may be
70 #       useful .) 
71 #       -- the data.update() function problem is 
72 #       corrected too
73 #       -- no more layers problem . CAM and MESH are
74 #       localised in layer 20 . This layer is
75 #       the active one for the image rendering .
76 #       -- mesh creation is cleaner, loop in double was
77 #       removed and the abskey is set in frame 1 
78 #       only  . This solves an other deform problem  .
79 #       -- if user does not want an autosaved image,
80 #       the "no replace" option leaves the render 
81 #       window on the screen 
82 #
83 #   0.2.7 
84 #      -- minor correction on line 147: "!=-1" added 
85 #
86 #   0.2.6
87 #       -- Creation of LAMP object is removed and replaced
88 #       by the use of the shadeless option  in material object
89 #
90 #       -- helpmsg corrected : the aim of the script
91 #       is to bake any type of textures so we have not
92 #       to mapinput its textures on UV .
93 #
94 #       --'pers' camera was replaced by an 'ortho' one. 
95 #
96 #   0.2.5
97 #       -- if a image file  with the same name exits the
98 #       system returns an error 
99 #
100 #   0.2.4
101 #       -- a LIMIT variable is added to unlock the uvcoords
102 #       autoframing
103 #
104 #
105 #   0.2.3 :  
106 #        Great thanks for Apollux  who sees a lot of these 
107 #        problems
108 #
109 #        --Everytime you run the script a new set 
110 #        of objects is created. File size and memory 
111 #        consumption can go  pretty high if you are 
112 #        not aware of that . 
113 #        Now it ONLY creates 3 objects: a flattened 
114 #         mesh, a camera and a lamp.
115 #        --all the 3 objects was placed on layer 1, but if 
116 #        that layer was not visible while you used the script 
117 #        all you will get a is an empty render. 
118 #        Now the layer is tst and activated befor the shoot 
119 #        --The flattened mesh was really flattend only after 
120 #        frame 100 (if you playbacked the animation, you can 
121 #        actually see the mesh becoming flat on the first 100 
122 #        frames). No more.
123 #        -- When the script is run, it changes temporary to 
124 #        the new cammera, set the render output to a square 
125 #        (i.e. 1024 x 1024 or else), does the render, and then 
126 #        resets the render output and the active camera to the 
127 #        original one. But if no original camera was found
128 #        this produce an error.    
129 #
130 #  0.2.2 : 
131 #        if the uv mesh objet exists it used,
132 #        no creation of a new one. As the lamp and  
133 #        the camera
134 #  0.2.1 : 
135 #        This script automaticaly frame and shoot the
136 #        new uv mesh . The image file is saved ine the
137 #        /render folder.
138 #
139 #---------------------------------------------
140 #  On user-friendly side :
141 #---------------------------------------------
142 #- Tadje Vobovnik adds the Select Image Size Menu
143 #
144 #---------------------------------------------
145 # Official Page :
146 #  http://jmsoler.free.fr/didacticiel/blender/tutor/cpl_mesh3d2uv2d_en.htm
147 # For problems and errors:
148 #   http://www.zoo-logique.org/3D.Blender/newsportal/thread.php?group=3D.Blender
149 #---------------------------------------------
150
151 import Blender
152 from Blender import NMesh, Draw, Object, Scene, Camera
153
154 #----------------------------------- 
155 # Last release : 0.2.5 ,  2005/05/22 , 20h00
156 #----------------------------------- 
157 # la fonction Blender.sys.dirname pose un
158 # probleme lorsque la memoire est trop encombree
159 # ---
160 # It seems that the Blender.sys.dirname function
161 # poses a problem when the memory is too much encumbered 
162 #----------------------------------- 
163 try:
164   import nt
165   os = nt
166   os.sep='\\'
167 except:
168   import posix
169   os = posix
170   os.sep='/'
171 DIRNAME=Blender.Get('filename')
172 #----------------------------------- 
173 #  decoupage de la chaine en fragment
174 #  de façon a isoler le nom du fichier
175 #  du repertoire
176 # ---
177 # split string in fragments to isolate
178 # the file name from the path name
179 #----------------------------------- 
180
181 if DIRNAME.find(os.sep)!=-1:
182     k0=DIRNAME.split(os.sep)
183 else:
184     k0=DIRNAME.split('/')   
185 DIRNAME=DIRNAME.replace(k0[-1],'')
186 #----------------------------------- 
187 # Last release : 0.2.5 ,  2005/05/22 , end
188 #----------------------------------- 
189
190 #----------------------------------- 
191 # Last release : 0.2.4 ,  2005/05/22 , 15h00
192 #----------------------------------- 
193 FRAME = Blender.Get('endframe')
194 #----------------------------------- 
195 # Last release : 0.2.4 ,  2005/05/22 , end
196 #----------------------------------- 
197
198 #----------------------------------- 
199 # Last release : 0.2.4 ,  2005/05/18 , 11h00
200 #
201 # Si  LIMIT == 0 le script n'essaye pas de realiser
202 # un nouveau cadrage pour que l'image presente toute les
203 # coordonnées uv.
204 # ---
205 # if  LIMIT == 0 the script do not try to make
206 # a new framing with all the uvcoord  in only one
207 # shoot...
208 #-----------------------------------
209 LIMIT=0
210 #----------------------------------- 
211 # Last release : 0.2.4 ,  2005/05/18 , END
212 #----------------------------------- 
213
214 XYLIMIT = [0.0, 0.0,1.0,1.0]    
215 OBJPOS = 100.0
216 DEBUG=1
217 RENDERLAYER=20
218 SCENELAYERS=[]
219
220
221
222 helpmsg = """
223 Texture Baker:
224
225 This script saves an uv texture layout of the chosen mesh, that can be used as
226 an uv map for it.  It is a way to export procedural textures from Blender as
227 normal image textures that can be edited with a 2d image manipulation program
228 or used with the mesh in games and other 3d applications.
229
230 Basic instructions:
231 - Enter face mode and define uv coordinates for your mesh (do not forget to
232   choose a development shape);
233 - Define its materials and textures ;
234 - Run this script and check the console.
235
236 """
237
238 def GET_newobject (TYPE,NAME):
239    """
240 # ---------------------------
241 # Function  GET_newobject 
242
243 #  IN : TYPE   string , object type ('Mesh','Empty',...)
244 #       NAME   string , name object  
245 #  OUT: OBJECT  Blender objetc described in teh string TYPE
246 #       SCENE   Blender current scene object
247 # ---------------------------   
248     Return and object and the current scene
249    """
250    SCENE = Blender.Scene.getCurrent()
251    OBJECT = Blender.Object.New(TYPE,NAME)
252    SCENE.link(OBJECT)
253    return OBJECT, SCENE
254
255 def RenameImage(RDIR, MYDIR, FILENAME, name):
256     """
257 # ---------------------------
258 # Function  RenameImage
259 #
260 #  IN : RDIR      string , current render directory
261 #       MYDIR     string , new render dir for this shoot
262 #       FILENAME  string , last rendered image filename 
263 #       name      string , new name for this image 
264 #  OUT:  nothing
265 # ---------------------------
266      Rename the file pointed by the string name
267      recall the  function if the file yet exists
268     """
269     newfname = RDIR + MYDIR + name
270     if newfname.find('.png', -4) < 0 : newfname += '.png'
271         
272     if not Blender.sys.exists(newfname):
273        os.rename(FILENAME, newfname)
274     else:
275         name = Draw.PupStrInput ('ReName Image, please :', name, 32)
276         RenameImage(RDIR, MYDIR, FILENAME, name)
277
278 def SAVE_image (rc, name, FRAME, result):
279    """  
280 # ---------------------------
281 # Function  SAVE_image
282 #
283 #  IN : rc    current render context object
284 #       name  string , image name
285 #       FRAME  integer, last numbre of the curent animation  
286 #  OUT: nothing  
287 # ---------------------------   
288    """
289    rc.enableExtensions(1)   
290    MYDIR = ''
291    RENDERDIR = rc.getRenderPath().replace('\\','/')
292    if RENDERDIR.find('//')==0 : 
293       print 'filename', Blender.Get('filename'),'/n', Blender.sys.dirname(Blender.Get('filename')) 
294       RDIR=RENDERDIR.replace('//',DIRNAME)
295    else:
296         RDIR=RENDERDIR[:]
297    if DEBUG : print  'RDIR : ', RDIR
298     
299    HOMEDIR=Blender.Get('homedir')
300    if DEBUG : print  'HOMEDIR', HOMEDIR
301    rc.setRenderPath(RENDERDIR + MYDIR)
302    if DEBUG : print "Render folder:", RENDERDIR + MYDIR
303    IMAGETYPE = Blender.Scene.Render.PNG
304    if DEBUG : print  'IMAGETYPE : ',IMAGETYPE
305    rc.setImageType(IMAGETYPE)
306    NEWFRAME = FRAME
307    OLDEFRAME = rc.endFrame()
308    OLDSFRAME = rc.startFrame()
309    rc.startFrame(NEWFRAME)
310    rc.endFrame(NEWFRAME)
311    rc.renderAnim()
312    if result!=2 : 
313       Blender.Scene.Render.CloseRenderWindow()
314       FILENAME = "%04d" % NEWFRAME
315       FILENAME = FILENAME.replace (' ', '0')
316       FILENAME = RDIR + MYDIR + FILENAME + '.png'
317       RenameImage(RDIR, MYDIR, FILENAME, name)
318
319    rc.endFrame(OLDEFRAME)
320    rc.startFrame(OLDSFRAME)
321    rc.setRenderPath(RENDERDIR)
322
323 def SHOOT (XYlimit, frame, obj, name, FRAME, result):
324    """
325 # ---------------------------
326 # Function  SHOOT
327 #
328 #  IN : XYlimit  list of 4 floats, smallest and biggest 
329 #                uvcoords
330 #       frame    current frame
331 #       obj      for object location
332 #       name     image name
333 #       FRAME    the last animation's frame 
334 #  OUT:  nothing 
335 # ---------------------------   
336       render and save the baked textures picture 
337    """
338    try:
339       CAM = Blender.Object.Get('UVCAMERA')
340       Cam = CAM.getData()
341       SC = Blender.Scene.getCurrent()
342    except:
343       Cam = Blender.Camera.New()
344       Cam.name = 'UVCamera'
345       CAM, SC = GET_newobject('Camera','UVCAMERA')
346       CAM.link(Cam)
347       CAM.setName('UVCAMERA')
348       CAM.layers=[RENDERLAYER]
349       Cam.lens = 30
350       Cam.name = 'UVCamera'
351
352    Cam.setType('ortho')
353    Cam.setScale(1.0)
354
355    CAM.setLocation(obj.getLocation())
356    CAM.LocX += XYlimit[2] * 0.500
357    CAM.LocY += XYlimit[3] * 0.500
358    CAM.LocZ += max (XYlimit[2], XYlimit[3])
359    CAM.setEuler (0.0, 0.0, 0.0)
360
361    context = SC.getRenderingContext()
362    Camold = SC.getCurrentCamera()
363    SC.setCurrentCamera(CAM)
364
365    OLDy = context.imageSizeY()
366    OLDx = context.imageSizeX()
367
368    TAILLEIMAGE='TEXTURE OUT RESOLUTION : %t |'
369    TAILLEIMAGE+='256 %x1 |'
370    TAILLEIMAGE+='512 %x2 |'
371    TAILLEIMAGE+='768 %x3 |'
372    TAILLEIMAGE+='1024 %x4 |'
373    TAILLEIMAGE+='2048 %x5 '
374    #TAILLEIMAGE+='| 4096 %x6 ' 
375    tres = Draw.PupMenu(TAILLEIMAGE)
376
377    if (tres) == 1: res = 256
378    elif (tres) == 2: res = 512
379    elif (tres) == 3: res = 768
380    elif (tres) == 4: res = 1024
381    elif (tres) == 5: res = 2048
382    # elif (tres) == 6: res = 4096
383    else: res = 512
384    #...
385  
386
387    SCENELAYERS=SC.layers
388    SC.layers = [20]
389    context.imageSizeY(res)
390    context.imageSizeX(res)
391    SAVE_image (context, name, FRAME, result)
392    context.imageSizeY(OLDy)
393    context.imageSizeX(OLDx)
394    SC.layers = SCENELAYERS
395    if Camold : SC.setCurrentCamera(Camold)
396
397    Blender.Set ('curframe', frame)
398
399
400 #----------------------------------- 
401 # release : 0.2.6 ,  2005/05/29 , 00h00
402 #----------------------------------- 
403 def PROV_Shadeless(MATList):
404     """
405 # ---------------------------
406 # Function  PROV_Shadeless
407 #
408 #  IN : MATList  a list of the mesh's materials
409 #  OUT: SHADEDict  a dictionnary  of the materials' shadeles value
410 # ---------------------------   
411     """
412     SHADEDict={}
413     for mat in MATList:
414        SHADEDict[mat.name]=mat.mode
415        mat.mode |= Blender.Material.Modes.SHADELESS
416     return SHADEDict
417 #----------------------------------- 
418 # Last release : 0.2.6 ,  2005/05/29 , end
419 #----------------------------------- 
420
421 #----------------------------------- 
422 # release : 0.2.6 ,  2005/05/29 , 00h00
423 #----------------------------------- 
424 def REST_Shadeless(SHADEDict):
425     """
426 # ---------------------------
427 # Function  REST_Shadeless
428 #
429 #  IN : SHADEDict   a dictionnary  of the materials' shadeles value
430 #  OUT : nothing
431 # ---------------------------   
432     """
433     for m in SHADEDict.keys():
434        mat=Blender.Material.Get(m)
435        mat.mode=SHADEDict[m]
436 #----------------------------------- 
437 # release : 0.2.6 ,  2005/05/29 , end
438 #----------------------------------- 
439
440
441 #----------------------------------- 
442 # release : 0.3.2 ,  2005/12/28 , 13h00
443 #----------------------------------- 
444 def Blender240update(MESH2,FRAME):
445      """ 
446 # ---------------------------
447 # Function  Blender240update
448 #        
449 #  IN : MESH2   a mesh data bloc
450 #       FRAME , the animation frame limit
451 #        
452 #  ADD : an ipo curve to the shape key
453 #        named "Key 1" 
454 #        
455 #  OUT : nothing
456 # ---------------------------   
457      """
458      # ---------------------------   
459      #  recuperation des clef de morphing pour ce mesh
460      # ---------------------------   
461      key = MESH2.getKey()
462      # ---------------------------   
463      #  recuperation de l'Ipo
464      # ---------------------------   
465      ipo = key.ipo
466      # ---------------------------   
467      #  si l'ipo n'existe pas on la cree 
468      # ---------------------------   
469      if ipo == None:
470         noipo = Blender.Ipo.New("Key","keyipo")
471         key.ipo = noipo
472      # ---------------------------   
473      #   raccourci de l'expression
474      # ---------------------------   
475      ipo = key.ipo
476      # ---------------------------   
477      #   identification de la clef de morphing
478      # ---------------------------   
479      keyidentity = "Key 1"
480      # ---------------------------   
481      #   recuperation de la courbe correspondante
482      #  c'est toujours la courbe 0
483      # ---------------------------   
484      ipocurve = ipo.getCurve(0)
485      # ---------------------------   
486      #  si la courbe n'existe pas (normalement, elle n'existe pas mais
487      # on gère le risque pour faciliter une eventuelle récupération de
488      # cette fonction dans un autre script ou pour les cas , certe peu
489      # probable, ou blender viendrait a etre modifie pour les ajouter 
490      # automatiquement ) on la cree ...
491      # ---------------------------   
492      if ipocurve == None:
493         ipocurve = ipo.addCurve(keyidentity)
494      # ---------------------------   
495      #  On applique l'attribut d'inetrpolation qui permet d'avoir
496      # une ligne droite
497      # ---------------------------   
498      ipocurve.setInterpolation("Linear")
499      # ---------------------------   
500      #  On retire tous les sommets qui pourraient se trouver sur la 
501      #  courbe (dans l'état actuel, cette opération est une sécurité 
502      #  superflue ) .
503      # ---------------------------   
504      while len(ipocurve.getPoints()) > 0:
505         ipocurve.delBezier(0)
506         ipocurve.recalc()
507      # ---------------------------   
508      #  On ajouter les sommets necessaires ...
509      # ---------------------------   
510      ipocurve.addBezier((-1,1))
511      # ---------------------------   
512      #   ... ce dernire n'est peut-être pas absolument obligatoire .
513      # ---------------------------   
514      ipocurve.addBezier((FRAME+1,1))
515 #----------------------------------- 
516 # release : 0.3.2 ,  2005/12/28 , end
517 #----------------------------------- 
518          
519 def Mesh2UVCoord (LIMIT):
520    """
521 # ---------------------------
522 # Function  Mesh2UVCoord
523 #
524 #  IN : LIMIT  integer, create or not a new framing for uvcoords 
525 #  OUT:  nothing
526 # ---------------------------   
527    """
528    global PUTRAW, FRAME, SCENELAYERS
529
530    try:
531       MESH3D = Object.GetSelected()[0]
532       if MESH3D.getType() == 'Mesh':
533          MESH = MESH3D.getData()
534
535          try:
536             NewOBJECT=Blender.Object.Get('UVOBJECT')
537             CurSCENE=Blender.Scene.getCurrent()            
538          except:
539             NewOBJECT, CurSCENE = GET_newobject('Mesh','UVOBJECT')
540          MESH2 = NewOBJECT.getData()
541          MESH2.edges=[] 
542          NewOBJECT.layers=[RENDERLAYER]
543
544          MESH2.faces=[]
545          for f in MESH.faces:
546             f1 = Blender.NMesh.Face()
547
548             for v in f.v:
549                v1 = Blender.NMesh.Vert (v.co[0], v.co[1], v.co[2])
550                MESH2.verts.append(v1)
551                f1.v.append(MESH2.verts[len(MESH2.verts) - 1])
552
553             MESH2.faces.append(f1)
554             f1.uv = f.uv[:]
555             f1.col = f.col[:]
556             f1.smooth = f.smooth
557             f1.mode = f.mode
558             f1.flag = f.flag
559             f1.mat = f.mat
560             #----------------------------------- 
561             # release : 0.2.8 ,  2005/07/19 , end
562             #----------------------------------- 
563             try:
564               f1.image=f.image
565             except :
566               pass
567
568          MESH2.materials = MESH.materials[:]
569
570          NewOBJECT.setLocation (OBJPOS, OBJPOS, 0.0)
571          NewOBJECT.setEuler (0.0, 0.0, 0.0)
572          MESH2.removeAllKeys()
573
574          MESH2.update()
575          MESH2.insertKey (1, 'absolute')
576          MESH2.update()
577   
578          for f in MESH2.faces:
579             for v in f.v:
580                for n in [0,1]:
581                   v.co[n] = f.uv[f.v.index(v)][n]
582                   exec "if v.co[%s] > XYLIMIT[%s]: XYLIMIT[%s] = v.co[%s]" % (n, n+2, n+2, n)
583                   exec "if v.co[%s] < XYLIMIT[%s]: XYLIMIT[%s] = v.co[%s]" % (n, n, n, n)
584                v.co[2] = 0.0
585
586          print XYLIMIT
587
588          MESH2.update()
589          MESH2.insertKey (FRAME, 'absolute')
590          MESH2.update()
591
592          #----------------------------------- 
593          # release : 0.3.2 ,  2005/12/28 , 13h00
594          #-----------------------------------            
595          Blender240update(MESH2,FRAME)       
596          #----------------------------------- 
597          # release : 0.3.2 ,  2005/12/28 , end
598          #-----------------------------------            
599          
600          imagename = 'uvtext'
601
602          name = "CHANGE IMAGE NAME ? %t | Replace it | No replace | Script help"
603          result = Draw.PupMenu(name)
604
605          if result == 1:
606             imagename = Draw.PupStrInput ('Image Name:', imagename, 32)
607
608          if result != 3:
609             #----------------------------------- 
610             # release : 0.2.6 ,  2005/05/29 , 00h00
611             #----------------------------------- 
612             SHADEDict=PROV_Shadeless(MESH2.materials)
613             #----------------------------------- 
614             # release : 0.2.6 ,  2005/05/29 , end
615             #----------------------------------- 
616
617             if LIMIT :
618                  SHOOT(XYLIMIT, FRAME, NewOBJECT, imagename, FRAME,result)
619             else :
620                  SHOOT([0.0,0.0,1.0,1.0], FRAME, NewOBJECT, imagename, FRAME, result)
621             #----------------------------------- 
622             # release : 0.2.6,  2005/05/29 , 00h00
623             #----------------------------------- 
624             REST_Shadeless(SHADEDict)
625             #----------------------------------- 
626             # release : 0.2.6 ,  2005/05/29 , end
627             #----------------------------------- 
628
629             Blender.Redraw()
630
631          else:
632             Draw.PupMenu("Ready%t|Please check console for instructions")
633             print helpmsg
634
635       else:
636          name = "Error%t|Active object is not a mesh or has no UV coordinates"
637          result = Draw.PupMenu(name)
638          print 'problem : no object selected or not mesh'
639
640    except:
641       name = "Error%t|Active object is not a mesh or has no UV coordinates"
642       result = Draw.PupMenu(name)
643       print 'problem : no object selected or not mesh'
644
645 Mesh2UVCoord(LIMIT)