1 # SPDX-License-Identifier: GPL-2.0-or-later
5 """Write the POV file using this file's functions and some from other modules then render it."""
10 from sys
import platform
14 ) # maybe move to scenography.py and topology_*****_data.py respectively with smoke and matrix
17 import tempfile
# generate temporary files with random names
18 from bpy
.types
import Operator
19 from bpy
.utils
import register_class
, unregister_class
23 ) # for writing, importing and rendering directly POV Scene Description Language items
24 from . import scenography
# for atmosphere, environment, effects, lighting, camera
25 from . import shading
# for BI POV shaders emulation
26 from . import object_mesh_topology
# for mesh based geometry
27 from . import object_curve_topology
# for curves based geometry
29 # from . import object_primitives # for import and export of POV specific primitives
32 from .scenography
import image_format
, img_map
, img_map_transforms
, path_image
34 from .shading
import write_object_material_interior
35 from .object_primitives
import write_object_modifiers
38 def string_strip_hyphen(name
):
40 """Remove hyphen characters from a string to avoid POV errors."""
42 return name
.replace("-", "")
45 def safety(name
, ref_level_bound
):
46 """append suffix characters to names of various material declinations.
48 Material declinations are necessary to POV syntax and used in shading.py
49 by the pov_has_no_specular_maps function to create the finish map trick and
50 the suffixes avoid name collisions.
52 name -- the initial material name as a string
53 ref_level_bound -- the enum number of the ref_level_bound being written:
54 ref_level_bound=1 is for texture with No specular nor Mirror reflection
55 ref_level_bound=2 is for texture with translation of spec and mir levels
56 for when no map influences them
57 ref_level_bound=3 is for texture with Maximum Spec and Mirror
59 # All the try except clause below seems useless as each time
60 # prefix rewritten even after and outside of it what was the point?
61 # It may not even be any longer possible to feed no arg from Blender UI
63 # if name: # if int(name) > 0: # could be zero if no argument provided
64 # # and always triggered exception so is this similar ?
66 # except BaseException as e:
68 # print('An exception occurred: {}'.format(e))
69 # prefix = "" # rewritten below...
71 name
= string_strip_hyphen(name
)
72 if ref_level_bound
== 2:
74 # implicit else-if (no return yet)
75 if ref_level_bound
== 1:
76 return prefix
+ name
+ "0" # used for 0 of specular map
77 # implicit else-if (no return yet)
78 if ref_level_bound
== 3:
79 return prefix
+ name
+ "1" # used for 1 of specular map
82 # -------- end safety string name material
88 def is_renderable(ob
):
89 """test for objects flagged as hidden or boolean operands not to render"""
90 return not ob
.hide_render
and ob
not in csg_list
93 def renderable_objects():
94 """test for non hidden, non boolean operands objects to render"""
95 return [ob
for ob
in bpy
.data
.objects
if is_renderable(ob
)]
98 def no_renderable_objects():
99 """Boolean operands only. Not to render"""
100 return list(csg_list
)
106 user_dir
= bpy
.utils
.resource_path('USER')
107 preview_dir
= os
.path
.join(user_dir
, "preview")
109 # Make sure Preview directory exists and is empty
110 smoke_path
= os
.path
.join(preview_dir
, "smoke.df3")
114 # below properties not added to __init__ yet to avoid conflicts with material sss scale
115 # unless it would override then should be interfaced also in scene units property tab
117 # if scene.pov.sslt_enable:
118 # file.write(" mm_per_unit %s\n"%scene.pov.mm_per_unit)
119 # file.write(" subsurface {\n")
120 # file.write(" samples %s, %s\n"%(scene.pov.sslt_samples_max,scene.pov.sslt_samples_min))
121 # if scene.pov.sslt_radiosity:
122 # file.write(" radiosity on\n")
128 # def write_object_modifiers(ob, File):
129 # """Translate some object level POV statements from Blender UI
130 # to POV syntax and write to exported file """
132 # # Maybe return that string to be added instead of directly written.
135 # import .object_mesh_topology.write_object_csg_inside_vector
136 # write_object_csg_inside_vector(ob, file)
140 # File.write("\thollow\n")
141 # if ob.pov.double_illuminate:
142 # File.write("\tdouble_illuminate\n")
144 # File.write("\tsturm\n")
145 # if ob.pov.no_shadow:
146 # File.write("\tno_shadow\n")
147 # if ob.pov.no_image:
148 # File.write("\tno_image\n")
149 # if ob.pov.no_reflection:
150 # File.write("\tno_reflection\n")
151 # if ob.pov.no_radiosity:
152 # File.write("\tno_radiosity\n")
154 # File.write("\tinverse\n")
155 # if ob.pov.hierarchy:
156 # File.write("\thierarchy\n")
158 # # XXX, Commented definitions
160 # if scene.pov.photon_enable:
161 # File.write("photons {\n")
163 # File.write("target %.4g\n"%ob.pov.target_value)
164 # if ob.pov.refraction:
165 # File.write("refraction on\n")
166 # if ob.pov.reflection:
167 # File.write("reflection on\n")
168 # if ob.pov.pass_through:
169 # File.write("pass_through\n")
171 # if ob.pov.object_ior > 1:
172 # File.write("interior {\n")
173 # File.write("ior %.4g\n"%ob.pov.object_ior)
174 # if scene.pov.photon_enable and ob.pov.target and ob.pov.refraction and ob.pov.dispersion:
175 # File.write("ior %.4g\n"%ob.pov.dispersion_value)
176 # File.write("ior %s\n"%ob.pov.dispersion_samples)
177 # if scene.pov.photon_enable == False:
178 # File.write("caustics %.4g\n"%ob.pov.fake_caustics_power)
182 def write_pov(filename
, scene
=None, info_callback
=None):
183 """Main export process from Blender UI to POV syntax and write to exported file """
187 with
open(filename
, "w") as file:
190 scene
= bpy
.data
.scenes
[0]
192 render
= scene
.render
194 global_matrix
= mathutils
.Matrix
.Rotation(-pi
/ 2.0, 4, 'X')
195 comments
= scene
.pov
.comments_enable
and not scene
.pov
.tempfiles_enable
196 linebreaksinlists
= scene
.pov
.list_lf_enable
and not scene
.pov
.tempfiles_enable
197 feature_set
= bpy
.context
.preferences
.addons
[__package__
].preferences
.branch_feature_set_povray
198 using_uberpov
= feature_set
== 'uberpov'
199 pov_binary
= PovrayRender
._locate
_binary
()
202 print("Unofficial UberPOV feature set chosen in preferences")
204 print("Official POV-Ray 3.7 feature set chosen in preferences")
205 if 'uber' in pov_binary
:
206 print("The name of the binary suggests you are probably rendering with Uber POV engine")
208 print("The name of the binary suggests you are probably rendering with standard POV engine")
210 def set_tab(tabtype
, spaces
):
212 if tabtype
== 'NONE':
214 elif tabtype
== 'TAB':
216 elif tabtype
== 'SPACE':
217 tab_str
= spaces
* " "
220 tab
= set_tab(scene
.pov
.indentation_character
, scene
.pov
.indentation_spaces
)
221 if not scene
.pov
.tempfiles_enable
:
223 def tab_write(str_o
):
224 """Indent POV syntax from brackets levels and write to exported file """
226 brackets
= str_o
.count("{") - str_o
.count("}") + str_o
.count("[") - str_o
.count("]")
228 tab_level
= tab_level
+ brackets
230 print("Indentation Warning: tab_level = %s" % tab_level
)
233 file.write("%s" % tab
* tab_level
)
236 tab_level
= tab_level
+ brackets
240 def tab_write(str_o
):
241 """write directly to exported file if user checked autonamed temp files (faster)."""
245 def unique_name(name
, name_seq
):
246 """Increment any generated POV name that could get identical to avoid collisions"""
248 if name
not in name_seq
:
249 name
= string_strip_hyphen(name
)
254 while name
in name_seq
:
255 name
= "%s_%.3d" % (name_orig
, i
)
257 name
= string_strip_hyphen(name
)
260 def write_matrix(matrix
):
261 """Translate some transform matrix from Blender UI
262 to POV syntax and write to exported file """
264 "matrix <%.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f>\n"
281 material_names_dictionary
= {}
282 DEF_MAT_NAME
= "" # or "Default"?
284 # -----------------------------------------------------------------------------
286 def export_meta(metas
):
287 """write all POV blob primitives and Blender Metas to exported file """
288 # TODO - blenders 'motherball' naming is not supported.
290 if comments
and len(metas
) >= 1:
291 file.write("//--Blob objects--\n\n")
292 # Get groups of metaballs by blender name prefix.
295 for meta_ob
in metas
:
296 prefix
= meta_ob
.name
.split(".")[0]
297 if prefix
not in meta_group
:
298 meta_group
[prefix
] = meta_ob
# .data.threshold
301 for elem
in meta_ob
.data
.elements
302 if elem
.type in {'BALL', 'ELLIPSOID', 'CAPSULE', 'CUBE', 'PLANE'}
304 if prefix
in meta_elems
:
305 meta_elems
[prefix
].extend(elems
)
307 meta_elems
[prefix
] = elems
311 tab_write("\n//dummy sphere to represent empty meta location\n")
313 "sphere {<%.6g, %.6g, %.6g>,0 pigment{rgbt 1} "
314 "no_image no_reflection no_radiosity "
315 "photons{pass_through collect off} hollow}\n\n"
316 % (meta_ob
.location
.x
, meta_ob
.location
.y
, meta_ob
.location
.z
)
317 ) # meta_ob.name > povdataname)
320 for mg
, mob
in meta_group
.items():
321 if len(meta_elems
[mg
]) != 0:
322 tab_write("blob{threshold %.4g // %s \n" % (mob
.data
.threshold
, mg
))
323 for elems
in meta_elems
[mg
]:
326 stiffness
= elem
.stiffness
327 if elem
.use_negative
:
328 stiffness
= -stiffness
329 if elem
.type == 'BALL':
331 "sphere { <%.6g, %.6g, %.6g>, %.4g, %.4g "
332 % (loc
.x
, loc
.y
, loc
.z
, elem
.radius
, stiffness
)
334 write_matrix(global_matrix
@ elems
[1].matrix_world
)
336 elif elem
.type == 'ELLIPSOID':
338 "sphere{ <%.6g, %.6g, %.6g>,%.4g,%.4g "
348 "scale <%.6g, %.6g, %.6g>"
349 % (elem
.size_x
, elem
.size_y
, elem
.size_z
)
351 write_matrix(global_matrix
@ elems
[1].matrix_world
)
353 elif elem
.type == 'CAPSULE':
355 "cylinder{ <%.6g, %.6g, %.6g>,<%.6g, %.6g, %.6g>,%.4g,%.4g "
357 (loc
.x
- elem
.size_x
),
360 (loc
.x
+ elem
.size_x
),
367 # tab_write("scale <%.6g, %.6g, %.6g>" % (elem.size_x, elem.size_y, elem.size_z))
368 write_matrix(global_matrix
@ elems
[1].matrix_world
)
371 elif elem
.type == 'CUBE':
373 "cylinder { -x*8, +x*8,%.4g,%.4g translate<%.6g,%.6g,%.6g> scale <1/4,1,1> scale <%.6g, %.6g, %.6g>\n"
385 write_matrix(global_matrix
@ elems
[1].matrix_world
)
388 "cylinder { -y*8, +y*8,%.4g,%.4g translate<%.6g,%.6g,%.6g> scale <1,1/4,1> scale <%.6g, %.6g, %.6g>\n"
400 write_matrix(global_matrix
@ elems
[1].matrix_world
)
403 "cylinder { -z*8, +z*8,%.4g,%.4g translate<%.6g,%.6g,%.6g> scale <1,1,1/4> scale <%.6g, %.6g, %.6g>\n"
415 write_matrix(global_matrix
@ elems
[1].matrix_world
)
418 elif elem
.type == 'PLANE':
420 "cylinder { -x*8, +x*8,%.4g,%.4g translate<%.6g,%.6g,%.6g> scale <1/4,1,1> scale <%.6g, %.6g, %.6g>\n"
432 write_matrix(global_matrix
@ elems
[1].matrix_world
)
435 "cylinder { -y*8, +y*8,%.4g,%.4g translate<%.6g,%.6g,%.6g> scale <1,1/4,1> scale <%.6g, %.6g, %.6g>\n"
447 write_matrix(global_matrix
@ elems
[1].matrix_world
)
451 one_material
= elems
[1].data
.materials
[
453 ] # lame! - blender cant do enything else.
454 except BaseException
as e
:
456 print('An exception occurred: {}'.format(e
))
459 diffuse_color
= one_material
.diffuse_color
460 trans
= 1.0 - one_material
.pov
.alpha
462 one_material
.use_transparency
463 and one_material
.transparency_method
== 'RAYTRACE'
465 pov_filter
= one_material
.pov_raytrace_transparency
.filter * (
466 1.0 - one_material
.alpha
468 trans
= (1.0 - one_material
.pov
.alpha
) - pov_filter
471 material_finish
= material_names_dictionary
[one_material
.name
]
473 "pigment {srgbft<%.3g, %.3g, %.3g, %.3g, %.3g>} \n"
482 tab_write("finish{%s} " % safety(material_finish
, ref_level_bound
=2))
484 material_finish
= DEF_MAT_NAME
487 "pigment{srgbt<1,1,1,%.3g>} finish{%s} "
488 % (trans
, safety(material_finish
, ref_level_bound
=2))
491 write_object_material_interior(one_material
, mob
, tab_write
)
492 # write_object_material_interior(one_material, elems[1])
493 tab_write("radiosity{importance %3g}\n" % mob
.pov
.importance_value
)
494 tab_write("}\n\n") # End of Metaball block
499 # important because no elements will break parsing.
500 elements = [elem for elem in meta.elements if elem.type in {'BALL', 'ELLIPSOID'}]
503 tab_write("blob {\n")
504 tab_write("threshold %.4g\n" % meta.threshold)
505 importance = ob.pov.importance_value
508 material = meta.materials[0] # lame! - blender cant do enything else.
512 for elem in elements:
515 stiffness = elem.stiffness
516 if elem.use_negative:
517 stiffness = - stiffness
519 if elem.type == 'BALL':
521 tab_write("sphere { <%.6g, %.6g, %.6g>, %.4g, %.4g }\n" %
522 (loc.x, loc.y, loc.z, elem.radius, stiffness))
524 # After this wecould do something simple like...
526 # except we'll write the color
528 elif elem.type == 'ELLIPSOID':
529 # location is modified by scale
530 tab_write("sphere { <%.6g, %.6g, %.6g>, %.4g, %.4g }\n" %
531 (loc.x / elem.size_x,
534 elem.radius, stiffness))
535 tab_write("scale <%.6g, %.6g, %.6g> \n" %
536 (elem.size_x, elem.size_y, elem.size_z))
539 diffuse_color = material.diffuse_color
540 trans = 1.0 - material.pov.alpha
541 if material.use_transparency and material.transparency_method == 'RAYTRACE':
542 pov_filter = material.pov_raytrace_transparency.filter * (1.0 - material.alpha)
543 trans = (1.0 - material.pov.alpha) - pov_filter
547 material_finish = material_names_dictionary[material.name]
549 tab_write("pigment {srgbft<%.3g, %.3g, %.3g, %.3g, %.3g>} \n" %
550 (diffuse_color[0], diffuse_color[1], diffuse_color[2],
552 tab_write("finish {%s}\n" % safety(material_finish, ref_level_bound=2))
555 tab_write("pigment {srgb 1} \n")
556 # Write the finish last.
557 tab_write("finish {%s}\n" % (safety(DEF_MAT_NAME, ref_level_bound=2)))
559 write_object_material_interior(material, elems[1])
561 write_matrix(global_matrix @ ob.matrix_world)
562 # Importance for radiosity sampling added here
563 tab_write("radiosity { \n")
564 # importance > ob.pov.importance_value
565 tab_write("importance %3g \n" % importance)
568 tab_write("}\n") # End of Metaball block
570 if comments and len(metas) >= 1:
574 def export_global_settings(scene
):
575 """write all POV global settings to exported file """
576 # Imperial units warning
577 if scene
.unit_settings
.system
== "IMPERIAL":
578 print("Warning: Imperial units not supported")
580 tab_write("global_settings {\n")
581 tab_write("assumed_gamma 1.0\n")
582 tab_write("max_trace_level %d\n" % scene
.pov
.max_trace_level
)
584 if scene
.pov
.global_settings_advanced
:
585 if not scene
.pov
.radio_enable
:
586 file.write(" adc_bailout %.6f\n" % scene
.pov
.adc_bailout
)
587 file.write(" ambient_light <%.6f,%.6f,%.6f>\n" % scene
.pov
.ambient_light
[:])
588 file.write(" irid_wavelength <%.6f,%.6f,%.6f>\n" % scene
.pov
.irid_wavelength
[:])
589 file.write(" number_of_waves %s\n" % scene
.pov
.number_of_waves
)
590 file.write(" noise_generator %s\n" % scene
.pov
.noise_generator
)
591 if scene
.pov
.radio_enable
:
592 tab_write("radiosity {\n")
593 tab_write("adc_bailout %.4g\n" % scene
.pov
.radio_adc_bailout
)
594 tab_write("brightness %.4g\n" % scene
.pov
.radio_brightness
)
595 tab_write("count %d\n" % scene
.pov
.radio_count
)
596 tab_write("error_bound %.4g\n" % scene
.pov
.radio_error_bound
)
597 tab_write("gray_threshold %.4g\n" % scene
.pov
.radio_gray_threshold
)
598 tab_write("low_error_factor %.4g\n" % scene
.pov
.radio_low_error_factor
)
599 tab_write("maximum_reuse %.4g\n" % scene
.pov
.radio_maximum_reuse
)
600 tab_write("minimum_reuse %.4g\n" % scene
.pov
.radio_minimum_reuse
)
601 tab_write("nearest_count %d\n" % scene
.pov
.radio_nearest_count
)
602 tab_write("pretrace_start %.3g\n" % scene
.pov
.radio_pretrace_start
)
603 tab_write("pretrace_end %.3g\n" % scene
.pov
.radio_pretrace_end
)
604 tab_write("recursion_limit %d\n" % scene
.pov
.radio_recursion_limit
)
605 tab_write("always_sample %d\n" % scene
.pov
.radio_always_sample
)
606 tab_write("normal %d\n" % scene
.pov
.radio_normal
)
607 tab_write("media %d\n" % scene
.pov
.radio_media
)
608 tab_write("subsurface %d\n" % scene
.pov
.radio_subsurface
)
613 for material
in bpy
.data
.materials
:
614 if material
.pov_subsurface_scattering
.use
and once_sss
:
615 # In pov, the scale has reversed influence compared to blender. these number
616 # should correct that
618 "mm_per_unit %.6f\n" % (material
.pov_subsurface_scattering
.scale
* 1000.0)
620 # 1000 rather than scale * (-100.0) + 15.0))
622 # In POV-Ray, the scale factor for all subsurface shaders needs to be the same
624 # formerly sslt_samples were multiplied by 100 instead of 10
625 sslt_samples
= (11 - material
.pov_subsurface_scattering
.error_threshold
) * 10
627 tab_write("subsurface { samples %d, %d }\n" % (sslt_samples
, sslt_samples
/ 10))
630 if world
and once_ambient
:
631 tab_write("ambient_light rgb<%.3g, %.3g, %.3g>\n" % world
.pov
.ambient_color
[:])
634 if scene
.pov
.photon_enable
:
635 if once_photons
and (
636 material
.pov
.refraction_type
== "2" or material
.pov
.photons_reflection
638 tab_write("photons {\n")
639 tab_write("spacing %.6f\n" % scene
.pov
.photon_spacing
)
640 tab_write("max_trace_level %d\n" % scene
.pov
.photon_max_trace_level
)
641 tab_write("adc_bailout %.3g\n" % scene
.pov
.photon_adc_bailout
)
644 % (scene
.pov
.photon_gather_min
, scene
.pov
.photon_gather_max
)
646 if scene
.pov
.photon_map_file_save_load
in {'save'}:
647 ph_file_name
= 'Photon_map_file.ph'
648 if scene
.pov
.photon_map_file
!= '':
649 ph_file_name
= scene
.pov
.photon_map_file
+ '.ph'
650 ph_file_dir
= tempfile
.gettempdir()
651 path
= bpy
.path
.abspath(scene
.pov
.photon_map_dir
)
652 if os
.path
.exists(path
):
654 full_file_name
= os
.path
.join(ph_file_dir
, ph_file_name
)
655 tab_write('save_file "%s"\n' % full_file_name
)
656 scene
.pov
.photon_map_file
= full_file_name
657 if scene
.pov
.photon_map_file_save_load
in {'load'}:
658 full_file_name
= bpy
.path
.abspath(scene
.pov
.photon_map_file
)
659 if os
.path
.exists(full_file_name
):
660 tab_write('load_file "%s"\n' % full_file_name
)
666 # sel = renderable_objects() #removed for booleans
669 "//----------------------------------------------\n"
670 "//--Exported with POV-Ray exporter for Blender--\n"
671 "//----------------------------------------------\n\n"
673 file.write("#version 3.7;\n") # Switch below as soon as 3.8 beta gets easy linked
674 # file.write("#version 3.8;\n")
676 "#declare Default_texture = texture{pigment {rgb 0.8} " "finish {brilliance 3.8} }\n\n"
679 file.write("\n//--Global settings--\n\n")
681 export_global_settings(scene
)
684 file.write("\n//--Custom Code--\n\n")
685 scripting
.export_custom_code(file)
688 file.write("\n//--Patterns Definitions--\n\n")
689 local_pattern_names
= []
690 for texture
in bpy
.data
.textures
: # ok?
691 if texture
.users
> 0:
692 current_pat_name
= string_strip_hyphen(bpy
.path
.clean_name(texture
.name
))
693 # string_strip_hyphen(patternNames[texture.name]) #maybe instead of the above
694 local_pattern_names
.append(current_pat_name
)
695 # use above list to prevent writing texture instances several times and assign in mats?
697 texture
.type not in {'NONE', 'IMAGE'} and texture
.pov
.tex_pattern_type
== 'emulator'
698 ) or (texture
.type in {'NONE', 'IMAGE'} and texture
.pov
.tex_pattern_type
!= 'emulator'):
699 file.write("\n#declare PAT_%s = \n" % current_pat_name
)
700 file.write(shading
.export_pattern(texture
))
703 file.write("\n//--Background--\n\n")
705 scenography
.export_world(scene
.world
, scene
, global_matrix
, tab_write
)
708 file.write("\n//--Cameras--\n\n")
710 scenography
.export_camera(scene
, global_matrix
, render
, tab_write
)
713 file.write("\n//--Lamps--\n\n")
715 for ob
in bpy
.data
.objects
:
716 if ob
.type == 'MESH':
717 for mod
in ob
.modifiers
:
718 if mod
.type == 'BOOLEAN' and mod
.object not in csg_list
:
719 csg_list
.append(mod
.object)
722 sel
= no_renderable_objects()
723 # export non rendered boolean objects operands
724 object_mesh_topology
.export_meshes(
732 write_object_modifiers
,
733 material_names_dictionary
,
734 write_object_material_interior
,
735 scenography
.exported_lights_count
,
754 sel
= renderable_objects()
756 scenography
.export_lights(
757 [L
for L
in sel
if (L
.type == 'LIGHT' and L
.pov
.object_as
!= 'RAINBOW')],
766 file.write("\n//--Rainbows--\n\n")
767 scenography
.export_rainbows(
768 [L
for L
in sel
if (L
.type == 'LIGHT' and L
.pov
.object_as
== 'RAINBOW')],
777 file.write("\n//--Special Curves--\n\n")
779 if c
.is_modified(scene
, 'RENDER'):
780 continue # don't export as pov curves objects with modifiers, but as mesh
781 # Implicit else-if (as not skipped by previous "continue")
782 if c
.type == 'CURVE' and (c
.pov
.curveshape
in {'lathe', 'sphere_sweep', 'loft', 'birail'}):
783 object_curve_topology
.export_curves(file, c
, string_strip_hyphen
, tab_write
)
786 file.write("\n//--Material Definitions--\n\n")
787 # write a default pigment for objects with no material (comment out to show black)
788 file.write("#default{ pigment{ color srgb 0.8 }}\n")
789 # Convert all materials to strings we can access directly per vertex.
791 shading
.write_material(
798 material_names_dictionary
,
801 for material
in bpy
.data
.materials
:
802 if material
.users
> 0:
803 r
, g
, b
, a
= material
.diffuse_color
[:]
804 pigment_color
= "pigment {rgbt <%.4g,%.4g,%.4g,%.4g>}" % (r
, g
, b
, 1 - a
)
805 if material
.pov
.material_use_nodes
:
806 # Also make here other pigment_color fallback using BSDF node main color ?
807 ntree
= material
.node_tree
808 pov_mat_name
= string_strip_hyphen(bpy
.path
.clean_name(material
.name
))
809 if len(ntree
.nodes
) == 0:
810 file.write('#declare %s = texture {%s}\n' % (pov_mat_name
, pigment_color
))
812 shading
.write_nodes(pov_mat_name
, ntree
, file)
814 for node
in ntree
.nodes
:
816 if node
.bl_idname
== "PovrayOutputNode":
817 if node
.inputs
["Texture"].is_linked
:
818 for link
in ntree
.links
:
819 if link
.to_node
.bl_idname
== "PovrayOutputNode":
822 bpy
.path
.clean_name(link
.from_node
.name
)
824 + "_%s" % pov_mat_name
828 '#declare %s = texture {%s}\n' % (pov_mat_name
, pigment_color
)
831 shading
.write_material(
838 material_names_dictionary
,
841 # attributes are all the variables needed by the other python file...
845 export_meta([m
for m
in sel
if m
.type == 'META'])
848 file.write("//--Mesh objects--\n")
850 # tbefore = time.time()
851 object_mesh_topology
.export_meshes(
859 write_object_modifiers
,
860 material_names_dictionary
,
861 write_object_material_interior
,
862 scenography
.exported_lights_count
,
879 # totime = time.time() - tbefore
880 # print("export_meshes took" + str(totime))
882 # What follow used to happen here:
884 # scenography.export_world(scene.world, scene, global_matrix, tab_write)
885 # export_global_settings(scene)
886 # MR:..and the order was important for implementing pov 3.7 baking
887 # (mesh camera) comment for the record
888 # CR: Baking should be a special case than. If "baking", than we could change the order.
893 def write_pov_ini(filename_ini
, filename_log
, filename_pov
, filename_image
):
894 """Write ini file."""
895 feature_set
= bpy
.context
.preferences
.addons
[__package__
].preferences
.branch_feature_set_povray
896 using_uberpov
= feature_set
== 'uberpov'
897 # scene = bpy.data.scenes[0]
898 scene
= bpy
.context
.scene
899 render
= scene
.render
901 x
= int(render
.resolution_x
* render
.resolution_percentage
* 0.01)
902 y
= int(render
.resolution_y
* render
.resolution_percentage
* 0.01)
904 with
open(filename_ini
, "w") as file:
905 file.write("Version=3.7\n")
906 # write povray text stream to temporary file of same name with _log suffix
907 # file.write("All_File='%s'\n" % filename_log)
908 # DEBUG.OUT log if none specified:
909 file.write("All_File=1\n")
911 file.write("Input_File_Name='%s'\n" % filename_pov
)
912 file.write("Output_File_Name='%s'\n" % filename_image
)
914 file.write("Width=%d\n" % x
)
915 file.write("Height=%d\n" % y
)
918 if render
.use_border
:
919 file.write("Start_Column=%4g\n" % render
.border_min_x
)
920 file.write("End_Column=%4g\n" % render
.border_max_x
)
922 file.write("Start_Row=%4g\n" % (1.0 - render
.border_max_y
))
923 file.write("End_Row=%4g\n" % (1.0 - render
.border_min_y
))
925 file.write("Bounding_Method=2\n") # The new automatic BSP is faster in most scenes
927 # Activated (turn this back off when better live exchange is done between the two programs
929 file.write("Display=1\n")
930 file.write("Pause_When_Done=0\n")
931 # PNG, with POV-Ray 3.7, can show background color with alpha. In the long run using the
932 # POV-Ray interactive preview like bishop 3D could solve the preview for all formats.
933 file.write("Output_File_Type=N\n")
934 # file.write("Output_File_Type=T\n") # TGA, best progressive loading
935 file.write("Output_Alpha=1\n")
937 if scene
.pov
.antialias_enable
:
938 # method 2 (recursive) with higher max subdiv forced because no mipmapping in POV-Ray
939 # needs higher sampling.
940 # aa_mapping = {"5": 2, "8": 3, "11": 4, "16": 5}
942 method
= {"0": 1, "1": 2, "2": 3}
944 method
= {"0": 1, "1": 2, "2": 2}
945 file.write("Antialias=on\n")
946 file.write("Antialias_Depth=%d\n" % scene
.pov
.antialias_depth
)
947 file.write("Antialias_Threshold=%.3g\n" % scene
.pov
.antialias_threshold
)
948 if using_uberpov
and scene
.pov
.antialias_method
== '2':
949 file.write("Sampling_Method=%s\n" % method
[scene
.pov
.antialias_method
])
950 file.write("Antialias_Confidence=%.3g\n" % scene
.pov
.antialias_confidence
)
952 file.write("Sampling_Method=%s\n" % method
[scene
.pov
.antialias_method
])
953 file.write("Antialias_Gamma=%.3g\n" % scene
.pov
.antialias_gamma
)
954 if scene
.pov
.jitter_enable
:
955 file.write("Jitter=on\n")
956 file.write("Jitter_Amount=%3g\n" % scene
.pov
.jitter_amount
)
958 file.write("Jitter=off\n") # prevent animation flicker
961 file.write("Antialias=off\n")
966 class PovrayRender(bpy
.types
.RenderEngine
):
967 """Define the external renderer"""
969 bl_idname
= 'POVRAY_RENDER'
970 bl_label
= "Persitence Of Vision"
971 bl_use_shading_nodes_custom
= False
975 def _locate_binary():
976 """Identify POV engine"""
977 addon_prefs
= bpy
.context
.preferences
.addons
[__package__
].preferences
979 # Use the system preference if its set.
980 pov_binary
= addon_prefs
.filepath_povray
982 if os
.path
.exists(pov_binary
):
984 # Implicit else, as here return was still not triggered:
985 print("User Preferences path to povray %r NOT FOUND, checking $PATH" % pov_binary
)
988 # assume if there is a 64bit binary that the user has a 64bit capable OS
989 if platform
.startswith('win'):
992 win_reg_key
= winreg
.OpenKey(
993 winreg
.HKEY_CURRENT_USER
, "Software\\POV-Ray\\v3.7\\Windows"
995 win_home
= winreg
.QueryValueEx(win_reg_key
, "Home")[0]
997 # First try 64bits UberPOV
998 pov_binary
= os
.path
.join(win_home
, "bin", "uberpov64.exe")
999 if os
.path
.exists(pov_binary
):
1002 # Then try 64bits POV
1003 pov_binary
= os
.path
.join(win_home
, "bin", "pvengine64.exe")
1004 if os
.path
.exists(pov_binary
):
1007 # search the path all os's
1008 pov_binary_default
= "povray"
1010 os_path_ls
= os
.getenv("PATH").split(':') + [""]
1012 for dir_name
in os_path_ls
:
1013 pov_binary
= os
.path
.join(dir_name
, pov_binary_default
)
1014 if os
.path
.exists(pov_binary
):
1018 def _export(self
, depsgraph
, pov_path
, image_render_path
):
1019 """gather all necessary output files paths user defined and auto generated and export there"""
1021 scene
= bpy
.context
.scene
1022 if scene
.pov
.tempfiles_enable
:
1023 self
._temp
_file
_in
= tempfile
.NamedTemporaryFile(suffix
=".pov", delete
=False).name
1024 # PNG with POV 3.7, can show the background color with alpha. In the long run using the
1025 # POV-Ray interactive preview like bishop 3D could solve the preview for all formats.
1026 self
._temp
_file
_out
= tempfile
.NamedTemporaryFile(suffix
=".png", delete
=False).name
1027 # self._temp_file_out = tempfile.NamedTemporaryFile(suffix=".tga", delete=False).name
1028 self
._temp
_file
_ini
= tempfile
.NamedTemporaryFile(suffix
=".ini", delete
=False).name
1029 self
._temp
_file
_log
= os
.path
.join(tempfile
.gettempdir(), "alltext.out")
1031 self
._temp
_file
_in
= pov_path
+ ".pov"
1032 # PNG with POV 3.7, can show the background color with alpha. In the long run using the
1033 # POV-Ray interactive preview like bishop 3D could solve the preview for all formats.
1034 self
._temp
_file
_out
= image_render_path
+ ".png"
1035 # self._temp_file_out = image_render_path + ".tga"
1036 self
._temp
_file
_ini
= pov_path
+ ".ini"
1037 log_path
= bpy
.path
.abspath(scene
.pov
.scene_path
).replace('\\', '/')
1038 self
._temp
_file
_log
= os
.path
.join(log_path
, "alltext.out")
1040 self._temp_file_in = "/test.pov"
1041 # PNG with POV 3.7, can show the background color with alpha. In the long run using the
1042 # POV-Ray interactive preview like bishop 3D could solve the preview for all formats.
1043 self._temp_file_out = "/test.png"
1044 #self._temp_file_out = "/test.tga"
1045 self._temp_file_ini = "/test.ini"
1047 if scene
.pov
.text_block
== "":
1049 def info_callback(txt
):
1050 self
.update_stats("", "POV-Ray 3.7: " + txt
)
1052 # os.makedirs(user_dir, exist_ok=True) # handled with previews
1053 os
.makedirs(preview_dir
, exist_ok
=True)
1055 write_pov(self
._temp
_file
_in
, scene
, info_callback
)
1059 def _render(self
, depsgraph
):
1060 """Export necessary files and render image."""
1061 scene
= bpy
.context
.scene
1063 os
.remove(self
._temp
_file
_out
) # so as not to load the old file
1067 pov_binary
= PovrayRender
._locate
_binary
()
1069 print("POV-Ray 3.7: could not execute povray, possibly POV-Ray isn't installed")
1073 self
._temp
_file
_ini
, self
._temp
_file
_log
, self
._temp
_file
_in
, self
._temp
_file
_out
1076 print("***-STARTING-***")
1080 if scene
.pov
.command_line_switches
!= "":
1081 for new_arg
in scene
.pov
.command_line_switches
.split(" "):
1082 extra_args
.append(new_arg
)
1084 self
._is
_windows
= False
1085 if platform
.startswith('win'):
1086 self
._is
_windows
= True
1087 if "/EXIT" not in extra_args
and not scene
.pov
.pov_editor
:
1088 extra_args
.append("/EXIT")
1090 # added -d option to prevent render window popup which leads to segfault on linux
1091 extra_args
.append("-d")
1095 self
._process
= subprocess
.Popen(
1096 [pov_binary
, self
._temp
_file
_ini
] + extra_args
,
1097 stdout
=subprocess
.PIPE
,
1098 stderr
=subprocess
.STDOUT
,
1102 print("POV-Ray 3.7: could not execute '%s'" % pov_binary
)
1105 traceback
.print_exc()
1106 print("***-DONE-***")
1110 print("Engine ready!...")
1111 print("Command line arguments passed: " + str(extra_args
))
1114 # Now that we have a valid process
1117 """Delete temp files and unpacked ones"""
1118 for f
in (self
._temp
_file
_in
, self
._temp
_file
_ini
, self
._temp
_file
_out
):
1124 # Wait a bit before retrying file might be still in use by Blender,
1125 # and Windows does not know how to delete a file in use!
1126 time
.sleep(self
.DELAY
)
1127 for i
in unpacked_images
:
1133 # Wait a bit before retrying file might be still in use by Blender,
1134 # and Windows does not know how to delete a file in use!
1135 time
.sleep(self
.DELAY
)
1137 def render(self
, depsgraph
):
1138 """Export necessary files from text editor and render image."""
1140 scene
= bpy
.context
.scene
1142 x
= int(r
.resolution_x
* r
.resolution_percentage
* 0.01)
1143 y
= int(r
.resolution_y
* r
.resolution_percentage
* 0.01)
1144 print("***INITIALIZING***")
1146 # This makes some tests on the render, returning True if all goes good, and False if
1147 # it was finished one way or the other.
1148 # It also pauses the script (time.sleep())
1150 time
.sleep(self
.DELAY
)
1152 # User interrupts the rendering
1153 if self
.test_break():
1155 self
._process
.terminate()
1156 print("***POV INTERRUPTED***")
1161 poll_result
= self
._process
.poll()
1162 except AttributeError:
1163 print("***CHECK POV PATH IN PREFERENCES***")
1165 # POV process is finisehd, one way or the other
1166 if poll_result
is not None:
1168 print("***POV PROCESS FAILED : %s ***" % poll_result
)
1169 self
.update_stats("", "POV-Ray 3.7: Failed")
1174 if bpy
.context
.scene
.pov
.text_block
!= "":
1175 if scene
.pov
.tempfiles_enable
:
1176 self
._temp
_file
_in
= tempfile
.NamedTemporaryFile(suffix
=".pov", delete
=False).name
1177 self
._temp
_file
_out
= tempfile
.NamedTemporaryFile(suffix
=".png", delete
=False).name
1178 # self._temp_file_out = tempfile.NamedTemporaryFile(suffix=".tga", delete=False).name
1179 self
._temp
_file
_ini
= tempfile
.NamedTemporaryFile(suffix
=".ini", delete
=False).name
1180 self
._temp
_file
_log
= os
.path
.join(tempfile
.gettempdir(), "alltext.out")
1182 pov_path
= scene
.pov
.text_block
1183 image_render_path
= os
.path
.splitext(pov_path
)[0]
1184 self
._temp
_file
_out
= os
.path
.join(preview_dir
, image_render_path
)
1185 self
._temp
_file
_in
= os
.path
.join(preview_dir
, pov_path
)
1186 self
._temp
_file
_ini
= os
.path
.join(
1187 preview_dir
, (os
.path
.splitext(self
._temp
_file
_in
)[0] + ".INI")
1189 self
._temp
_file
_log
= os
.path
.join(preview_dir
, "alltext.out")
1193 os.remove(self._temp_file_in) # so as not to load the old file
1197 print(scene
.pov
.text_block
)
1198 text
= bpy
.data
.texts
[scene
.pov
.text_block
]
1199 with
open(self
._temp
_file
_in
, "w") as file:
1200 # Why are the newlines needed?
1202 file.write(text
.as_string())
1207 # has to be called to update the frame on exporting animations
1208 scene
.frame_set(scene
.frame_current
)
1210 pov_binary
= PovrayRender
._locate
_binary
()
1213 print("POV-Ray 3.7: could not execute povray, possibly POV-Ray isn't installed")
1216 # start ini UI options export
1217 self
.update_stats("", "POV-Ray 3.7: Exporting ini options from Blender")
1220 self
._temp
_file
_ini
,
1221 self
._temp
_file
_log
,
1223 self
._temp
_file
_out
,
1226 print("***-STARTING-***")
1230 if scene
.pov
.command_line_switches
!= "":
1231 for new_arg
in scene
.pov
.command_line_switches
.split(" "):
1232 extra_args
.append(new_arg
)
1234 if platform
.startswith('win'):
1235 if "/EXIT" not in extra_args
and not scene
.pov
.pov_editor
:
1236 extra_args
.append("/EXIT")
1238 # added -d option to prevent render window popup which leads to segfault on linux
1239 extra_args
.append("-d")
1243 if scene
.pov
.sdl_window_enable
and not platform
.startswith(
1245 ): # segfault on linux == False !!!
1246 env
= {'POV_DISPLAY_SCALED': 'off'}
1247 env
.update(os
.environ
)
1248 self
._process
= subprocess
.Popen(
1249 [pov_binary
, self
._temp
_file
_ini
],
1250 stdout
=subprocess
.PIPE
,
1251 stderr
=subprocess
.STDOUT
,
1255 self
._process
= subprocess
.Popen(
1256 [pov_binary
, self
._temp
_file
_ini
] + extra_args
,
1257 stdout
=subprocess
.PIPE
,
1258 stderr
=subprocess
.STDOUT
,
1262 print("POV-Ray 3.7: could not execute '%s'" % pov_binary
)
1265 traceback
.print_exc()
1266 print("***-DONE-***")
1270 print("Engine ready!...")
1271 print("Command line arguments passed: " + str(extra_args
))
1273 self
.update_stats("", "POV-Ray 3.7: Parsing File")
1275 # Indented in main function now so repeated here but still not working
1276 # to bring back render result to its buffer
1278 if os
.path
.exists(self
._temp
_file
_out
):
1279 xmin
= int(r
.border_min_x
* x
)
1280 ymin
= int(r
.border_min_y
* y
)
1281 xmax
= int(r
.border_max_x
* x
)
1282 ymax
= int(r
.border_max_y
* y
)
1283 result
= self
.begin_result(0, 0, x
, y
)
1284 lay
= result
.layers
[0]
1286 time
.sleep(self
.DELAY
)
1288 lay
.load_from_file(self
._temp
_file
_out
)
1289 except RuntimeError:
1290 print("***POV ERROR WHILE READING OUTPUT FILE***")
1291 self
.end_result(result
)
1292 # print(self._temp_file_log) #bring the pov log to blender console with proper path?
1295 ) as f
: # The with keyword automatically closes the file when you are done
1298 self
.update_stats("", "")
1300 if scene
.pov
.tempfiles_enable
or scene
.pov
.deletefiles_enable
:
1305 # if r.image_settings.file_format == 'OPENEXR':
1307 # render.image_settings.color_mode = 'RGBA'
1310 # r.image_settings.file_format = 'TARGA'
1311 # r.image_settings.color_mode = 'RGBA'
1313 blend_scene_name
= bpy
.data
.filepath
.split(os
.path
.sep
)[-1].split(".")[0]
1316 image_render_path
= ""
1318 # has to be called to update the frame on exporting animations
1319 scene
.frame_set(scene
.frame_current
)
1321 if not scene
.pov
.tempfiles_enable
:
1324 pov_path
= bpy
.path
.abspath(scene
.pov
.scene_path
).replace('\\', '/')
1326 if bpy
.data
.is_saved
:
1327 pov_path
= bpy
.path
.abspath("//")
1329 pov_path
= tempfile
.gettempdir()
1330 elif pov_path
.endswith("/"):
1332 pov_path
= bpy
.path
.abspath("//")
1334 pov_path
= bpy
.path
.abspath(scene
.pov
.scene_path
)
1336 if not os
.path
.exists(pov_path
):
1338 os
.makedirs(pov_path
)
1339 except BaseException
as e
:
1341 print('An exception occurred: {}'.format(e
))
1344 traceback
.print_exc()
1346 print("POV-Ray 3.7: Cannot create scenes directory: %r" % pov_path
)
1348 "", "POV-Ray 3.7: Cannot create scenes directory %r" % pov_path
1354 # Bug in POV-Ray RC3
1355 image_render_path = bpy.path.abspath(scene.pov.renderimage_path).replace('\\','/')
1356 if image_render_path == "":
1357 if bpy.data.is_saved:
1358 image_render_path = bpy.path.abspath("//")
1360 image_render_path = tempfile.gettempdir()
1361 #print("Path: " + image_render_path)
1362 elif path.endswith("/"):
1363 if image_render_path == "/":
1364 image_render_path = bpy.path.abspath("//")
1366 image_render_path = bpy.path.abspath(scene.pov.)
1367 if not os.path.exists(path):
1368 print("POV-Ray 3.7: Cannot find render image directory")
1369 self.update_stats("", "POV-Ray 3.7: Cannot find render image directory")
1375 if scene
.pov
.scene_name
== "":
1376 if blend_scene_name
!= "":
1377 pov_scene_name
= blend_scene_name
1379 pov_scene_name
= "untitled"
1381 pov_scene_name
= scene
.pov
.scene_name
1382 if os
.path
.isfile(pov_scene_name
):
1383 pov_scene_name
= os
.path
.basename(pov_scene_name
)
1384 pov_scene_name
= pov_scene_name
.split('/')[-1].split('\\')[-1]
1385 if not pov_scene_name
:
1386 print("POV-Ray 3.7: Invalid scene name")
1387 self
.update_stats("", "POV-Ray 3.7: Invalid scene name")
1390 pov_scene_name
= os
.path
.splitext(pov_scene_name
)[0]
1392 print("Scene name: " + pov_scene_name
)
1393 print("Export path: " + pov_path
)
1394 pov_path
= os
.path
.join(pov_path
, pov_scene_name
)
1395 pov_path
= os
.path
.realpath(pov_path
)
1397 image_render_path
= pov_path
1398 # print("Render Image path: " + image_render_path)
1401 self
.update_stats("", "POV-Ray 3.7: Exporting data from Blender")
1402 self
._export
(depsgraph
, pov_path
, image_render_path
)
1403 self
.update_stats("", "POV-Ray 3.7: Parsing File")
1405 if not self
._render
(depsgraph
):
1406 self
.update_stats("", "POV-Ray 3.7: Not found")
1410 # compute resolution
1411 # x = int(r.resolution_x * r.resolution_percentage * 0.01)
1412 # y = int(r.resolution_y * r.resolution_percentage * 0.01)
1414 # Wait for the file to be created
1415 # XXX This is no more valid, as 3.7 always creates output file once render is finished!
1416 parsing
= re
.compile(br
"= \[Parsing\.\.\.\] =")
1417 rendering
= re
.compile(br
"= \[Rendering\.\.\.\] =")
1418 percent
= re
.compile(r
"\(([0-9]{1,3})%\)")
1419 # print("***POV WAITING FOR FILE***")
1424 # POV in Windows did not output its stdout/stderr, it displayed them in its GUI
1425 # But now writes file
1426 if self
._is
_windows
:
1427 self
.update_stats("", "POV-Ray 3.7: Rendering File")
1429 t_data
= self
._process
.stdout
.read(10000)
1434 # XXX This is working for UNIX, not sure whether it might need adjustments for
1436 # First replace is for windows
1437 t_data
= str(t_data
).replace('\\r\\n', '\\n').replace('\\r', '\r')
1438 lines
= t_data
.split('\\n')
1439 last_line
+= lines
[0]
1440 lines
[0] = last_line
1441 print('\n'.join(lines
), end
="")
1442 last_line
= lines
[-1]
1444 if rendering
.search(data
):
1445 _pov_rendering
= True
1446 match
= percent
.findall(str(data
))
1448 self
.update_stats("", "POV-Ray 3.7: Rendering File (%s%%)" % match
[-1])
1450 self
.update_stats("", "POV-Ray 3.7: Rendering File")
1452 elif parsing
.search(data
):
1453 self
.update_stats("", "POV-Ray 3.7: Parsing File")
1455 if os
.path
.exists(self
._temp
_file
_out
):
1456 # print("***POV FILE OK***")
1457 # self.update_stats("", "POV-Ray 3.7: Rendering")
1461 xmin
= int(r
.border_min_x
* x
)
1462 ymin
= int(r
.border_min_y
* y
)
1463 xmax
= int(r
.border_max_x
* x
)
1464 ymax
= int(r
.border_max_y
* y
)
1466 # print("***POV UPDATING IMAGE***")
1467 result
= self
.begin_result(0, 0, x
, y
)
1468 # XXX, tests for border render.
1469 # result = self.begin_result(xmin, ymin, xmax - xmin, ymax - ymin)
1470 # result = self.begin_result(0, 0, xmax - xmin, ymax - ymin)
1471 lay
= result
.layers
[0]
1473 # This assumes the file has been fully written We wait a bit, just in case!
1474 time
.sleep(self
.DELAY
)
1476 lay
.load_from_file(self
._temp
_file
_out
)
1477 # XXX, tests for border render.
1478 # lay.load_from_file(self._temp_file_out, xmin, ymin)
1479 except RuntimeError:
1480 print("***POV ERROR WHILE READING OUTPUT FILE***")
1482 # Not needed right now, might only be useful if we find a way to use temp raw output of
1483 # pov 3.7 (in which case it might go under _test_wait()).
1486 # possible the image wont load early on.
1488 lay.load_from_file(self._temp_file_out)
1489 # XXX, tests for border render.
1490 #lay.load_from_file(self._temp_file_out, xmin, ymin)
1491 #lay.load_from_file(self._temp_file_out, xmin, ymin)
1492 except RuntimeError:
1495 # Update while POV-Ray renders
1497 # print("***POV RENDER LOOP***")
1499 # test if POV-Ray exists
1500 if self._process.poll() is not None:
1501 print("***POV PROCESS FINISHED***")
1506 if self.test_break():
1508 self._process.terminate()
1509 print("***POV PROCESS INTERRUPTED***")
1515 # Would be nice to redirect the output
1516 # stdout_value, stderr_value = self._process.communicate() # locks
1518 # check if the file updated
1519 new_size = os.path.getsize(self._temp_file_out)
1521 if new_size != prev_size:
1523 prev_size = new_size
1525 time.sleep(self.DELAY)
1528 self
.end_result(result
)
1531 print("***POV FILE NOT FOUND***")
1533 print("***POV FILE FINISHED***")
1535 # print(filename_log) #bring the pov log to blender console with proper path?
1537 self
._temp
_file
_log
, encoding
='utf-8'
1538 ) as f
: # The with keyword automatically closes the file when you are done
1540 # if isinstance(msg, str):
1544 # if type(msg) == bytes:
1545 # stdmsg = msg.split('\n')
1546 # stdmsg = msg.encode('utf-8', "replace")
1547 # stdmsg = msg.encode("utf-8", "replace")
1549 # stdmsg = msg.decode(encoding)
1551 # msg.encode('utf-8').decode('utf-8')
1552 msg
.replace("\t", " ")
1554 # Also print to the interactive console used in POV centric workspace
1555 # To do: get a grip on new line encoding
1556 # and make this a function to be used elsewhere
1557 for win
in bpy
.context
.window_manager
.windows
:
1558 if win
.screen
is not None:
1560 for area
in scr
.areas
:
1561 if area
.type == 'CONSOLE':
1570 # bpy.ops.console.banner(ctx, text = "Hello world")
1571 bpy
.ops
.console
.clear_line(ctx
)
1572 for i
in msg
.split('\n'):
1573 bpy
.ops
.console
.scrollback_append(
1578 # bpy.ops.console.insert(ctx, text=(i + "\n"))
1579 except BaseException
as e
:
1581 print('An exception occurred: {}'.format(e
))
1584 self
.update_stats("", "")
1586 if scene
.pov
.tempfiles_enable
or scene
.pov
.deletefiles_enable
:
1589 sound_on
= bpy
.context
.preferences
.addons
[__package__
].preferences
.use_sounds
1590 finished_render_message
= "\'Et Voilà!\'"
1592 if platform
.startswith('win') and sound_on
:
1593 # Could not find tts Windows command so playing beeps instead :-)
1594 # "Korobeiniki"(Коробе́йники)
1595 # aka "A-Type" Tetris theme
1598 winsound
.Beep(494, 250) # B
1599 winsound
.Beep(370, 125) # F
1600 winsound
.Beep(392, 125) # G
1601 winsound
.Beep(440, 250) # A
1602 winsound
.Beep(392, 125) # G
1603 winsound
.Beep(370, 125) # F#
1604 winsound
.Beep(330, 275) # E
1605 winsound
.Beep(330, 125) # E
1606 winsound
.Beep(392, 125) # G
1607 winsound
.Beep(494, 275) # B
1608 winsound
.Beep(440, 125) # A
1609 winsound
.Beep(392, 125) # G
1610 winsound
.Beep(370, 275) # F
1611 winsound
.Beep(370, 125) # F
1612 winsound
.Beep(392, 125) # G
1613 winsound
.Beep(440, 250) # A
1614 winsound
.Beep(494, 250) # B
1615 winsound
.Beep(392, 250) # G
1616 winsound
.Beep(330, 350) # E
1618 winsound
.Beep(440, 250) # A
1619 winsound
.Beep(440, 150) # A
1620 winsound
.Beep(523, 125) # D8
1621 winsound
.Beep(659, 250) # E8
1622 winsound
.Beep(587, 125) # D8
1623 winsound
.Beep(523, 125) # C8
1624 winsound
.Beep(494, 250) # B
1625 winsound
.Beep(494, 125) # B
1626 winsound
.Beep(392, 125) # G
1627 winsound
.Beep(494, 250) # B
1628 winsound
.Beep(440, 150) # A
1629 winsound
.Beep(392, 125) # G
1630 winsound
.Beep(370, 250) # F#
1631 winsound
.Beep(370, 125) # F#
1632 winsound
.Beep(392, 125) # G
1633 winsound
.Beep(440, 250) # A
1634 winsound
.Beep(494, 250) # B
1635 winsound
.Beep(392, 250) # G
1636 winsound
.Beep(330, 300) # E
1638 # Mac supports natively say command
1639 elif platform
== "darwin":
1640 # We don't want the say command to block Python,
1641 # so we add an ampersand after the message
1642 # but if the os TTS package isn't up to date it
1643 # still does thus, the try except clause
1645 os
.system("say %s &" % finished_render_message
)
1646 except BaseException
as e
:
1648 print("your Mac may need an update, try to restart computer")
1650 # While Linux frequently has espeak installed or at least can suggest
1651 # Maybe windows could as well ?
1652 elif platform
== "linux":
1653 # We don't want the espeak command to block Python,
1654 # so we add an ampersand after the message
1655 # but if the espeak TTS package isn't installed it
1656 # still does thus, the try except clause
1658 os
.system("echo %s | espeak &" % finished_render_message
)
1659 except BaseException
as e
:
1665 # --------------------------------------------------------------------------------- #
1666 # ----------------------------------- Operators ----------------------------------- #
1667 # --------------------------------------------------------------------------------- #
1668 class RenderPovTexturePreview(Operator
):
1669 """Export only files necessary to texture preview and render image"""
1671 bl_idname
= "tex.preview_update"
1672 bl_label
= "Update preview"
1674 def execute(self
, context
):
1675 tex
= bpy
.context
.object.active_material
.active_texture
# context.texture
1676 tex_prev_name
= string_strip_hyphen(bpy
.path
.clean_name(tex
.name
)) + "_prev"
1678 # Make sure Preview directory exists and is empty
1679 if not os
.path
.isdir(preview_dir
):
1680 os
.mkdir(preview_dir
)
1682 ini_prev_file
= os
.path
.join(preview_dir
, "Preview.ini")
1683 input_prev_file
= os
.path
.join(preview_dir
, "Preview.pov")
1684 output_prev_file
= os
.path
.join(preview_dir
, tex_prev_name
)
1685 # ---------------------------------- ini ---------------------------------- #
1686 with
open(ini_prev_file
, "w") as file_ini
:
1687 file_ini
.write('Version=3.8\n')
1688 file_ini
.write('Input_File_Name="%s"\n' % input_prev_file
)
1689 file_ini
.write('Output_File_Name="%s.png"\n' % output_prev_file
)
1690 file_ini
.write('Library_Path="%s"\n' % preview_dir
)
1691 file_ini
.write('Width=256\n')
1692 file_ini
.write('Height=256\n')
1693 file_ini
.write('Pause_When_Done=0\n')
1694 file_ini
.write('Output_File_Type=N\n')
1695 file_ini
.write('Output_Alpha=1\n')
1696 file_ini
.write('Antialias=on\n')
1697 file_ini
.write('Sampling_Method=2\n')
1698 file_ini
.write('Antialias_Depth=3\n')
1699 file_ini
.write('-d\n')
1700 if not file_ini
.closed
:
1702 # ---------------------------------- pov ---------------------------------- #
1703 with
open(input_prev_file
, "w") as file_pov
:
1704 pat_name
= "PAT_" + string_strip_hyphen(bpy
.path
.clean_name(tex
.name
))
1705 file_pov
.write("#declare %s = \n" % pat_name
)
1706 file_pov
.write(shading
.export_pattern(tex
))
1708 file_pov
.write("#declare Plane =\n")
1709 file_pov
.write("mesh {\n")
1711 " triangle {<-2.021,-1.744,2.021>,<-2.021,-1.744,-2.021>,<2.021,-1.744,2.021>}\n"
1714 " triangle {<-2.021,-1.744,-2.021>,<2.021,-1.744,-2.021>,<2.021,-1.744,2.021>}\n"
1716 file_pov
.write(" texture{%s}\n" % pat_name
)
1717 file_pov
.write("}\n")
1718 file_pov
.write("object {Plane}\n")
1719 file_pov
.write("light_source {\n")
1720 file_pov
.write(" <0,4.38,-1.92e-07>\n")
1721 file_pov
.write(" color rgb<4, 4, 4>\n")
1722 file_pov
.write(" parallel\n")
1723 file_pov
.write(" point_at <0, 0, -1>\n")
1724 file_pov
.write("}\n")
1725 file_pov
.write("camera {\n")
1726 file_pov
.write(" location <0, 0, 0>\n")
1727 file_pov
.write(" look_at <0, 0, -1>\n")
1728 file_pov
.write(" right <-1.0, 0, 0>\n")
1729 file_pov
.write(" up <0, 1, 0>\n")
1730 file_pov
.write(" angle 96.805211\n")
1731 file_pov
.write(" rotate <-90.000003, -0.000000, 0.000000>\n")
1732 file_pov
.write(" translate <0.000000, 0.000000, 0.000000>\n")
1733 file_pov
.write("}\n")
1734 if not file_pov
.closed
:
1736 # ------------------------------- end write ------------------------------- #
1738 pov_binary
= PovrayRender
._locate
_binary
()
1740 if platform
.startswith('win'):
1741 p1
= subprocess
.Popen(
1742 ["%s" % pov_binary
, "/EXIT", "%s" % ini_prev_file
],
1743 stdout
=subprocess
.PIPE
,
1744 stderr
=subprocess
.STDOUT
,
1747 p1
= subprocess
.Popen(
1748 ["%s" % pov_binary
, "-d", "%s" % ini_prev_file
],
1749 stdout
=subprocess
.PIPE
,
1750 stderr
=subprocess
.STDOUT
,
1754 tex
.use_nodes
= True
1755 tree
= tex
.node_tree
1757 for n
in tree
.nodes
:
1758 tree
.nodes
.remove(n
)
1759 im
= tree
.nodes
.new("TextureNodeImage")
1760 path_prev
= "%s.png" % output_prev_file
1761 im
.image
= bpy
.data
.images
.load(path_prev
)
1763 name
= name
.split("/")
1764 name
= name
[len(name
) - 1]
1766 im
.location
= 200, 200
1767 previewer
= tree
.nodes
.new('TextureNodeOutput')
1768 previewer
.label
= "Preview"
1769 previewer
.location
= 400, 400
1770 links
.new(im
.outputs
[0], previewer
.inputs
[0])
1771 # tex.type="IMAGE" # makes clip extend possible
1772 # tex.extension="CLIP"
1776 class RunPovTextRender(Operator
):
1777 """Export files depending on text editor options and render image."""
1779 bl_idname
= "text.run"
1782 bl_description
= "Run a render with this text only"
1784 def execute(self
, context
):
1785 scene
= context
.scene
1786 scene
.pov
.text_block
= context
.space_data
.text
.name
1788 bpy
.ops
.render
.render()
1790 # empty text name property again
1791 scene
.pov
.text_block
= ""
1797 RenderPovTexturePreview
,
1805 scripting
.register()
1809 scripting
.unregister()
1810 for cls
in reversed(classes
):
1811 unregister_class(cls
)