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