Export_3ds: Improved distance cue node search
[blender-addons.git] / render_povray / render.py
blobb228d5a04505ccf0de7a3a5cb1cb7ccd885ed77d
1 # SPDX-FileCopyrightText: 2010-2023 Blender Foundation
3 # 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."""
7 import bpy
8 import subprocess
9 import os
10 from sys import platform
11 #import time
12 from math import (
13 pi,
14 ) # maybe move to scenography.py and topology_*****_data.py respectively with smoke and matrix
15 import mathutils #import less than full
16 import tempfile # generate temporary files with random names
17 from bpy.types import Operator
18 from bpy.utils import register_class, unregister_class
20 from . import (
21 scripting,
22 ) # for writing, importing and rendering directly POV Scene Description Language items
23 from . import render_core
24 from . import scenography # for atmosphere, environment, effects, lighting, camera
25 from . import shading # for BI POV shaders emulation
26 from . import nodes_fn
27 from . import texturing_procedural # for Blender procedurals to POV patterns emulation
28 from . import model_all # for mesh based geometry
29 from . import model_meta_topology # for mesh based geometry
30 from . import model_curve_topology # for curves based geometry
32 # from . import model_primitives # for import and export of POV specific primitives
35 from .scenography import image_format, img_map, img_map_transforms, path_image
37 from .shading import write_object_material_interior
38 from .model_primitives import write_object_modifiers
41 tab_level = 0
42 tab=""
43 comments = False
44 using_uberpov = False
45 unpacked_images = []
47 from .render_core import (
48 preview_dir,
49 PovRender,
52 def string_strip_hyphen(name):
54 """Remove hyphen characters from a string to avoid POV errors."""
56 return name.replace("-", "")
59 def safety(name, ref_level_bound):
60 """append suffix characters to names of various material declinations.
62 Material declinations are necessary to POV syntax and used in shading.py
63 by the pov_has_no_specular_maps function to create the finish map trick and
64 the suffixes avoid name collisions.
65 Keyword arguments:
66 name -- the initial material name as a string
67 ref_level_bound -- the enum number of the ref_level_bound being written:
68 ref_level_bound=1 is for texture with No specular nor Mirror reflection
69 ref_level_bound=2 is for texture with translation of spec and mir levels
70 for when no map influences them
71 ref_level_bound=3 is for texture with Maximum Spec and Mirror
72 """
73 # All the try except clause below seems useless as each time
74 # prefix rewritten even after and outside of it what was the point?
75 # It may not even be any longer possible to feed no arg from Blender UI
76 # try:
77 # if name: # if int(name) > 0: # could be zero if no argument provided
78 # # and always triggered exception so is this similar ?
79 # prefix = "shader"
80 # except BaseException as e:
81 # print(e.__doc__)
82 # print('An exception occurred: {}'.format(e))
83 # prefix = "" # rewritten below...
84 prefix = "shader_"
85 name = string_strip_hyphen(name)
86 if ref_level_bound == 2:
87 return prefix + name
88 # implicit else-if (no return yet)
89 if ref_level_bound == 1:
90 return prefix + name + "0" # used for 0 of specular map
91 # implicit else-if (no return yet)
92 if ref_level_bound == 3:
93 return prefix + name + "1" # used for 1 of specular map
96 # -------- end safety string name material
99 csg_list = []
102 def is_renderable(ob):
103 """test for objects flagged as hidden or boolean operands not to render"""
104 return not ob.hide_render and ob not in csg_list
107 def renderable_objects():
108 """test for non hidden, non boolean operands objects to render"""
109 return [ob for ob in bpy.data.objects if is_renderable(ob)]
112 def non_renderable_objects():
113 """Boolean operands only. Not to render"""
114 return list(csg_list)
117 def set_tab(tabtype, spaces):
118 """Apply the configured indentation all along the exported POV file
120 Arguments:
121 tabtype -- Specifies user preference between tabs or spaces indentation
122 spaces -- If using spaces, sets the number of space characters to use
123 Returns:
124 The beginning blank space for each line of the generated pov file
126 tab_str = ""
127 match tabtype:
128 case 'SPACE':
129 tab_str = spaces * " "
130 case 'NONE':
131 tab_str = ""
132 case 'TAB':
133 tab_str = "\t"
134 return tab_str
140 # below properties not added to __init__ yet to avoid conflicts with material sss scale
141 # unless it would override then should be interfaced also in scene units property tab
143 # if scene.pov.sslt_enable:
144 # file.write(" mm_per_unit %s\n"%scene.pov.mm_per_unit)
145 # file.write(" subsurface {\n")
146 # file.write(" samples %s, %s\n"%(scene.pov.sslt_samples_max,scene.pov.sslt_samples_min))
147 # if scene.pov.sslt_radiosity:
148 # file.write(" radiosity on\n")
149 # file.write("}\n")
154 # def write_object_modifiers(ob, File):
155 # """Translate some object level POV statements from Blender UI
156 # to POV syntax and write to exported file """
158 # # Maybe return that string to be added instead of directly written.
160 # '''XXX WIP
161 # import .model_all.write_object_csg_inside_vector
162 # write_object_csg_inside_vector(ob, file)
163 # '''
165 # if ob.pov.hollow:
166 # File.write("\thollow\n")
167 # if ob.pov.double_illuminate:
168 # File.write("\tdouble_illuminate\n")
169 # if ob.pov.sturm:
170 # File.write("\tsturm\n")
171 # if ob.pov.no_shadow:
172 # File.write("\tno_shadow\n")
173 # if ob.pov.no_image:
174 # File.write("\tno_image\n")
175 # if ob.pov.no_reflection:
176 # File.write("\tno_reflection\n")
177 # if ob.pov.no_radiosity:
178 # File.write("\tno_radiosity\n")
179 # if ob.pov.inverse:
180 # File.write("\tinverse\n")
181 # if ob.pov.hierarchy:
182 # File.write("\thierarchy\n")
184 # # XXX, Commented definitions
185 # '''
186 # if scene.pov.photon_enable:
187 # File.write("photons {\n")
188 # if ob.pov.target:
189 # File.write("target %.4g\n"%ob.pov.target_value)
190 # if ob.pov.refraction:
191 # File.write("refraction on\n")
192 # if ob.pov.reflection:
193 # File.write("reflection on\n")
194 # if ob.pov.pass_through:
195 # File.write("pass_through\n")
196 # File.write("}\n")
197 # if ob.pov.object_ior > 1:
198 # File.write("interior {\n")
199 # File.write("ior %.4g\n"%ob.pov.object_ior)
200 # if scene.pov.photon_enable and ob.pov.target and ob.pov.refraction and ob.pov.dispersion:
201 # File.write("ior %.4g\n"%ob.pov.dispersion_value)
202 # File.write("ior %s\n"%ob.pov.dispersion_samples)
203 # if scene.pov.photon_enable == False:
204 # File.write("caustics %.4g\n"%ob.pov.fake_caustics_power)
205 # '''
207 def tab_write(file, str_o, scene=None):
208 """write directly to exported file if user checked autonamed temp files (faster).
209 Otherwise, indent POV syntax from brackets levels and write to exported file"""
211 if not scene:
212 scene = bpy.data.scenes[0]
213 global tab
214 tab = set_tab(scene.pov.indentation_character, scene.pov.indentation_spaces)
215 if scene.pov.tempfiles_enable:
216 file.write(str_o)
217 else:
218 global tab_level
219 brackets = str_o.count("{") - str_o.count("}") + str_o.count("[") - str_o.count("]")
220 if brackets < 0:
221 tab_level = tab_level + brackets
222 if tab_level < 0:
223 print("Indentation Warning: tab_level = %s" % tab_level)
224 tab_level = 0
225 if tab_level >= 1:
226 file.write("%s" % tab * tab_level)
227 file.write(str_o)
228 if brackets > 0:
229 tab_level = tab_level + brackets
231 def write_matrix(file, matrix):
232 """Translate some transform matrix from Blender UI
233 to POV syntax and write to exported file """
234 tab_write(file,
235 "matrix <%.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f>\n"
237 matrix[0][0],
238 matrix[1][0],
239 matrix[2][0],
240 matrix[0][1],
241 matrix[1][1],
242 matrix[2][1],
243 matrix[0][2],
244 matrix[1][2],
245 matrix[2][2],
246 matrix[0][3],
247 matrix[1][3],
248 matrix[2][3],
251 global_matrix = mathutils.Matrix.Rotation(-pi / 2.0, 4, 'X')
253 def write_pov(filename, scene=None, info_callback=None):
254 """Main export process from Blender UI to POV syntax and write to exported file """
256 with open(filename, "w") as file:
257 # Only for testing
258 if not scene:
259 scene = bpy.data.scenes[0]
261 render = scene.render
262 world = scene.world
263 global comments
264 comments = scene.pov.comments_enable and not scene.pov.tempfiles_enable
266 feature_set = bpy.context.preferences.addons[__package__].preferences.branch_feature_set_povray
267 global using_uberpov
268 using_uberpov = feature_set == 'uberpov'
269 pov_binary = PovRender._locate_binary()
271 if using_uberpov:
272 print("Unofficial UberPOV feature set chosen in preferences")
273 else:
274 print("Official POV-Ray 3.7 feature set chosen in preferences")
275 if 'uber' in pov_binary:
276 print("The name of the binary suggests you are probably rendering with Uber POV engine")
277 else:
278 print("The name of the binary suggests you are probably rendering with standard POV engine")
281 def unique_name(name, name_seq):
282 """Increment any generated POV name that could get identical to avoid collisions"""
284 if name not in name_seq:
285 name = string_strip_hyphen(name)
286 return name
288 name_orig = name
289 i = 1
290 while name in name_seq:
291 name = "%s_%.3d" % (name_orig, i)
292 i += 1
293 name = string_strip_hyphen(name)
294 return name
296 material_names_dictionary = {}
297 DEF_MAT_NAME = "" # or "Default"?
299 # -----------------------------------------------------------------------------
301 def export_global_settings(scene):
302 """write all POV global settings to exported file """
303 # Imperial units warning
304 if scene.unit_settings.system == "IMPERIAL":
305 print("Warning: Imperial units not supported")
307 tab_write(file, "global_settings {\n")
308 tab_write(file, "assumed_gamma 1.0\n")
309 tab_write(file, "max_trace_level %d\n" % scene.pov.max_trace_level)
311 if scene.pov.global_settings_advanced:
312 if not scene.pov.radio_enable:
313 file.write(" adc_bailout %.6f\n" % scene.pov.adc_bailout)
314 file.write(" ambient_light <%.6f,%.6f,%.6f>\n" % scene.pov.ambient_light[:])
315 file.write(" irid_wavelength <%.6f,%.6f,%.6f>\n" % scene.pov.irid_wavelength[:])
316 file.write(" number_of_waves %s\n" % scene.pov.number_of_waves)
317 file.write(" noise_generator %s\n" % scene.pov.noise_generator)
318 if scene.pov.radio_enable:
319 tab_write(file, "radiosity {\n")
320 tab_write(file, "adc_bailout %.4g\n" % scene.pov.radio_adc_bailout)
321 tab_write(file, "brightness %.4g\n" % scene.pov.radio_brightness)
322 tab_write(file, "count %d\n" % scene.pov.radio_count)
323 tab_write(file, "error_bound %.4g\n" % scene.pov.radio_error_bound)
324 tab_write(file, "gray_threshold %.4g\n" % scene.pov.radio_gray_threshold)
325 tab_write(file, "low_error_factor %.4g\n" % scene.pov.radio_low_error_factor)
326 tab_write(file, "maximum_reuse %.4g\n" % scene.pov.radio_maximum_reuse)
327 tab_write(file, "minimum_reuse %.4g\n" % scene.pov.radio_minimum_reuse)
328 tab_write(file, "nearest_count %d\n" % scene.pov.radio_nearest_count)
329 tab_write(file, "pretrace_start %.3g\n" % scene.pov.radio_pretrace_start)
330 tab_write(file, "pretrace_end %.3g\n" % scene.pov.radio_pretrace_end)
331 tab_write(file, "recursion_limit %d\n" % scene.pov.radio_recursion_limit)
332 tab_write(file, "always_sample %d\n" % scene.pov.radio_always_sample)
333 tab_write(file, "normal %d\n" % scene.pov.radio_normal)
334 tab_write(file, "media %d\n" % scene.pov.radio_media)
335 tab_write(file, "subsurface %d\n" % scene.pov.radio_subsurface)
336 tab_write(file, "}\n")
337 once_sss = 1
338 once_ambient = 1
339 once_photons = 1
340 for material in bpy.data.materials:
341 if material.pov_subsurface_scattering.use and once_sss:
342 # In pov, the scale has reversed influence compared to blender. these number
343 # should correct that
344 tab_write(file,
345 "mm_per_unit %.6f\n" % (material.pov_subsurface_scattering.scale * 1000.0)
347 # 1000 rather than scale * (-100.0) + 15.0))
349 # In POV-Ray, the scale factor for all subsurface shaders needs to be the same
351 # formerly sslt_samples were multiplied by 100 instead of 10
352 sslt_samples = (11 - material.pov_subsurface_scattering.error_threshold) * 10
354 tab_write(file, "subsurface { samples %d, %d }\n" % (sslt_samples, sslt_samples / 10))
355 once_sss = 0
357 if world and once_ambient:
358 tab_write(file, "ambient_light rgb<%.3g, %.3g, %.3g>\n" % world.pov.ambient_color[:])
359 once_ambient = 0
361 if (
362 scene.pov.photon_enable
363 and once_photons
364 and (
365 material.pov.refraction_type == "2"
366 or material.pov.photons_reflection
369 tab_write(file, "photons {\n")
370 tab_write(file, "spacing %.6f\n" % scene.pov.photon_spacing)
371 tab_write(file, "max_trace_level %d\n" % scene.pov.photon_max_trace_level)
372 tab_write(file, "adc_bailout %.3g\n" % scene.pov.photon_adc_bailout)
373 tab_write(file,
374 "gather %d, %d\n"
375 % (scene.pov.photon_gather_min, scene.pov.photon_gather_max)
377 if scene.pov.photon_map_file_save_load in {'save'}:
378 ph_file_name = 'Photon_map_file.ph'
379 if scene.pov.photon_map_file != '':
380 ph_file_name = scene.pov.photon_map_file + '.ph'
381 ph_file_dir = tempfile.gettempdir()
382 path = bpy.path.abspath(scene.pov.photon_map_dir)
383 if os.path.exists(path):
384 ph_file_dir = path
385 full_file_name = os.path.join(ph_file_dir, ph_file_name)
386 tab_write(file, 'save_file "%s"\n' % full_file_name)
387 scene.pov.photon_map_file = full_file_name
388 if scene.pov.photon_map_file_save_load in {'load'}:
389 full_file_name = bpy.path.abspath(scene.pov.photon_map_file)
390 if os.path.exists(full_file_name):
391 tab_write(file, 'load_file "%s"\n' % full_file_name)
392 tab_write(file, "}\n")
393 once_photons = 0
395 tab_write(file, "}\n")
397 # sel = renderable_objects() #removed for booleans
398 if comments:
399 file.write(
400 "//----------------------------------------------\n"
401 "//--Exported with POV-Ray exporter for Blender--\n"
402 "//----------------------------------------------\n\n"
404 file.write("#version 3.7;\n") # Switch below as soon as 3.8 beta gets easy linked
405 # file.write("#version 3.8;\n")
406 file.write(
407 "#declare Default_texture = texture{pigment {rgb 0.8} " "finish {brilliance 3.8} }\n\n"
409 if comments:
410 file.write("\n//--Global settings--\n\n")
412 export_global_settings(scene)
414 if comments:
415 file.write("\n//--Custom Code--\n\n")
416 scripting.export_custom_code(file)
418 if comments:
419 file.write("\n//--Patterns Definitions--\n\n")
420 local_pattern_names = []
421 for texture in bpy.data.textures: # ok?
422 if texture.users > 0:
423 current_pat_name = string_strip_hyphen(bpy.path.clean_name(texture.name))
424 # string_strip_hyphen(patternNames[texture.name]) #maybe instead of the above
425 local_pattern_names.append(current_pat_name)
426 # use above list to prevent writing texture instances several times and assign in mats?
427 if (
428 texture.type not in {'NONE', 'IMAGE'} and texture.pov.tex_pattern_type == 'emulator'
429 ) or (texture.type in {'NONE', 'IMAGE'} and texture.pov.tex_pattern_type != 'emulator'):
430 file.write("\n#declare PAT_%s = \n" % current_pat_name)
431 file.write(texturing_procedural.export_pattern(texture))
432 file.write("\n")
433 if comments:
434 file.write("\n//--Background--\n\n")
436 scenography.export_world(file, scene.world, scene, global_matrix, tab_write)
438 if comments:
439 file.write("\n//--Cameras--\n\n")
441 scenography.export_camera(file, scene, global_matrix, render, tab_write)
443 if comments:
444 file.write("\n//--Lamps--\n\n")
446 for ob in bpy.data.objects:
447 if ob.type == 'MESH':
448 for mod in ob.modifiers:
449 if mod.type == 'BOOLEAN' and mod.object not in csg_list:
450 csg_list.append(mod.object)
451 if csg_list:
452 csg = False
453 sel = non_renderable_objects()
454 # export non rendered boolean objects operands
455 model_all.objects_loop(
456 file,
457 scene,
458 sel,
459 csg,
460 material_names_dictionary,
461 unpacked_images,
462 tab_level,
463 tab_write,
464 info_callback,
467 csg = True
468 sel = renderable_objects()
470 scenography.export_lights(
471 [L for L in sel if (L.type == 'LIGHT' and L.pov.object_as != 'RAINBOW')],
472 file,
473 scene,
474 global_matrix,
475 tab_write,
478 if comments:
479 file.write("\n//--Rainbows--\n\n")
480 scenography.export_rainbows(
481 [L for L in sel if (L.type == 'LIGHT' and L.pov.object_as == 'RAINBOW')],
482 file,
483 scene,
484 global_matrix,
485 tab_write,
488 if comments:
489 file.write("\n//--Special Curves--\n\n")
490 for c in sel:
491 if c.is_modified(scene, 'RENDER'):
492 continue # don't export as pov curves objects with modifiers, but as mesh
493 # Implicit else-if (as not skipped by previous "continue")
494 if c.type == 'CURVE' and (c.pov.curveshape in {'lathe', 'sphere_sweep', 'loft', 'birail'}):
495 model_curve_topology.export_curves(file, c, tab_write)
497 if comments:
498 file.write("\n//--Material Definitions--\n\n")
499 # write a default pigment for objects with no material (comment out to show black)
500 file.write("#default{ pigment{ color srgb 0.8 }}\n")
501 # Convert all materials to strings we can access directly per vertex.
502 # exportMaterials()
503 shading.write_material(
504 file,
505 using_uberpov,
506 DEF_MAT_NAME,
507 tab_write,
508 comments,
509 unique_name,
510 material_names_dictionary,
511 None,
512 ) # default material
513 for material in bpy.data.materials:
514 if material.users > 0:
515 r, g, b, a = material.diffuse_color[:]
516 pigment_color = "pigment {rgbt <%.4g,%.4g,%.4g,%.4g>}" % (r, g, b, 1 - a)
517 if material.pov.material_use_nodes:
518 # Also make here other pigment_color fallback using BSDF node main color ?
519 ntree = material.node_tree
520 pov_mat_name = string_strip_hyphen(bpy.path.clean_name(material.name))
521 if len(ntree.nodes) == 0:
522 file.write('#declare %s = texture {%s}\n' % (pov_mat_name, pigment_color))
523 else:
524 nodes_fn.write_nodes(pov_mat_name, ntree, file)
526 for node in ntree.nodes:
527 if node:
528 if node.bl_idname == "PovrayOutputNode":
529 if node.inputs["Texture"].is_linked:
530 for link in ntree.links:
531 if link.to_node.bl_idname == "PovrayOutputNode":
532 pov_mat_name = (
533 string_strip_hyphen(
534 bpy.path.clean_name(link.from_node.name)
536 + "_%s" % pov_mat_name
538 else:
539 file.write(
540 '#declare %s = texture {%s}\n' % (pov_mat_name, pigment_color)
542 else:
543 shading.write_material(
544 file,
545 using_uberpov,
546 DEF_MAT_NAME,
547 tab_write,
548 comments,
549 unique_name,
550 material_names_dictionary,
551 material,
553 # attributes are all the variables needed by the other python file...
554 if comments:
555 file.write("\n")
557 model_meta_topology.export_meta(file,
558 [m for m in sel if m.type == 'META'],
559 material_names_dictionary,
560 tab_write,
561 DEF_MAT_NAME,)
563 if comments:
564 file.write("//--Mesh objects--\n")
566 # tbefore = time.time()
567 model_all.objects_loop(
568 file,
569 scene,
570 sel,
571 csg,
572 material_names_dictionary,
573 unpacked_images,
574 tab_level,
575 tab_write,
576 info_callback,
578 # totime = time.time() - tbefore
579 # print("objects_loop took" + str(totime))
581 # What follow used to happen here:
582 # export_camera()
583 # scenography.export_world(file, scene.world, scene, global_matrix, tab_write)
584 # export_global_settings(scene)
585 # MR:..and the order was important for implementing pov 3.7 baking
586 # (mesh camera) comment for the record
587 # CR: Baking should be a special case than. If "baking", than we could change the order.
589 if not file.closed:
590 file.close()
592 def write_pov_ini(filename_ini, filename_log, filename_pov, filename_image):
593 """Write ini file."""
594 feature_set = bpy.context.preferences.addons[__package__].preferences.branch_feature_set_povray
595 global using_uberpov
596 using_uberpov = feature_set == 'uberpov'
597 # scene = bpy.data.scenes[0]
598 scene = bpy.context.scene
599 render = scene.render
601 x = int(render.resolution_x * render.resolution_percentage * 0.01)
602 y = int(render.resolution_y * render.resolution_percentage * 0.01)
604 with open(filename_ini, "w") as file:
605 file.write("Version=3.7\n")
606 # write povray text stream to temporary file of same name with _log suffix
607 # file.write("All_File='%s'\n" % filename_log)
608 # DEBUG.OUT log if none specified:
609 file.write("All_File=1\n")
611 file.write("Input_File_Name='%s'\n" % filename_pov)
612 file.write("Output_File_Name='%s'\n" % filename_image)
614 file.write("Width=%d\n" % x)
615 file.write("Height=%d\n" % y)
617 # Border render.
618 if render.use_border:
619 file.write("Start_Column=%4g\n" % render.border_min_x)
620 file.write("End_Column=%4g\n" % render.border_max_x)
622 file.write("Start_Row=%4g\n" % (1.0 - render.border_max_y))
623 file.write("End_Row=%4g\n" % (1.0 - render.border_min_y))
625 file.write("Bounding_Method=2\n") # The new automatic BSP is faster in most scenes
627 # Activated (turn this back off when better live exchange is done between the two programs
628 # (see next comment)
629 file.write("Display=1\n")
630 file.write("Pause_When_Done=0\n")
631 # PNG, with POV-Ray 3.7, can show background color with alpha. In the long run using the
632 # POV-Ray interactive preview like bishop 3D could solve the preview for all formats.
633 file.write("Output_File_Type=N\n")
634 # file.write("Output_File_Type=T\n") # TGA, best progressive loading
635 file.write("Output_Alpha=1\n")
637 if scene.pov.antialias_enable:
638 # method 2 (recursive) with higher max subdiv forced because no mipmapping in POV-Ray
639 # needs higher sampling.
640 # aa_mapping = {"5": 2, "8": 3, "11": 4, "16": 5}
641 if using_uberpov:
642 method = {"0": 1, "1": 2, "2": 3}
643 else:
644 method = {"0": 1, "1": 2, "2": 2}
645 file.write("Antialias=on\n")
646 file.write("Antialias_Depth=%d\n" % scene.pov.antialias_depth)
647 file.write("Antialias_Threshold=%.3g\n" % scene.pov.antialias_threshold)
648 if using_uberpov and scene.pov.antialias_method == '2':
649 file.write("Sampling_Method=%s\n" % method[scene.pov.antialias_method])
650 file.write("Antialias_Confidence=%.3g\n" % scene.pov.antialias_confidence)
651 else:
652 file.write("Sampling_Method=%s\n" % method[scene.pov.antialias_method])
653 file.write("Antialias_Gamma=%.3g\n" % scene.pov.antialias_gamma)
654 if scene.pov.jitter_enable:
655 file.write("Jitter=on\n")
656 file.write("Jitter_Amount=%3g\n" % scene.pov.jitter_amount)
657 else:
658 file.write("Jitter=off\n") # prevent animation flicker
660 else:
661 file.write("Antialias=off\n")
662 if not file.closed:
663 file.close()
666 # --------------------------------------------------------------------------------- #
667 # ----------------------------------- Operators ----------------------------------- #
668 # --------------------------------------------------------------------------------- #
669 class RenderPovTexturePreview(Operator):
670 """Export only files necessary to texture preview and render image"""
672 bl_idname = "tex.preview_update"
673 bl_label = "Update preview"
675 def execute(self, context):
676 tex = bpy.context.object.active_material.active_texture # context.texture
677 tex_prev_name = string_strip_hyphen(bpy.path.clean_name(tex.name)) + "_prev"
679 # Make sure Preview directory exists and is empty
680 if not os.path.isdir(preview_dir):
681 os.mkdir(preview_dir)
683 ini_prev_file = os.path.join(preview_dir, "Preview.ini")
684 input_prev_file = os.path.join(preview_dir, "Preview.pov")
685 output_prev_file = os.path.join(preview_dir, tex_prev_name)
686 # ---------------------------------- ini ---------------------------------- #
687 with open(ini_prev_file, "w") as file_ini:
688 file_ini.write('Version=3.8\n')
689 file_ini.write('Input_File_Name="%s"\n' % input_prev_file)
690 file_ini.write('Output_File_Name="%s.png"\n' % output_prev_file)
691 file_ini.write('Library_Path="%s"\n' % preview_dir)
692 file_ini.write('Width=256\n')
693 file_ini.write('Height=256\n')
694 file_ini.write('Pause_When_Done=0\n')
695 file_ini.write('Output_File_Type=N\n')
696 file_ini.write('Output_Alpha=1\n')
697 file_ini.write('Antialias=on\n')
698 file_ini.write('Sampling_Method=2\n')
699 file_ini.write('Antialias_Depth=3\n')
700 file_ini.write('-d\n')
701 if not file_ini.closed:
702 file_ini.close()
703 # ---------------------------------- pov ---------------------------------- #
704 with open(input_prev_file, "w") as file_pov:
705 pat_name = "PAT_" + string_strip_hyphen(bpy.path.clean_name(tex.name))
706 file_pov.write("#declare %s = \n" % pat_name)
707 file_pov.write(texturing_procedural.export_pattern(tex))
709 file_pov.write("#declare Plane =\n")
710 file_pov.write("mesh {\n")
711 file_pov.write(
712 " triangle {<-2.021,-1.744,2.021>,<-2.021,-1.744,-2.021>,<2.021,-1.744,2.021>}\n"
714 file_pov.write(
715 " triangle {<-2.021,-1.744,-2.021>,<2.021,-1.744,-2.021>,<2.021,-1.744,2.021>}\n"
717 file_pov.write(" texture{%s}\n" % pat_name)
718 file_pov.write("}\n")
719 file_pov.write("object {Plane}\n")
720 file_pov.write("light_source {\n")
721 file_pov.write(" <0,4.38,-1.92e-07>\n")
722 file_pov.write(" color rgb<4, 4, 4>\n")
723 file_pov.write(" parallel\n")
724 file_pov.write(" point_at <0, 0, -1>\n")
725 file_pov.write("}\n")
726 file_pov.write("camera {\n")
727 file_pov.write(" location <0, 0, 0>\n")
728 file_pov.write(" look_at <0, 0, -1>\n")
729 file_pov.write(" right <-1.0, 0, 0>\n")
730 file_pov.write(" up <0, 1, 0>\n")
731 file_pov.write(" angle 96.805211\n")
732 file_pov.write(" rotate <-90.000003, -0.000000, 0.000000>\n")
733 file_pov.write(" translate <0.000000, 0.000000, 0.000000>\n")
734 file_pov.write("}\n")
735 if not file_pov.closed:
736 file_pov.close()
737 # ------------------------------- end write ------------------------------- #
739 pov_binary = PovRender._locate_binary()
741 if platform.startswith('win'):
742 with subprocess.Popen(
743 ["%s" % pov_binary, "/EXIT", "%s" % ini_prev_file],
744 stdout=subprocess.PIPE,
745 stderr=subprocess.STDOUT,
746 ) as p1:
747 p1.wait()
748 else:
749 with subprocess.Popen(
750 ["%s" % pov_binary, "-d", "%s" % ini_prev_file],
751 stdout=subprocess.PIPE,
752 stderr=subprocess.STDOUT,
753 ) as p1:
754 p1.wait()
756 tex.use_nodes = True
757 tree = tex.node_tree
758 links = tree.links
759 for n in tree.nodes:
760 tree.nodes.remove(n)
761 im = tree.nodes.new("TextureNodeImage")
762 path_prev = "%s.png" % output_prev_file
763 im.image = bpy.data.images.load(path_prev)
764 name = path_prev
765 name = name.split("/")
766 name = name[len(name) - 1]
767 im.name = name
768 im.location = 200, 200
769 previewer = tree.nodes.new('TextureNodeOutput')
770 previewer.label = "Preview"
771 previewer.location = 400, 400
772 links.new(im.outputs[0], previewer.inputs[0])
773 # tex.type="IMAGE" # makes clip extend possible
774 # tex.extension="CLIP"
775 return {'FINISHED'}
778 class RunPovTextRender(Operator):
779 """Export files depending on text editor options and render image."""
781 bl_idname = "text.run"
782 bl_label = "Run"
783 bl_context = "text"
784 bl_description = "Run a render with this text only"
786 def execute(self, context):
787 scene = context.scene
788 scene.pov.text_block = context.space_data.text.name
790 bpy.ops.render.render()
792 # empty text name property again
793 scene.pov.text_block = ""
794 return {'FINISHED'}
797 classes = (
798 #PovRender,
799 RenderPovTexturePreview,
800 RunPovTextRender,
804 def register():
805 for cls in classes:
806 register_class(cls)
807 scripting.register()
810 def unregister():
811 scripting.unregister()
812 for cls in reversed(classes):
813 unregister_class(cls)