Undo revision 23130 which was a merge with 2.5, a messy one because I did something...
[blender.git] / release / io / engine_render_pov.py
1 import bpy
2
3 from math import atan, pi, degrees
4 import subprocess
5 import os
6 import sys
7 import time
8
9 import platform as pltfrm
10
11 if pltfrm.architecture()[0] == '64bit':
12         bitness = 64
13 else:
14         bitness = 32
15
16 def write_pov(filename, scene=None, info_callback = None):
17         file = open(filename, 'w')
18         
19         # Only for testing
20         if not scene:
21                 scene = bpy.data.scenes[0]
22         
23         render = scene.render_data
24         world = scene.world
25         
26         # --- taken from fbx exporter 
27         ## This was used to make V, but faster not to do all that
28         ##valid = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_,.()[]{}'
29         ##v = range(255)
30         ##for c in valid: v.remove(ord(c))
31         v = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,42,43,46,47,58,59,60,61,62,63,64,92,94,96,124,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254]
32         invalid = ''.join([chr(i) for i in v])
33         def cleanName(name):
34                 for ch in invalid:      name = name.replace(ch, '_')
35                 return name
36         del v
37         
38         # --- done with clean name.
39         
40         def uniqueName(name, nameSeq):
41                 
42                 if name not in nameSeq:
43                         return name
44                 
45                 name_orig = name
46                 i = 1
47                 while name in nameSeq:
48                         name = '%s_%.3d' % (name_orig, i)
49                         i+=1
50                 
51                 return name
52                 
53         
54         def writeMatrix(matrix):
55                 file.write('\tmatrix <%.6f, %.6f, %.6f,  %.6f, %.6f, %.6f,  %.6f, %.6f, %.6f,  %.6f, %.6f, %.6f>\n' %\
56                 (matrix[0][0], matrix[0][1], matrix[0][2],  matrix[1][0], matrix[1][1], matrix[1][2],  matrix[2][0], matrix[2][1], matrix[2][2],  matrix[3][0], matrix[3][1], matrix[3][2]) )
57         
58         def writeObjectMaterial(material):
59                 if material and material.transparency_method=='RAYTRACE':
60                         file.write('\tinterior { ior %.6f }\n' % material.raytrace_transparency.ior)
61                         
62                         # Other interior args
63                         # fade_distance 2
64                         # fade_power [Value]
65                         # fade_color
66                         
67                         # dispersion
68                         # dispersion_samples
69         
70         materialNames = {}
71         DEF_MAT_NAME = 'Default'
72         def writeMaterial(material):
73                 # Assumes only called once on each material
74                 
75                 if material:
76                         name_orig = material.name
77                 else:
78                         name_orig = DEF_MAT_NAME
79                 
80                 name = materialNames[name_orig] = uniqueName(cleanName(name_orig), materialNames)
81                 
82                 file.write('#declare %s = finish {\n' % name)
83                 
84                 if material:
85                         file.write('\tdiffuse %.3g\n' % material.diffuse_reflection)
86                         file.write('\tspecular %.3g\n' % material.specular_reflection)
87                         
88                         file.write('\tambient %.3g\n' % material.ambient)
89                         #file.write('\tambient rgb <%.3g, %.3g, %.3g>\n' % tuple([c*material.ambient for c in world.ambient_color])) # povray blends the global value
90                         
91                         # map hardness between 0.0 and 1.0
92                         roughness = ((1.0 - ((material.specular_hardness-1.0)/510.0)))
93                         # scale from 0.0 to 0.1
94                         roughness *= 0.1
95                         # add a small value because 0.0 is invalid
96                         roughness += (1/511.0)
97                         
98                         file.write('\troughness %.3g\n' % roughness)
99                         
100                         # 'phong 70.0 '
101                         
102                         if material.raytrace_mirror.enabled:
103                                 raytrace_mirror= material.raytrace_mirror
104                                 if raytrace_mirror.reflect:
105                                         file.write('\treflection {\n')
106                                         file.write('\t\trgb <%.3g, %.3g, %.3g>' % tuple(material.mirror_color))
107                                         file.write('\t\tfresnel 1 falloff %.3g exponent %.3g metallic %.3g} ' % (raytrace_mirror.fresnel, raytrace_mirror.fresnel_fac, raytrace_mirror.reflect))
108                 
109                 else:
110                         file.write('\tdiffuse 0.8\n')
111                         file.write('\tspecular 0.2\n')
112                         
113                         
114                 
115                 # This is written into the object
116                 '''
117                 if material and material.transparency_method=='RAYTRACE':
118                         'interior { ior %.3g} ' % material.raytrace_transparency.ior
119                 '''
120                 
121                 #file.write('\t\t\tcrand 1.0\n') # Sand granyness
122                 #file.write('\t\t\tmetallic %.6f\n' % material.spec)
123                 #file.write('\t\t\tphong %.6f\n' % material.spec)
124                 #file.write('\t\t\tphong_size %.6f\n' % material.spec)
125                 #file.write('\t\t\tbrilliance %.6f ' % (material.specular_hardness/256.0) # Like hardness
126                 
127                 file.write('}\n')
128         
129         def exportCamera():
130                 camera = scene.camera
131                 matrix = camera.matrix
132                 
133                 # compute resolution
134                 Qsize=float(render.resolution_x)/float(render.resolution_y)
135                 
136                 file.write('camera {\n')
137                 file.write('\tlocation  <0, 0, 0>\n')
138                 file.write('\tlook_at  <0, 0, -1>\n')
139                 file.write('\tright <%s, 0, 0>\n' % -Qsize)
140                 file.write('\tup <0, 1, 0>\n')
141                 file.write('\tangle  %f \n' % (360.0*atan(16.0/camera.data.lens)/pi))
142                 
143                 file.write('\trotate  <%.6f, %.6f, %.6f>\n' % tuple([degrees(e) for e in matrix.rotationPart().toEuler()]))
144                 file.write('\ttranslate <%.6f, %.6f, %.6f>\n' % (matrix[3][0], matrix[3][1], matrix[3][2]))
145                 file.write('}\n')
146         
147         
148         
149         def exportLamps(lamps):
150                 # Get all lamps
151                 for ob in lamps:
152                         lamp = ob.data
153                         
154                         matrix = ob.matrix
155                         
156                         color = tuple([c * lamp.energy for c in lamp.color]) # Colour is modified by energy
157                         
158                         file.write('light_source {\n')
159                         file.write('\t< 0,0,0 >\n')
160                         file.write('\tcolor rgb<%.3g, %.3g, %.3g>\n' % color)
161                         
162                         if lamp.type == 'POINT': # Point Lamp 
163                                 pass
164                         elif lamp.type == 'SPOT': # Spot
165                                 file.write('\tspotlight\n')
166                                 
167                                 # Falloff is the main radius from the centre line
168                                 file.write('\tfalloff %.2f\n' % (lamp.spot_size/2.0) ) # 1 TO 179 FOR BOTH
169                                 file.write('\tradius %.6f\n' % ((lamp.spot_size/2.0) * (1-lamp.spot_blend)) ) 
170                                 
171                                 # Blender does not have a tightness equivilent, 0 is most like blender default.
172                                 file.write('\ttightness 0\n') # 0:10f
173                                 
174                                 file.write('\tpoint_at  <0, 0, -1>\n')
175                         elif lamp.type == 'SUN':
176                                 file.write('\tparallel\n')
177                                 file.write('\tpoint_at  <0, 0, -1>\n') # *must* be after 'parallel'
178                                 
179                         elif lamp.type == 'AREA':
180                                 
181                                 size_x = lamp.size
182                                 samples_x = lamp.shadow_ray_samples_x
183                                 if lamp.shape == 'SQUARE':
184                                         size_y = size_x
185                                         samples_y = samples_x
186                                 else:
187                                         size_y = lamp.size_y
188                                         samples_y = lamp.shadow_ray_samples_y
189                                 
190                                 
191                                 
192                                 file.write('\tarea_light <%d,0,0>,<0,0,%d> %d, %d\n' % (size_x, size_y, samples_x, samples_y))
193                                 if lamp.shadow_ray_sampling_method == 'CONSTANT_JITTERED':
194                                         if lamp.jitter:
195                                                 file.write('\tjitter\n')
196                                 else:
197                                         file.write('\tadaptive 1\n')
198                                         file.write('\tjitter\n')
199                         
200                         if lamp.shadow_method == 'NOSHADOW':
201                                 file.write('\tshadowless\n')    
202                         
203                         file.write('\tfade_distance %.6f\n' % lamp.distance)
204                         file.write('\tfade_power %d\n' % 1) # Could use blenders lamp quad?
205                         writeMatrix(matrix)
206                         
207                         file.write('}\n')
208         
209         def exportMeta(metas):
210                 
211                 # TODO - blenders 'motherball' naming is not supported.
212                 
213                 for ob in metas:
214                         meta = ob.data
215                         
216                         file.write('blob {\n')
217                         file.write('\t\tthreshold %.4g\n' % meta.threshold)
218                         
219                         try:
220                                 material= meta.materials[0] # lame! - blender cant do enything else.
221                         except:
222                                 material= None
223                         
224                         for elem in meta.elements:
225                                 
226                                 if elem.type not in ('BALL', 'ELLIPSOID'):
227                                         continue # Not supported
228                                 
229                                 loc = elem.location
230                                 
231                                 stiffness= elem.stiffness
232                                 if elem.negative:
233                                         stiffness = -stiffness
234                                 
235                                 if elem.type == 'BALL':
236                                         
237                                         file.write('\tsphere { <%.6g, %.6g, %.6g>, %.4g, %.4g ' % (loc.x, loc.y, loc.z, elem.radius, stiffness))
238                                         
239                                         # After this wecould do something simple like...
240                                         #       "pigment {Blue} }"
241                                         # except we'll write the color
242                                 
243                                 elif elem.type == 'ELLIPSOID':
244                                         # location is modified by scale
245                                         file.write('\tsphere { <%.6g, %.6g, %.6g>, %.4g, %.4g ' % (loc.x/elem.size_x, loc.y/elem.size_y, loc.z/elem.size_z, elem.radius, stiffness))
246                                         file.write(     'scale <%.6g, %.6g, %.6g> ' % (elem.size_x, elem.size_y, elem.size_z))
247                                 
248                                 if material:
249                                         diffuse_color = material.diffuse_color
250                                         
251                                         if material.transparency and material.transparency_method=='RAYTRACE':  trans = 1-material.raytrace_transparency.filter
252                                         else:                                                                                                                                   trans = 0.0
253                                         
254                                         file.write(
255                                                 'pigment {rgbft<%.3g, %.3g, %.3g, %.3g, %.3g>} finish {%s} }\n' % \
256                                                 (diffuse_color[0], diffuse_color[1], diffuse_color[2], 1-material.alpha, trans, materialNames[material.name])
257                                         )
258                                         
259                                 else:
260                                         file.write('pigment {rgb<1 1 1>} finish {%s} }\n' % DEF_MAT_NAME)               # Write the finish last.
261                         
262                         writeObjectMaterial(material)
263
264                         writeMatrix(ob.matrix)
265                         
266                         file.write('}\n')
267                 
268                 
269         
270         
271         def exportMeshs(sel):
272                 
273                 ob_num = 0
274                 
275                 for ob in sel:
276                         ob_num+= 1
277                         
278                         if ob.type in ('LAMP', 'CAMERA', 'EMPTY'):
279                                 continue
280                         
281                         me = ob.data
282                         me_materials= me.materials
283                         
284                         me = ob.create_render_mesh(scene)
285                         
286                         if not me:
287                                 continue
288                         
289                         if info_callback:
290                                 info_callback('Object %2.d of %2.d (%s)' % (ob_num, len(sel), ob.name))
291                         
292                         #if ob.type!='MESH':
293                         #       continue
294                         # me = ob.data
295                         
296                         matrix = ob.matrix
297                         try:    uv_layer = me.active_uv_texture.data
298                         except:uv_layer = None
299                                 
300                         try:    vcol_layer = me.active_vertex_color.data
301                         except:vcol_layer = None
302                         
303                         
304                         def regular_face(f):
305                                 fv = f.verts
306                                 if fv[3]== 0:
307                                         return fv[0], fv[1], fv[2]
308                                 return fv[0], fv[1], fv[2], fv[3]
309                         
310                         faces_verts = [regular_face(f) for f in me.faces]
311                         faces_normals = [tuple(f.normal) for f in me.faces]
312                         verts_normals = [tuple(v.normal) for v in me.verts]
313                         
314                         # quads incur an extra face
315                         quadCount = len([f for f in faces_verts if len(f)==4])
316                         
317                         file.write('mesh2 {\n')
318                         file.write('\tvertex_vectors {\n')
319                         file.write('\t\t%s' % (len(me.verts))) # vert count
320                         for v in me.verts:
321                                 file.write(',\n\t\t<%.6f, %.6f, %.6f>' % tuple(v.co)) # vert count
322                         file.write('\n  }\n')
323                         
324                         
325                         # Build unique Normal list
326                         uniqueNormals = {}
327                         for fi, f in enumerate(me.faces):
328                                 fv = faces_verts[fi]
329                                 # [-1] is a dummy index, use a list so we can modify in place
330                                 if f.smooth: # Use vertex normals
331                                         for v in fv:
332                                                 key = verts_normals[v]
333                                                 uniqueNormals[key] = [-1]
334                                 else: # Use face normal
335                                         key = faces_normals[fi]
336                                         uniqueNormals[key] = [-1]
337                         
338                         file.write('\tnormal_vectors {\n')
339                         file.write('\t\t%d' % len(uniqueNormals)) # vert count
340                         idx = 0
341                         for no, index in uniqueNormals.items():
342                                 file.write(',\n\t\t<%.6f, %.6f, %.6f>' % no) # vert count
343                                 index[0] = idx
344                                 idx +=1
345                         file.write('\n  }\n')
346                         
347                         
348                         # Vertex colours
349                         vertCols = {} # Use for material colours also.
350                         
351                         if uv_layer:
352                                 # Generate unique UV's
353                                 uniqueUVs = {}
354                                 
355                                 for fi, uv in enumerate(uv_layer):
356                                         
357                                         if len(faces_verts[fi])==4:
358                                                 uvs = uv.uv1, uv.uv2, uv.uv3, uv.uv4
359                                         else:
360                                                 uvs = uv.uv1, uv.uv2, uv.uv3
361                                         
362                                         for uv in uvs:
363                                                 uniqueUVs[tuple(uv)] = [-1]
364                                 
365                                 file.write('\tuv_vectors {\n')
366                                 #print unique_uvs
367                                 file.write('\t\t%s' % (len(uniqueUVs))) # vert count
368                                 idx = 0
369                                 for uv, index in uniqueUVs.items():
370                                         file.write(',\n\t\t<%.6f, %.6f>' % uv)
371                                         index[0] = idx
372                                         idx +=1
373                                 '''
374                                 else:
375                                         # Just add 1 dummy vector, no real UV's
376                                         file.write('\t\t1') # vert count
377                                         file.write(',\n\t\t<0.0, 0.0>')
378                                 '''
379                                 file.write('\n  }\n')
380                         
381                         
382                         if me.vertex_colors:
383                                 
384                                 for fi, f in enumerate(me.faces):
385                                         material_index = f.material_index
386                                         material = me_materials[material_index]
387                                         
388                                         if material and material.vertex_color_paint:
389                                                 
390                                                 col = vcol_layer[fi]
391                                                 
392                                                 if len(faces_verts[fi])==4:
393                                                         cols = col.color1, col.color2, col.color3, col.color4
394                                                 else:
395                                                         cols = col.color1, col.color2, col.color3
396                                                 
397                                                 for col in cols:                                        
398                                                         key = col[0], col[1], col[2], material_index # Material index!
399                                                         vertCols[key] = [-1]
400                                                 
401                                         else:
402                                                 if material:
403                                                         diffuse_color = tuple(material.diffuse_color)
404                                                         key = diffuse_color[0], diffuse_color[1], diffuse_color[2], material_index
405                                                         vertCols[key] = [-1]
406                                                 
407                         
408                         else:
409                                 # No vertex colours, so write material colours as vertex colours
410                                 for i, material in enumerate(me_materials):
411                                         
412                                         if material:
413                                                 diffuse_color = tuple(material.diffuse_color)
414                                                 key = diffuse_color[0], diffuse_color[1], diffuse_color[2], i # i == f.mat
415                                                 vertCols[key] = [-1]
416                                 
417                         
418                         # Vert Colours
419                         file.write('\ttexture_list {\n')
420                         file.write('\t\t%s' % (len(vertCols))) # vert count
421                         idx=0
422                         for col, index in vertCols.items():
423                                 
424                                 if me_materials:
425                                         material = me_materials[col[3]]
426                                         material_finish = materialNames[material.name]
427                                         
428                                         if material.transparency and material.transparency_method=='RAYTRACE':  trans = 1-material.raytrace_transparency.filter
429                                         else:                                                                                                                                   trans = 0.0
430                                         
431                                 else:
432                                         material_finish = DEF_MAT_NAME # not working properly,
433                                         trans = 0.0
434                                 
435                                 #print material.apl
436                                 file.write(     ',\n\t\ttexture { pigment {rgbft<%.3g, %.3g, %.3g, %.3g, %.3g>} finish {%s}}' %
437                                                         (col[0], col[1], col[2], 1-material.alpha, trans, material_finish) )
438                                 
439                                 index[0] = idx
440                                 idx+=1
441                         
442                         file.write( '\n  }\n' )
443                         
444                         # Face indicies
445                         file.write('\tface_indices {\n')
446                         file.write('\t\t%d' % (len(me.faces) + quadCount)) # faces count
447                         for fi, f in enumerate(me.faces):
448                                 fv = faces_verts[fi]
449                                 material_index= f.material_index
450                                 if len(fv) == 4:        indicies = (0,1,2), (0,2,3)
451                                 else:                           indicies = ((0,1,2),)
452                                 
453                                 if vcol_layer:
454                                         col = vcol_layer[fi]
455                                         
456                                         if len(fv) == 4:
457                                                 cols = col.color1, col.color2, col.color3, col.color4
458                                         else:
459                                                 cols = col.color1, col.color2, col.color3
460                                 
461                                 
462                                 if not me_materials or me_materials[material_index] == None: # No materials
463                                         for i1, i2, i3 in indicies:
464                                                 file.write(',\n\t\t<%d,%d,%d>' % (fv[i1], fv[i2], fv[i3])) # vert count
465                                 else:
466                                         material = me_materials[material_index]
467                                         for i1, i2, i3 in indicies:
468                                                 if me.vertex_colors and material.vertex_color_paint:
469                                                         # Colour per vertex - vertex colour
470                                                         
471                                                         col1 = cols[i1]
472                                                         col2 = cols[i2]
473                                                         col3 = cols[i3]
474                                                 
475                                                         ci1 = vertCols[col1[0], col1[1], col1[2], material_index][0]
476                                                         ci2 = vertCols[col2[0], col2[1], col2[2], material_index][0]
477                                                         ci3 = vertCols[col3[0], col3[1], col3[2], material_index][0]
478                                                 else:
479                                                         # Colour per material - flat material colour
480                                                         diffuse_color= material.diffuse_color
481                                                         ci1 = ci2 = ci3 = vertCols[diffuse_color[0], diffuse_color[1], diffuse_color[2], f.material_index][0]
482                                                 
483                                                 file.write(',\n\t\t<%d,%d,%d>, %d,%d,%d' % (fv[i1], fv[i2], fv[i3], ci1, ci2, ci3)) # vert count
484                                         
485                                         
486                                         
487                         file.write('\n  }\n')
488                         
489                         # normal_indices indicies
490                         file.write('\tnormal_indices {\n')
491                         file.write('\t\t%d' % (len(me.faces) + quadCount)) # faces count
492                         for fi, fv in enumerate(faces_verts):
493                                 
494                                 if len(fv) == 4:        indicies = (0,1,2), (0,2,3)
495                                 else:                           indicies = ((0,1,2),)
496                                 
497                                 for i1, i2, i3 in indicies:
498                                         if f.smooth:
499                                                 file.write(',\n\t\t<%d,%d,%d>' %\
500                                                 (uniqueNormals[verts_normals[fv[i1]]][0],\
501                                                  uniqueNormals[verts_normals[fv[i2]]][0],\
502                                                  uniqueNormals[verts_normals[fv[i3]]][0])) # vert count
503                                         else:
504                                                 idx = uniqueNormals[faces_normals[fi]][0]
505                                                 file.write(',\n\t\t<%d,%d,%d>' % (idx, idx, idx)) # vert count
506                                                 
507                         file.write('\n  }\n')
508                         
509                         if uv_layer:
510                                 file.write('\tuv_indices {\n')
511                                 file.write('\t\t%d' % (len(me.faces) + quadCount)) # faces count
512                                 for fi, fv in enumerate(faces_verts):
513                                         
514                                         if len(fv) == 4:        indicies = (0,1,2), (0,2,3)
515                                         else:                           indicies = ((0,1,2),)
516                                         
517                                         uv = uv_layer[fi]
518                                         if len(faces_verts[fi])==4:
519                                                 uvs = tuple(uv.uv1), tuple(uv.uv2), tuple(uv.uv3), tuple(uv.uv4)
520                                         else:
521                                                 uvs = tuple(uv.uv1), tuple(uv.uv2), tuple(uv.uv3)
522                                         
523                                         for i1, i2, i3 in indicies:
524                                                 file.write(',\n\t\t<%d,%d,%d>' %\
525                                                 (uniqueUVs[uvs[i1]][0],\
526                                                  uniqueUVs[uvs[i2]][0],\
527                                                  uniqueUVs[uvs[i2]][0])) # vert count
528                                 file.write('\n  }\n')
529                         
530                         if me.materials:
531                                 material = me.materials[0] # dodgy
532                                 writeObjectMaterial(material)
533                         
534                         writeMatrix(matrix)
535                         file.write('}\n')
536                         
537                         bpy.data.remove_mesh(me)
538         
539         def exportWorld(world):
540                 if not world:
541                         return
542                 
543                 mist = world.mist
544                 
545                 if mist.enabled:
546                         file.write('fog {\n')
547                         file.write('\tdistance %.6f\n' % mist.depth)
548                         file.write('\tcolor rgbt<%.3g, %.3g, %.3g, %.3g>\n' % (tuple(world.horizon_color) + (1-mist.intensity,)))
549                         #file.write('\tfog_offset %.6f\n' % mist.start)
550                         #file.write('\tfog_alt 5\n')
551                         #file.write('\tturbulence 0.2\n')
552                         #file.write('\tturb_depth 0.3\n')
553                         file.write('\tfog_type 1\n')
554                         file.write('}\n')
555         
556         def exportGlobalSettings(scene):
557                 
558                 file.write('global_settings {\n')
559
560                 if scene.pov_radio_enable:
561                         file.write('\tradiosity {\n')
562                         file.write("\t\tadc_bailout %.4g\n" % scene.pov_radio_adc_bailout)
563                         file.write("\t\talways_sample %d\n" % scene.pov_radio_always_sample)
564                         file.write("\t\tbrightness %.4g\n" % scene.pov_radio_brightness)
565                         file.write("\t\tcount %d\n" % scene.pov_radio_count)
566                         file.write("\t\terror_bound %.4g\n" % scene.pov_radio_error_bound)
567                         file.write("\t\tgray_threshold %.4g\n" % scene.pov_radio_gray_threshold)
568                         file.write("\t\tlow_error_factor %.4g\n" % scene.pov_radio_low_error_factor)
569                         file.write("\t\tmedia %d\n" % scene.pov_radio_media)
570                         file.write("\t\tminimum_reuse %.4g\n" % scene.pov_radio_minimum_reuse)
571                         file.write("\t\tnearest_count %d\n" % scene.pov_radio_nearest_count)
572                         file.write("\t\tnormal %d\n" % scene.pov_radio_normal)
573                         file.write("\t\trecursion_limit %d\n" % scene.pov_radio_recursion_limit)
574                         file.write('\t}\n')
575                 
576                 if world:
577                         file.write("\tambient_light rgb<%.3g, %.3g, %.3g>\n" % tuple(world.ambient_color))
578                 
579                 file.write('}\n')
580         
581         
582         # Convert all materials to strings we can access directly per vertex.
583         writeMaterial(None) # default material
584         
585         for material in bpy.data.materials:
586                 writeMaterial(material)
587         
588         exportCamera()
589         #exportMaterials()
590         sel = scene.objects
591         exportLamps([l for l in sel if l.type == 'LAMP'])
592         exportMeta([l for l in sel if l.type == 'META'])
593         exportMeshs(sel)
594         exportWorld(scene.world)
595         exportGlobalSettings(scene)
596         
597         file.close()
598
599
600 def write_pov_ini(filename_ini, filename_pov, filename_image):
601         scene = bpy.data.scenes[0]
602         render = scene.render_data
603         
604         x= int(render.resolution_x*render.resolution_percentage*0.01)
605         y= int(render.resolution_y*render.resolution_percentage*0.01)
606         
607         file = open(filename_ini, 'w')
608         
609         file.write('Input_File_Name="%s"\n' % filename_pov)
610         file.write('Output_File_Name="%s"\n' % filename_image)
611         
612         file.write('Width=%d\n' % x)
613         file.write('Height=%d\n' % y)
614         
615         # Needed for border render.
616         '''
617         file.write('Start_Column=%d\n' % part.x)
618         file.write('End_Column=%d\n' % (part.x+part.w))
619         
620         file.write('Start_Row=%d\n' % (part.y))
621         file.write('End_Row=%d\n' % (part.y+part.h))
622         '''
623         
624         file.write('Display=0\n')
625         file.write('Pause_When_Done=0\n')
626         file.write('Output_File_Type=C\n') # TGA, best progressive loading
627         file.write('Output_Alpha=1\n')
628         
629         if render.antialiasing: 
630                 aa_mapping = {'OVERSAMPLE_5':2, 'OVERSAMPLE_8':3, 'OVERSAMPLE_11':4, 'OVERSAMPLE_16':5} # method 1 assumed
631                 file.write('Antialias=1\n')
632                 file.write('Antialias_Depth=%d\n' % aa_mapping[render.antialiasing_samples])
633         else:
634                 file.write('Antialias=0\n')
635         
636         file.close()
637
638
639 class PovrayRender(bpy.types.RenderEngine):
640         __idname__ = 'POVRAY_RENDER'
641         __label__ = "Povray"
642         DELAY = 0.02
643         def _export(self, scene):
644                 import tempfile
645                 
646                 self.temp_file_in = tempfile.mktemp(suffix='.pov')
647                 self.temp_file_out = tempfile.mktemp(suffix='.tga')
648                 self.temp_file_ini = tempfile.mktemp(suffix='.ini')
649                 '''
650                 self.temp_file_in = '/test.pov'
651                 self.temp_file_out = '/test.tga'
652                 self.temp_file_ini = '/test.ini'
653                 '''
654                 
655                 def info_callback(txt):
656                         self.update_stats("", "POVRAY: " + txt)
657                         
658                 write_pov(self.temp_file_in, scene, info_callback)
659                 
660         def _render(self):
661                 
662                 try:            os.remove(self.temp_file_out) # so as not to load the old file
663                 except: pass
664                 
665                 write_pov_ini(self.temp_file_ini, self.temp_file_in, self.temp_file_out)
666                 
667                 print ("***-STARTING-***")
668                 
669                 pov_binary = "povray"
670                 
671                 if sys.platform=='win32':
672                         if bitness == 64:
673                                 pov_binary = "pvengine64"
674                         else:
675                                 pov_binary = "pvengine"
676                         
677                 if 1:
678                         self.process = subprocess.Popen([pov_binary, self.temp_file_ini]) # stdout=subprocess.PIPE, stderr=subprocess.PIPE
679                 else:
680                         # This works too but means we have to wait until its done
681                         os.system('%s %s' % (pov_binary, self.temp_file_ini))
682                 
683                 print ("***-DONE-***")
684         
685         def _cleanup(self):
686                 for f in (self.temp_file_in, self.temp_file_ini, self.temp_file_out):
687                         try:            os.remove(f)
688                         except: pass
689                 
690                 self.update_stats("", "")
691         
692         def render(self, scene):
693                 
694                 self.update_stats("", "POVRAY: Exporting data from Blender")
695                 self._export(scene)
696                 self.update_stats("", "POVRAY: Parsing File")
697                 self._render()
698                 
699                 r = scene.render_data
700                 
701                 # compute resolution
702                 x= int(r.resolution_x*r.resolution_percentage*0.01)
703                 y= int(r.resolution_y*r.resolution_percentage*0.01)
704                 
705                 
706                 
707                 # Wait for the file to be created
708                 while not os.path.exists(self.temp_file_out):
709                         if self.test_break():
710                                 try:            self.process.terminate()
711                                 except: pass
712                                 break
713                         
714                         if self.process.poll() != None:
715                                 self.update_stats("", "POVRAY: Failed")
716                                 break
717                         
718                         time.sleep(self.DELAY)
719                 
720                 if os.path.exists(self.temp_file_out):
721                         
722                         self.update_stats("", "POVRAY: Rendering")
723                         
724                         prev_size = -1
725                         
726                         def update_image():
727                                 result = self.begin_result(0, 0, x, y)
728                                 lay = result.layers[0]
729                                 # possible the image wont load early on.
730                                 try:            lay.load_from_file(self.temp_file_out)
731                                 except: pass
732                                 self.end_result(result)
733                         
734                         # Update while povray renders
735                         while True:
736                                 
737                                 # test if povray exists
738                                 if self.process.poll() != None:
739                                         update_image();
740                                         break
741                                 
742                                 # user exit
743                                 if self.test_break():
744                                         try:            self.process.terminate()
745                                         except: pass
746                                         
747                                         break
748                                 
749                                 # Would be nice to redirect the output
750                                 # stdout_value, stderr_value = self.process.communicate() # locks
751                                 
752                                 
753                                 # check if the file updated
754                                 new_size = os.path.getsize(self.temp_file_out)
755                                 
756                                 if new_size != prev_size:
757                                         update_image()
758                                         prev_size = new_size
759                                 
760                                 time.sleep(self.DELAY)
761                 
762                 self._cleanup()
763
764
765 bpy.types.register(PovrayRender)
766
767
768
769 # Use some of the existing buttons.
770 import buttons_scene
771 buttons_scene.SCENE_PT_render.COMPAT_ENGINES.add('POVRAY_RENDER')
772 buttons_scene.SCENE_PT_dimensions.COMPAT_ENGINES.add('POVRAY_RENDER')
773 buttons_scene.SCENE_PT_antialiasing.COMPAT_ENGINES.add('POVRAY_RENDER')
774 buttons_scene.SCENE_PT_output.COMPAT_ENGINES.add('POVRAY_RENDER')
775 del buttons_scene
776
777 # Use only a subset of the world panels
778 import buttons_world
779 buttons_world.WORLD_PT_preview.COMPAT_ENGINES.add('POVRAY_RENDER')
780 buttons_world.WORLD_PT_context_world.COMPAT_ENGINES.add('POVRAY_RENDER')
781 buttons_world.WORLD_PT_world.COMPAT_ENGINES.add('POVRAY_RENDER')
782 buttons_world.WORLD_PT_mist.COMPAT_ENGINES.add('POVRAY_RENDER')
783 del buttons_world
784
785 # Example of wrapping every class 'as is'
786 import buttons_material
787 for member in dir(buttons_material):
788         subclass = getattr(buttons_material, member)
789         try:            subclass.COMPAT_ENGINES.add('POVRAY_RENDER')
790         except: pass
791 del buttons_material
792
793 class RenderButtonsPanel(bpy.types.Panel):
794         __space_type__ = "PROPERTIES"
795         __region_type__ = "WINDOW"
796         __context__ = "scene"
797         # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
798         
799         def poll(self, context):
800                 rd = context.scene.render_data
801                 return (rd.use_game_engine==False) and (rd.engine in self.COMPAT_ENGINES)
802
803 # Radiosity panel, use in the scene for now.
804 class SCENE_PT_povray_radiosity(RenderButtonsPanel):
805         __label__ = "Radiosity"
806         COMPAT_ENGINES = set(['POVRAY_RENDER'])
807
808         def draw_header(self, context):
809                 layout = self.layout
810                 scene = context.scene
811                 layout.itemR(scene, "pov_radio_enable", text="")
812
813         def draw(self, context):
814                 layout = self.layout
815                 scene = context.scene
816                 rd = scene.render_data
817                 
818                 layout.active = scene.pov_radio_enable
819                 
820                 split = layout.split()
821                 
822                 col = split.column()
823                 
824                 col.itemR(scene, "pov_radio_count")
825                 col.itemR(scene, "pov_radio_recursion_limit")
826                 col.itemR(scene, "pov_radio_error_bound")
827                 
828                 col.itemR(scene, "pov_radio_display_advanced")
829                 
830                 if scene.pov_radio_display_advanced:
831                         col.itemR(scene, "pov_radio_adc_bailout")
832                         col.itemR(scene, "pov_radio_brightness")
833                         col.itemR(scene, "pov_radio_gray_threshold")
834                         col.itemR(scene, "pov_radio_low_error_factor")
835                         col.itemR(scene, "pov_radio_minimum_reuse")
836                         col.itemR(scene, "pov_radio_media")
837                         col.itemR(scene, "pov_radio_nearest_count")
838                         col.itemR(scene, "pov_radio_normal")
839                         col.itemR(scene, "pov_radio_always_sample")
840                 
841
842 bpy.types.register(SCENE_PT_povray_radiosity)
843
844
845 FloatProperty= bpy.types.Scene.FloatProperty
846 IntProperty= bpy.types.Scene.IntProperty
847 BoolProperty= bpy.types.Scene.BoolProperty
848
849 # Not a real pov option, just to know if we should write
850 BoolProperty(   attr="pov_radio_enable",
851                                 name="Enable Radiosity",
852                                 description="Enable povrays radiosity calculation.",
853                                 default= False)
854 BoolProperty(   attr="pov_radio_display_advanced",
855                                 name="Advanced Options",
856                                 description="Show advanced options.",
857                                 default= False)
858
859 # Real pov options
860 FloatProperty(  attr="pov_radio_adc_bailout",
861                                 name="ADC Bailout",
862                                 description="The adc_bailout for radiosity rays. Use adc_bailout = 0.01 / brightest_ambient_object for good results.",
863                                 min=0.0, max=1000.0, soft_min=0.0, soft_max=1.0, default= 0.01)
864
865 BoolProperty(   attr="pov_radio_always_sample",
866                                 name="Always Sample",
867                                 description="Only use the data from the pretrace step and not gather any new samples during the final radiosity pass..",
868                                 default= True)
869
870 FloatProperty(  attr="pov_radio_brightness",
871                                 name="Brightness",
872                                 description="Ammount objects are brightened before being returned upwards to the rest of the system.",
873                                 min=0.0, max=1000.0, soft_min=0.0, soft_max=10.0, default= 1.0)
874
875 IntProperty(    attr="pov_radio_count",
876                                 name="Ray Count",
877                                 description="number of rays that are sent out whenever a new radiosity value has to be calculated.",
878                                 min=1, max=1600, default= 35)
879
880 FloatProperty(  attr="pov_radio_error_bound",
881                                 name="Error Bound",
882                                 description="one of the two main speed/quality tuning values, lower values are more accurate.",
883                                 min=0.0, max=1000.0, soft_min=0.1, soft_max=10.0, default= 1.8)
884
885 FloatProperty(  attr="pov_radio_gray_threshold",
886                                 name="Gray Threshold",
887                                 description="one of the two main speed/quality tuning values, lower values are more accurate.",
888                                 min=0.0, max=1.0, default= 0.0)
889                                                                 
890 FloatProperty(  attr="pov_radio_low_error_factor",
891                                 name="Low Error Factor",
892                                 description="If you calculate just enough samples, but no more, you will get an image which has slightly blotchy lighting.",
893                                 min=0.0, max=1.0, default= 0.5)
894
895 # max_sample - not available yet
896
897 BoolProperty(   attr="pov_radio_media",
898                                 name="Use Media",
899                                 description="Radiosity estimation can be affected by media.",
900                                 default= False)
901
902 FloatProperty(  attr="pov_radio_minimum_reuse",
903                                 name="Minimum Reuse",
904                                 description="Fraction of the screen width which sets the minimum radius of reuse for each sample point (At values higher than 2% expect errors).",
905                                 min=0.0, max=1.0, soft_min=0.1, soft_max=0.1, default= 0.015)
906                                 
907 IntProperty(    attr="pov_radio_nearest_count",
908                                 name="Nearest Count",
909                                 description="Number of old ambient values blended together to create a new interpolated value.",
910                                 min=1, max=20, default= 5)
911                                 
912 BoolProperty(   attr="pov_radio_normal",
913                                 name="Normals",
914                                 description="Radiosity estimation can be affected by normals.",
915                                 default= False)
916
917 IntProperty(    attr="pov_radio_recursion_limit",
918                                 name="Recursion Limit",
919                                 description="how many recursion levels are used to calculate the diffuse inter-reflection.",
920                                 min=1, max=20, default= 3)