wasnt returning the images in python bake api
[blender.git] / release / scripts / bpymodules / BPyRender.py
1 import Blender
2 from Blender import Scene, sys, Camera, Object, Image
3 from Blender.Scene import Render
4 Vector= Blender.Mathutils.Vector
5
6
7 def extFromFormat(format):
8         if format == Render.TARGA: return 'tga'
9         if format == Render.RAWTGA: return 'tga'
10         if format == Render.HDR: return 'hdr'
11         if format == Render.PNG: return 'png'
12         if format == Render.BMP: return 'bmp'
13         if format == Render.JPEG: return 'jpg'
14         if format == Render.HAMX: return 'ham'
15         if format == Render.TIFF: return 'tif'
16         if format == Render.CINEON: return 'cine'
17         if format == Render.DPX: return 'tif'
18         if format == Render.OPENEXR: return 'exr'
19         if format == Render.IRIS: return 'rgb'
20         return ''
21
22         
23
24 def imageFromObjectsOrtho(objects, path, width, height, smooth, alpha= True, camera_matrix= None, format=Render.PNG):
25         '''
26         Takes any number of objects and renders them on the z axis, between x:y-0 and x:y-1
27         Usefull for making images from a mesh without per pixel operations
28         - objects must be alredy placed
29         - smooth, anti alias True/False
30         - path renders to a PNG image
31         - alpha weather to render background as alpha
32         
33         returns the blender image
34         '''
35         ext = '.' + extFromFormat(format)
36         print ext
37         # remove an extension if its alredy there
38         if path.lower().endswith(ext):
39                 path= path[:-4] 
40         
41         path_expand= sys.expandpath(path) + ext
42         
43         print path_expand, 'path'
44         
45         # Touch the path
46         try:
47                 f= open(path_expand, 'w')
48                 f.close()
49         except:
50                 raise 'Error, could not write to path:' + path_expand
51         
52         
53         # RENDER THE FACES.
54         scn= Scene.GetCurrent()
55         render_scn= Scene.New()
56         render_scn.makeCurrent()
57         render_scn.Layers |= (1<<20)-1 # all layers enabled
58          
59         # Add objects into the current scene
60         for ob in objects:
61                 render_scn.link(ob)
62         
63         render_context= render_scn.getRenderingContext()
64         render_context.setRenderPath('') # so we can ignore any existing path and save to the abs path.
65         
66         
67         render_context.imageSizeX(width)
68         render_context.imageSizeY(height)
69         
70         if smooth:
71                 render_context.enableOversampling(True) 
72                 render_context.setOversamplingLevel(16)
73         else:
74                 render_context.enableOversampling(False) 
75         
76         render_context.setRenderWinSize(100)
77         render_context.setImageType(format)
78         render_context.enableExtensions(True) 
79         #render_context.enableSky() # No alpha needed.
80         if alpha:
81                 render_context.alphaMode= 1
82                 render_context.enableRGBAColor()
83         else:
84                 render_context.alphaMode= 0
85                 render_context.enableRGBColor()
86         
87         render_context.displayMode= 0 # fullscreen
88         
89         # New camera and object
90         render_cam_data= Camera.New('ortho')
91         render_cam_ob= Object.New('Camera')
92         render_cam_ob.link(render_cam_data)
93         render_scn.link(render_cam_ob)
94         render_scn.objects.camera = render_cam_ob
95         
96         render_cam_data.type= 'ortho'
97         
98         
99         
100         # Position the camera
101         if camera_matrix:
102                 render_cam_ob.setMatrix(camera_matrix)
103                 # We need to take into account the matrix scaling when setting the size
104                 # so we get the image bounds defined by the matrix
105                 # first get the x and y factors from the matrix.
106                 # To render the correct dimensions we must use the aspy and aspy to force the matrix scale to
107                 # override the aspect enforced by the width and weight.
108                 cent= Vector() * camera_matrix
109                 xvec= Vector(1,0,0) * camera_matrix
110                 yvec= Vector(0,1,0) * camera_matrix
111                 # zvec= Vector(0,0,1) * camera_matrix
112                 xlen = (cent-xvec).length # half height of the image
113                 ylen = (cent-yvec).length # half width of the image
114                 # zlen = (cent-zvec).length # dist to place the camera? - just use the loc for now.
115                 
116                 
117                 # less then 1.0 portrate, 1.0 or more is portrate
118                 asp_cam_mat= xlen/ylen # divide by zero? - possible but scripters fault.
119                 asp_image_res= float(width)/height
120                 #print 'asp quad', asp_cam_mat, 'asp_image', asp_image_res
121                 #print 'xylen', xlen, ylen, 'w/h', width, height
122                 # Setup the aspect
123                 
124                 if asp_cam_mat > asp_image_res:
125                         # camera is wider then image res.
126                         # to make the image wider, reduce the aspy
127                         asp_diff= asp_image_res/asp_cam_mat
128                         min_asp= int(round(asp_diff * 200))
129                         #print 'X', min_asp
130                         
131                 elif asp_cam_mat < asp_image_res: # asp_cam_mat < asp_image_res
132                         # camera is narrower then image res
133                         # to make the image narrower, reduce the aspx
134                         asp_diff= asp_cam_mat/asp_image_res
135                         min_asp= int(round(asp_diff * 200))
136                         #print 'Y', min_asp
137                 else:
138                         min_asp= 200
139                 
140                 # set the camera size
141                 if xlen > ylen:
142                         if asp_cam_mat > asp_image_res:
143                                 render_context.aspectX= 200 # get the greatest range possible
144                                 render_context.aspectY= min_asp # get the greatest range possible
145                         else:
146                                 render_context.aspectY= 200 # get the greatest range possible
147                                 render_context.aspectX= min_asp # get the greatest range possible
148                         #print "xlen bigger"
149                         render_cam_data.scale= xlen * 2
150                 elif xlen < ylen:# ylen is bigger
151                         if asp_cam_mat > asp_image_res:
152                                 render_context.aspectX= 200 # get the greatest range possible
153                                 render_context.aspectY= min_asp # get the greatest range possible
154                         else:
155                                 render_context.aspectY= 200 # get the greatest range possible
156                                 render_context.aspectX= min_asp # get the greatest range possible
157                         #print "ylen bigger"
158                         render_cam_data.scale= ylen *2 
159                 else:
160                         # asppect 1:1
161                         #print 'NOLEN Bigger'
162                         render_cam_data.scale= xlen * 2
163
164                 #print xlen, ylen, 'xlen, ylen'
165                 
166         else:
167                 if width > height:
168                         min_asp = int((float(height) / width) * 200)
169                         render_context.aspectX= min_asp
170                         render_context.aspectY= 200
171                 else:
172                         min_asp = int((float(width) / height) * 200)
173                         render_context.aspectX= 200
174                         render_context.aspectY= min_asp
175                 
176                 
177                 render_cam_data.scale= 1.0
178                 render_cam_ob.LocZ= 1.0
179                 render_cam_ob.LocX= 0.5
180                 render_cam_ob.LocY= 0.5
181         
182         Blender.Window.RedrawAll()
183         
184         render_context.render()
185         render_context.saveRenderedImage(path)
186         Render.CloseRenderWindow()
187         #if not B.sys.exists(PREF_IMAGE_PATH_EXPAND):
188         #       raise 'Error!!!'
189         
190         scn.makeCurrent()
191         Scene.Unlink(render_scn)
192         
193         # NOW APPLY THE SAVED IMAGE TO THE FACES!
194         #print PREF_IMAGE_PATH_EXPAND
195         try:
196                 target_image= Image.Load(path_expand)
197                 return target_image
198         except:
199                 raise 'Error: Could not render or load the image at path "%s"' % path_expand
200                 return
201
202
203
204 #-----------------------------------------------------------------------------#
205 # UV Baking functions, make a picture from mesh(es) uvs                       #
206 #-----------------------------------------------------------------------------#
207
208 def mesh2uv(me_s, PREF_SEL_FACES_ONLY=False):
209         '''
210         Converts a uv mapped mesh into a 2D Mesh from UV coords.
211         returns a triple -
212         (mesh2d, face_list, col_list)
213         "mesh" is the new mesh and...
214         "face_list" is the faces that were used to make the mesh,
215         "material_list" is a list of materials used by each face
216         These are in alligned with the meshes faces, so you can easerly copy data between them
217         
218         '''
219         render_me= Blender.Mesh.New()
220         render_me.verts.extend( [Vector(0,0,0),] ) # 0 vert uv bugm dummy vert
221         face_list= []
222         material_list= []
223         for me in me_s:
224                 me_materials= me.materials
225                 if PREF_SEL_FACES_ONLY:
226                         me_faces= [f for f in me.faces if f.sel]
227                 else:
228                         me_faces= me.faces
229                 
230                 face_list.extend(me_faces)
231                 
232                 # Dittro
233                 if me_materials:
234                         material_list.extend([me_materials[f.mat] for f in me_faces])
235                 else:
236                         material_list.extend([None]*len(me_faces))
237                 
238         # Now add the verts
239         render_me.verts.extend( [ Vector(uv.x, uv.y, 0) for f in face_list for uv in f.uv ] )
240         
241         # Now add the faces
242         tmp_faces= []
243         vert_offset= 1
244         for f in face_list:
245                 tmp_faces.append( [ii+vert_offset for ii in xrange(len(f))] )
246                 vert_offset+= len(f)
247         
248         render_me.faces.extend(tmp_faces)
249         render_me.faceUV=1
250         return render_me, face_list, material_list
251
252
253 def uvmesh_apply_normals(render_me, face_list):
254         '''Worldspace normals to vertex colors'''
255         for i, f in enumerate(render_me.faces):
256                 face_orig= face_list[i]
257                 f_col= f.col
258                 for j, v in enumerate(face_orig):
259                         c= f_col[j]
260                         nx, ny, nz= v.no
261                         c.r= int((nx+1)*128)-1
262                         c.g= int((ny+1)*128)-1
263                         c.b= int((nz+1)*128)-1
264
265 def uvmesh_apply_image(render_me, face_list):
266         '''Copy the image and uvs from the original faces'''
267         for i, f in enumerate(render_me.faces):
268                 f.uv= face_list[i].uv
269                 f.image= face_list[i].image
270
271
272 def uvmesh_apply_vcol(render_me, face_list):
273         '''Copy the vertex colors from the original faces'''
274         for i, f in enumerate(render_me.faces):
275                 face_orig= face_list[i]
276                 f_col= f.col
277                 for j, c_orig in enumerate(face_orig.col):
278                         c= f_col[j]
279                         c.r= c_orig.r
280                         c.g= c_orig.g
281                         c.b= c_orig.b
282
283 def uvmesh_apply_matcol(render_me, material_list):
284         '''Get the vertex colors from the original materials'''
285         for i, f in enumerate(render_me.faces):
286                 mat_orig= material_list[i]
287                 f_col= f.col
288                 if mat_orig:
289                         for c in f_col:
290                                 c.r= int(mat_orig.R*255)
291                                 c.g= int(mat_orig.G*255)
292                                 c.b= int(mat_orig.B*255)
293                 else:
294                         for c in f_col:
295                                 c.r= 255
296                                 c.g= 255
297                                 c.b= 255
298
299 def uvmesh_apply_col(render_me, color):
300         '''Get the vertex colors from the original materials'''
301         r,g,b= color
302         for i, f in enumerate(render_me.faces):
303                 f_col= f.col
304                 for c in f_col:
305                         c.r= r
306                         c.g= g
307                         c.b= b
308
309
310 def vcol2image(me_s,\
311         PREF_IMAGE_PATH,\
312         PREF_IMAGE_SIZE,\
313         PREF_IMAGE_BLEED,\
314         PREF_IMAGE_SMOOTH,\
315         PREF_IMAGE_WIRE,\
316         PREF_IMAGE_WIRE_INVERT,\
317         PREF_IMAGE_WIRE_UNDERLAY,\
318         PREF_USE_IMAGE,\
319         PREF_USE_VCOL,\
320         PREF_USE_MATCOL,\
321         PREF_USE_NORMAL,\
322         PREF_USE_TEXTURE,\
323         PREF_SEL_FACES_ONLY):
324         
325         
326         def rnd_mat():
327                 render_mat= Blender.Material.New()
328                 mode= render_mat.mode
329                 
330                 # Dont use lights ever
331                 mode |= Blender.Material.Modes.SHADELESS
332                 
333                 if PREF_IMAGE_WIRE:
334                         # Set the wire color
335                         if PREF_IMAGE_WIRE_INVERT:
336                                 render_mat.rgbCol= (1,1,1)
337                         else:
338                                 render_mat.rgbCol= (0,0,0)
339                         
340                         mode |= Blender.Material.Modes.WIRE
341                 if PREF_USE_VCOL or PREF_USE_MATCOL or PREF_USE_NORMAL: # both vcol and material color use vertex cols to avoid the 16 max limit in materials
342                         mode |= Blender.Material.Modes.VCOL_PAINT
343                 if PREF_USE_IMAGE:
344                         mode |= Blender.Material.Modes.TEXFACE
345                 
346                 # Copy back the mode
347                 render_mat.mode |= mode
348                 return render_mat
349         
350         
351         render_me, face_list, material_list= mesh2uv(me_s, PREF_SEL_FACES_ONLY)
352
353         # Normals exclude all others
354         if PREF_USE_NORMAL:
355                 uvmesh_apply_normals(render_me, face_list)
356         else:
357                 if PREF_USE_IMAGE:
358                         uvmesh_apply_image(render_me, face_list)
359                         uvmesh_apply_vcol(render_me, face_list)
360         
361                 elif PREF_USE_VCOL:
362                         uvmesh_apply_vcol(render_me, face_list)
363                 
364                 elif PREF_USE_MATCOL:
365                         uvmesh_apply_matcol(render_me, material_list)
366                 
367                 elif PREF_USE_TEXTURE:
368                         # if we have more then 16 materials across all the mesh objects were stuffed :/
369                         # get unique materials
370                         tex_unique_materials= dict([(mat.name, mat) for mat in material_list]).values()[:16] # just incase we have more then 16 
371                         tex_me= Blender.Mesh.New()
372                         
373                         # Backup the original shadless setting
374                         tex_unique_materials_shadeless= [ mat.mode & Blender.Material.Modes.SHADELESS for mat in tex_unique_materials ]
375                         
376                         # Turn shadeless on
377                         for mat in tex_unique_materials:
378                                 mat.mode |= Blender.Material.Modes.SHADELESS
379                         
380                         # Assign materials
381                         render_me.materials= tex_unique_materials
382                         
383                         
384                         
385                         tex_material_indicies= dict([(mat.name, i) for i, mat in enumerate(tex_unique_materials)])
386                         
387                         tex_me.verts.extend([Vector(0,0,0),]) # dummy
388                         tex_me.verts.extend( [ Vector(v.co) for f in face_list for v in f ] )
389                         
390                         # Now add the faces
391                         tmp_faces= []
392                         vert_offset= 1
393                         for f in face_list:
394                                 tmp_faces.append( [ii+vert_offset for ii in xrange(len(f))] )
395                                 vert_offset+= len(f)
396                         
397                         tex_me.faces.extend(tmp_faces)
398                         
399                         # Now we have the faces, put materials and normal, uvs into the mesh
400                         if len(tex_me.faces) != len(face_list):
401                                 # Should never happen
402                                 raise "Error face length mismatch"
403                         
404                         # Copy data to the mesh that could be used as texture coords
405                         for i, tex_face in enumerate(tex_me.faces):
406                                 orig_face= face_list[i]
407                                 
408                                 # Set the material index
409                                 try:
410                                         render_face.mat= tex_material_indicies[ material_list[i].name ]
411                                 except:
412                                         # more then 16 materials
413                                         pass
414                                 
415                                 
416                                 # set the uvs on the texmesh mesh
417                                 tex_face.uv= orig_face.uv
418                                 
419                                 orig_face_v= orig_face.v
420                                 # Set the normals
421                                 for j, v in enumerate(tex_face):
422                                         v.no= orig_face_v[j].no
423                         
424                         # Set the texmesh
425                         render_me.texMesh= tex_me
426                 # END TEXMESH
427                         
428                         
429         # Handel adding objects
430         render_ob= Blender.Object.New('Mesh')
431         render_ob.link(render_me)
432         
433         if not PREF_USE_TEXTURE: # textures use the original materials
434                 render_me.materials= [rnd_mat()]
435         
436         
437         obs= [render_ob]
438         
439         
440         if PREF_IMAGE_WIRE_UNDERLAY:
441                 # Make another mesh with the material colors
442                 render_me_under, face_list, material_list= mesh2uv(me_s, PREF_SEL_FACES_ONLY)
443                 
444                 uvmesh_apply_matcol(render_me_under, material_list)
445                 
446                 # Handel adding objects
447                 render_ob= Blender.Object.New('Mesh')
448                 render_ob.link(render_me_under)
449                 render_ob.LocZ= -0.01
450                 
451                 # Add material and disable wire
452                 mat= rnd_mat()
453                 mat.rgbCol= 1,1,1
454                 mat.alpha= 0.5
455                 mat.mode &= ~Blender.Material.Modes.WIRE
456                 mat.mode |= Blender.Material.Modes.VCOL_PAINT
457                 
458                 render_me_under.materials= [mat]
459                 
460                 obs.append(render_ob)
461                 
462         elif PREF_IMAGE_BLEED and not PREF_IMAGE_WIRE:
463                 # EVIL BLEEDING CODE!! - Just do copys of the mesh and place behind. Crufty but better then many other methods I have seen. - Cam
464                 BLEED_PIXEL= 1.0/PREF_IMAGE_SIZE
465                 z_offset= 0.0
466                 for i in xrange(PREF_IMAGE_BLEED):
467                         for diag1, diag2 in ((-1,-1),(-1,1),(1,-1),(1,1), (1,0), (0,1), (-1,0), (0, -1)): # This line extends the object in 8 different directions, top avoid bleeding.
468                                 
469                                 render_ob= Blender.Object.New('Mesh')
470                                 render_ob.link(render_me)
471                                 
472                                 render_ob.LocX= (i+1)*diag1*BLEED_PIXEL
473                                 render_ob.LocY= (i+1)*diag2*BLEED_PIXEL
474                                 render_ob.LocZ= -z_offset
475                                 
476                                 obs.append(render_ob)
477                                 z_offset += 0.01
478         
479         
480         
481         image= imageFromObjectsOrtho(obs, PREF_IMAGE_PATH, PREF_IMAGE_SIZE, PREF_IMAGE_SIZE, PREF_IMAGE_SMOOTH)
482         
483         # Clear from memory as best as we can
484         render_me.verts= None
485         
486         if PREF_IMAGE_WIRE_UNDERLAY:
487                 render_me_under.verts= None
488         
489         if PREF_USE_TEXTURE:
490                 tex_me.verts= None
491                 # Restire Shadeless setting
492                 for i, mat in enumerate(tex_unique_materials):
493                         # we know there all on so turn it off of its not set
494                         if not tex_unique_materials_shadeless[i]:
495                                 mat.mode &= ~Blender.Material.Modes.SHADELESS
496         
497         return image
498
499 def bakeToPlane(sce, ob_from, width, height, bakemodes, axis='z', margin=0):
500         '''
501         Bakes terrain onto a plane from one object
502         sce - scene to bake with
503         ob_from - mesh object
504         width/height - image size
505         bakemodes - list of baking modes to use, Blender.Scene.Render.BakeModes.NORMALS, Blender.Scene.Render.BakeModes.AO ... etc
506         axis - axis to allign the plane to.
507         margin - margin setting for baking.
508         
509         Example:
510                 import Blender
511                 from Blender import *
512                 import BPyRender
513                 sce = Scene.GetCurrent()
514                 ob = Object.Get('Plane')
515                 BPyRender.bakeToPlane(sce, ob, 512, 512, [Scene.Render.BakeModes.DISPLACEMENT, Scene.Render.BakeModes.NORMALS], 'z', 8 )
516         '''
517         
518         # Backup bake settings
519         rend = sce.render
520         BACKUP_bakeDist = rend.bakeDist
521         BACKUP_bakeBias = rend.bakeBias
522         BACKUP_bakeMode = rend.bakeMode
523         BACKUP_bakeClear = rend.bakeClear
524         BACKUP_bakeMargin = rend.bakeMargin
525         BACKUP_bakeToActive = rend.bakeToActive
526         
527         # Backup object selection
528         BACKUP_obsel = list(sce.objects.selected)
529         BACKUP_obact = sce.objects.active
530         
531         # New bake settings
532         rend.bakeClear = True
533         rend.bakeMargin = margin
534         BACKUP_bakeToActive = True
535         
536         # Assume a mesh
537         me_from = ob_from.getData(mesh=1)
538         
539         xmin = ymin = zmin = 10000000000
540         xmax = ymax = zmax =-10000000000
541         
542         # Dont trust bounding boxes :/
543         #bounds = ob_from.boundingBox
544         #for v in bounds:
545         #       x,y,z = tuple(v)
546         mtx = ob_from.matrixWorld
547         for v in me_from.verts:
548                 x,y,z = tuple(v.co*mtx)
549                 
550                 xmax = max(xmax, x)
551                 ymax = max(ymax, y)
552                 zmax = max(zmax, z)
553                 
554                 xmin = min(xmin, x)
555                 ymin = min(ymin, y)
556                 zmin = min(zmin, z)
557
558         if axis=='x':
559                 xmed = (xmin+xmax)/2.0
560                 co1 = (xmed, ymin, zmin)
561                 co2 = (xmed, ymin, zmax)
562                 co3 = (xmed, ymax, zmax)
563                 co4 = (xmed, ymax, zmin)
564                 rend.bakeDist = (xmax-xmin)/2.0
565         elif axis=='y':
566                 ymed = (ymin+ymax)/2.0
567                 co1 = (xmin, ymed, zmin)
568                 co2 = (xmin, ymed, zmax)
569                 co3 = (xmax, ymed, zmax)
570                 co4 = (xmax, ymed, zmin)
571                 rend.bakeDist = (ymax-ymin)/2.0
572         elif axis=='z':
573                 zmed = (zmin+zmax)/2.0
574                 co1 = (xmin, ymin, zmed)
575                 co2 = (xmin, ymax, zmed)
576                 co3 = (xmax, ymax, zmed)
577                 co4 = (xmax, ymin, zmed)
578                 rend.bakeDist = (zmax-zmin)/2.0
579         else:
580                 raise "invalid axis"
581         me_plane = Blender.Mesh.New()
582         ob_plane = Blender.Object.New('Mesh')
583         ob_plane.link(me_plane)
584         sce.objects.link(ob_plane)
585         ob_plane.Layers = ob_from.Layers
586         
587         ob_from.sel = 1 # make active
588         sce.objects.active = ob_plane
589         ob_plane.sel = 1
590         
591         me_plane.verts.extend([co4, co3, co2, co1])
592         me_plane.faces.extend([(0,1,2,3)])
593         me_plane.faceUV = True
594         me_plane_face = me_plane.faces[0]
595         uvs = me_plane_face.uv
596         uvs[0].x = 0.0; uvs[0].y = 0.0
597         uvs[1].x = 0.0; uvs[1].y = 1.0
598         uvs[2].x = 1.0; uvs[2].y = 1.0
599         uvs[3].x = 1.0; uvs[3].y = 0.0
600         
601         images_return = []
602         
603         for mode in bakemodes:
604                 img = Blender.Image.New('bake', width, height, 24)
605                 
606                 me_plane_face.image = img
607                 rend.bakeMode = mode
608                 rend.bake()
609                 images_return.append( img )
610         
611         # Restore bake settings
612         #'''
613         rend.bakeDist = BACKUP_bakeDist
614         rend.bakeBias = BACKUP_bakeBias
615         rend.bakeMode = BACKUP_bakeMode
616         rend.bakeClear = BACKUP_bakeClear
617         rend.bakeMargin = BACKUP_bakeMargin
618         rend.bakeToActive = BACKUP_bakeToActive
619         
620         # Restore obsel
621         sce.objects.selected = BACKUP_obsel
622         sce.objects.active = BACKUP_obact
623         
624         me_plane.verts = None
625         sce.objects.unlink(ob_plane)
626         #'''
627         
628         return images_return
629