Cleanup: trailing space
[blender-addons.git] / render_povray / render.py
blobc4a93ebd4f00f8b38c49369472b530a4084d9376
1 # ***** BEGIN GPL LICENSE BLOCK *****
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # #**** END GPL LICENSE BLOCK #****
19 # <pep8 compliant>
21 import bpy
22 import subprocess
23 import os
24 import sys
25 import time
26 from math import atan, pi, degrees, sqrt, cos, sin
27 ####################
28 ## Faster mesh export
29 import numpy as np
30 ####################
31 import re
32 import random
33 import platform #
34 import subprocess #
35 import tempfile # generate temporary files with random names
36 from bpy.types import Operator
37 from imghdr import what # imghdr is a python lib to identify image file types
38 from bpy.utils import register_class
40 from . import df3 # for smoke rendering
41 from . import shading # for BI POV shaders emulation
42 from . import primitives # for import and export of POV specific primitives
43 from . import nodes # for POV specific nodes
45 ##############################SF###########################
46 ##############find image texture
47 def imageFormat(imgF):
48 """Identify input image filetypes to transmit to POV."""
49 # First use the below explicit extensions to identify image file prospects
50 ext = {
51 'JPG': "jpeg",
52 'JPEG': "jpeg",
53 'GIF': "gif",
54 'TGA': "tga",
55 'IFF': "iff",
56 'PPM': "ppm",
57 'PNG': "png",
58 'SYS': "sys",
59 'TIFF': "tiff",
60 'TIF': "tiff",
61 'EXR': "exr",
62 'HDR': "hdr",
63 }.get(os.path.splitext(imgF)[-1].upper(), "")
64 # Then, use imghdr to really identify the filetype as it can be different
65 if not ext:
66 # maybe add a check for if path exists here?
67 print(" WARNING: texture image has no extension") # too verbose
69 ext = what(imgF) # imghdr is a python lib to identify image file types
70 return ext
73 def imgMap(ts):
74 """Translate mapping type from Blender UI to POV syntax and return that string."""
75 image_map = ""
76 texdata = bpy.data.textures[ts.texture]
77 if ts.mapping == 'FLAT':
78 image_map = "map_type 0 "
79 elif ts.mapping == 'SPHERE':
80 image_map = "map_type 1 "
81 elif ts.mapping == 'TUBE':
82 image_map = "map_type 2 "
84 ## map_type 3 and 4 in development (?) (ENV in pov 3.8)
85 ## for POV-Ray, currently they just seem to default back to Flat (type 0)
86 # elif ts.mapping=="?":
87 # image_map = " map_type 3 "
88 # elif ts.mapping=="?":
89 # image_map = " map_type 4 "
90 if ts.use_interpolation: # Available if image sampling class reactivated?
91 image_map += " interpolate 2 "
92 if texdata.extension == 'CLIP':
93 image_map += " once "
94 # image_map += "}"
95 # if ts.mapping=='CUBE':
96 # image_map+= "warp { cubic } rotate <-90,0,180>"
97 # no direct cube type mapping. Though this should work in POV 3.7
98 # it doesn't give that good results(best suited to environment maps?)
99 # if image_map == "":
100 # print(" No texture image found ")
101 return image_map
104 def imgMapTransforms(ts):
105 """Translate mapping transformations from Blender UI to POV syntax and return that string."""
106 # XXX TODO: unchecked textures give error of variable referenced before assignment XXX
107 # POV-Ray "scale" is not a number of repetitions factor, but ,its
108 # inverse, a standard scale factor.
109 # 0.5 Offset is needed relatively to scale because center of the
110 # scale is 0.5,0.5 in blender and 0,0 in POV
111 # Strange that the translation factor for scale is not the same as for
112 # translate.
113 # TODO: verify both matches with blender internal.
114 image_map_transforms = ""
115 image_map_transforms = (
116 "scale <%.4g,%.4g,%.4g> translate <%.4g,%.4g,%.4g>"
118 ts.scale[0],
119 ts.scale[1],
120 ts.scale[2],
121 ts.offset[0],
122 ts.offset[1],
123 ts.offset[2],
126 # image_map_transforms = (" translate <-0.5,-0.5,0.0> scale <%.4g,%.4g,%.4g> translate <%.4g,%.4g,%.4g>" % \
127 # ( 1.0 / ts.scale.x,
128 # 1.0 / ts.scale.y,
129 # 1.0 / ts.scale.z,
130 # (0.5 / ts.scale.x) + ts.offset.x,
131 # (0.5 / ts.scale.y) + ts.offset.y,
132 # ts.offset.z))
133 # image_map_transforms = ("translate <-0.5,-0.5,0> scale <-1,-1,1> * <%.4g,%.4g,%.4g> translate <0.5,0.5,0> + <%.4g,%.4g,%.4g>" % \
134 # (1.0 / ts.scale.x,
135 # 1.0 / ts.scale.y,
136 # 1.0 / ts.scale.z,
137 # ts.offset.x,
138 # ts.offset.y,
139 # ts.offset.z))
140 return image_map_transforms
143 def imgMapBG(wts):
144 """Translate world mapping from Blender UI to POV syntax and return that string."""
145 tex = bpy.data.textures[wts.texture]
146 image_mapBG = ""
147 # texture_coords refers to the mapping of world textures:
148 if wts.texture_coords == 'VIEW' or wts.texture_coords == 'GLOBAL':
149 image_mapBG = " map_type 0 "
150 elif wts.texture_coords == 'ANGMAP':
151 image_mapBG = " map_type 1 "
152 elif wts.texture_coords == 'TUBE':
153 image_mapBG = " map_type 2 "
155 if tex.use_interpolation:
156 image_mapBG += " interpolate 2 "
157 if tex.extension == 'CLIP':
158 image_mapBG += " once "
159 # image_mapBG += "}"
160 # if wts.mapping == 'CUBE':
161 # image_mapBG += "warp { cubic } rotate <-90,0,180>"
162 # no direct cube type mapping. Though this should work in POV 3.7
163 # it doesn't give that good results(best suited to environment maps?)
164 # if image_mapBG == "":
165 # print(" No background texture image found ")
166 return image_mapBG
169 def path_image(image):
170 """Conform a path string to POV syntax to avoid POV errors."""
171 return bpy.path.abspath(image.filepath, library=image.library).replace(
172 "\\", "/"
174 # .replace("\\","/") to get only forward slashes as it's what POV prefers,
175 # even on windows
178 # end find image texture
179 # -----------------------------------------------------------------------------
182 def string_strip_hyphen(name):
183 """Remove hyphen characters from a string to avoid POV errors."""
184 return name.replace("-", "")
187 def safety(name, Level):
188 """append suffix characters to names of various material declinations.
190 Material declinations are necessary to POV syntax and used in shading.py
191 by the povHasnoSpecularMaps function to create the finish map trick and
192 the suffixes avoid name collisions.
193 Keyword arguments:
194 name -- the initial material name as a string
195 Level -- the enum number of the Level being written:
196 Level=1 is for texture with No specular nor Mirror reflection
197 Level=2 is for texture with translation of spec and mir levels
198 for when no map influences them
199 Level=3 is for texture with Maximum Spec and Mirror
202 try:
203 if int(name) > 0:
204 prefix = "shader"
205 except:
206 prefix = ""
207 prefix = "shader_"
208 name = string_strip_hyphen(name)
209 if Level == 2:
210 return prefix + name
211 elif Level == 1:
212 return prefix + name + "0" # used for 0 of specular map
213 elif Level == 3:
214 return prefix + name + "1" # used for 1 of specular map
217 ##############end safety string name material
218 ##############################EndSF###########################
220 csg_list = []
223 def is_renderable(scene, ob):
224 return not ob.hide_render and ob not in csg_list
227 def renderable_objects(scene):
228 return [ob for ob in bpy.data.objects if is_renderable(scene, ob)]
231 def no_renderable_objects(scene):
232 return [ob for ob in csg_list]
235 tabLevel = 0
236 unpacked_images = []
238 user_dir = bpy.utils.resource_path('USER')
239 preview_dir = os.path.join(user_dir, "preview")
241 ## Make sure Preview directory exists and is empty
242 smokePath = os.path.join(preview_dir, "smoke.df3")
244 def write_global_setting(scene,file):
245 file.write("global_settings {\n")
246 file.write(" assumed_gamma %.6f\n"%scene.pov.assumed_gamma)
247 if scene.pov.global_settings_advanced:
248 if scene.pov.radio_enable == False:
249 file.write(" adc_bailout %.6f\n"%scene.pov.adc_bailout)
250 file.write(" ambient_light <%.6f,%.6f,%.6f>\n"%scene.pov.ambient_light[:])
251 file.write(" irid_wavelength <%.6f,%.6f,%.6f>\n"%scene.pov.irid_wavelength[:])
252 file.write(" charset %s\n"%scene.pov.charset)
253 file.write(" max_trace_level %s\n"%scene.pov.max_trace_level)
254 file.write(" max_intersections %s\n"%scene.pov.max_intersections)
255 file.write(" number_of_waves %s\n"%scene.pov.number_of_waves)
256 file.write(" noise_generator %s\n"%scene.pov.noise_generator)
258 # below properties not added to __init__ yet to avoid conflicts with material sss scale
259 # unless it would override then should be interfaced also in scene units property tab
261 # if scene.pov.sslt_enable:
262 # file.write(" mm_per_unit %s\n"%scene.pov.mm_per_unit)
263 # file.write(" subsurface {\n")
264 # file.write(" samples %s, %s\n"%(scene.pov.sslt_samples_max,scene.pov.sslt_samples_min))
265 # if scene.pov.sslt_radiosity:
266 # file.write(" radiosity on\n")
267 # file.write("}\n")
269 if scene.pov.radio_enable:
270 file.write(" radiosity {\n")
271 file.write(" pretrace_start %.6f\n"%scene.pov.radio_pretrace_start)
272 file.write(" pretrace_end %.6f\n"%scene.pov.radio_pretrace_end)
273 file.write(" count %s\n"%scene.pov.radio_count)
274 file.write(" nearest_count %s\n"%scene.pov.radio_nearest_count)
275 file.write(" error_bound %.6f\n"%scene.pov.radio_error_bound)
276 file.write(" recursion_limit %s\n"%scene.pov.radio_recursion_limit)
277 file.write(" low_error_factor %.6f\n"%scene.pov.radio_low_error_factor)
278 file.write(" gray_threshold %.6f\n"%scene.pov.radio_gray_threshold)
279 file.write(" maximum_reuse %.6f\n"%scene.pov.radio_maximum_reuse)
280 file.write(" minimum_reuse %.6f\n"%scene.pov.radio_minimum_reuse)
281 file.write(" brightness %.6f\n"%scene.pov.radio_brightness)
282 file.write(" adc_bailout %.6f\n"%scene.pov.radio_adc_bailout)
283 if scene.pov.radio_normal:
284 file.write(" normal on\n")
285 if scene.pov.radio_always_sample:
286 file.write(" always_sample on\n")
287 if scene.pov.radio_media:
288 file.write(" media on\n")
289 if scene.pov.radio_subsurface:
290 file.write(" subsurface on\n")
291 file.write(" }\n")
293 if scene.pov.photon_enable:
294 file.write(" photons {\n")
295 if scene.pov.photon_enable_count:
296 file.write(" count %s\n"%scene.pov.photon_count)
297 else:
298 file.write(" spacing %.6g\n"%scene.pov.photon_spacing)
299 if scene.pov.photon_gather:
300 file.write(" gather %s, %s\n"%(scene.pov.photon_gather_min,scene.pov.photon_gather_max))
301 if scene.pov.photon_autostop:
302 file.write(" autostop %.4g\n"%scene.pov.photon_autostop_value)
303 if scene.pov.photon_jitter_enable:
304 file.write(" jitter %.4g\n"%scene.pov.photon_jitter)
305 file.write(" max_trace_level %s\n"%scene.pov.photon_max_trace_level)
306 if scene.pov.photon_adc:
307 file.write(" adc_bailout %.6f\n"%scene.pov.photon_adc_bailout)
308 if scene.pov.photon_media_enable:
309 file.write(" media %s, %s\n"%(scene.pov.photon_media_steps,scene.pov.photon_media_factor))
310 if scene.pov.photon_map_file_save_load in {'save'}:
311 filePhName = 'Photon_map_file.ph'
312 if scene.pov.photon_map_file != '':
313 filePhName = scene.pov.photon_map_file+'.ph'
314 filePhDir = tempfile.gettempdir()
315 path = bpy.path.abspath(scene.pov.photon_map_dir)
316 if os.path.exists(path):
317 filePhDir = path
318 fullFileName = os.path.join(filePhDir,filePhName)
319 file.write(' save_file "%s"\n'%fullFileName)
320 scene.pov.photon_map_file = fullFileName
321 if scene.pov.photon_map_file_save_load in {'load'}:
322 fullFileName = bpy.path.abspath(scene.pov.photon_map_file)
323 if os.path.exists(fullFileName):
324 file.write(' load_file "%s"\n'%fullFileName)
325 file.write("}\n")
326 file.write("}\n")
330 def write_object_modifiers(scene, ob, File):
331 """Translate some object level POV statements from Blender UI
332 to POV syntax and write to exported file """
334 # Maybe return that string to be added instead of directly written.
336 '''XXX WIP
337 onceCSG = 0
338 for mod in ob.modifiers:
339 if onceCSG == 0:
340 if mod :
341 if mod.type == 'BOOLEAN':
342 if ob.pov.boolean_mod == "POV":
343 File.write("\tinside_vector <%.6g, %.6g, %.6g>\n" %
344 (ob.pov.inside_vector[0],
345 ob.pov.inside_vector[1],
346 ob.pov.inside_vector[2]))
347 onceCSG = 1
350 if ob.pov.hollow:
351 File.write("\thollow\n")
352 if ob.pov.double_illuminate:
353 File.write("\tdouble_illuminate\n")
354 if ob.pov.sturm:
355 File.write("\tsturm\n")
356 if ob.pov.no_shadow:
357 File.write("\tno_shadow\n")
358 if ob.pov.no_image:
359 File.write("\tno_image\n")
360 if ob.pov.no_reflection:
361 File.write("\tno_reflection\n")
362 if ob.pov.no_radiosity:
363 File.write("\tno_radiosity\n")
364 if ob.pov.inverse:
365 File.write("\tinverse\n")
366 if ob.pov.hierarchy:
367 File.write("\thierarchy\n")
369 # XXX, Commented definitions
371 if scene.pov.photon_enable:
372 File.write("photons {\n")
373 if ob.pov.target:
374 File.write("target %.4g\n"%ob.pov.target_value)
375 if ob.pov.refraction:
376 File.write("refraction on\n")
377 if ob.pov.reflection:
378 File.write("reflection on\n")
379 if ob.pov.pass_through:
380 File.write("pass_through\n")
381 File.write("}\n")
382 if ob.pov.object_ior > 1:
383 File.write("interior {\n")
384 File.write("ior %.4g\n"%ob.pov.object_ior)
385 if scene.pov.photon_enable and ob.pov.target and ob.pov.refraction and ob.pov.dispersion:
386 File.write("ior %.4g\n"%ob.pov.dispersion_value)
387 File.write("ior %s\n"%ob.pov.dispersion_samples)
388 if scene.pov.photon_enable == False:
389 File.write("caustics %.4g\n"%ob.pov.fake_caustics_power)
393 def write_pov(filename, scene=None, info_callback=None):
394 """Main export process from Blender UI to POV syntax and write to exported file """
396 import mathutils
398 # file = filename
399 file = open(filename, "w")
401 # Only for testing
402 if not scene:
403 scene = bpy.data.scenes[0]
405 render = scene.render
406 world = scene.world
407 global_matrix = mathutils.Matrix.Rotation(-pi / 2.0, 4, 'X')
408 comments = scene.pov.comments_enable and not scene.pov.tempfiles_enable
409 linebreaksinlists = (
410 scene.pov.list_lf_enable and not scene.pov.tempfiles_enable
412 feature_set = bpy.context.preferences.addons[
413 __package__
414 ].preferences.branch_feature_set_povray
415 using_uberpov = feature_set == 'uberpov'
416 pov_binary = PovrayRender._locate_binary()
418 if using_uberpov:
419 print("Unofficial UberPOV feature set chosen in preferences")
420 else:
421 print("Official POV-Ray 3.7 feature set chosen in preferences")
422 if 'uber' in pov_binary:
423 print(
424 "The name of the binary suggests you are probably rendering with Uber POV engine"
426 else:
427 print(
428 "The name of the binary suggests you are probably rendering with standard POV engine"
431 def setTab(tabtype, spaces):
432 TabStr = ""
433 if tabtype == 'NONE':
434 TabStr = ""
435 elif tabtype == 'TAB':
436 TabStr = "\t"
437 elif tabtype == 'SPACE':
438 TabStr = spaces * " "
439 return TabStr
441 tab = setTab(scene.pov.indentation_character, scene.pov.indentation_spaces)
442 if not scene.pov.tempfiles_enable:
444 def tabWrite(str_o):
445 """Indent POV syntax from brackets levels and write to exported file """
446 global tabLevel
447 brackets = (
448 str_o.count("{")
449 - str_o.count("}")
450 + str_o.count("[")
451 - str_o.count("]")
453 if brackets < 0:
454 tabLevel = tabLevel + brackets
455 if tabLevel < 0:
456 print("Indentation Warning: tabLevel = %s" % tabLevel)
457 tabLevel = 0
458 if tabLevel >= 1:
459 file.write("%s" % tab * tabLevel)
460 file.write(str_o)
461 if brackets > 0:
462 tabLevel = tabLevel + brackets
464 else:
466 def tabWrite(str_o):
467 """write directly to exported file if user checked autonamed temp files (faster)."""
469 file.write(str_o)
471 def uniqueName(name, nameSeq):
472 """Increment any generated POV name that could get identical to avoid collisions"""
474 if name not in nameSeq:
475 name = string_strip_hyphen(name)
476 return name
478 name_orig = name
479 i = 1
480 while name in nameSeq:
481 name = "%s_%.3d" % (name_orig, i)
482 i += 1
483 name = string_strip_hyphen(name)
484 return name
486 def writeMatrix(matrix):
487 """Translate some tranform matrix from Blender UI
488 to POV syntax and write to exported file """
489 tabWrite(
490 "matrix <%.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f>\n"
492 matrix[0][0],
493 matrix[1][0],
494 matrix[2][0],
495 matrix[0][1],
496 matrix[1][1],
497 matrix[2][1],
498 matrix[0][2],
499 matrix[1][2],
500 matrix[2][2],
501 matrix[0][3],
502 matrix[1][3],
503 matrix[2][3],
507 def MatrixAsPovString(matrix):
508 """Translate some tranform matrix from Blender UI
509 to POV syntax and return that string """
510 sMatrix = (
511 "matrix <%.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f, %.6f>\n"
513 matrix[0][0],
514 matrix[1][0],
515 matrix[2][0],
516 matrix[0][1],
517 matrix[1][1],
518 matrix[2][1],
519 matrix[0][2],
520 matrix[1][2],
521 matrix[2][2],
522 matrix[0][3],
523 matrix[1][3],
524 matrix[2][3],
527 return sMatrix
529 def writeObjectMaterial(material, ob):
530 """Translate some object level material from Blender UI (VS data level)
531 to POV interior{} syntax and write it to exported file """
533 # DH - modified some variables to be function local, avoiding RNA write
534 # this should be checked to see if it is functionally correct
536 # Commented out: always write IOR to be able to use it for SSS, Fresnel reflections...
537 # if material and material.transparency_method == 'RAYTRACE':
538 if material:
539 # But there can be only one!
540 if (
541 material.pov_subsurface_scattering.use
542 ): # SSS IOR get highest priority
543 tabWrite("interior {\n")
544 tabWrite("ior %.6f\n" % material.pov_subsurface_scattering.ior)
545 # Then the raytrace IOR taken from raytrace transparency properties and used for
546 # reflections if IOR Mirror option is checked.
547 elif material.pov.mirror_use_IOR:
548 tabWrite("interior {\n")
549 tabWrite("ior %.6f\n" % material.pov_raytrace_transparency.ior)
550 elif material.pov.transparency_method == 'Z_TRANSPARENCY':
551 tabWrite("interior {\n")
552 tabWrite("ior 1.0\n")
553 else:
554 tabWrite("interior {\n")
555 tabWrite("ior %.6f\n" % material.pov_raytrace_transparency.ior)
557 pov_fake_caustics = False
558 pov_photons_refraction = False
559 pov_photons_reflection = False
561 if material.pov.photons_reflection:
562 pov_photons_reflection = True
563 if not material.pov.refraction_caustics:
564 pov_fake_caustics = False
565 pov_photons_refraction = False
566 elif material.pov.refraction_type == "1":
567 pov_fake_caustics = True
568 pov_photons_refraction = False
569 elif material.pov.refraction_type == "2":
570 pov_fake_caustics = False
571 pov_photons_refraction = True
573 # If only Raytrace transparency is set, its IOR will be used for refraction, but user
574 # can set up 'un-physical' fresnel reflections in raytrace mirror parameters.
575 # Last, if none of the above is specified, user can set up 'un-physical' fresnel
576 # reflections in raytrace mirror parameters. And pov IOR defaults to 1.
577 if material.pov.caustics_enable:
578 if pov_fake_caustics:
579 tabWrite(
580 "caustics %.3g\n" % material.pov.fake_caustics_power
582 if pov_photons_refraction:
583 # Default of 1 means no dispersion
584 tabWrite(
585 "dispersion %.6f\n" % material.pov.photons_dispersion
587 tabWrite(
588 "dispersion_samples %.d\n"
589 % material.pov.photons_dispersion_samples
591 # TODO
592 # Other interior args
593 if (
594 material.pov.use_transparency
595 and material.pov.transparency_method == 'RAYTRACE'
597 # fade_distance
598 # In Blender this value has always been reversed compared to what tooltip says.
599 # 100.001 rather than 100 so that it does not get to 0
600 # which deactivates the feature in POV
601 tabWrite(
602 "fade_distance %.3g\n"
603 % (100.001 - material.pov_raytrace_transparency.depth_max)
605 # fade_power
606 tabWrite(
607 "fade_power %.3g\n"
608 % material.pov_raytrace_transparency.falloff
610 # fade_color
611 tabWrite(
612 "fade_color <%.3g, %.3g, %.3g>\n"
613 % material.pov.interior_fade_color[:]
616 # (variable) dispersion_samples (constant count for now)
617 tabWrite("}\n")
618 if (
619 material.pov.photons_reflection
620 or material.pov.refraction_type == "2"
622 tabWrite("photons{")
623 tabWrite("target %.3g\n" % ob.pov.spacing_multiplier)
624 if not ob.pov.collect_photons:
625 tabWrite("collect off\n")
626 if pov_photons_refraction:
627 tabWrite("refraction on\n")
628 if pov_photons_reflection:
629 tabWrite("reflection on\n")
630 tabWrite("}\n")
632 materialNames = {}
633 DEF_MAT_NAME = "" # or "Default"?
635 def exportCamera():
636 """Translate camera from Blender UI to POV syntax and write to exported file."""
637 camera = scene.camera
639 # DH disabled for now, this isn't the correct context
640 active_object = (
641 None
642 ) # bpy.context.active_object # does not always work MR
643 matrix = global_matrix @ camera.matrix_world
644 focal_point = camera.data.dof.focus_distance
646 # compute resolution
647 Qsize = render.resolution_x / render.resolution_y
648 tabWrite(
649 "#declare camLocation = <%.6f, %.6f, %.6f>;\n"
650 % matrix.translation[:]
652 tabWrite(
653 "#declare camLookAt = <%.6f, %.6f, %.6f>;\n"
654 % tuple([degrees(e) for e in matrix.to_3x3().to_euler()])
657 tabWrite("camera {\n")
658 if (
659 scene.pov.baking_enable
660 and active_object
661 and active_object.type == 'MESH'
663 tabWrite(
664 "mesh_camera{ 1 3\n"
665 ) # distribution 3 is what we want here
666 tabWrite("mesh{%s}\n" % active_object.name)
667 tabWrite("}\n")
668 tabWrite("location <0,0,.01>")
669 tabWrite("direction <0,0,-1>")
671 else:
672 if camera.data.type == 'ORTHO':
673 SensorHeightRatio = render.resolution_x * camera.data.ortho_scale / render.resolution_y
674 tabWrite("orthographic\n")
675 # Blender angle is radian so should be converted to degrees:
676 # % (camera.data.angle * (180.0 / pi) )
677 # but actually argument is not compulsory after angle in pov ortho mode
678 tabWrite("angle\n")
679 tabWrite("right <%6f, 0, 0>\n" % -camera.data.ortho_scale)
680 tabWrite("location <0, 0, 0>\n")
681 tabWrite("look_at <0, 0, -1>\n")
682 tabWrite("up <0, %6f, 0>\n" % (camera.data.ortho_scale / Qsize))
684 elif camera.data.type == 'PANO':
685 tabWrite("panoramic\n")
686 tabWrite("location <0, 0, 0>\n")
687 tabWrite("look_at <0, 0, -1>\n")
688 tabWrite("right <%s, 0, 0>\n" % -Qsize)
689 tabWrite("up <0, 1, 0>\n")
690 tabWrite(
691 "angle %f\n" % (360.0 * atan(16.0 / camera.data.lens) / pi)
693 elif camera.data.type == 'PERSP':
694 # Standard camera otherwise would be default in pov
695 tabWrite("location <0, 0, 0>\n")
696 tabWrite("look_at <0, 0, -1>\n")
697 tabWrite("right <%s, 0, 0>\n" % -Qsize)
698 tabWrite("up <0, 1, 0>\n")
699 tabWrite(
700 "angle %f\n" % ( 2 * atan(camera.data.sensor_width / 2 / camera.data.lens) * 180.0 / pi )
703 tabWrite(
704 "rotate <%.6f, %.6f, %.6f>\n"
705 % tuple([degrees(e) for e in matrix.to_3x3().to_euler()])
707 tabWrite("translate <%.6f, %.6f, %.6f>\n" % matrix.translation[:])
708 if camera.data.dof.use_dof and (
709 focal_point != 0 or camera.data.dof.focus_object
711 tabWrite(
712 "aperture %.3g\n"
713 % (1 / camera.data.dof.aperture_fstop * 1000)
715 tabWrite(
716 "blur_samples %d %d\n"
718 camera.data.pov.dof_samples_min,
719 camera.data.pov.dof_samples_max,
722 tabWrite("variance 1/%d\n" % camera.data.pov.dof_variance)
723 tabWrite("confidence %.3g\n" % camera.data.pov.dof_confidence)
724 if camera.data.dof.focus_object:
725 focalOb = scene.objects[camera.data.dof.focus_object.name]
726 matrixBlur = global_matrix @ focalOb.matrix_world
727 tabWrite(
728 "focal_point <%.4f,%.4f,%.4f>\n"
729 % matrixBlur.translation[:]
731 else:
732 tabWrite("focal_point <0, 0, %f>\n" % focal_point)
733 if camera.data.pov.normal_enable:
734 tabWrite(
735 "normal {%s %.4f turbulence %.4f scale %.4f}\n"
737 camera.data.pov.normal_patterns,
738 camera.data.pov.cam_normal,
739 camera.data.pov.turbulence,
740 camera.data.pov.scale,
743 tabWrite("}\n")
745 def exportLamps(lamps):
746 """Translate lights from Blender UI to POV syntax and write to exported file."""
748 # Incremented after each lamp export to declare its target
749 # currently used for Fresnel diffuse shader as their slope vector:
750 global lampCount
751 lampCount = 0
752 # Get all lamps
753 for ob in lamps:
754 lamp = ob.data
756 matrix = global_matrix @ ob.matrix_world
758 # Color is no longer modified by energy
759 color = tuple([c for c in lamp.color])
761 tabWrite("light_source {\n")
762 tabWrite("< 0,0,0 >\n")
763 tabWrite("color srgb<%.3g, %.3g, %.3g>\n" % color)
765 if lamp.type == 'POINT':
766 pass
767 elif lamp.type == 'SPOT':
768 tabWrite("spotlight\n")
770 # Falloff is the main radius from the centre line
771 tabWrite(
772 "falloff %.2f\n" % (degrees(lamp.spot_size) / 2.0)
773 ) # 1 TO 179 FOR BOTH
774 tabWrite(
775 "radius %.6f\n"
777 (degrees(lamp.spot_size) / 2.0)
778 * (1.0 - lamp.spot_blend)
782 # Blender does not have a tightness equivalent, 0 is most like blender default.
783 tabWrite("tightness 0\n") # 0:10f
785 tabWrite("point_at <0, 0, -1>\n")
786 if lamp.pov.use_halo:
787 tabWrite("looks_like{\n")
788 tabWrite("sphere{<0,0,0>,%.6f\n" % lamp.distance)
789 tabWrite("hollow\n")
790 tabWrite("material{\n")
791 tabWrite("texture{\n")
792 tabWrite(
793 "pigment{rgbf<1,1,1,%.4f>}\n"
794 % (lamp.pov.halo_intensity * 5.0)
796 tabWrite("}\n")
797 tabWrite("interior{\n")
798 tabWrite("media{\n")
799 tabWrite("emission 1\n")
800 tabWrite("scattering {1, 0.5}\n")
801 tabWrite("density{\n")
802 tabWrite("spherical\n")
803 tabWrite("color_map{\n")
804 tabWrite("[0.0 rgb <0,0,0>]\n")
805 tabWrite("[0.5 rgb <1,1,1>]\n")
806 tabWrite("[1.0 rgb <1,1,1>]\n")
807 tabWrite("}\n")
808 tabWrite("}\n")
809 tabWrite("}\n")
810 tabWrite("}\n")
811 tabWrite("}\n")
812 tabWrite("}\n")
813 tabWrite("}\n")
814 elif lamp.type == 'SUN':
815 tabWrite("parallel\n")
816 tabWrite("point_at <0, 0, -1>\n") # *must* be after 'parallel'
818 elif lamp.type == 'AREA':
819 tabWrite("fade_distance %.6f\n" % (lamp.distance / 2.0))
820 # Area lights have no falloff type, so always use blenders lamp quad equivalent
821 # for those?
822 tabWrite("fade_power %d\n" % 2)
823 size_x = lamp.size
824 samples_x = lamp.pov.shadow_ray_samples_x
825 if lamp.shape == 'SQUARE':
826 size_y = size_x
827 samples_y = samples_x
828 else:
829 size_y = lamp.size_y
830 samples_y = lamp.pov.shadow_ray_samples_y
832 tabWrite(
833 "area_light <%.6f,0,0>,<0,%.6f,0> %d, %d\n"
834 % (size_x, size_y, samples_x, samples_y)
836 tabWrite("area_illumination\n")
837 if lamp.pov.shadow_ray_sample_method == 'CONSTANT_JITTERED':
838 if lamp.pov.use_jitter:
839 tabWrite("jitter\n")
840 else:
841 tabWrite("adaptive 1\n")
842 tabWrite("jitter\n")
844 # No shadow checked either at global or light level:
845 if not scene.pov.use_shadows or (
846 lamp.pov.shadow_method == 'NOSHADOW'
848 tabWrite("shadowless\n")
850 # Sun shouldn't be attenuated. Area lights have no falloff attribute so they
851 # are put to type 2 attenuation a little higher above.
852 if lamp.type not in {'SUN', 'AREA'}:
853 if lamp.falloff_type == 'INVERSE_SQUARE':
854 tabWrite(
855 "fade_distance %.6f\n" % (sqrt(lamp.distance / 2.0))
857 tabWrite(
858 "fade_power %d\n" % 2
859 ) # Use blenders lamp quad equivalent
860 elif lamp.falloff_type == 'INVERSE_LINEAR':
861 tabWrite("fade_distance %.6f\n" % (lamp.distance / 2.0))
862 tabWrite("fade_power %d\n" % 1) # Use blenders lamp linear
863 elif lamp.falloff_type == 'CONSTANT':
864 tabWrite("fade_distance %.6f\n" % (lamp.distance / 2.0))
865 tabWrite("fade_power %d\n" % 3)
866 # Use blenders lamp constant equivalent no attenuation.
867 # Using Custom curve for fade power 3 for now.
868 elif lamp.falloff_type == 'CUSTOM_CURVE':
869 tabWrite("fade_power %d\n" % 4)
871 writeMatrix(matrix)
873 tabWrite("}\n")
875 lampCount += 1
877 # v(A,B) rotates vector A about origin by vector B.
878 file.write(
879 "#declare lampTarget%s= vrotate(<%.4g,%.4g,%.4g>,<%.4g,%.4g,%.4g>);\n"
881 lampCount,
882 -(ob.location.x),
883 -(ob.location.y),
884 -(ob.location.z),
885 ob.rotation_euler.x,
886 ob.rotation_euler.y,
887 ob.rotation_euler.z,
891 ####################################################################################################
892 def exportRainbows(rainbows):
893 """write all POV rainbows primitives to exported file """
894 for ob in rainbows:
895 povdataname = ob.data.name # enough?
896 angle = degrees(ob.data.spot_size / 2.5) # radians in blender (2
897 width = ob.data.spot_blend * 10
898 distance = ob.data.shadow_buffer_clip_start
899 # eps=0.0000001
900 # angle = br/(cr+eps) * 10 #eps is small epsilon variable to avoid dividing by zero
901 # width = ob.dimensions[2] #now let's say width of rainbow is the actual proxy height
902 # formerly:
903 # cz-bz # let's say width of the rainbow is height of the cone (interfacing choice
905 # v(A,B) rotates vector A about origin by vector B.
906 # and avoid a 0 length vector by adding 1
908 # file.write("#declare %s_Target= vrotate(<%.6g,%.6g,%.6g>,<%.4g,%.4g,%.4g>);\n" % \
909 # (povdataname, -(ob.location.x+0.1), -(ob.location.y+0.1), -(ob.location.z+0.1),
910 # ob.rotation_euler.x, ob.rotation_euler.y, ob.rotation_euler.z))
912 direction = (
913 ob.location.x,
914 ob.location.y,
915 ob.location.z,
916 ) # not taking matrix into account
917 rmatrix = global_matrix @ ob.matrix_world
919 # ob.rotation_euler.to_matrix().to_4x4() * mathutils.Vector((0,0,1))
920 # XXX Is result of the below offset by 90 degrees?
921 up = ob.matrix_world.to_3x3()[1].xyz # * global_matrix
923 # XXX TO CHANGE:
924 # formerly:
925 # tabWrite("#declare %s = rainbow {\n"%povdataname)
927 # clumsy for now but remove the rainbow from instancing
928 # system because not an object. use lamps later instead of meshes
930 # del data_ref[dataname]
931 tabWrite("rainbow {\n")
933 tabWrite("angle %.4f\n" % angle)
934 tabWrite("width %.4f\n" % width)
935 tabWrite("distance %.4f\n" % distance)
936 tabWrite("arc_angle %.4f\n" % ob.pov.arc_angle)
937 tabWrite("falloff_angle %.4f\n" % ob.pov.falloff_angle)
938 tabWrite("direction <%.4f,%.4f,%.4f>\n" % rmatrix.translation[:])
939 tabWrite("up <%.4f,%.4f,%.4f>\n" % (up[0], up[1], up[2]))
940 tabWrite("color_map {\n")
941 tabWrite("[0.000 color srgbt<1.0, 0.5, 1.0, 1.0>]\n")
942 tabWrite("[0.130 color srgbt<0.5, 0.5, 1.0, 0.9>]\n")
943 tabWrite("[0.298 color srgbt<0.2, 0.2, 1.0, 0.7>]\n")
944 tabWrite("[0.412 color srgbt<0.2, 1.0, 1.0, 0.4>]\n")
945 tabWrite("[0.526 color srgbt<0.2, 1.0, 0.2, 0.4>]\n")
946 tabWrite("[0.640 color srgbt<1.0, 1.0, 0.2, 0.4>]\n")
947 tabWrite("[0.754 color srgbt<1.0, 0.5, 0.2, 0.6>]\n")
948 tabWrite("[0.900 color srgbt<1.0, 0.2, 0.2, 0.7>]\n")
949 tabWrite("[1.000 color srgbt<1.0, 0.2, 0.2, 1.0>]\n")
950 tabWrite("}\n")
952 povMatName = "Default_texture"
953 # tabWrite("texture {%s}\n"%povMatName)
954 write_object_modifiers(scene, ob, file)
955 # tabWrite("rotate x*90\n")
956 # matrix = global_matrix @ ob.matrix_world
957 # writeMatrix(matrix)
958 tabWrite("}\n")
959 # continue #Don't render proxy mesh, skip to next object
961 ################################XXX LOFT, ETC.
962 def exportCurves(scene, ob):
963 """write all curves based POV primitives to exported file """
964 name_orig = "OB" + ob.name
965 dataname_orig = "DATA" + ob.data.name
967 name = string_strip_hyphen(bpy.path.clean_name(name_orig))
968 dataname = string_strip_hyphen(bpy.path.clean_name(dataname_orig))
970 global_matrix = mathutils.Matrix.Rotation(-pi / 2.0, 4, 'X')
971 matrix = global_matrix @ ob.matrix_world
972 bezier_sweep = False
973 if ob.pov.curveshape == 'sphere_sweep':
974 # inlined spheresweep macro, which itself calls Shapes.inc:
975 file.write(' #include "shapes.inc"\n')
977 file.write(
978 ' #macro Shape_Bezierpoints_Sphere_Sweep(_merge_shape, _resolution, _points_array, _radius_array)\n'
980 file.write(' //input adjusting and inspection\n')
981 file.write(' #if(_resolution <= 1)\n')
982 file.write(' #local res = 1;\n')
983 file.write(' #else\n')
984 file.write(' #local res = int(_resolution);\n')
985 file.write(' #end\n')
986 file.write(
987 ' #if(dimensions(_points_array) != 1 | dimensions(_radius_array) != 1)\n'
989 file.write(' #error ""\n')
990 file.write(
991 ' #elseif(div(dimension_size(_points_array,1),4) - dimension_size(_points_array,1)/4 != 0)\n'
993 file.write(' #error ""\n')
994 file.write(
995 ' #elseif(dimension_size(_points_array,1) != dimension_size(_radius_array,1))\n'
997 file.write(' #error ""\n')
998 file.write(' #else\n')
999 file.write(
1000 ' #local n_of_seg = div(dimension_size(_points_array,1), 4);\n'
1002 file.write(' #local ctrl_pts_array = array[n_of_seg]\n')
1003 file.write(' #local ctrl_rs_array = array[n_of_seg]\n')
1004 file.write(' #for(i, 0, n_of_seg-1)\n')
1005 file.write(
1006 ' #local ctrl_pts_array[i] = array[4] {_points_array[4*i], _points_array[4*i+1], _points_array[4*i+2], _points_array[4*i+3]}\n'
1008 file.write(
1009 ' #local ctrl_rs_array[i] = array[4] {abs(_radius_array[4*i]), abs(_radius_array[4*i+1]), abs(_radius_array[4*i+2]), abs(_radius_array[4*i+3])}\n'
1011 file.write(' #end\n')
1012 file.write(' #end\n')
1014 file.write(' //drawing\n')
1015 file.write(' #local mockup1 =\n')
1016 file.write(' #if(_merge_shape) merge{ #else union{ #end\n')
1017 file.write(' #for(i, 0, n_of_seg-1)\n')
1018 file.write(' #local has_head = true;\n')
1019 file.write(' #if(i = 0)\n')
1020 file.write(
1021 ' #if(vlength(ctrl_pts_array[i][0]-ctrl_pts_array[n_of_seg-1][3]) = 0 & ctrl_rs_array[i][0]-ctrl_rs_array[n_of_seg-1][3] <= 0)\n'
1023 file.write(' #local has_head = false;\n')
1024 file.write(' #end\n')
1025 file.write(' #else\n')
1026 file.write(
1027 ' #if(vlength(ctrl_pts_array[i][0]-ctrl_pts_array[i-1][3]) = 0 & ctrl_rs_array[i][0]-ctrl_rs_array[i-1][3] <= 0)\n'
1029 file.write(' #local has_head = false;\n')
1030 file.write(' #end\n')
1031 file.write(' #end\n')
1032 file.write(' #if(has_head = true)\n')
1033 file.write(' sphere{\n')
1034 file.write(
1035 ' ctrl_pts_array[i][0], ctrl_rs_array[i][0]\n'
1037 file.write(' }\n')
1038 file.write(' #end\n')
1039 file.write(' #local para_t = (1/2)/res;\n')
1040 file.write(
1041 ' #local this_point = ctrl_pts_array[i][0]*pow(1-para_t,3) + ctrl_pts_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_pts_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_pts_array[i][3]*pow(para_t,3);\n'
1043 file.write(
1044 ' #local this_radius = ctrl_rs_array[i][0]*pow(1-para_t,3) + ctrl_rs_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_rs_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_rs_array[i][3]*pow(para_t,3);\n'
1046 file.write(
1047 ' #if(vlength(this_point-ctrl_pts_array[i][0]) > abs(this_radius-ctrl_rs_array[i][0]))\n'
1049 file.write(' object{\n')
1050 file.write(
1051 ' Connect_Spheres(ctrl_pts_array[i][0], ctrl_rs_array[i][0], this_point, this_radius)\n'
1053 file.write(' }\n')
1054 file.write(' #end\n')
1055 file.write(' sphere{\n')
1056 file.write(' this_point, this_radius\n')
1057 file.write(' }\n')
1058 file.write(' #for(j, 1, res-1)\n')
1059 file.write(' #local last_point = this_point;\n')
1060 file.write(
1061 ' #local last_radius = this_radius;\n'
1063 file.write(' #local para_t = (1/2+j)/res;\n')
1064 file.write(
1065 ' #local this_point = ctrl_pts_array[i][0]*pow(1-para_t,3) + ctrl_pts_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_pts_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_pts_array[i][3]*pow(para_t,3);\n'
1067 file.write(
1068 ' #local this_radius = ctrl_rs_array[i][0]*pow(1-para_t,3) + ctrl_rs_array[i][1]*3*pow(1-para_t,2)*para_t + ctrl_rs_array[i][2]*3*(1-para_t)*pow(para_t,2) + ctrl_rs_array[i][3]*pow(para_t,3);\n'
1070 file.write(
1071 ' #if(vlength(this_point-last_point) > abs(this_radius-last_radius))\n'
1073 file.write(' object{\n')
1074 file.write(
1075 ' Connect_Spheres(last_point, last_radius, this_point, this_radius)\n'
1077 file.write(' }\n')
1078 file.write(' #end\n')
1079 file.write(' sphere{\n')
1080 file.write(' this_point, this_radius\n')
1081 file.write(' }\n')
1082 file.write(' #end\n')
1083 file.write(' #local last_point = this_point;\n')
1084 file.write(' #local last_radius = this_radius;\n')
1085 file.write(
1086 ' #local this_point = ctrl_pts_array[i][3];\n'
1088 file.write(
1089 ' #local this_radius = ctrl_rs_array[i][3];\n'
1091 file.write(
1092 ' #if(vlength(this_point-last_point) > abs(this_radius-last_radius))\n'
1094 file.write(' object{\n')
1095 file.write(
1096 ' Connect_Spheres(last_point, last_radius, this_point, this_radius)\n'
1098 file.write(' }\n')
1099 file.write(' #end\n')
1100 file.write(' sphere{\n')
1101 file.write(' this_point, this_radius\n')
1102 file.write(' }\n')
1103 file.write(' #end\n')
1104 file.write(' }\n')
1105 file.write(' mockup1\n')
1106 file.write(' #end\n')
1108 for spl in ob.data.splines:
1109 if spl.type == "BEZIER":
1110 bezier_sweep = True
1111 if ob.pov.curveshape in {'loft', 'birail'}:
1112 n = 0
1113 for spline in ob.data.splines:
1114 n += 1
1115 tabWrite('#declare %s%s=spline {\n' % (dataname, n))
1116 tabWrite('cubic_spline\n')
1117 lp = len(spline.points)
1118 delta = 1 / (lp)
1119 d = -delta
1120 point = spline.points[lp - 1]
1121 x, y, z, w = point.co[:]
1122 tabWrite('%.6f, <%.6f,%.6f,%.6f>\n' % (d, x, y, z))
1123 d += delta
1124 for point in spline.points:
1125 x, y, z, w = point.co[:]
1126 tabWrite('%.6f, <%.6f,%.6f,%.6f>\n' % (d, x, y, z))
1127 d += delta
1128 for i in range(2):
1129 point = spline.points[i]
1130 x, y, z, w = point.co[:]
1131 tabWrite('%.6f, <%.6f,%.6f,%.6f>\n' % (d, x, y, z))
1132 d += delta
1133 tabWrite('}\n')
1134 if ob.pov.curveshape in {'loft'}:
1135 n = len(ob.data.splines)
1136 tabWrite('#declare %s = array[%s]{\n' % (dataname, (n + 3)))
1137 tabWrite('spline{%s%s},\n' % (dataname, n))
1138 for i in range(n):
1139 tabWrite('spline{%s%s},\n' % (dataname, (i + 1)))
1140 tabWrite('spline{%s1},\n' % (dataname))
1141 tabWrite('spline{%s2}\n' % (dataname))
1142 tabWrite('}\n')
1143 # Use some of the Meshmaker.inc macro, here inlined
1144 file.write('#macro CheckFileName(FileName)\n')
1145 file.write(' #local Len=strlen(FileName);\n')
1146 file.write(' #if(Len>0)\n')
1147 file.write(' #if(file_exists(FileName))\n')
1148 file.write(' #if(Len>=4)\n')
1149 file.write(
1150 ' #local Ext=strlwr(substr(FileName,Len-3,4))\n'
1152 file.write(
1153 ' #if (strcmp(Ext,".obj")=0 | strcmp(Ext,".pcm")=0 | strcmp(Ext,".arr")=0)\n'
1155 file.write(' #local Return=99;\n')
1156 file.write(' #else\n')
1157 file.write(' #local Return=0;\n')
1158 file.write(' #end\n')
1159 file.write(' #else\n')
1160 file.write(' #local Return=0;\n')
1161 file.write(' #end\n')
1162 file.write(' #else\n')
1163 file.write(' #if(Len>=4)\n')
1164 file.write(
1165 ' #local Ext=strlwr(substr(FileName,Len-3,4))\n'
1167 file.write(
1168 ' #if (strcmp(Ext,".obj")=0 | strcmp(Ext,".pcm")=0 | strcmp(Ext,".arr")=0)\n'
1170 file.write(' #if (strcmp(Ext,".obj")=0)\n')
1171 file.write(' #local Return=2;\n')
1172 file.write(' #end\n')
1173 file.write(' #if (strcmp(Ext,".pcm")=0)\n')
1174 file.write(' #local Return=3;\n')
1175 file.write(' #end\n')
1176 file.write(' #if (strcmp(Ext,".arr")=0)\n')
1177 file.write(' #local Return=4;\n')
1178 file.write(' #end\n')
1179 file.write(' #else\n')
1180 file.write(' #local Return=1;\n')
1181 file.write(' #end\n')
1182 file.write(' #else\n')
1183 file.write(' #local Return=1;\n')
1184 file.write(' #end\n')
1185 file.write(' #end\n')
1186 file.write(' #else\n')
1187 file.write(' #local Return=1;\n')
1188 file.write(' #end\n')
1189 file.write(' (Return)\n')
1190 file.write('#end\n')
1192 file.write('#macro BuildSpline(Arr, SplType)\n')
1193 file.write(' #local Ds=dimension_size(Arr,1);\n')
1194 file.write(' #local Asc=asc(strupr(SplType));\n')
1195 file.write(' #if(Asc!=67 & Asc!=76 & Asc!=81) \n')
1196 file.write(' #local Asc=76;\n')
1197 file.write(
1198 ' #debug "\nWrong spline type defined (C/c/L/l/N/n/Q/q), using default linear_spline\\n"\n'
1200 file.write(' #end\n')
1201 file.write(' spline {\n')
1202 file.write(' #switch (Asc)\n')
1203 file.write(' #case (67) //C cubic_spline\n')
1204 file.write(' cubic_spline\n')
1205 file.write(' #break\n')
1206 file.write(' #case (76) //L linear_spline\n')
1207 file.write(' linear_spline\n')
1208 file.write(' #break\n')
1209 file.write(' #case (78) //N linear_spline\n')
1210 file.write(' natural_spline\n')
1211 file.write(' #break\n')
1212 file.write(' #case (81) //Q Quadratic_spline\n')
1213 file.write(' quadratic_spline\n')
1214 file.write(' #break\n')
1215 file.write(' #end\n')
1216 file.write(' #local Add=1/((Ds-2)-1);\n')
1217 file.write(' #local J=0-Add;\n')
1218 file.write(' #local I=0;\n')
1219 file.write(' #while (I<Ds)\n')
1220 file.write(' J\n')
1221 file.write(' Arr[I]\n')
1222 file.write(' #local I=I+1;\n')
1223 file.write(' #local J=J+Add;\n')
1224 file.write(' #end\n')
1225 file.write(' }\n')
1226 file.write('#end\n')
1228 file.write(
1229 '#macro BuildWriteMesh2(VecArr, NormArr, UVArr, U, V, FileName)\n'
1231 # suppressed some file checking from original macro because no more separate files
1232 file.write(' #local Write=0;\n')
1233 file.write(
1234 ' #debug concat("\\n\\n Building mesh2: \\n - vertex_vectors\\n")\n'
1236 file.write(' #local NumVertices=dimension_size(VecArr,1);\n')
1237 file.write(' #switch (Write)\n')
1238 file.write(' #case(1)\n')
1239 file.write(' #write(\n')
1240 file.write(' MeshFile,\n')
1241 file.write(' " vertex_vectors {\\n",\n')
1242 file.write(' " ", str(NumVertices,0,0),"\\n "\n')
1243 file.write(' )\n')
1244 file.write(' #break\n')
1245 file.write(' #case(2)\n')
1246 file.write(' #write(\n')
1247 file.write(' MeshFile,\n')
1248 file.write(' "# Vertices: ",str(NumVertices,0,0),"\\n"\n')
1249 file.write(' )\n')
1250 file.write(' #break\n')
1251 file.write(' #case(3)\n')
1252 file.write(' #write(\n')
1253 file.write(' MeshFile,\n')
1254 file.write(' str(2*NumVertices,0,0),",\\n"\n')
1255 file.write(' )\n')
1256 file.write(' #break\n')
1257 file.write(' #case(4)\n')
1258 file.write(' #write(\n')
1259 file.write(' MeshFile,\n')
1260 file.write(
1261 ' "#declare VertexVectors= array[",str(NumVertices,0,0),"] {\\n "\n'
1263 file.write(' )\n')
1264 file.write(' #break\n')
1265 file.write(' #end\n')
1266 file.write(' mesh2 {\n')
1267 file.write(' vertex_vectors {\n')
1268 file.write(' NumVertices\n')
1269 file.write(' #local I=0;\n')
1270 file.write(' #while (I<NumVertices)\n')
1271 file.write(' VecArr[I]\n')
1272 file.write(' #switch(Write)\n')
1273 file.write(' #case(1)\n')
1274 file.write(' #write(MeshFile, VecArr[I])\n')
1275 file.write(' #break\n')
1276 file.write(' #case(2)\n')
1277 file.write(' #write(\n')
1278 file.write(' MeshFile,\n')
1279 file.write(
1280 ' "v ", VecArr[I].x," ", VecArr[I].y," ", VecArr[I].z,"\\n"\n'
1282 file.write(' )\n')
1283 file.write(' #break\n')
1284 file.write(' #case(3)\n')
1285 file.write(' #write(\n')
1286 file.write(' MeshFile,\n')
1287 file.write(
1288 ' VecArr[I].x,",", VecArr[I].y,",", VecArr[I].z,",\\n"\n'
1290 file.write(' )\n')
1291 file.write(' #break\n')
1292 file.write(' #case(4)\n')
1293 file.write(' #write(MeshFile, VecArr[I])\n')
1294 file.write(' #break\n')
1295 file.write(' #end\n')
1296 file.write(' #local I=I+1;\n')
1297 file.write(' #if(Write=1 | Write=4)\n')
1298 file.write(' #if(mod(I,3)=0)\n')
1299 file.write(' #write(MeshFile,"\\n ")\n')
1300 file.write(' #end\n')
1301 file.write(' #end \n')
1302 file.write(' #end\n')
1303 file.write(' #switch(Write)\n')
1304 file.write(' #case(1)\n')
1305 file.write(' #write(MeshFile,"\\n }\\n")\n')
1306 file.write(' #break\n')
1307 file.write(' #case(2)\n')
1308 file.write(' #write(MeshFile,"\\n")\n')
1309 file.write(' #break\n')
1310 file.write(' #case(3)\n')
1311 file.write(' // do nothing\n')
1312 file.write(' #break\n')
1313 file.write(' #case(4) \n')
1314 file.write(' #write(MeshFile,"\\n}\\n")\n')
1315 file.write(' #break\n')
1316 file.write(' #end\n')
1317 file.write(' }\n')
1319 file.write(' #debug concat(" - normal_vectors\\n") \n')
1320 file.write(' #local NumVertices=dimension_size(NormArr,1);\n')
1321 file.write(' #switch(Write)\n')
1322 file.write(' #case(1)\n')
1323 file.write(' #write(\n')
1324 file.write(' MeshFile,\n')
1325 file.write(' " normal_vectors {\\n",\n')
1326 file.write(' " ", str(NumVertices,0,0),"\\n "\n')
1327 file.write(' )\n')
1328 file.write(' #break\n')
1329 file.write(' #case(2)\n')
1330 file.write(' #write(\n')
1331 file.write(' MeshFile,\n')
1332 file.write(
1333 ' "# Normals: ",str(NumVertices,0,0),"\\n"\n'
1335 file.write(' )\n')
1336 file.write(' #break\n')
1337 file.write(' #case(3)\n')
1338 file.write(' // do nothing\n')
1339 file.write(' #break\n')
1340 file.write(' #case(4)\n')
1341 file.write(' #write(\n')
1342 file.write(' MeshFile,\n')
1343 file.write(
1344 ' "#declare NormalVectors= array[",str(NumVertices,0,0),"] {\\n "\n'
1346 file.write(' )\n')
1347 file.write(' #break\n')
1348 file.write(' #end\n')
1349 file.write(' normal_vectors {\n')
1350 file.write(' NumVertices\n')
1351 file.write(' #local I=0;\n')
1352 file.write(' #while (I<NumVertices)\n')
1353 file.write(' NormArr[I]\n')
1354 file.write(' #switch(Write)\n')
1355 file.write(' #case(1)\n')
1356 file.write(' #write(MeshFile NormArr[I])\n')
1357 file.write(' #break\n')
1358 file.write(' #case(2)\n')
1359 file.write(' #write(\n')
1360 file.write(' MeshFile,\n')
1361 file.write(
1362 ' "vn ", NormArr[I].x," ", NormArr[I].y," ", NormArr[I].z,"\\n"\n'
1364 file.write(' )\n')
1365 file.write(' #break\n')
1366 file.write(' #case(3)\n')
1367 file.write(' #write(\n')
1368 file.write(' MeshFile,\n')
1369 file.write(
1370 ' NormArr[I].x,",", NormArr[I].y,",", NormArr[I].z,",\\n"\n'
1372 file.write(' )\n')
1373 file.write(' #break\n')
1374 file.write(' #case(4)\n')
1375 file.write(' #write(MeshFile NormArr[I])\n')
1376 file.write(' #break\n')
1377 file.write(' #end\n')
1378 file.write(' #local I=I+1;\n')
1379 file.write(' #if(Write=1 | Write=4) \n')
1380 file.write(' #if(mod(I,3)=0)\n')
1381 file.write(' #write(MeshFile,"\\n ")\n')
1382 file.write(' #end\n')
1383 file.write(' #end\n')
1384 file.write(' #end\n')
1385 file.write(' #switch(Write)\n')
1386 file.write(' #case(1)\n')
1387 file.write(' #write(MeshFile,"\\n }\\n")\n')
1388 file.write(' #break\n')
1389 file.write(' #case(2)\n')
1390 file.write(' #write(MeshFile,"\\n")\n')
1391 file.write(' #break\n')
1392 file.write(' #case(3)\n')
1393 file.write(' //do nothing\n')
1394 file.write(' #break\n')
1395 file.write(' #case(4)\n')
1396 file.write(' #write(MeshFile,"\\n}\\n")\n')
1397 file.write(' #break\n')
1398 file.write(' #end\n')
1399 file.write(' }\n')
1401 file.write(' #debug concat(" - uv_vectors\\n") \n')
1402 file.write(' #local NumVertices=dimension_size(UVArr,1);\n')
1403 file.write(' #switch(Write)\n')
1404 file.write(' #case(1)\n')
1405 file.write(' #write(\n')
1406 file.write(' MeshFile, \n')
1407 file.write(' " uv_vectors {\\n",\n')
1408 file.write(' " ", str(NumVertices,0,0),"\\n "\n')
1409 file.write(' )\n')
1410 file.write(' #break\n')
1411 file.write(' #case(2)\n')
1412 file.write(' #write(\n')
1413 file.write(' MeshFile,\n')
1414 file.write(
1415 ' "# UV-vectors: ",str(NumVertices,0,0),"\\n"\n'
1417 file.write(' )\n')
1418 file.write(' #break\n')
1419 file.write(' #case(3)\n')
1420 file.write(
1421 ' // do nothing, *.pcm does not support uv-vectors\n'
1423 file.write(' #break\n')
1424 file.write(' #case(4)\n')
1425 file.write(' #write(\n')
1426 file.write(' MeshFile,\n')
1427 file.write(
1428 ' "#declare UVVectors= array[",str(NumVertices,0,0),"] {\\n "\n'
1430 file.write(' )\n')
1431 file.write(' #break\n')
1432 file.write(' #end\n')
1433 file.write(' uv_vectors {\n')
1434 file.write(' NumVertices\n')
1435 file.write(' #local I=0;\n')
1436 file.write(' #while (I<NumVertices)\n')
1437 file.write(' UVArr[I]\n')
1438 file.write(' #switch(Write)\n')
1439 file.write(' #case(1)\n')
1440 file.write(' #write(MeshFile UVArr[I])\n')
1441 file.write(' #break\n')
1442 file.write(' #case(2)\n')
1443 file.write(' #write(\n')
1444 file.write(' MeshFile,\n')
1445 file.write(
1446 ' "vt ", UVArr[I].u," ", UVArr[I].v,"\\n"\n'
1448 file.write(' )\n')
1449 file.write(' #break\n')
1450 file.write(' #case(3)\n')
1451 file.write(' //do nothing\n')
1452 file.write(' #break\n')
1453 file.write(' #case(4)\n')
1454 file.write(' #write(MeshFile UVArr[I])\n')
1455 file.write(' #break\n')
1456 file.write(' #end\n')
1457 file.write(' #local I=I+1; \n')
1458 file.write(' #if(Write=1 | Write=4)\n')
1459 file.write(' #if(mod(I,3)=0)\n')
1460 file.write(' #write(MeshFile,"\\n ")\n')
1461 file.write(' #end \n')
1462 file.write(' #end\n')
1463 file.write(' #end \n')
1464 file.write(' #switch(Write)\n')
1465 file.write(' #case(1)\n')
1466 file.write(' #write(MeshFile,"\\n }\\n")\n')
1467 file.write(' #break\n')
1468 file.write(' #case(2)\n')
1469 file.write(' #write(MeshFile,"\\n")\n')
1470 file.write(' #break\n')
1471 file.write(' #case(3)\n')
1472 file.write(' //do nothing\n')
1473 file.write(' #break\n')
1474 file.write(' #case(4)\n')
1475 file.write(' #write(MeshFile,"\\n}\\n")\n')
1476 file.write(' #break\n')
1477 file.write(' #end\n')
1478 file.write(' }\n')
1479 file.write('\n')
1480 file.write(' #debug concat(" - face_indices\\n") \n')
1481 file.write(' #declare NumFaces=U*V*2;\n')
1482 file.write(' #switch(Write)\n')
1483 file.write(' #case(1)\n')
1484 file.write(' #write(\n')
1485 file.write(' MeshFile,\n')
1486 file.write(' " face_indices {\\n"\n')
1487 file.write(' " ", str(NumFaces,0,0),"\\n "\n')
1488 file.write(' )\n')
1489 file.write(' #break\n')
1490 file.write(' #case(2)\n')
1491 file.write(' #write (\n')
1492 file.write(' MeshFile,\n')
1493 file.write(' "# faces: ",str(NumFaces,0,0),"\\n"\n')
1494 file.write(' )\n')
1495 file.write(' #break\n')
1496 file.write(' #case(3)\n')
1497 file.write(' #write (\n')
1498 file.write(' MeshFile,\n')
1499 file.write(' "0,",str(NumFaces,0,0),",\\n"\n')
1500 file.write(' )\n')
1501 file.write(' #break\n')
1502 file.write(' #case(4)\n')
1503 file.write(' #write(\n')
1504 file.write(' MeshFile,\n')
1505 file.write(
1506 ' "#declare FaceIndices= array[",str(NumFaces,0,0),"] {\\n "\n'
1508 file.write(' )\n')
1509 file.write(' #break\n')
1510 file.write(' #end\n')
1511 file.write(' face_indices {\n')
1512 file.write(' NumFaces\n')
1513 file.write(' #local I=0;\n')
1514 file.write(' #local H=0;\n')
1515 file.write(' #local NumVertices=dimension_size(VecArr,1);\n')
1516 file.write(' #while (I<V)\n')
1517 file.write(' #local J=0;\n')
1518 file.write(' #while (J<U)\n')
1519 file.write(' #local Ind=(I*U)+I+J;\n')
1520 file.write(
1521 ' <Ind, Ind+1, Ind+U+2>, <Ind, Ind+U+1, Ind+U+2>\n'
1523 file.write(' #switch(Write)\n')
1524 file.write(' #case(1)\n')
1525 file.write(' #write(\n')
1526 file.write(' MeshFile,\n')
1527 file.write(
1528 ' <Ind, Ind+1, Ind+U+2>, <Ind, Ind+U+1, Ind+U+2>\n'
1530 file.write(' )\n')
1531 file.write(' #break\n')
1532 file.write(' #case(2)\n')
1533 file.write(' #write(\n')
1534 file.write(' MeshFile,\n')
1535 file.write(
1536 ' "f ",Ind+1,"/",Ind+1,"/",Ind+1," ",Ind+1+1,"/",Ind+1+1,"/",Ind+1+1," ",Ind+U+2+1,"/",Ind+U+2+1,"/",Ind+U+2+1,"\\n",\n'
1538 file.write(
1539 ' "f ",Ind+U+1+1,"/",Ind+U+1+1,"/",Ind+U+1+1," ",Ind+1,"/",Ind+1,"/",Ind+1," ",Ind+U+2+1,"/",Ind+U+2+1,"/",Ind+U+2+1,"\\n"\n'
1541 file.write(' )\n')
1542 file.write(' #break\n')
1543 file.write(' #case(3)\n')
1544 file.write(' #write(\n')
1545 file.write(' MeshFile,\n')
1546 file.write(
1547 ' Ind,",",Ind+NumVertices,",",Ind+1,",",Ind+1+NumVertices,",",Ind+U+2,",",Ind+U+2+NumVertices,",\\n"\n'
1549 file.write(
1550 ' Ind+U+1,",",Ind+U+1+NumVertices,",",Ind,",",Ind+NumVertices,",",Ind+U+2,",",Ind+U+2+NumVertices,",\\n"\n'
1552 file.write(' )\n')
1553 file.write(' #break\n')
1554 file.write(' #case(4)\n')
1555 file.write(' #write(\n')
1556 file.write(' MeshFile,\n')
1557 file.write(
1558 ' <Ind, Ind+1, Ind+U+2>, <Ind, Ind+U+1, Ind+U+2>\n'
1560 file.write(' )\n')
1561 file.write(' #break\n')
1562 file.write(' #end\n')
1563 file.write(' #local J=J+1;\n')
1564 file.write(' #local H=H+1;\n')
1565 file.write(' #if(Write=1 | Write=4)\n')
1566 file.write(' #if(mod(H,3)=0)\n')
1567 file.write(' #write(MeshFile,"\\n ")\n')
1568 file.write(' #end \n')
1569 file.write(' #end\n')
1570 file.write(' #end\n')
1571 file.write(' #local I=I+1;\n')
1572 file.write(' #end\n')
1573 file.write(' }\n')
1574 file.write(' #switch(Write)\n')
1575 file.write(' #case(1)\n')
1576 file.write(' #write(MeshFile, "\\n }\\n}")\n')
1577 file.write(' #fclose MeshFile\n')
1578 file.write(' #debug concat(" Done writing\\n")\n')
1579 file.write(' #break\n')
1580 file.write(' #case(2)\n')
1581 file.write(' #fclose MeshFile\n')
1582 file.write(' #debug concat(" Done writing\\n")\n')
1583 file.write(' #break\n')
1584 file.write(' #case(3)\n')
1585 file.write(' #fclose MeshFile\n')
1586 file.write(' #debug concat(" Done writing\\n")\n')
1587 file.write(' #break\n')
1588 file.write(' #case(4)\n')
1589 file.write(' #write(MeshFile, "\\n}\\n}")\n')
1590 file.write(' #fclose MeshFile\n')
1591 file.write(' #debug concat(" Done writing\\n")\n')
1592 file.write(' #break\n')
1593 file.write(' #end\n')
1594 file.write(' }\n')
1595 file.write('#end\n')
1597 file.write(
1598 '#macro MSM(SplineArray, SplRes, Interp_type, InterpRes, FileName)\n'
1600 file.write(' #declare Build=CheckFileName(FileName);\n')
1601 file.write(' #if(Build=0)\n')
1602 file.write(
1603 ' #debug concat("\\n Parsing mesh2 from file: ", FileName, "\\n")\n'
1605 file.write(' #include FileName\n')
1606 file.write(' object{Surface}\n')
1607 file.write(' #else\n')
1608 file.write(' #local NumVertices=(SplRes+1)*(InterpRes+1);\n')
1609 file.write(' #local NumFaces=SplRes*InterpRes*2;\n')
1610 file.write(
1611 ' #debug concat("\\n Calculating ",str(NumVertices,0,0)," vertices for ", str(NumFaces,0,0)," triangles\\n\\n")\n'
1613 file.write(' #local VecArr=array[NumVertices]\n')
1614 file.write(' #local NormArr=array[NumVertices]\n')
1615 file.write(' #local UVArr=array[NumVertices]\n')
1616 file.write(' #local N=dimension_size(SplineArray,1);\n')
1617 file.write(' #local TempSplArr0=array[N];\n')
1618 file.write(' #local TempSplArr1=array[N];\n')
1619 file.write(' #local TempSplArr2=array[N];\n')
1620 file.write(' #local PosStep=1/SplRes;\n')
1621 file.write(' #local InterpStep=1/InterpRes;\n')
1622 file.write(' #local Count=0;\n')
1623 file.write(' #local Pos=0;\n')
1624 file.write(' #while(Pos<=1)\n')
1625 file.write(' #local I=0;\n')
1626 file.write(' #if (Pos=0)\n')
1627 file.write(' #while (I<N)\n')
1628 file.write(
1629 ' #local Spl=spline{SplineArray[I]}\n'
1631 file.write(
1632 ' #local TempSplArr0[I]=<0,0,0>+Spl(Pos);\n'
1634 file.write(
1635 ' #local TempSplArr1[I]=<0,0,0>+Spl(Pos+PosStep);\n'
1637 file.write(
1638 ' #local TempSplArr2[I]=<0,0,0>+Spl(Pos-PosStep);\n'
1640 file.write(' #local I=I+1;\n')
1641 file.write(' #end\n')
1642 file.write(
1643 ' #local S0=BuildSpline(TempSplArr0, Interp_type)\n'
1645 file.write(
1646 ' #local S1=BuildSpline(TempSplArr1, Interp_type)\n'
1648 file.write(
1649 ' #local S2=BuildSpline(TempSplArr2, Interp_type)\n'
1651 file.write(' #else\n')
1652 file.write(' #while (I<N)\n')
1653 file.write(
1654 ' #local Spl=spline{SplineArray[I]}\n'
1656 file.write(
1657 ' #local TempSplArr1[I]=<0,0,0>+Spl(Pos+PosStep);\n'
1659 file.write(' #local I=I+1;\n')
1660 file.write(' #end\n')
1661 file.write(
1662 ' #local S1=BuildSpline(TempSplArr1, Interp_type)\n'
1664 file.write(' #end\n')
1665 file.write(' #local J=0;\n')
1666 file.write(' #while (J<=1)\n')
1667 file.write(' #local P0=<0,0,0>+S0(J);\n')
1668 file.write(' #local P1=<0,0,0>+S1(J);\n')
1669 file.write(' #local P2=<0,0,0>+S2(J);\n')
1670 file.write(' #local P3=<0,0,0>+S0(J+InterpStep);\n')
1671 file.write(' #local P4=<0,0,0>+S0(J-InterpStep);\n')
1672 file.write(' #local B1=P4-P0;\n')
1673 file.write(' #local B2=P2-P0;\n')
1674 file.write(' #local B3=P3-P0;\n')
1675 file.write(' #local B4=P1-P0;\n')
1676 file.write(' #local N1=vcross(B1,B2);\n')
1677 file.write(' #local N2=vcross(B2,B3);\n')
1678 file.write(' #local N3=vcross(B3,B4);\n')
1679 file.write(' #local N4=vcross(B4,B1);\n')
1680 file.write(
1681 ' #local Norm=vnormalize((N1+N2+N3+N4));\n'
1683 file.write(' #local VecArr[Count]=P0;\n')
1684 file.write(' #local NormArr[Count]=Norm;\n')
1685 file.write(' #local UVArr[Count]=<J,Pos>;\n')
1686 file.write(' #local J=J+InterpStep;\n')
1687 file.write(' #local Count=Count+1;\n')
1688 file.write(' #end\n')
1689 file.write(' #local S2=spline{S0}\n')
1690 file.write(' #local S0=spline{S1}\n')
1691 file.write(
1692 ' #debug concat("\\r Done ", str(Count,0,0)," vertices : ", str(100*Count/NumVertices,0,2)," %")\n'
1694 file.write(' #local Pos=Pos+PosStep;\n')
1695 file.write(' #end\n')
1696 file.write(
1697 ' BuildWriteMesh2(VecArr, NormArr, UVArr, InterpRes, SplRes, "")\n'
1699 file.write(' #end\n')
1700 file.write('#end\n\n')
1702 file.write(
1703 '#macro Coons(Spl1, Spl2, Spl3, Spl4, Iter_U, Iter_V, FileName)\n'
1705 file.write(' #declare Build=CheckFileName(FileName);\n')
1706 file.write(' #if(Build=0)\n')
1707 file.write(
1708 ' #debug concat("\\n Parsing mesh2 from file: ", FileName, "\\n")\n'
1710 file.write(' #include FileName\n')
1711 file.write(' object{Surface}\n')
1712 file.write(' #else\n')
1713 file.write(' #local NumVertices=(Iter_U+1)*(Iter_V+1);\n')
1714 file.write(' #local NumFaces=Iter_U*Iter_V*2;\n')
1715 file.write(
1716 ' #debug concat("\\n Calculating ", str(NumVertices,0,0), " vertices for ",str(NumFaces,0,0), " triangles\\n\\n")\n'
1718 file.write(' #declare VecArr=array[NumVertices] \n')
1719 file.write(' #declare NormArr=array[NumVertices] \n')
1720 file.write(' #local UVArr=array[NumVertices] \n')
1721 file.write(' #local Spl1_0=Spl1(0);\n')
1722 file.write(' #local Spl2_0=Spl2(0);\n')
1723 file.write(' #local Spl3_0=Spl3(0);\n')
1724 file.write(' #local Spl4_0=Spl4(0);\n')
1725 file.write(' #local UStep=1/Iter_U;\n')
1726 file.write(' #local VStep=1/Iter_V;\n')
1727 file.write(' #local Count=0;\n')
1728 file.write(' #local I=0;\n')
1729 file.write(' #while (I<=1)\n')
1730 file.write(' #local Im=1-I;\n')
1731 file.write(' #local J=0;\n')
1732 file.write(' #while (J<=1)\n')
1733 file.write(' #local Jm=1-J;\n')
1734 file.write(
1735 ' #local C0=Im*Jm*(Spl1_0)+Im*J*(Spl2_0)+I*J*(Spl3_0)+I*Jm*(Spl4_0);\n'
1737 file.write(
1738 ' #local P0=LInterpolate(I, Spl1(J), Spl3(Jm)) + \n'
1740 file.write(
1741 ' LInterpolate(Jm, Spl2(I), Spl4(Im))-C0;\n'
1743 file.write(' #declare VecArr[Count]=P0;\n')
1744 file.write(' #local UVArr[Count]=<J,I>;\n')
1745 file.write(' #local J=J+UStep;\n')
1746 file.write(' #local Count=Count+1;\n')
1747 file.write(' #end\n')
1748 file.write(' #debug concat(\n')
1749 file.write(
1750 ' "\r Done ", str(Count,0,0)," vertices : ",\n'
1752 file.write(' str(100*Count/NumVertices,0,2)," %"\n')
1753 file.write(' )\n')
1754 file.write(' #local I=I+VStep;\n')
1755 file.write(' #end\n')
1756 file.write(
1757 ' #debug "\r Normals "\n'
1759 file.write(' #local Count=0;\n')
1760 file.write(' #local I=0;\n')
1761 file.write(' #while (I<=Iter_V)\n')
1762 file.write(' #local J=0;\n')
1763 file.write(' #while (J<=Iter_U)\n')
1764 file.write(' #local Ind=(I*Iter_U)+I+J;\n')
1765 file.write(' #local P0=VecArr[Ind];\n')
1766 file.write(' #if(J=0)\n')
1767 file.write(' #local P1=P0+(P0-VecArr[Ind+1]);\n')
1768 file.write(' #else\n')
1769 file.write(' #local P1=VecArr[Ind-1];\n')
1770 file.write(' #end\n')
1771 file.write(' #if (J=Iter_U)\n')
1772 file.write(' #local P2=P0+(P0-VecArr[Ind-1]);\n')
1773 file.write(' #else\n')
1774 file.write(' #local P2=VecArr[Ind+1];\n')
1775 file.write(' #end\n')
1776 file.write(' #if (I=0)\n')
1777 file.write(
1778 ' #local P3=P0+(P0-VecArr[Ind+Iter_U+1]);\n'
1780 file.write(' #else\n')
1781 file.write(' #local P3=VecArr[Ind-Iter_U-1];\n')
1782 file.write(' #end\n')
1783 file.write(' #if (I=Iter_V)\n')
1784 file.write(
1785 ' #local P4=P0+(P0-VecArr[Ind-Iter_U-1]);\n'
1787 file.write(' #else\n')
1788 file.write(' #local P4=VecArr[Ind+Iter_U+1];\n')
1789 file.write(' #end\n')
1790 file.write(' #local B1=P4-P0;\n')
1791 file.write(' #local B2=P2-P0;\n')
1792 file.write(' #local B3=P3-P0;\n')
1793 file.write(' #local B4=P1-P0;\n')
1794 file.write(' #local N1=vcross(B1,B2);\n')
1795 file.write(' #local N2=vcross(B2,B3);\n')
1796 file.write(' #local N3=vcross(B3,B4);\n')
1797 file.write(' #local N4=vcross(B4,B1);\n')
1798 file.write(' #local Norm=vnormalize((N1+N2+N3+N4));\n')
1799 file.write(' #declare NormArr[Count]=Norm;\n')
1800 file.write(' #local J=J+1;\n')
1801 file.write(' #local Count=Count+1;\n')
1802 file.write(' #end\n')
1803 file.write(
1804 ' #debug concat("\r Done ", str(Count,0,0)," normals : ",str(100*Count/NumVertices,0,2), " %")\n'
1806 file.write(' #local I=I+1;\n')
1807 file.write(' #end\n')
1808 file.write(
1809 ' BuildWriteMesh2(VecArr, NormArr, UVArr, Iter_U, Iter_V, FileName)\n'
1811 file.write(' #end\n')
1812 file.write('#end\n\n')
1813 # Empty curves
1814 if len(ob.data.splines) == 0:
1815 tabWrite("\n//dummy sphere to represent empty curve location\n")
1816 tabWrite("#declare %s =\n" % dataname)
1817 tabWrite(
1818 "sphere {<%.6g, %.6g, %.6g>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n\n"
1819 % (ob.location.x, ob.location.y, ob.location.z)
1820 ) # ob.name > povdataname)
1821 # And non empty curves
1822 else:
1823 if bezier_sweep == False:
1824 tabWrite("#declare %s =\n" % dataname)
1825 if ob.pov.curveshape == 'sphere_sweep' and bezier_sweep == False:
1826 tabWrite("union {\n")
1827 for spl in ob.data.splines:
1828 if spl.type != "BEZIER":
1829 spl_type = "linear"
1830 if spl.type == "NURBS":
1831 spl_type = "cubic"
1832 points = spl.points
1833 numPoints = len(points)
1834 if spl.use_cyclic_u:
1835 numPoints += 3
1837 tabWrite(
1838 "sphere_sweep { %s_spline %s,\n"
1839 % (spl_type, numPoints)
1841 if spl.use_cyclic_u:
1842 pt1 = points[len(points) - 1]
1843 wpt1 = pt1.co
1844 tabWrite(
1845 "<%.4g,%.4g,%.4g>,%.4g\n"
1847 wpt1[0],
1848 wpt1[1],
1849 wpt1[2],
1850 pt1.radius * ob.data.bevel_depth,
1853 for pt in points:
1854 wpt = pt.co
1855 tabWrite(
1856 "<%.4g,%.4g,%.4g>,%.4g\n"
1858 wpt[0],
1859 wpt[1],
1860 wpt[2],
1861 pt.radius * ob.data.bevel_depth,
1864 if spl.use_cyclic_u:
1865 for i in range(0, 2):
1866 endPt = points[i]
1867 wpt = endPt.co
1868 tabWrite(
1869 "<%.4g,%.4g,%.4g>,%.4g\n"
1871 wpt[0],
1872 wpt[1],
1873 wpt[2],
1874 endPt.radius * ob.data.bevel_depth,
1878 tabWrite("}\n")
1879 # below not used yet?
1880 if ob.pov.curveshape == 'sor':
1881 for spl in ob.data.splines:
1882 if spl.type in {'POLY', 'NURBS'}:
1883 points = spl.points
1884 numPoints = len(points)
1885 tabWrite("sor { %s,\n" % numPoints)
1886 for pt in points:
1887 wpt = pt.co
1888 tabWrite("<%.4g,%.4g>\n" % (wpt[0], wpt[1]))
1889 else:
1890 tabWrite("box { 0,0\n")
1891 if ob.pov.curveshape in {'lathe', 'prism'}:
1892 spl = ob.data.splines[0]
1893 if spl.type == "BEZIER":
1894 points = spl.bezier_points
1895 lenCur = len(points) - 1
1896 lenPts = lenCur * 4
1897 ifprism = ''
1898 if ob.pov.curveshape in {'prism'}:
1899 height = ob.data.extrude
1900 ifprism = '-%s, %s,' % (height, height)
1901 lenCur += 1
1902 lenPts += 4
1903 tabWrite(
1904 "%s { bezier_spline %s %s,\n"
1905 % (ob.pov.curveshape, ifprism, lenPts)
1907 for i in range(0, lenCur):
1908 p1 = points[i].co
1909 pR = points[i].handle_right
1910 end = i + 1
1911 if i == lenCur - 1 and ob.pov.curveshape in {'prism'}:
1912 end = 0
1913 pL = points[end].handle_left
1914 p2 = points[end].co
1915 line = "<%.4g,%.4g>" % (p1[0], p1[1])
1916 line += "<%.4g,%.4g>" % (pR[0], pR[1])
1917 line += "<%.4g,%.4g>" % (pL[0], pL[1])
1918 line += "<%.4g,%.4g>" % (p2[0], p2[1])
1919 tabWrite("%s\n" % line)
1920 else:
1921 points = spl.points
1922 lenCur = len(points)
1923 lenPts = lenCur
1924 ifprism = ''
1925 if ob.pov.curveshape in {'prism'}:
1926 height = ob.data.extrude
1927 ifprism = '-%s, %s,' % (height, height)
1928 lenPts += 3
1929 spl_type = 'quadratic'
1930 if spl.type == 'POLY':
1931 spl_type = 'linear'
1932 tabWrite(
1933 "%s { %s_spline %s %s,\n"
1934 % (ob.pov.curveshape, spl_type, ifprism, lenPts)
1936 if ob.pov.curveshape in {'prism'}:
1937 pt = points[len(points) - 1]
1938 wpt = pt.co
1939 tabWrite("<%.4g,%.4g>\n" % (wpt[0], wpt[1]))
1940 for pt in points:
1941 wpt = pt.co
1942 tabWrite("<%.4g,%.4g>\n" % (wpt[0], wpt[1]))
1943 if ob.pov.curveshape in {'prism'}:
1944 for i in range(2):
1945 pt = points[i]
1946 wpt = pt.co
1947 tabWrite("<%.4g,%.4g>\n" % (wpt[0], wpt[1]))
1948 if bezier_sweep:
1949 for p in range(len(ob.data.splines)):
1950 br = []
1951 depth = ob.data.bevel_depth
1952 spl = ob.data.splines[p]
1953 points = spl.bezier_points
1954 lenCur = len(points) - 1
1955 numPoints = lenCur * 4
1956 if spl.use_cyclic_u:
1957 lenCur += 1
1958 numPoints += 4
1959 tabWrite(
1960 "#declare %s_points_%s = array[%s]{\n"
1961 % (dataname, p, numPoints)
1963 for i in range(lenCur):
1964 p1 = points[i].co
1965 pR = points[i].handle_right
1966 end = i + 1
1967 if spl.use_cyclic_u and i == (lenCur - 1):
1968 end = 0
1969 pL = points[end].handle_left
1970 p2 = points[end].co
1971 r3 = points[end].radius * depth
1972 r0 = points[i].radius * depth
1973 r1 = 2 / 3 * r0 + 1 / 3 * r3
1974 r2 = 1 / 3 * r0 + 2 / 3 * r3
1975 br.append((r0, r1, r2, r3))
1976 line = "<%.4g,%.4g,%.4f>" % (p1[0], p1[1], p1[2])
1977 line += "<%.4g,%.4g,%.4f>" % (pR[0], pR[1], pR[2])
1978 line += "<%.4g,%.4g,%.4f>" % (pL[0], pL[1], pL[2])
1979 line += "<%.4g,%.4g,%.4f>" % (p2[0], p2[1], p2[2])
1980 tabWrite("%s\n" % line)
1981 tabWrite("}\n")
1982 tabWrite(
1983 "#declare %s_radii_%s = array[%s]{\n"
1984 % (dataname, p, len(br) * 4)
1986 for Tuple in br:
1987 tabWrite(
1988 '%.4f,%.4f,%.4f,%.4f\n'
1989 % (Tuple[0], Tuple[1], Tuple[2], Tuple[3])
1991 tabWrite("}\n")
1992 if len(ob.data.splines) == 1:
1993 tabWrite('#declare %s = object{\n' % dataname)
1994 tabWrite(
1995 ' Shape_Bezierpoints_Sphere_Sweep(yes,%s, %s_points_%s, %s_radii_%s) \n'
1996 % (ob.data.resolution_u, dataname, p, dataname, p)
1998 else:
1999 tabWrite('#declare %s = union{\n' % dataname)
2000 for p in range(len(ob.data.splines)):
2001 tabWrite(
2002 ' object{Shape_Bezierpoints_Sphere_Sweep(yes,%s, %s_points_%s, %s_radii_%s)} \n'
2003 % (ob.data.resolution_u, dataname, p, dataname, p)
2005 # tabWrite('#include "bezier_spheresweep.inc"\n') #now inlined
2006 # tabWrite('#declare %s = object{Shape_Bezierpoints_Sphere_Sweep(yes,%s, %s_bezier_points, %.4f) \n'%(dataname,ob.data.resolution_u,dataname,ob.data.bevel_depth))
2007 if ob.pov.curveshape in {'loft'}:
2008 tabWrite(
2009 'object {MSM(%s,%s,"c",%s,"")\n'
2010 % (dataname, ob.pov.res_u, ob.pov.res_v)
2012 if ob.pov.curveshape in {'birail'}:
2013 splines = '%s1,%s2,%s3,%s4' % (
2014 dataname,
2015 dataname,
2016 dataname,
2017 dataname,
2019 tabWrite(
2020 'object {Coons(%s, %s, %s, "")\n'
2021 % (splines, ob.pov.res_u, ob.pov.res_v)
2023 povMatName = "Default_texture"
2024 if ob.active_material:
2025 # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
2026 try:
2027 material = ob.active_material
2028 writeObjectMaterial(material, ob)
2029 except IndexError:
2030 print(me)
2031 # tabWrite("texture {%s}\n"%povMatName)
2032 if ob.pov.curveshape in {'prism'}:
2033 tabWrite("rotate <90,0,0>\n")
2034 tabWrite("scale y*-1\n")
2035 tabWrite("}\n")
2037 #################################################################
2039 def exportMeta(metas):
2040 """write all POV blob primitives and Blender Metas to exported file """
2041 # TODO - blenders 'motherball' naming is not supported.
2043 if comments and len(metas) >= 1:
2044 file.write("//--Blob objects--\n\n")
2045 # Get groups of metaballs by blender name prefix.
2046 meta_group = {}
2047 meta_elems = {}
2048 for ob in metas:
2049 prefix = ob.name.split(".")[0]
2050 if not prefix in meta_group:
2051 meta_group[prefix] = ob # .data.threshold
2052 elems = [
2053 (elem, ob)
2054 for elem in ob.data.elements
2055 if elem.type
2056 in {'BALL', 'ELLIPSOID', 'CAPSULE', 'CUBE', 'PLANE'}
2058 if prefix in meta_elems:
2059 meta_elems[prefix].extend(elems)
2060 else:
2061 meta_elems[prefix] = elems
2063 # empty metaball
2064 if len(elems) == 0:
2065 tabWrite("\n//dummy sphere to represent empty meta location\n")
2066 tabWrite(
2067 "sphere {<%.6g, %.6g, %.6g>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n\n"
2068 % (ob.location.x, ob.location.y, ob.location.z)
2069 ) # ob.name > povdataname)
2070 # other metaballs
2071 else:
2072 for mg, ob in meta_group.items():
2073 if len(meta_elems[mg]) != 0:
2074 tabWrite(
2075 "blob{threshold %.4g // %s \n"
2076 % (ob.data.threshold, mg)
2078 for elems in meta_elems[mg]:
2079 elem = elems[0]
2080 loc = elem.co
2081 stiffness = elem.stiffness
2082 if elem.use_negative:
2083 stiffness = -stiffness
2084 if elem.type == 'BALL':
2085 tabWrite(
2086 "sphere { <%.6g, %.6g, %.6g>, %.4g, %.4g "
2088 loc.x,
2089 loc.y,
2090 loc.z,
2091 elem.radius,
2092 stiffness,
2095 writeMatrix(
2096 global_matrix @ elems[1].matrix_world
2098 tabWrite("}\n")
2099 elif elem.type == 'ELLIPSOID':
2100 tabWrite(
2101 "sphere{ <%.6g, %.6g, %.6g>,%.4g,%.4g "
2103 loc.x / elem.size_x,
2104 loc.y / elem.size_y,
2105 loc.z / elem.size_z,
2106 elem.radius,
2107 stiffness,
2110 tabWrite(
2111 "scale <%.6g, %.6g, %.6g>"
2112 % (elem.size_x, elem.size_y, elem.size_z)
2114 writeMatrix(
2115 global_matrix @ elems[1].matrix_world
2117 tabWrite("}\n")
2118 elif elem.type == 'CAPSULE':
2119 tabWrite(
2120 "cylinder{ <%.6g, %.6g, %.6g>,<%.6g, %.6g, %.6g>,%.4g,%.4g "
2122 (loc.x - elem.size_x),
2123 (loc.y),
2124 (loc.z),
2125 (loc.x + elem.size_x),
2126 (loc.y),
2127 (loc.z),
2128 elem.radius,
2129 stiffness,
2132 # tabWrite("scale <%.6g, %.6g, %.6g>" % (elem.size_x, elem.size_y, elem.size_z))
2133 writeMatrix(
2134 global_matrix @ elems[1].matrix_world
2136 tabWrite("}\n")
2138 elif elem.type == 'CUBE':
2139 tabWrite(
2140 "cylinder { -x*8, +x*8,%.4g,%.4g translate<%.6g,%.6g,%.6g> scale <1/4,1,1> scale <%.6g, %.6g, %.6g>\n"
2142 elem.radius * 2.0,
2143 stiffness / 4.0,
2144 loc.x,
2145 loc.y,
2146 loc.z,
2147 elem.size_x,
2148 elem.size_y,
2149 elem.size_z,
2152 writeMatrix(
2153 global_matrix @ elems[1].matrix_world
2155 tabWrite("}\n")
2156 tabWrite(
2157 "cylinder { -y*8, +y*8,%.4g,%.4g translate<%.6g,%.6g,%.6g> scale <1,1/4,1> scale <%.6g, %.6g, %.6g>\n"
2159 elem.radius * 2.0,
2160 stiffness / 4.0,
2161 loc.x,
2162 loc.y,
2163 loc.z,
2164 elem.size_x,
2165 elem.size_y,
2166 elem.size_z,
2169 writeMatrix(
2170 global_matrix @ elems[1].matrix_world
2172 tabWrite("}\n")
2173 tabWrite(
2174 "cylinder { -z*8, +z*8,%.4g,%.4g translate<%.6g,%.6g,%.6g> scale <1,1,1/4> scale <%.6g, %.6g, %.6g>\n"
2176 elem.radius * 2.0,
2177 stiffness / 4.0,
2178 loc.x,
2179 loc.y,
2180 loc.z,
2181 elem.size_x,
2182 elem.size_y,
2183 elem.size_z,
2186 writeMatrix(
2187 global_matrix @ elems[1].matrix_world
2189 tabWrite("}\n")
2191 elif elem.type == 'PLANE':
2192 tabWrite(
2193 "cylinder { -x*8, +x*8,%.4g,%.4g translate<%.6g,%.6g,%.6g> scale <1/4,1,1> scale <%.6g, %.6g, %.6g>\n"
2195 elem.radius * 2.0,
2196 stiffness / 4.0,
2197 loc.x,
2198 loc.y,
2199 loc.z,
2200 elem.size_x,
2201 elem.size_y,
2202 elem.size_z,
2205 writeMatrix(
2206 global_matrix @ elems[1].matrix_world
2208 tabWrite("}\n")
2209 tabWrite(
2210 "cylinder { -y*8, +y*8,%.4g,%.4g translate<%.6g,%.6g,%.6g> scale <1,1/4,1> scale <%.6g, %.6g, %.6g>\n"
2212 elem.radius * 2.0,
2213 stiffness / 4.0,
2214 loc.x,
2215 loc.y,
2216 loc.z,
2217 elem.size_x,
2218 elem.size_y,
2219 elem.size_z,
2222 writeMatrix(
2223 global_matrix @ elems[1].matrix_world
2225 tabWrite("}\n")
2227 try:
2228 material = elems[1].data.materials[
2230 ] # lame! - blender cant do enything else.
2231 except:
2232 material = None
2233 if material:
2234 diffuse_color = material.diffuse_color
2235 trans = 1.0 - material.pov.alpha
2236 if (
2237 material.use_transparency
2238 and material.transparency_method == 'RAYTRACE'
2240 povFilter = (
2241 material.pov_raytrace_transparency.filter
2242 * (1.0 - material.alpha)
2244 trans = (1.0 - material.pov.alpha) - povFilter
2245 else:
2246 povFilter = 0.0
2247 material_finish = materialNames[material.name]
2248 tabWrite(
2249 "pigment {srgbft<%.3g, %.3g, %.3g, %.3g, %.3g>} \n"
2251 diffuse_color[0],
2252 diffuse_color[1],
2253 diffuse_color[2],
2254 povFilter,
2255 trans,
2258 tabWrite(
2259 "finish{%s} " % safety(material_finish, Level=2)
2261 else:
2262 tabWrite(
2263 "pigment{srgb 1} finish{%s} "
2264 % (safety(DEF_MAT_NAME, Level=2))
2267 writeObjectMaterial(material, ob)
2268 # writeObjectMaterial(material, elems[1])
2269 tabWrite(
2270 "radiosity{importance %3g}\n"
2271 % ob.pov.importance_value
2273 tabWrite("}\n\n") # End of Metaball block
2276 meta = ob.data
2278 # important because no elements will break parsing.
2279 elements = [elem for elem in meta.elements if elem.type in {'BALL', 'ELLIPSOID'}]
2281 if elements:
2282 tabWrite("blob {\n")
2283 tabWrite("threshold %.4g\n" % meta.threshold)
2284 importance = ob.pov.importance_value
2286 try:
2287 material = meta.materials[0] # lame! - blender cant do enything else.
2288 except:
2289 material = None
2291 for elem in elements:
2292 loc = elem.co
2294 stiffness = elem.stiffness
2295 if elem.use_negative:
2296 stiffness = - stiffness
2298 if elem.type == 'BALL':
2300 tabWrite("sphere { <%.6g, %.6g, %.6g>, %.4g, %.4g }\n" %
2301 (loc.x, loc.y, loc.z, elem.radius, stiffness))
2303 # After this wecould do something simple like...
2304 # "pigment {Blue} }"
2305 # except we'll write the color
2307 elif elem.type == 'ELLIPSOID':
2308 # location is modified by scale
2309 tabWrite("sphere { <%.6g, %.6g, %.6g>, %.4g, %.4g }\n" %
2310 (loc.x / elem.size_x,
2311 loc.y / elem.size_y,
2312 loc.z / elem.size_z,
2313 elem.radius, stiffness))
2314 tabWrite("scale <%.6g, %.6g, %.6g> \n" %
2315 (elem.size_x, elem.size_y, elem.size_z))
2317 if material:
2318 diffuse_color = material.diffuse_color
2319 trans = 1.0 - material.pov.alpha
2320 if material.use_transparency and material.transparency_method == 'RAYTRACE':
2321 povFilter = material.pov_raytrace_transparency.filter * (1.0 - material.alpha)
2322 trans = (1.0 - material.pov.alpha) - povFilter
2323 else:
2324 povFilter = 0.0
2326 material_finish = materialNames[material.name]
2328 tabWrite("pigment {srgbft<%.3g, %.3g, %.3g, %.3g, %.3g>} \n" %
2329 (diffuse_color[0], diffuse_color[1], diffuse_color[2],
2330 povFilter, trans))
2331 tabWrite("finish {%s}\n" % safety(material_finish, Level=2))
2333 else:
2334 tabWrite("pigment {srgb 1} \n")
2335 # Write the finish last.
2336 tabWrite("finish {%s}\n" % (safety(DEF_MAT_NAME, Level=2)))
2338 writeObjectMaterial(material, elems[1])
2340 writeMatrix(global_matrix @ ob.matrix_world)
2341 # Importance for radiosity sampling added here
2342 tabWrite("radiosity { \n")
2343 # importance > ob.pov.importance_value
2344 tabWrite("importance %3g \n" % importance)
2345 tabWrite("}\n")
2347 tabWrite("}\n") # End of Metaball block
2349 if comments and len(metas) >= 1:
2350 file.write("\n")
2352 # objectNames = {}
2353 DEF_OBJ_NAME = "Default"
2355 def exportMeshes(scene, sel, csg):
2356 """write all meshes as POV mesh2{} syntax to exported file """
2357 #some numpy functions to speed up mesh export
2359 # TODO: also write a numpy function to read matrices at object level?
2360 # feed below with mesh object.data, but only after doing data.calc_loop_triangles()
2361 def read_verts_co(self, mesh):
2362 #'float64' would be a slower 64-bit floating-point number numpy datatype
2363 # using 'float32' vert coordinates for now until any issue is reported
2364 mverts_co = np.zeros((len(mesh.vertices)*3), dtype=np.float32)
2365 mesh.vertices.foreach_get("co", mverts_co)
2366 return np.reshape(mverts_co, (len(mesh.vertices), 3))
2368 def read_verts_idx(self, mesh):
2369 mverts_idx = np.zeros((len(mesh.vertices)), dtype=np.int64)
2370 mesh.vertices.foreach_get("index", mverts_idx)
2371 return np.reshape(mverts_idx, (len(mesh.vertices), 1))
2373 def read_verts_norms(self, mesh):
2374 #'float64' would be a slower 64-bit floating-point number numpy datatype
2375 # using less accurate 'float16' normals for now until any issue is reported
2376 mverts_no = np.zeros((len(mesh.vertices)*3), dtype=np.float16)
2377 mesh.vertices.foreach_get("normal", mverts_no)
2378 return np.reshape(mverts_no, (len(mesh.vertices), 3))
2380 def read_faces_idx(self, mesh):
2381 mfaces_idx = np.zeros((len(mesh.loop_triangles)), dtype=np.int64)
2382 mesh.loop_triangles.foreach_get("index", mfaces_idx)
2383 return np.reshape(mfaces_idx, (len(mesh.loop_triangles), 1))
2385 def read_faces_verts_indices(self, mesh):
2386 mfaces_verts_idx = np.zeros((len(mesh.loop_triangles)*3), dtype=np.int64)
2387 mesh.loop_triangles.foreach_get("vertices", mfaces_verts_idx)
2388 return np.reshape(mfaces_verts_idx, (len(mesh.loop_triangles), 3))
2390 #Why is below different from verex indices?
2391 def read_faces_verts_loops(self, mesh):
2392 mfaces_verts_loops = np.zeros((len(mesh.loop_triangles)*3), dtype=np.int64)
2393 mesh.loop_triangles.foreach_get("loops", mfaces_verts_loops)
2394 return np.reshape(mfaces_verts_loops, (len(mesh.loop_triangles), 3))
2396 def read_faces_norms(self, mesh):
2397 #'float64' would be a slower 64-bit floating-point number numpy datatype
2398 # using less accurate 'float16' normals for now until any issue is reported
2399 mfaces_no = np.zeros((len(mesh.loop_triangles)*3), dtype=np.float16)
2400 mesh.loop_triangles.foreach_get("normal", mfaces_no)
2401 return np.reshape(mfaces_no, (len(mesh.loop_triangles), 3))
2403 def read_faces_smooth(self, mesh):
2404 mfaces_smth = np.zeros((len(mesh.loop_triangles)*1), dtype=np.bool)
2405 mesh.loop_triangles.foreach_get("use_smooth", mfaces_smth)
2406 return np.reshape(mfaces_smth, (len(mesh.loop_triangles), 1))
2408 def read_faces_material_indices(self, mesh):
2409 mfaces_mats_idx = np.zeros((len(mesh.loop_triangles)), dtype=np.int16)
2410 mesh.loop_triangles.foreach_get("material_index", mfaces_mats_idx)
2411 return np.reshape(mfaces_mats_idx, (len(mesh.loop_triangles), 1))
2415 # obmatslist = []
2416 # def hasUniqueMaterial():
2417 # # Grab materials attached to object instances ...
2418 # if hasattr(ob, 'material_slots'):
2419 # for ms in ob.material_slots:
2420 # if ms.material is not None and ms.link == 'OBJECT':
2421 # if ms.material in obmatslist:
2422 # return False
2423 # else:
2424 # obmatslist.append(ms.material)
2425 # return True
2426 # def hasObjectMaterial(ob):
2427 # # Grab materials attached to object instances ...
2428 # if hasattr(ob, 'material_slots'):
2429 # for ms in ob.material_slots:
2430 # if ms.material is not None and ms.link == 'OBJECT':
2431 # # If there is at least one material slot linked to the object
2432 # # and not the data (mesh), always create a new, "private" data instance.
2433 # return True
2434 # return False
2435 # For objects using local material(s) only!
2436 # This is a mapping between a tuple (dataname, materialnames, ...), and the POV dataname.
2437 # As only objects using:
2438 # * The same data.
2439 # * EXACTLY the same materials, in EXACTLY the same sockets.
2440 # ... can share a same instance in POV export.
2441 obmats2data = {}
2443 def checkObjectMaterials(ob, name, dataname):
2444 if hasattr(ob, 'material_slots'):
2445 has_local_mats = False
2446 key = [dataname]
2447 for ms in ob.material_slots:
2448 if ms.material is not None:
2449 key.append(ms.material.name)
2450 if ms.link == 'OBJECT' and not has_local_mats:
2451 has_local_mats = True
2452 else:
2453 # Even if the slot is empty, it is important to grab it...
2454 key.append("")
2455 if has_local_mats:
2456 # If this object uses local material(s), lets find if another object
2457 # using the same data and exactly the same list of materials
2458 # (in the same slots) has already been processed...
2459 # Note that here also, we use object name as new, unique dataname for Pov.
2460 key = tuple(key) # Lists are not hashable...
2461 if key not in obmats2data:
2462 obmats2data[key] = name
2463 return obmats2data[key]
2464 return None
2466 data_ref = {}
2468 def store(scene, ob, name, dataname, matrix):
2469 # The Object needs to be written at least once but if its data is
2470 # already in data_ref this has already been done.
2471 # This func returns the "povray" name of the data, or None
2472 # if no writing is needed.
2473 if ob.is_modified(scene, 'RENDER'):
2474 # Data modified.
2475 # Create unique entry in data_ref by using object name
2476 # (always unique in Blender) as data name.
2477 data_ref[name] = [(name, MatrixAsPovString(matrix))]
2478 return name
2479 # Here, we replace dataname by the value returned by checkObjectMaterials, only if
2480 # it is not evaluated to False (i.e. only if the object uses some local material(s)).
2481 dataname = checkObjectMaterials(ob, name, dataname) or dataname
2482 if dataname in data_ref:
2483 # Data already known, just add the object instance.
2484 data_ref[dataname].append((name, MatrixAsPovString(matrix)))
2485 # No need to write data
2486 return None
2487 else:
2488 # Data not yet processed, create a new entry in data_ref.
2489 data_ref[dataname] = [(name, MatrixAsPovString(matrix))]
2490 return dataname
2492 def exportSmoke(smoke_obj_name):
2493 # if LuxManager.CurrentScene.name == 'preview':
2494 # return 1, 1, 1, 1.0
2495 # else:
2496 flowtype = -1
2497 smoke_obj = bpy.data.objects[smoke_obj_name]
2498 domain = None
2500 # Search smoke domain target for smoke modifiers
2501 for mod in smoke_obj.modifiers:
2502 if mod.name == 'Smoke':
2503 if mod.smoke_type == 'FLOW':
2504 if mod.flow_settings.smoke_flow_type == 'BOTH':
2505 flowtype = 2
2506 else:
2507 if mod.flow_settings.smoke_flow_type == 'SMOKE':
2508 flowtype = 0
2509 else:
2510 if mod.flow_settings.smoke_flow_type == 'FIRE':
2511 flowtype = 1
2513 if mod.smoke_type == 'DOMAIN':
2514 domain = smoke_obj
2515 smoke_modifier = mod
2517 eps = 0.000001
2518 if domain is not None:
2519 # if bpy.app.version[0] >= 2 and bpy.app.version[1] >= 71:
2520 # Blender version 2.71 supports direct access to smoke data structure
2521 set = mod.domain_settings
2522 channeldata = []
2523 for v in set.density_grid:
2524 channeldata.append(v.real)
2525 print(v.real)
2526 ## Usage en voxel texture:
2527 # channeldata = []
2528 # if channel == 'density':
2529 # for v in set.density_grid:
2530 # channeldata.append(v.real)
2532 # if channel == 'fire':
2533 # for v in set.flame_grid:
2534 # channeldata.append(v.real)
2536 resolution = set.resolution_max
2537 big_res = []
2538 big_res.append(set.domain_resolution[0])
2539 big_res.append(set.domain_resolution[1])
2540 big_res.append(set.domain_resolution[2])
2542 if set.use_high_resolution:
2543 big_res[0] = big_res[0] * (set.amplify + 1)
2544 big_res[1] = big_res[1] * (set.amplify + 1)
2545 big_res[2] = big_res[2] * (set.amplify + 1)
2546 # else:
2547 # p = []
2548 ##gather smoke domain settings
2549 # BBox = domain.bound_box
2550 # p.append([BBox[0][0], BBox[0][1], BBox[0][2]])
2551 # p.append([BBox[6][0], BBox[6][1], BBox[6][2]])
2552 # set = mod.domain_settings
2553 # resolution = set.resolution_max
2554 # smokecache = set.point_cache
2555 # ret = read_cache(smokecache, set.use_high_resolution, set.amplify + 1, flowtype)
2556 # res_x = ret[0]
2557 # res_y = ret[1]
2558 # res_z = ret[2]
2559 # density = ret[3]
2560 # fire = ret[4]
2562 # if res_x * res_y * res_z > 0:
2563 ##new cache format
2564 # big_res = []
2565 # big_res.append(res_x)
2566 # big_res.append(res_y)
2567 # big_res.append(res_z)
2568 # else:
2569 # max = domain.dimensions[0]
2570 # if (max - domain.dimensions[1]) < -eps:
2571 # max = domain.dimensions[1]
2573 # if (max - domain.dimensions[2]) < -eps:
2574 # max = domain.dimensions[2]
2576 # big_res = [int(round(resolution * domain.dimensions[0] / max, 0)),
2577 # int(round(resolution * domain.dimensions[1] / max, 0)),
2578 # int(round(resolution * domain.dimensions[2] / max, 0))]
2580 # if set.use_high_resolution:
2581 # big_res = [big_res[0] * (set.amplify + 1), big_res[1] * (set.amplify + 1),
2582 # big_res[2] * (set.amplify + 1)]
2584 # if channel == 'density':
2585 # channeldata = density
2587 # if channel == 'fire':
2588 # channeldata = fire
2590 # sc_fr = '%s/%s/%s/%05d' % (efutil.export_path, efutil.scene_filename(), bpy.context.scene.name, bpy.context.scene.frame_current)
2591 # if not os.path.exists( sc_fr ):
2592 # os.makedirs(sc_fr)
2594 # smoke_filename = '%s.smoke' % bpy.path.clean_name(domain.name)
2595 # smoke_path = '/'.join([sc_fr, smoke_filename])
2597 # with open(smoke_path, 'wb') as smoke_file:
2598 # # Binary densitygrid file format
2600 # # File header
2601 # smoke_file.write(b'SMOKE') #magic number
2602 # smoke_file.write(struct.pack('<I', big_res[0]))
2603 # smoke_file.write(struct.pack('<I', big_res[1]))
2604 # smoke_file.write(struct.pack('<I', big_res[2]))
2605 # Density data
2606 # smoke_file.write(struct.pack('<%df'%len(channeldata), *channeldata))
2608 # LuxLog('Binary SMOKE file written: %s' % (smoke_path))
2610 # return big_res[0], big_res[1], big_res[2], channeldata
2612 mydf3 = df3.df3(big_res[0], big_res[1], big_res[2])
2613 sim_sizeX, sim_sizeY, sim_sizeZ = mydf3.size()
2614 for x in range(sim_sizeX):
2615 for y in range(sim_sizeY):
2616 for z in range(sim_sizeZ):
2617 mydf3.set(
2621 channeldata[
2622 ((z * sim_sizeY + y) * sim_sizeX + x)
2626 mydf3.exportDF3(smokePath)
2627 print('Binary smoke.df3 file written in preview directory')
2628 if comments:
2629 file.write("\n//--Smoke--\n\n")
2631 # Note: We start with a default unit cube.
2632 # This is mandatory to read correctly df3 data - otherwise we could just directly use bbox
2633 # coordinates from the start, and avoid scale/translate operations at the end...
2634 file.write("box{<0,0,0>, <1,1,1>\n")
2635 file.write(" pigment{ rgbt 1 }\n")
2636 file.write(" hollow\n")
2637 file.write(" interior{ //---------------------\n")
2638 file.write(" media{ method 3\n")
2639 file.write(
2640 " emission <1,1,1>*1\n"
2641 ) # 0>1 for dark smoke to white vapour
2642 file.write(" scattering{ 1, // Type\n")
2643 file.write(" <1,1,1>*0.1\n")
2644 file.write(" } // end scattering\n")
2645 file.write(
2646 " density{density_file df3 \"%s\"\n"
2647 % (smokePath)
2649 file.write(" color_map {\n")
2650 file.write(" [0.00 rgb 0]\n")
2651 file.write(" [0.05 rgb 0]\n")
2652 file.write(" [0.20 rgb 0.2]\n")
2653 file.write(" [0.30 rgb 0.6]\n")
2654 file.write(" [0.40 rgb 1]\n")
2655 file.write(" [1.00 rgb 1]\n")
2656 file.write(" } // end color_map\n")
2657 file.write(" } // end of density\n")
2658 file.write(
2659 " samples %i // higher = more precise\n"
2660 % resolution
2662 file.write(
2663 " } // end of media --------------------------\n"
2665 file.write(" } // end of interior\n")
2667 # START OF TRANSFORMATIONS
2669 # Size to consider here are bbox dimensions (i.e. still in object space, *before* applying
2670 # loc/rot/scale and other transformations (like parent stuff), aka matrix_world).
2671 bbox = smoke_obj.bound_box
2672 dim = [
2673 abs(bbox[6][0] - bbox[0][0]),
2674 abs(bbox[6][1] - bbox[0][1]),
2675 abs(bbox[6][2] - bbox[0][2]),
2678 # We scale our cube to get its final size and shapes but still in *object* space (same as Blender's bbox).
2679 file.write("scale<%.6g,%.6g,%.6g>\n" % (dim[0], dim[1], dim[2]))
2681 # We offset our cube such that (0,0,0) coordinate matches Blender's object center.
2682 file.write(
2683 "translate<%.6g,%.6g,%.6g>\n"
2684 % (bbox[0][0], bbox[0][1], bbox[0][2])
2687 # We apply object's transformations to get final loc/rot/size in world space!
2688 # Note: we could combine the two previous transformations with this matrix directly...
2689 writeMatrix(global_matrix @ smoke_obj.matrix_world)
2691 # END OF TRANSFORMATIONS
2693 file.write("}\n")
2695 # file.write(" interpolate 1\n")
2696 # file.write(" frequency 0\n")
2697 # file.write(" }\n")
2698 # file.write("}\n")
2700 ob_num = 0
2701 for ob in sel:
2702 # subtract original from the count of their instances as were not counted before 2.8
2703 if not (ob.is_instancer and ob.original != ob):
2704 ob_num += 1
2706 # XXX I moved all those checks here, as there is no need to compute names
2707 # for object we won't export here!
2708 if ob.type in {
2709 'LIGHT',
2710 'CAMERA', #'EMPTY', #empties can bear dupligroups
2711 'META',
2712 'ARMATURE',
2713 'LATTICE',
2715 continue
2716 smokeFlag = False
2717 for mod in ob.modifiers:
2718 if mod and hasattr(mod, 'smoke_type'):
2719 smokeFlag = True
2720 if mod.smoke_type == 'DOMAIN':
2721 exportSmoke(ob.name)
2722 break # don't render domain mesh or flow emitter mesh, skip to next object.
2723 if not smokeFlag:
2724 # Export Hair
2725 renderEmitter = True
2726 if hasattr(ob, 'particle_systems'):
2727 renderEmitter = False
2728 if ob.show_instancer_for_render:
2729 renderEmitter = True
2730 for pSys in ob.particle_systems:
2731 for mod in [
2733 for m in ob.modifiers
2734 if (m is not None)
2735 and (m.type == 'PARTICLE_SYSTEM')
2737 if (
2738 (pSys.settings.render_type == 'PATH')
2739 and mod.show_render
2740 and (pSys.name == mod.particle_system.name)
2742 tstart = time.time()
2743 texturedHair = 0
2744 if (
2745 ob.material_slots[
2746 pSys.settings.material - 1
2747 ].material
2748 and ob.active_material is not None
2750 pmaterial = ob.material_slots[
2751 pSys.settings.material - 1
2752 ].material
2753 #XXX Todo: replace by pov_(Particles?)_texture_slot
2754 for th in pmaterial.texture_slots:
2755 if th and th.use:
2756 if (
2758 th.texture.type
2759 == 'IMAGE'
2760 and th.texture.image
2762 or th.texture.type
2763 != 'IMAGE'
2765 if th.use_map_color_diffuse:
2766 texturedHair = 1
2767 if pmaterial.strand.use_blender_units:
2768 strandStart = (
2769 pmaterial.strand.root_size
2771 strandEnd = (
2772 pmaterial.strand.tip_size
2774 strandShape = pmaterial.strand.shape
2775 else: # Blender unit conversion
2776 strandStart = (
2777 pmaterial.strand.root_size
2778 / 200.0
2780 strandEnd = (
2781 pmaterial.strand.tip_size
2782 / 200.0
2784 strandShape = pmaterial.strand.shape
2785 else:
2786 pmaterial = (
2787 "default"
2788 ) # No material assigned in blender, use default one
2789 strandStart = 0.01
2790 strandEnd = 0.01
2791 strandShape = 0.0
2792 # Set the number of particles to render count rather than 3d view display
2793 # pSys.set_resolution(scene, ob, 'RENDER') # DEPRECATED
2794 # When you render, the entire dependency graph will be
2795 # evaluated at render resolution, including the particles.
2796 # In the viewport it will be at viewport resolution.
2797 # So there is no need fo render engines to use this function anymore,
2798 # it's automatic now.
2799 steps = pSys.settings.display_step
2800 steps = (
2801 3 ** steps
2802 ) # or (power of 2 rather than 3) + 1 # Formerly : len(particle.hair_keys)
2804 totalNumberOfHairs = (
2805 pSys.settings.count
2806 + pSys.settings.rendered_child_count
2808 # hairCounter = 0
2809 file.write(
2810 '#declare HairArray = array[%i] {\n'
2811 % totalNumberOfHairs
2813 for pindex in range(0, totalNumberOfHairs):
2815 # if particle.is_exist and particle.is_visible:
2816 # hairCounter += 1
2817 # controlPointCounter = 0
2818 # Each hair is represented as a separate sphere_sweep in POV-Ray.
2820 file.write('sphere_sweep{')
2821 if pSys.settings.use_hair_bspline:
2822 file.write('b_spline ')
2823 file.write(
2824 '%i,\n' % (steps + 2)
2825 ) # +2 because the first point needs tripling to be more than a handle in POV
2826 else:
2827 file.write('linear_spline ')
2828 file.write('%i,\n' % (steps))
2829 # changing world coordinates to object local coordinates by multiplying with inverted matrix
2830 initCo = ob.matrix_world.inverted() @ (
2831 pSys.co_hair(
2832 ob, particle_no=pindex, step=0
2835 if (
2836 ob.material_slots[
2837 pSys.settings.material - 1
2838 ].material
2839 and ob.active_material is not None
2841 pmaterial = ob.material_slots[
2842 pSys.settings.material - 1
2843 ].material
2844 for th in pmaterial.texture_slots:
2845 if (
2847 and th.use
2848 and th.use_map_color_diffuse
2850 # treat POV textures as bitmaps
2851 if (
2852 th.texture.type
2853 == 'IMAGE'
2854 and th.texture.image
2855 and th.texture_coords
2856 == 'UV'
2857 and ob.data.uv_textures
2858 is not None
2859 ): # or (th.texture.pov.tex_pattern_type != 'emulator' and th.texture_coords == 'UV' and ob.data.uv_textures is not None):
2860 image = th.texture.image
2861 image_width = image.size[
2864 image_height = image.size[
2867 image_pixels = image.pixels[
2870 uv_co = pSys.uv_on_emitter(
2871 mod,
2872 pSys.particles[
2873 pindex
2875 pindex,
2878 x_co = round(
2879 uv_co[0]
2880 * (image_width - 1)
2882 y_co = round(
2883 uv_co[1]
2884 * (image_height - 1)
2886 pixelnumber = (
2887 image_width * y_co
2888 ) + x_co
2889 r = image_pixels[
2890 pixelnumber * 4
2892 g = image_pixels[
2893 pixelnumber * 4 + 1
2895 b = image_pixels[
2896 pixelnumber * 4 + 2
2898 a = image_pixels[
2899 pixelnumber * 4 + 3
2901 initColor = (r, g, b, a)
2902 else:
2903 # only overwrite variable for each competing texture for now
2904 initColor = th.texture.evaluate(
2906 initCo[0],
2907 initCo[1],
2908 initCo[2],
2911 for step in range(0, steps):
2912 co = ob.matrix_world.inverted() @ (
2913 pSys.co_hair(
2915 particle_no=pindex,
2916 step=step,
2919 # for controlPoint in particle.hair_keys:
2920 if pSys.settings.clump_factor != 0:
2921 hDiameter = (
2922 pSys.settings.clump_factor
2923 / 200.0
2924 * random.uniform(0.5, 1)
2926 elif step == 0:
2927 hDiameter = strandStart
2928 else:
2929 hDiameter += (
2930 strandEnd - strandStart
2931 ) / (
2932 pSys.settings.display_step
2934 ) # XXX +1 or not?
2935 if (
2936 step == 0
2937 and pSys.settings.use_hair_bspline
2939 # Write three times the first point to compensate pov Bezier handling
2940 file.write(
2941 '<%.6g,%.6g,%.6g>,%.7g,\n'
2943 co[0],
2944 co[1],
2945 co[2],
2946 abs(hDiameter),
2949 file.write(
2950 '<%.6g,%.6g,%.6g>,%.7g,\n'
2952 co[0],
2953 co[1],
2954 co[2],
2955 abs(hDiameter),
2958 # file.write('<%.6g,%.6g,%.6g>,%.7g' % (particle.location[0], particle.location[1], particle.location[2], abs(hDiameter))) # Useless because particle location is the tip, not the root.
2959 # file.write(',\n')
2960 # controlPointCounter += 1
2961 # totalNumberOfHairs += len(pSys.particles)# len(particle.hair_keys)
2963 # Each control point is written out, along with the radius of the
2964 # hair at that point.
2965 file.write(
2966 '<%.6g,%.6g,%.6g>,%.7g'
2968 co[0],
2969 co[1],
2970 co[2],
2971 abs(hDiameter),
2975 # All coordinates except the last need a following comma.
2977 if step != steps - 1:
2978 file.write(',\n')
2979 else:
2980 if texturedHair:
2981 # Write pigment and alpha (between Pov and Blender alpha 0 and 1 are reversed)
2982 file.write(
2983 '\npigment{ color srgbf < %.3g, %.3g, %.3g, %.3g> }\n'
2985 initColor[0],
2986 initColor[1],
2987 initColor[2],
2988 1.0 - initColor[3],
2991 # End the sphere_sweep declaration for this hair
2992 file.write('}\n')
2994 # All but the final sphere_sweep (each array element) needs a terminating comma.
2995 if pindex != totalNumberOfHairs:
2996 file.write(',\n')
2997 else:
2998 file.write('\n')
3000 # End the array declaration.
3002 file.write('}\n')
3003 file.write('\n')
3005 if not texturedHair:
3006 # Pick up the hair material diffuse color and create a default POV-Ray hair texture.
3008 file.write('#ifndef (HairTexture)\n')
3009 file.write(
3010 ' #declare HairTexture = texture {\n'
3012 file.write(
3013 ' pigment {srgbt <%s,%s,%s,%s>}\n'
3015 pmaterial.diffuse_color[0],
3016 pmaterial.diffuse_color[1],
3017 pmaterial.diffuse_color[2],
3019 pmaterial.strand.width_fade
3020 + 0.05
3024 file.write(' }\n')
3025 file.write('#end\n')
3026 file.write('\n')
3028 # Dynamically create a union of the hairstrands (or a subset of them).
3029 # By default use every hairstrand, commented line is for hand tweaking test renders.
3030 file.write(
3031 '//Increasing HairStep divides the amount of hair for test renders.\n'
3033 file.write(
3034 '#ifndef(HairStep) #declare HairStep = 1; #end\n'
3036 file.write('union{\n')
3037 file.write(' #local I = 0;\n')
3038 file.write(
3039 ' #while (I < %i)\n'
3040 % totalNumberOfHairs
3042 file.write(' object {HairArray[I]')
3043 if not texturedHair:
3044 file.write(' texture{HairTexture}\n')
3045 else:
3046 file.write('\n')
3047 # Translucency of the hair:
3048 file.write(' hollow\n')
3049 file.write(' double_illuminate\n')
3050 file.write(' interior {\n')
3051 file.write(' ior 1.45\n')
3052 file.write(' media {\n')
3053 file.write(
3054 ' scattering { 1, 10*<0.73, 0.35, 0.15> /*extinction 0*/ }\n'
3056 file.write(
3057 ' absorption 10/<0.83, 0.75, 0.15>\n'
3059 file.write(' samples 1\n')
3060 file.write(' method 2\n')
3061 file.write(
3062 ' density {cylindrical\n'
3064 file.write(
3065 ' color_map {\n'
3067 file.write(
3068 ' [0.0 rgb <0.83, 0.45, 0.35>]\n'
3070 file.write(
3071 ' [0.5 rgb <0.8, 0.8, 0.4>]\n'
3073 file.write(
3074 ' [1.0 rgb <1,1,1>]\n'
3076 file.write(' }\n')
3077 file.write(' }\n')
3078 file.write(' }\n')
3079 file.write(' }\n')
3080 file.write(' }\n')
3082 file.write(' #local I = I + HairStep;\n')
3083 file.write(' #end\n')
3085 writeMatrix(global_matrix @ ob.matrix_world)
3087 file.write('}')
3088 print(
3089 'Totals hairstrands written: %i'
3090 % totalNumberOfHairs
3092 print(
3093 'Number of tufts (particle systems)',
3094 len(ob.particle_systems),
3097 # Set back the displayed number of particles to preview count
3098 # pSys.set_resolution(scene, ob, 'PREVIEW') #DEPRECATED
3099 # When you render, the entire dependency graph will be
3100 # evaluated at render resolution, including the particles.
3101 # In the viewport it will be at viewport resolution.
3102 # So there is no need fo render engines to use this function anymore,
3103 # it's automatic now.
3104 if renderEmitter == False:
3105 continue # don't render mesh, skip to next object.
3107 #############################################
3108 # Generating a name for object just like materials to be able to use it
3109 # (baking for now or anything else).
3110 # XXX I don't understand that if we are here, sel if a non-empty iterable,
3111 # so this condition is always True, IMO -- mont29
3112 if ob.data:
3113 name_orig = "OB" + ob.name
3114 dataname_orig = "DATA" + ob.data.name
3115 elif ob.is_instancer:
3116 if ob.instance_type == 'COLLECTION':
3117 name_orig = "OB" + ob.name
3118 dataname_orig = "DATA" + ob.instance_collection.name
3119 else:
3120 # hoping only dupligroups have several source datablocks
3121 # ob_dupli_list_create(scene) #deprecated in 2.8
3122 depsgraph = bpy.context.evaluated_depsgraph_get()
3123 for eachduplicate in depsgraph.object_instances:
3124 if (
3125 eachduplicate.is_instance
3126 ): # Real dupli instance filtered because original included in list since 2.8
3127 dataname_orig = (
3128 "DATA" + eachduplicate.object.name
3130 # ob.dupli_list_clear() #just don't store any reference to instance since 2.8
3131 elif ob.type == 'EMPTY':
3132 name_orig = "OB" + ob.name
3133 dataname_orig = "DATA" + ob.name
3134 else:
3135 name_orig = DEF_OBJ_NAME
3136 dataname_orig = DEF_OBJ_NAME
3137 name = string_strip_hyphen(bpy.path.clean_name(name_orig))
3138 dataname = string_strip_hyphen(
3139 bpy.path.clean_name(dataname_orig)
3141 ## for slot in ob.material_slots:
3142 ## if slot.material is not None and slot.link == 'OBJECT':
3143 ## obmaterial = slot.material
3145 #############################################
3147 if info_callback:
3148 info_callback(
3149 "Object %2.d of %2.d (%s)"
3150 % (ob_num, len(sel), ob.name)
3153 # if ob.type != 'MESH':
3154 # continue
3155 # me = ob.data
3157 matrix = global_matrix @ ob.matrix_world
3158 povdataname = store(scene, ob, name, dataname, matrix)
3159 if povdataname is None:
3160 print("This is an instance of " + name)
3161 continue
3163 print("Writing Down First Occurrence of " + name)
3165 ############################################Povray Primitives
3166 # special exportCurves() function takes care of writing
3167 # lathe, sphere_sweep, birail, and loft except with modifiers
3168 # converted to mesh
3169 if not ob.is_modified(scene, 'RENDER'):
3170 if ob.type == 'CURVE' and (
3171 ob.pov.curveshape
3172 in {'lathe', 'sphere_sweep', 'loft'}
3174 continue # Don't render proxy mesh, skip to next object
3176 if ob.pov.object_as == 'ISOSURFACE':
3177 tabWrite("#declare %s = isosurface{ \n" % povdataname)
3178 tabWrite("function{ \n")
3179 textName = ob.pov.iso_function_text
3180 if textName:
3181 node_tree = bpy.context.scene.node_tree
3182 for node in node_tree.nodes:
3183 if (
3184 node.bl_idname == "IsoPropsNode"
3185 and node.label == ob.name
3187 for inp in node.inputs:
3188 if inp:
3189 tabWrite(
3190 "#declare %s = %.6g;\n"
3191 % (inp.name, inp.default_value)
3194 text = bpy.data.texts[textName]
3195 for line in text.lines:
3196 split = line.body.split()
3197 if split[0] != "#declare":
3198 tabWrite("%s\n" % line.body)
3199 else:
3200 tabWrite("abs(x) - 2 + y")
3201 tabWrite("}\n")
3202 tabWrite("threshold %.6g\n" % ob.pov.threshold)
3203 tabWrite("max_gradient %.6g\n" % ob.pov.max_gradient)
3204 tabWrite("accuracy %.6g\n" % ob.pov.accuracy)
3205 tabWrite("contained_by { ")
3206 if ob.pov.contained_by == "sphere":
3207 tabWrite(
3208 "sphere {0,%.6g}}\n" % ob.pov.container_scale
3210 else:
3211 tabWrite(
3212 "box {-%.6g,%.6g}}\n"
3214 ob.pov.container_scale,
3215 ob.pov.container_scale,
3218 if ob.pov.all_intersections:
3219 tabWrite("all_intersections\n")
3220 else:
3221 if ob.pov.max_trace > 1:
3222 tabWrite("max_trace %.6g\n" % ob.pov.max_trace)
3223 povMatName = "Default_texture"
3224 if ob.active_material:
3225 # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
3226 try:
3227 material = ob.active_material
3228 writeObjectMaterial(material, ob)
3229 except IndexError:
3230 print(me)
3231 # tabWrite("texture {%s}\n"%povMatName)
3232 tabWrite("scale %.6g\n" % (1 / ob.pov.container_scale))
3233 tabWrite("}\n")
3234 continue # Don't render proxy mesh, skip to next object
3236 if ob.pov.object_as == 'SUPERELLIPSOID':
3237 tabWrite(
3238 "#declare %s = superellipsoid{ <%.4f,%.4f>\n"
3239 % (povdataname, ob.pov.se_n2, ob.pov.se_n1)
3241 povMatName = "Default_texture"
3242 if ob.active_material:
3243 # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
3244 try:
3245 material = ob.active_material
3246 writeObjectMaterial(material, ob)
3247 except IndexError:
3248 print(me)
3249 # tabWrite("texture {%s}\n"%povMatName)
3250 write_object_modifiers(scene, ob, file)
3251 tabWrite("}\n")
3252 continue # Don't render proxy mesh, skip to next object
3254 if ob.pov.object_as == 'SUPERTORUS':
3255 rMajor = ob.pov.st_major_radius
3256 rMinor = ob.pov.st_minor_radius
3257 ring = ob.pov.st_ring
3258 cross = ob.pov.st_cross
3259 accuracy = ob.pov.st_accuracy
3260 gradient = ob.pov.st_max_gradient
3261 ############Inline Supertorus macro
3262 file.write(
3263 "#macro Supertorus(RMj, RMn, MajorControl, MinorControl, Accuracy, MaxGradient)\n"
3265 file.write(" #local CP = 2/MinorControl;\n")
3266 file.write(" #local RP = 2/MajorControl;\n")
3267 file.write(" isosurface {\n")
3268 file.write(
3269 " function { pow( pow(abs(pow(pow(abs(x),RP) + pow(abs(z),RP), 1/RP) - RMj),CP) + pow(abs(y),CP) ,1/CP) - RMn }\n"
3271 file.write(" threshold 0\n")
3272 file.write(
3273 " contained_by {box {<-RMj-RMn,-RMn,-RMj-RMn>, < RMj+RMn, RMn, RMj+RMn>}}\n"
3275 file.write(" #if(MaxGradient >= 1)\n")
3276 file.write(" max_gradient MaxGradient\n")
3277 file.write(" #else\n")
3278 file.write(" evaluate 1, 10, 0.1\n")
3279 file.write(" #end\n")
3280 file.write(" accuracy Accuracy\n")
3281 file.write(" }\n")
3282 file.write("#end\n")
3283 ############
3284 tabWrite(
3285 "#declare %s = object{ Supertorus( %.4g,%.4g,%.4g,%.4g,%.4g,%.4g)\n"
3287 povdataname,
3288 rMajor,
3289 rMinor,
3290 ring,
3291 cross,
3292 accuracy,
3293 gradient,
3296 povMatName = "Default_texture"
3297 if ob.active_material:
3298 # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
3299 try:
3300 material = ob.active_material
3301 writeObjectMaterial(material, ob)
3302 except IndexError:
3303 print(me)
3304 # tabWrite("texture {%s}\n"%povMatName)
3305 write_object_modifiers(scene, ob, file)
3306 tabWrite("rotate x*90\n")
3307 tabWrite("}\n")
3308 continue # Don't render proxy mesh, skip to next object
3310 if ob.pov.object_as == 'PLANE':
3311 tabWrite(
3312 "#declare %s = plane{ <0,0,1>,1\n" % povdataname
3314 povMatName = "Default_texture"
3315 if ob.active_material:
3316 # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
3317 try:
3318 material = ob.active_material
3319 writeObjectMaterial(material, ob)
3320 except IndexError:
3321 print(me)
3322 # tabWrite("texture {%s}\n"%povMatName)
3323 write_object_modifiers(scene, ob, file)
3324 # tabWrite("rotate x*90\n")
3325 tabWrite("}\n")
3326 continue # Don't render proxy mesh, skip to next object
3328 if ob.pov.object_as == 'BOX':
3329 tabWrite("#declare %s = box { -1,1\n" % povdataname)
3330 povMatName = "Default_texture"
3331 if ob.active_material:
3332 # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
3333 try:
3334 material = ob.active_material
3335 writeObjectMaterial(material, ob)
3336 except IndexError:
3337 print(me)
3338 # tabWrite("texture {%s}\n"%povMatName)
3339 write_object_modifiers(scene, ob, file)
3340 # tabWrite("rotate x*90\n")
3341 tabWrite("}\n")
3342 continue # Don't render proxy mesh, skip to next object
3344 if ob.pov.object_as == 'CONE':
3345 br = ob.pov.cone_base_radius
3346 cr = ob.pov.cone_cap_radius
3347 bz = ob.pov.cone_base_z
3348 cz = ob.pov.cone_cap_z
3349 tabWrite(
3350 "#declare %s = cone { <0,0,%.4f>,%.4f,<0,0,%.4f>,%.4f\n"
3351 % (povdataname, bz, br, cz, cr)
3353 povMatName = "Default_texture"
3354 if ob.active_material:
3355 # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
3356 try:
3357 material = ob.active_material
3358 writeObjectMaterial(material, ob)
3359 except IndexError:
3360 print(me)
3361 # tabWrite("texture {%s}\n"%povMatName)
3362 write_object_modifiers(scene, ob, file)
3363 # tabWrite("rotate x*90\n")
3364 tabWrite("}\n")
3365 continue # Don't render proxy mesh, skip to next object
3367 if ob.pov.object_as == 'CYLINDER':
3368 r = ob.pov.cylinder_radius
3369 x2 = ob.pov.cylinder_location_cap[0]
3370 y2 = ob.pov.cylinder_location_cap[1]
3371 z2 = ob.pov.cylinder_location_cap[2]
3372 tabWrite(
3373 "#declare %s = cylinder { <0,0,0>,<%6f,%6f,%6f>,%6f\n"
3374 % (povdataname, x2, y2, z2, r)
3376 povMatName = "Default_texture"
3377 if ob.active_material:
3378 # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
3379 try:
3380 material = ob.active_material
3381 writeObjectMaterial(material, ob)
3382 except IndexError:
3383 print(me)
3384 # tabWrite("texture {%s}\n"%povMatName)
3385 # cylinders written at origin, translated below
3386 write_object_modifiers(scene, ob, file)
3387 # tabWrite("rotate x*90\n")
3388 tabWrite("}\n")
3389 continue # Don't render proxy mesh, skip to next object
3391 if ob.pov.object_as == 'HEIGHT_FIELD':
3392 data = ""
3393 filename = ob.pov.hf_filename
3394 data += '"%s"' % filename
3395 gamma = ' gamma %.4f' % ob.pov.hf_gamma
3396 data += gamma
3397 if ob.pov.hf_premultiplied:
3398 data += ' premultiplied on'
3399 if ob.pov.hf_smooth:
3400 data += ' smooth'
3401 if ob.pov.hf_water > 0:
3402 data += ' water_level %.4f' % ob.pov.hf_water
3403 # hierarchy = ob.pov.hf_hierarchy
3404 tabWrite(
3405 '#declare %s = height_field { %s\n'
3406 % (povdataname, data)
3408 povMatName = "Default_texture"
3409 if ob.active_material:
3410 # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
3411 try:
3412 material = ob.active_material
3413 writeObjectMaterial(material, ob)
3414 except IndexError:
3415 print(me)
3416 # tabWrite("texture {%s}\n"%povMatName)
3417 write_object_modifiers(scene, ob, file)
3418 tabWrite("rotate x*90\n")
3419 tabWrite("translate <-0.5,0.5,0>\n")
3420 tabWrite("scale <0,-1,0>\n")
3421 tabWrite("}\n")
3422 continue # Don't render proxy mesh, skip to next object
3424 if ob.pov.object_as == 'SPHERE':
3426 tabWrite(
3427 "#declare %s = sphere { 0,%6f\n"
3428 % (povdataname, ob.pov.sphere_radius)
3430 povMatName = "Default_texture"
3431 if ob.active_material:
3432 # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
3433 try:
3434 material = ob.active_material
3435 writeObjectMaterial(material, ob)
3436 except IndexError:
3437 print(me)
3438 # tabWrite("texture {%s}\n"%povMatName)
3439 write_object_modifiers(scene, ob, file)
3440 # tabWrite("rotate x*90\n")
3441 tabWrite("}\n")
3442 continue # Don't render proxy mesh, skip to next object
3444 if ob.pov.object_as == 'TORUS':
3445 tabWrite(
3446 "#declare %s = torus { %.4f,%.4f\n"
3448 povdataname,
3449 ob.pov.torus_major_radius,
3450 ob.pov.torus_minor_radius,
3453 povMatName = "Default_texture"
3454 if ob.active_material:
3455 # povMatName = string_strip_hyphen(bpy.path.clean_name(ob.active_material.name))
3456 try:
3457 material = ob.active_material
3458 writeObjectMaterial(material, ob)
3459 except IndexError:
3460 print(me)
3461 # tabWrite("texture {%s}\n"%povMatName)
3462 write_object_modifiers(scene, ob, file)
3463 tabWrite("rotate x*90\n")
3464 tabWrite("}\n")
3465 continue # Don't render proxy mesh, skip to next object
3467 if ob.pov.object_as == 'PARAMETRIC':
3468 tabWrite("#declare %s = parametric {\n" % povdataname)
3469 tabWrite("function { %s }\n" % ob.pov.x_eq)
3470 tabWrite("function { %s }\n" % ob.pov.y_eq)
3471 tabWrite("function { %s }\n" % ob.pov.z_eq)
3472 tabWrite(
3473 "<%.4f,%.4f>, <%.4f,%.4f>\n"
3475 ob.pov.u_min,
3476 ob.pov.v_min,
3477 ob.pov.u_max,
3478 ob.pov.v_max,
3481 # Previous to 3.8 default max_gradient 1.0 was too slow
3482 tabWrite("max_gradient 0.001\n")
3483 if ob.pov.contained_by == "sphere":
3484 tabWrite("contained_by { sphere{0, 2} }\n")
3485 else:
3486 tabWrite("contained_by { box{-2, 2} }\n")
3487 tabWrite("max_gradient %.6f\n" % ob.pov.max_gradient)
3488 tabWrite("accuracy %.6f\n" % ob.pov.accuracy)
3489 tabWrite("precompute 10 x,y,z\n")
3490 tabWrite("}\n")
3491 continue # Don't render proxy mesh, skip to next object
3493 if ob.pov.object_as == 'POLYCIRCLE':
3494 # TODO write below macro Once:
3495 # if write_polytocircle_macro_once == 0:
3496 file.write("/****************************\n")
3497 file.write("This macro was written by 'And'.\n")
3498 file.write(
3499 "Link:(http://news.povray.org/povray.binaries.scene-files/)\n"
3501 file.write("****************************/\n")
3502 file.write("//from math.inc:\n")
3503 file.write("#macro VPerp_Adjust(V, Axis)\n")
3504 file.write(
3505 " vnormalize(vcross(vcross(Axis, V), Axis))\n"
3507 file.write("#end\n")
3508 file.write("//Then for the actual macro\n")
3509 file.write(
3510 "#macro Shape_Slice_Plane_2P_1V(point1, point2, clip_direct)\n"
3512 file.write("#local p1 = point1 + <0,0,0>;\n")
3513 file.write("#local p2 = point2 + <0,0,0>;\n")
3514 file.write(
3515 "#local clip_v = vnormalize(clip_direct + <0,0,0>);\n"
3517 file.write("#local direct_v1 = vnormalize(p2 - p1);\n")
3518 file.write("#if(vdot(direct_v1, clip_v) = 1)\n")
3519 file.write(
3520 ' #error "Shape_Slice_Plane_2P_1V error: Can\'t decide plane"\n'
3522 file.write("#end\n\n")
3523 file.write(
3524 "#local norm = -vnormalize(clip_v - direct_v1*vdot(direct_v1,clip_v));\n"
3526 file.write("#local d = vdot(norm, p1);\n")
3527 file.write("plane{\n")
3528 file.write("norm, d\n")
3529 file.write("}\n")
3530 file.write("#end\n\n")
3531 file.write("//polygon to circle\n")
3532 file.write(
3533 "#macro Shape_Polygon_To_Circle_Blending(_polygon_n, _side_face, _polygon_circumscribed_radius, _circle_radius, _height)\n"
3535 file.write("#local n = int(_polygon_n);\n")
3536 file.write("#if(n < 3)\n")
3537 file.write(" #error " "\n")
3538 file.write("#end\n\n")
3539 file.write(
3540 "#local front_v = VPerp_Adjust(_side_face, z);\n"
3542 file.write("#if(vdot(front_v, x) >= 0)\n")
3543 file.write(
3544 " #local face_ang = acos(vdot(-y, front_v));\n"
3546 file.write("#else\n")
3547 file.write(
3548 " #local face_ang = -acos(vdot(-y, front_v));\n"
3550 file.write("#end\n")
3551 file.write("#local polyg_ext_ang = 2*pi/n;\n")
3552 file.write(
3553 "#local polyg_outer_r = _polygon_circumscribed_radius;\n"
3555 file.write(
3556 "#local polyg_inner_r = polyg_outer_r*cos(polyg_ext_ang/2);\n"
3558 file.write("#local cycle_r = _circle_radius;\n")
3559 file.write("#local h = _height;\n")
3560 file.write(
3561 "#if(polyg_outer_r < 0 | cycle_r < 0 | h <= 0)\n"
3563 file.write(
3564 ' #error "error: each side length must be positive"\n'
3566 file.write("#end\n\n")
3567 file.write("#local multi = 1000;\n")
3568 file.write("#local poly_obj =\n")
3569 file.write("polynomial{\n")
3570 file.write("4,\n")
3571 file.write("xyz(0,2,2): multi*1,\n")
3572 file.write("xyz(2,0,1): multi*2*h,\n")
3573 file.write(
3574 "xyz(1,0,2): multi*2*(polyg_inner_r-cycle_r),\n"
3576 file.write("xyz(2,0,0): multi*(-h*h),\n")
3577 file.write(
3578 "xyz(0,0,2): multi*(-pow(cycle_r - polyg_inner_r, 2)),\n"
3580 file.write(
3581 "xyz(1,0,1): multi*2*h*(-2*polyg_inner_r + cycle_r),\n"
3583 file.write("xyz(1,0,0): multi*2*h*h*polyg_inner_r,\n")
3584 file.write(
3585 "xyz(0,0,1): multi*2*h*polyg_inner_r*(polyg_inner_r - cycle_r),\n"
3587 file.write(
3588 "xyz(0,0,0): multi*(-pow(polyg_inner_r*h, 2))\n"
3590 file.write("sturm\n")
3591 file.write("}\n\n")
3592 file.write("#local mockup1 =\n")
3593 file.write("difference{\n")
3594 file.write(" cylinder{\n")
3595 file.write(
3596 " <0,0,0.0>,<0,0,h>, max(polyg_outer_r, cycle_r)\n"
3598 file.write(" }\n\n")
3599 file.write(" #for(i, 0, n-1)\n")
3600 file.write(" object{\n")
3601 file.write(" poly_obj\n")
3602 file.write(" inverse\n")
3603 file.write(
3604 " rotate <0, 0, -90 + degrees(polyg_ext_ang*i)>\n"
3606 file.write(" }\n")
3607 file.write(" object{\n")
3608 file.write(
3609 " Shape_Slice_Plane_2P_1V(<polyg_inner_r,0,0>,<cycle_r,0,h>,x)\n"
3611 file.write(
3612 " rotate <0, 0, -90 + degrees(polyg_ext_ang*i)>\n"
3614 file.write(" }\n")
3615 file.write(" #end\n")
3616 file.write("}\n\n")
3617 file.write("object{\n")
3618 file.write("mockup1\n")
3619 file.write("rotate <0, 0, degrees(face_ang)>\n")
3620 file.write("}\n")
3621 file.write("#end\n")
3622 # Use the macro
3623 ngon = ob.pov.polytocircle_ngon
3624 ngonR = ob.pov.polytocircle_ngonR
3625 circleR = ob.pov.polytocircle_circleR
3626 tabWrite(
3627 "#declare %s = object { Shape_Polygon_To_Circle_Blending(%s, z, %.4f, %.4f, 2) rotate x*180 translate z*1\n"
3628 % (povdataname, ngon, ngonR, circleR)
3630 tabWrite("}\n")
3631 continue # Don't render proxy mesh, skip to next object
3633 ############################################else try to export mesh
3634 elif (
3635 ob.is_instancer == False
3636 ): # except duplis which should be instances groups for now but all duplis later
3637 if ob.type == 'EMPTY':
3638 tabWrite(
3639 "\n//dummy sphere to represent Empty location\n"
3641 tabWrite(
3642 "#declare %s =sphere {<0, 0, 0>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n"
3643 % povdataname
3646 # TODO(sergey): PovRay is a render engine, so should be using dependency graph
3647 # which was given to it via render engine API.
3648 depsgraph = bpy.context.evaluated_depsgraph_get()
3649 ob_eval = ob.evaluated_get(depsgraph)
3650 try:
3651 me = ob_eval.to_mesh()
3653 # XXX Here? identify the specific exception for mesh object with no data
3654 # XXX So that we can write something for the dataname !
3655 except:
3657 # also happens when curves cant be made into meshes because of no-data
3658 continue
3660 importance = ob.pov.importance_value
3661 if me:
3662 me.calc_loop_triangles()
3663 me_materials = me.materials
3664 me_faces = me.loop_triangles[:]
3665 ## numpytest
3666 #me_looptris = me.loops
3668 ## otypes = ['int32'] is a 32-bit signed integer number numpy datatype
3669 #get_v_index = np.vectorize(lambda l: l.vertex_index, otypes = ['int32'], cache = True)
3670 #faces_verts_idx = get_v_index(me_looptris)
3673 # if len(me_faces)==0:
3674 # tabWrite("\n//dummy sphere to represent empty mesh location\n")
3675 # tabWrite("#declare %s =sphere {<0, 0, 0>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n" % povdataname)
3677 if not me or not me_faces:
3678 tabWrite(
3679 "\n//dummy sphere to represent empty mesh location\n"
3681 tabWrite(
3682 "#declare %s =sphere {<0, 0, 0>,0 pigment{rgbt 1} no_image no_reflection no_radiosity photons{pass_through collect off} hollow}\n"
3683 % povdataname
3685 continue
3687 uv_layers = me.uv_layers
3688 if len(uv_layers) > 0:
3689 if me.uv_layers.active and uv_layers.active.data:
3690 uv_layer = uv_layers.active.data
3691 else:
3692 uv_layer = None
3694 try:
3695 # vcol_layer = me.vertex_colors.active.data
3696 vcol_layer = me.vertex_colors.active.data
3697 except AttributeError:
3698 vcol_layer = None
3700 faces_verts = [f.vertices[:] for f in me_faces]
3701 faces_normals = [f.normal[:] for f in me_faces]
3702 verts_normals = [v.normal[:] for v in me.vertices]
3704 # Use named declaration to allow reference e.g. for baking. MR
3705 file.write("\n")
3706 tabWrite("#declare %s =\n" % povdataname)
3707 tabWrite("mesh2 {\n")
3708 tabWrite("vertex_vectors {\n")
3709 tabWrite("%d" % len(me.vertices)) # vert count
3711 tabStr = tab * tabLevel
3712 for v in me.vertices:
3713 if linebreaksinlists:
3714 file.write(",\n")
3715 file.write(
3716 tabStr + "<%.6f, %.6f, %.6f>" % v.co[:]
3717 ) # vert count
3718 else:
3719 file.write(", ")
3720 file.write(
3721 "<%.6f, %.6f, %.6f>" % v.co[:]
3722 ) # vert count
3723 # tabWrite("<%.6f, %.6f, %.6f>" % v.co[:]) # vert count
3724 file.write("\n")
3725 tabWrite("}\n")
3727 # Build unique Normal list
3728 uniqueNormals = {}
3729 for fi, f in enumerate(me_faces):
3730 fv = faces_verts[fi]
3731 # [-1] is a dummy index, use a list so we can modify in place
3732 if f.use_smooth: # Use vertex normals
3733 for v in fv:
3734 key = verts_normals[v]
3735 uniqueNormals[key] = [-1]
3736 else: # Use face normal
3737 key = faces_normals[fi]
3738 uniqueNormals[key] = [-1]
3740 tabWrite("normal_vectors {\n")
3741 tabWrite("%d" % len(uniqueNormals)) # vert count
3742 idx = 0
3743 tabStr = tab * tabLevel
3744 for no, index in uniqueNormals.items():
3745 if linebreaksinlists:
3746 file.write(",\n")
3747 file.write(
3748 tabStr + "<%.6f, %.6f, %.6f>" % no
3749 ) # vert count
3750 else:
3751 file.write(", ")
3752 file.write(
3753 "<%.6f, %.6f, %.6f>" % no
3754 ) # vert count
3755 index[0] = idx
3756 idx += 1
3757 file.write("\n")
3758 tabWrite("}\n")
3760 # Vertex colors
3761 vertCols = {} # Use for material colors also.
3763 if uv_layer:
3764 # Generate unique UV's
3765 uniqueUVs = {}
3766 # n = 0
3767 for f in me_faces: # me.faces in 2.7
3768 uvs = [uv_layer[l].uv[:] for l in f.loops]
3770 for uv in uvs:
3771 uniqueUVs[uv[:]] = [-1]
3773 tabWrite("uv_vectors {\n")
3774 # print unique_uvs
3775 tabWrite("%d" % len(uniqueUVs)) # vert count
3776 idx = 0
3777 tabStr = tab * tabLevel
3778 for uv, index in uniqueUVs.items():
3779 if linebreaksinlists:
3780 file.write(",\n")
3781 file.write(tabStr + "<%.6f, %.6f>" % uv)
3782 else:
3783 file.write(", ")
3784 file.write("<%.6f, %.6f>" % uv)
3785 index[0] = idx
3786 idx += 1
3788 else:
3789 # Just add 1 dummy vector, no real UV's
3790 tabWrite('1') # vert count
3791 file.write(',\n\t\t<0.0, 0.0>')
3793 file.write("\n")
3794 tabWrite("}\n")
3796 if me.vertex_colors:
3797 # Write down vertex colors as a texture for each vertex
3798 tabWrite("texture_list {\n")
3799 tabWrite(
3800 "%d\n" % (len(me_faces) * 3)
3801 ) # assumes we have only triangles
3802 VcolIdx = 0
3803 if comments:
3804 file.write(
3805 "\n //Vertex colors: one simple pigment texture per vertex\n"
3807 for fi, f in enumerate(me_faces):
3808 # annoying, index may be invalid
3809 material_index = f.material_index
3810 try:
3811 material = me_materials[material_index]
3812 except:
3813 material = None
3814 if (
3815 material
3816 ): # and material.use_vertex_color_paint: #Always use vertex color when there is some for now
3818 cols = [
3819 vcol_layer[l].color[:] for l in f.loops
3822 for col in cols:
3823 key = (
3824 col[0],
3825 col[1],
3826 col[2],
3827 material_index,
3828 ) # Material index!
3829 VcolIdx += 1
3830 vertCols[key] = [VcolIdx]
3831 if linebreaksinlists:
3832 tabWrite(
3833 "texture {pigment{ color srgb <%6f,%6f,%6f> }}\n"
3834 % (col[0], col[1], col[2])
3836 else:
3837 tabWrite(
3838 "texture {pigment{ color srgb <%6f,%6f,%6f> }}"
3839 % (col[0], col[1], col[2])
3841 tabStr = tab * tabLevel
3842 else:
3843 if material:
3844 # Multiply diffuse with SSS Color
3845 if (
3846 material.pov_subsurface_scattering.use
3848 diffuse_color = [
3849 i * j
3850 for i, j in zip(
3851 material.pov_subsurface_scattering.color[
3854 material.diffuse_color[:],
3857 key = (
3858 diffuse_color[0],
3859 diffuse_color[1],
3860 diffuse_color[2],
3861 material_index,
3863 vertCols[key] = [-1]
3864 else:
3865 diffuse_color = material.diffuse_color[
3868 key = (
3869 diffuse_color[0],
3870 diffuse_color[1],
3871 diffuse_color[2],
3872 material_index,
3874 vertCols[key] = [-1]
3876 tabWrite("\n}\n")
3877 # Face indices
3878 tabWrite("\nface_indices {\n")
3879 tabWrite("%d" % (len(me_faces))) # faces count
3880 tabStr = tab * tabLevel
3882 for fi, f in enumerate(me_faces):
3883 fv = faces_verts[fi]
3884 material_index = f.material_index
3886 if vcol_layer:
3887 cols = [
3888 vcol_layer[l].color[:] for l in f.loops
3891 if (
3892 not me_materials
3893 or me_materials[material_index] is None
3894 ): # No materials
3895 if linebreaksinlists:
3896 file.write(",\n")
3897 # vert count
3898 file.write(
3899 tabStr
3900 + "<%d,%d,%d>"
3901 % (fv[0], fv[1], fv[2])
3903 else:
3904 file.write(", ")
3905 file.write(
3906 "<%d,%d,%d>" % (fv[0], fv[1], fv[2])
3907 ) # vert count
3908 else:
3909 material = me_materials[material_index]
3910 if (
3911 me.vertex_colors
3912 ): # and material.use_vertex_color_paint:
3913 # Color per vertex - vertex color
3915 col1 = cols[0]
3916 col2 = cols[1]
3917 col3 = cols[2]
3919 ci1 = vertCols[
3920 col1[0],
3921 col1[1],
3922 col1[2],
3923 material_index,
3924 ][0]
3925 ci2 = vertCols[
3926 col2[0],
3927 col2[1],
3928 col2[2],
3929 material_index,
3930 ][0]
3931 ci3 = vertCols[
3932 col3[0],
3933 col3[1],
3934 col3[2],
3935 material_index,
3936 ][0]
3937 else:
3938 # Color per material - flat material color
3939 if (
3940 material.pov_subsurface_scattering.use
3942 diffuse_color = [
3943 i * j
3944 for i, j in zip(
3945 material.pov_subsurface_scattering.color[
3948 material.diffuse_color[:],
3951 else:
3952 diffuse_color = material.diffuse_color[
3955 ci1 = ci2 = ci3 = vertCols[
3956 diffuse_color[0],
3957 diffuse_color[1],
3958 diffuse_color[2],
3959 f.material_index,
3960 ][0]
3961 # ci are zero based index so we'll subtract 1 from them
3962 if linebreaksinlists:
3963 file.write(",\n")
3964 file.write(
3965 tabStr
3966 + "<%d,%d,%d>, %d,%d,%d"
3968 fv[0],
3969 fv[1],
3970 fv[2],
3971 ci1 - 1,
3972 ci2 - 1,
3973 ci3 - 1,
3975 ) # vert count
3976 else:
3977 file.write(", ")
3978 file.write(
3979 "<%d,%d,%d>, %d,%d,%d"
3981 fv[0],
3982 fv[1],
3983 fv[2],
3984 ci1 - 1,
3985 ci2 - 1,
3986 ci3 - 1,
3988 ) # vert count
3990 file.write("\n")
3991 tabWrite("}\n")
3993 # normal_indices indices
3994 tabWrite("normal_indices {\n")
3995 tabWrite("%d" % (len(me_faces))) # faces count
3996 tabStr = tab * tabLevel
3997 for fi, fv in enumerate(faces_verts):
3999 if me_faces[fi].use_smooth:
4000 if linebreaksinlists:
4001 file.write(",\n")
4002 file.write(
4003 tabStr
4004 + "<%d,%d,%d>"
4006 uniqueNormals[
4007 verts_normals[fv[0]]
4008 ][0],
4009 uniqueNormals[
4010 verts_normals[fv[1]]
4011 ][0],
4012 uniqueNormals[
4013 verts_normals[fv[2]]
4014 ][0],
4016 ) # vert count
4017 else:
4018 file.write(", ")
4019 file.write(
4020 "<%d,%d,%d>"
4022 uniqueNormals[
4023 verts_normals[fv[0]]
4024 ][0],
4025 uniqueNormals[
4026 verts_normals[fv[1]]
4027 ][0],
4028 uniqueNormals[
4029 verts_normals[fv[2]]
4030 ][0],
4032 ) # vert count
4033 else:
4034 idx = uniqueNormals[faces_normals[fi]][0]
4035 if linebreaksinlists:
4036 file.write(",\n")
4037 file.write(
4038 tabStr
4039 + "<%d,%d,%d>" % (idx, idx, idx)
4040 ) # vert count
4041 else:
4042 file.write(", ")
4043 file.write(
4044 "<%d,%d,%d>" % (idx, idx, idx)
4045 ) # vert count
4047 file.write("\n")
4048 tabWrite("}\n")
4050 if uv_layer:
4051 tabWrite("uv_indices {\n")
4052 tabWrite("%d" % (len(me_faces))) # faces count
4053 tabStr = tab * tabLevel
4054 for f in me_faces:
4055 uvs = [uv_layer[l].uv[:] for l in f.loops]
4057 if linebreaksinlists:
4058 file.write(",\n")
4059 file.write(
4060 tabStr
4061 + "<%d,%d,%d>"
4063 uniqueUVs[uvs[0]][0],
4064 uniqueUVs[uvs[1]][0],
4065 uniqueUVs[uvs[2]][0],
4068 else:
4069 file.write(", ")
4070 file.write(
4071 "<%d,%d,%d>"
4073 uniqueUVs[uvs[0]][0],
4074 uniqueUVs[uvs[1]][0],
4075 uniqueUVs[uvs[2]][0],
4079 file.write("\n")
4080 tabWrite("}\n")
4082 # XXX BOOLEAN
4083 onceCSG = 0
4084 for mod in ob.modifiers:
4085 if onceCSG == 0:
4086 if mod:
4087 if mod.type == 'BOOLEAN':
4088 if ob.pov.boolean_mod == "POV":
4089 file.write(
4090 "\tinside_vector <%.6g, %.6g, %.6g>\n"
4092 ob.pov.inside_vector[0],
4093 ob.pov.inside_vector[1],
4094 ob.pov.inside_vector[2],
4097 onceCSG = 1
4099 if me.materials:
4100 try:
4101 material = me.materials[0] # dodgy
4102 writeObjectMaterial(material, ob)
4103 except IndexError:
4104 print(me)
4106 # POV object modifiers such as
4107 # hollow / sturm / double_illuminate etc.
4108 write_object_modifiers(scene, ob, file)
4110 # Importance for radiosity sampling added here:
4111 tabWrite("radiosity { \n")
4112 tabWrite("importance %3g \n" % importance)
4113 tabWrite("}\n")
4115 tabWrite("}\n") # End of mesh block
4116 else:
4117 facesMaterials = [] # WARNING!!!!!!!!!!!!!!!!!!!!!!
4118 if me_materials:
4119 for f in me_faces:
4120 if f.material_index not in facesMaterials:
4121 facesMaterials.append(f.material_index)
4122 # No vertex colors, so write material colors as vertex colors
4123 for i, material in enumerate(me_materials):
4125 if (
4126 material
4127 and material.pov.material_use_nodes == False
4128 ): # WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
4129 # Multiply diffuse with SSS Color
4130 if material.pov_subsurface_scattering.use:
4131 diffuse_color = [
4132 i * j
4133 for i, j in zip(
4134 material.pov_subsurface_scattering.color[
4137 material.diffuse_color[:],
4140 key = (
4141 diffuse_color[0],
4142 diffuse_color[1],
4143 diffuse_color[2],
4145 ) # i == f.mat
4146 vertCols[key] = [-1]
4147 else:
4148 diffuse_color = material.diffuse_color[
4151 key = (
4152 diffuse_color[0],
4153 diffuse_color[1],
4154 diffuse_color[2],
4156 ) # i == f.mat
4157 vertCols[key] = [-1]
4159 idx = 0
4160 LocalMaterialNames = []
4161 for col, index in vertCols.items():
4162 # if me_materials:
4163 mater = me_materials[col[3]]
4164 if me_materials is None: # XXX working?
4165 material_finish = (
4166 DEF_MAT_NAME
4167 ) # not working properly,
4168 trans = 0.0
4170 else:
4171 shading.writeTextureInfluence(
4172 using_uberpov,
4173 mater,
4174 materialNames,
4175 LocalMaterialNames,
4176 path_image,
4177 lampCount,
4178 imageFormat,
4179 imgMap,
4180 imgMapTransforms,
4181 tabWrite,
4182 comments,
4183 string_strip_hyphen,
4184 safety,
4185 col,
4187 preview_dir,
4188 unpacked_images,
4190 ###################################################################
4191 index[0] = idx
4192 idx += 1
4194 # Vert Colors
4195 tabWrite("texture_list {\n")
4196 # In case there's is no material slot, give at least one texture
4197 # (an empty one so it uses pov default)
4198 if len(vertCols) == 0:
4199 file.write(tabStr + "1")
4200 else:
4201 file.write(
4202 tabStr + "%s" % (len(vertCols))
4203 ) # vert count
4205 # below "material" alias, added check ob.active_material
4206 # to avoid variable referenced before assignment error
4207 try:
4208 material = ob.active_material
4209 except IndexError:
4210 # when no material slot exists,
4211 material = None
4213 # WARNING!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
4214 if (
4215 material
4216 and ob.active_material is not None
4217 and material.pov.material_use_nodes == False
4219 if material.pov.replacement_text != "":
4220 file.write("\n")
4221 file.write(
4222 " texture{%s}\n"
4223 % material.pov.replacement_text
4226 else:
4227 # Loop through declared materials list
4228 for cMN in LocalMaterialNames:
4229 if material != "Default":
4230 file.write(
4231 "\n texture{MAT_%s}\n" % cMN
4233 # use string_strip_hyphen(materialNames[material]))
4234 # or Something like that to clean up the above?
4235 elif material and material.pov.material_use_nodes:
4236 for index in facesMaterials:
4237 faceMaterial = string_strip_hyphen(
4238 bpy.path.clean_name(
4239 me_materials[index].name
4242 file.write(
4243 "\n texture{%s}\n" % faceMaterial
4245 # END!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
4246 else:
4247 file.write(" texture{}\n")
4248 tabWrite("}\n")
4250 # Face indices
4251 tabWrite("face_indices {\n")
4252 tabWrite("%d" % (len(me_faces))) # faces count
4253 tabStr = tab * tabLevel
4255 for fi, f in enumerate(me_faces):
4256 fv = faces_verts[fi]
4257 material_index = f.material_index
4259 if vcol_layer:
4260 cols = [
4261 vcol_layer[l].color[:] for l in f.loops
4264 if (
4265 not me_materials
4266 or me_materials[material_index] is None
4267 ): # No materials
4268 if linebreaksinlists:
4269 file.write(",\n")
4270 # vert count
4271 file.write(
4272 tabStr
4273 + "<%d,%d,%d>"
4274 % (fv[0], fv[1], fv[2])
4276 else:
4277 file.write(", ")
4278 file.write(
4279 "<%d,%d,%d>" % (fv[0], fv[1], fv[2])
4280 ) # vert count
4281 else:
4282 material = me_materials[material_index]
4283 ci1 = ci2 = ci3 = f.material_index
4284 if (
4285 me.vertex_colors
4286 ): # and material.use_vertex_color_paint:
4287 # Color per vertex - vertex color
4289 col1 = cols[0]
4290 col2 = cols[1]
4291 col3 = cols[2]
4293 ci1 = vertCols[
4294 col1[0],
4295 col1[1],
4296 col1[2],
4297 material_index,
4298 ][0]
4299 ci2 = vertCols[
4300 col2[0],
4301 col2[1],
4302 col2[2],
4303 material_index,
4304 ][0]
4305 ci3 = vertCols[
4306 col3[0],
4307 col3[1],
4308 col3[2],
4309 material_index,
4310 ][0]
4311 elif material.pov.material_use_nodes:
4312 ci1 = ci2 = ci3 = 0
4313 else:
4314 # Color per material - flat material color
4315 if (
4316 material.pov_subsurface_scattering.use
4318 diffuse_color = [
4319 i * j
4320 for i, j in zip(
4321 material.pov_subsurface_scattering.color[
4324 material.diffuse_color[:],
4327 else:
4328 diffuse_color = material.diffuse_color[
4331 ci1 = ci2 = ci3 = vertCols[
4332 diffuse_color[0],
4333 diffuse_color[1],
4334 diffuse_color[2],
4335 f.material_index,
4336 ][0]
4338 if linebreaksinlists:
4339 file.write(",\n")
4340 file.write(
4341 tabStr
4342 + "<%d,%d,%d>, %d,%d,%d"
4344 fv[0],
4345 fv[1],
4346 fv[2],
4347 ci1,
4348 ci2,
4349 ci3,
4351 ) # vert count
4352 else:
4353 file.write(", ")
4354 file.write(
4355 "<%d,%d,%d>, %d,%d,%d"
4357 fv[0],
4358 fv[1],
4359 fv[2],
4360 ci1,
4361 ci2,
4362 ci3,
4364 ) # vert count
4366 file.write("\n")
4367 tabWrite("}\n")
4369 # normal_indices indices
4370 tabWrite("normal_indices {\n")
4371 tabWrite("%d" % (len(me_faces))) # faces count
4372 tabStr = tab * tabLevel
4373 for fi, fv in enumerate(faces_verts):
4374 if me_faces[fi].use_smooth:
4375 if linebreaksinlists:
4376 file.write(",\n")
4377 file.write(
4378 tabStr
4379 + "<%d,%d,%d>"
4381 uniqueNormals[
4382 verts_normals[fv[0]]
4383 ][0],
4384 uniqueNormals[
4385 verts_normals[fv[1]]
4386 ][0],
4387 uniqueNormals[
4388 verts_normals[fv[2]]
4389 ][0],
4391 ) # vert count
4392 else:
4393 file.write(", ")
4394 file.write(
4395 "<%d,%d,%d>"
4397 uniqueNormals[
4398 verts_normals[fv[0]]
4399 ][0],
4400 uniqueNormals[
4401 verts_normals[fv[1]]
4402 ][0],
4403 uniqueNormals[
4404 verts_normals[fv[2]]
4405 ][0],
4407 ) # vert count
4408 else:
4409 idx = uniqueNormals[faces_normals[fi]][0]
4410 if linebreaksinlists:
4411 file.write(",\n")
4412 file.write(
4413 tabStr
4414 + "<%d,%d,%d>" % (idx, idx, idx)
4415 ) # vertcount
4416 else:
4417 file.write(", ")
4418 file.write(
4419 "<%d,%d,%d>" % (idx, idx, idx)
4420 ) # vert count
4422 file.write("\n")
4423 tabWrite("}\n")
4425 if uv_layer:
4426 tabWrite("uv_indices {\n")
4427 tabWrite("%d" % (len(me_faces))) # faces count
4428 tabStr = tab * tabLevel
4429 for f in me_faces:
4430 uvs = [uv_layer[l].uv[:] for l in f.loops]
4432 if linebreaksinlists:
4433 file.write(",\n")
4434 file.write(
4435 tabStr
4436 + "<%d,%d,%d>"
4438 uniqueUVs[uvs[0]][0],
4439 uniqueUVs[uvs[1]][0],
4440 uniqueUVs[uvs[2]][0],
4443 else:
4444 file.write(", ")
4445 file.write(
4446 "<%d,%d,%d>"
4448 uniqueUVs[uvs[0]][0],
4449 uniqueUVs[uvs[1]][0],
4450 uniqueUVs[uvs[2]][0],
4454 file.write("\n")
4455 tabWrite("}\n")
4457 # XXX BOOLEAN
4458 onceCSG = 0
4459 for mod in ob.modifiers:
4460 if onceCSG == 0:
4461 if mod:
4462 if mod.type == 'BOOLEAN':
4463 if ob.pov.boolean_mod == "POV":
4464 file.write(
4465 "\tinside_vector <%.6g, %.6g, %.6g>\n"
4467 ob.pov.inside_vector[0],
4468 ob.pov.inside_vector[1],
4469 ob.pov.inside_vector[2],
4472 onceCSG = 1
4474 if me.materials:
4475 try:
4476 material = me.materials[0] # dodgy
4477 writeObjectMaterial(material, ob)
4478 except IndexError:
4479 print(me)
4481 # POV object modifiers such as
4482 # hollow / sturm / double_illuminate etc.
4483 write_object_modifiers(scene, ob, file)
4485 # Importance for radiosity sampling added here:
4486 tabWrite("radiosity { \n")
4487 tabWrite("importance %3g \n" % importance)
4488 tabWrite("}\n")
4490 tabWrite("}\n") # End of mesh block
4492 ob_eval.to_mesh_clear()
4494 if csg:
4495 duplidata_ref = []
4496 _dupnames_seen = (
4497 dict()
4498 ) # avoid duplicate output during introspection
4499 for ob in sel:
4500 # matrix = global_matrix @ ob.matrix_world
4501 if ob.is_instancer:
4502 tabWrite("\n//--DupliObjects in %s--\n\n" % ob.name)
4503 # ob.dupli_list_create(scene) #deprecated in 2.8
4504 depsgraph = bpy.context.evaluated_depsgraph_get()
4505 dup = ""
4506 if ob.is_modified(scene, 'RENDER'):
4507 # modified object always unique so using object name rather than data name
4508 dup = "#declare OB%s = union{\n" % (
4509 string_strip_hyphen(bpy.path.clean_name(ob.name))
4511 else:
4512 dup = "#declare DATA%s = union{\n" % (
4513 string_strip_hyphen(bpy.path.clean_name(ob.name))
4515 for eachduplicate in depsgraph.object_instances:
4516 if (
4517 eachduplicate.is_instance
4518 ): # Real dupli instance filtered because original included in list since 2.8
4519 _dupname = eachduplicate.object.name
4520 _dupobj = bpy.data.objects[_dupname]
4521 # BEGIN introspection for troubleshooting purposes
4522 if not "name" in dir(_dupobj.data):
4523 if _dupname not in _dupnames_seen:
4524 print(
4525 "WARNING: bpy.data.objects[%s].data (of type %s) has no 'name' attribute"
4526 % (_dupname, type(_dupobj.data))
4528 for _thing in dir(_dupobj):
4529 print(
4530 "|| %s.%s = %s"
4532 _dupname,
4533 _thing,
4534 getattr(_dupobj, _thing),
4537 _dupnames_seen[_dupname] = 1
4538 print(
4539 "''=> Unparseable objects so far: %s"
4540 % (_dupnames_seen)
4542 else:
4543 _dupnames_seen[_dupname] += 1
4544 continue # don't try to parse data objects with no name attribute
4545 # END introspection for troubleshooting purposes
4546 duplidataname = "OB" + string_strip_hyphen(
4547 bpy.path.clean_name(_dupobj.data.name)
4549 dupmatrix = (
4550 eachduplicate.matrix_world.copy()
4551 ) # has to be copied to not store instance since 2.8
4552 dup += "\tobject {\n\t\tDATA%s\n\t\t%s\t}\n" % (
4553 string_strip_hyphen(
4554 bpy.path.clean_name(_dupobj.data.name)
4556 MatrixAsPovString(
4557 ob.matrix_world.inverted() @ dupmatrix
4560 # add object to a list so that it is not rendered for some instance_types
4561 if (
4562 ob.instance_type not in {'COLLECTION'}
4563 and duplidataname not in duplidata_ref
4565 duplidata_ref.append(
4566 duplidataname
4567 ) # older key [string_strip_hyphen(bpy.path.clean_name("OB"+ob.name))]
4568 dup += "}\n"
4569 # ob.dupli_list_clear()# just do not store any reference to instance since 2.8
4570 tabWrite(dup)
4571 else:
4572 continue
4573 print(
4574 "WARNING: Unparseable objects in current .blend file:\n''=> %s"
4575 % (_dupnames_seen)
4577 print("duplidata_ref = %s" % (duplidata_ref))
4578 for data_name, inst in data_ref.items():
4579 for ob_name, matrix_str in inst:
4580 if (
4581 ob_name not in duplidata_ref
4582 ): # .items() for a dictionary
4583 tabWrite(
4584 "\n//----Blender Object Name:%s----\n" % ob_name
4586 if ob.pov.object_as == '':
4587 tabWrite("object { \n")
4588 tabWrite("%s\n" % data_name)
4589 tabWrite("%s\n" % matrix_str)
4590 tabWrite("}\n")
4591 else:
4592 no_boolean = True
4593 for mod in ob.modifiers:
4594 if mod.type == 'BOOLEAN':
4595 operation = None
4596 no_boolean = False
4597 if mod.operation == 'INTERSECT':
4598 operation = 'intersection'
4599 else:
4600 operation = mod.operation.lower()
4601 mod_ob_name = string_strip_hyphen(
4602 bpy.path.clean_name(mod.object.name)
4604 mod_matrix = (
4605 global_matrix @ mod.object.matrix_world
4607 mod_ob_matrix = MatrixAsPovString(
4608 mod_matrix
4610 tabWrite("%s { \n" % operation)
4611 tabWrite("object { \n")
4612 tabWrite("%s\n" % data_name)
4613 tabWrite("%s\n" % matrix_str)
4614 tabWrite("}\n")
4615 tabWrite("object { \n")
4616 tabWrite("%s\n" % ('DATA' + mod_ob_name))
4617 tabWrite("%s\n" % mod_ob_matrix)
4618 tabWrite("}\n")
4619 tabWrite("}\n")
4620 break
4621 if no_boolean:
4622 tabWrite("object { \n")
4623 tabWrite("%s\n" % data_name)
4624 tabWrite("%s\n" % matrix_str)
4625 tabWrite("}\n")
4627 def exportWorld(world):
4628 """write world as POV backgrounbd and sky_sphere to exported file """
4629 render = scene.pov
4630 camera = scene.camera
4631 matrix = global_matrix @ camera.matrix_world
4632 if not world:
4633 return
4634 #############Maurice####################################
4635 # These lines added to get sky gradient (visible with PNG output)
4636 if world:
4637 # For simple flat background:
4638 if not world.pov.use_sky_blend:
4639 # Non fully transparent background could premultiply alpha and avoid anti-aliasing
4640 # display issue:
4641 if render.alpha_mode == 'TRANSPARENT':
4642 tabWrite(
4643 "background {rgbt<%.3g, %.3g, %.3g, 0.75>}\n"
4644 % (world.pov.horizon_color[:])
4646 # Currently using no alpha with Sky option:
4647 elif render.alpha_mode == 'SKY':
4648 tabWrite(
4649 "background {rgbt<%.3g, %.3g, %.3g, 0>}\n"
4650 % (world.pov.horizon_color[:])
4652 # StraightAlpha:
4653 # XXX Does not exists anymore
4654 # else:
4655 # tabWrite("background {rgbt<%.3g, %.3g, %.3g, 1>}\n" % (world.pov.horizon_color[:]))
4657 worldTexCount = 0
4658 # For Background image textures
4659 for (
4661 ) in (
4662 world.pov_texture_slots
4663 ): # risk to write several sky_spheres but maybe ok.
4664 if t:
4665 tex = bpy.data.textures[t.texture]
4666 if tex.type is not None:
4667 worldTexCount += 1
4668 # XXX No enable checkbox for world textures yet (report it?)
4669 # if t and tex.type == 'IMAGE' and t.use:
4670 if tex.type == 'IMAGE':
4671 image_filename = path_image(tex.image)
4672 if tex.image.filepath != image_filename:
4673 tex.image.filepath = image_filename
4674 if image_filename != "" and t.use_map_blend:
4675 texturesBlend = image_filename
4676 # colvalue = t.default_value
4677 t_blend = t
4679 # Commented below was an idea to make the Background image oriented as camera
4680 # taken here:
4681 # http://news.pov.org/pov.newusers/thread/%3Cweb.4a5cddf4e9c9822ba2f93e20@news.pov.org%3E/
4682 # Replace 4/3 by the ratio of each image found by some custom or existing
4683 # function
4684 # mappingBlend = (" translate <%.4g,%.4g,%.4g> rotate z*degrees" \
4685 # "(atan((camLocation - camLookAt).x/(camLocation - " \
4686 # "camLookAt).y)) rotate x*degrees(atan((camLocation - " \
4687 # "camLookAt).y/(camLocation - camLookAt).z)) rotate y*" \
4688 # "degrees(atan((camLocation - camLookAt).z/(camLocation - " \
4689 # "camLookAt).x)) scale <%.4g,%.4g,%.4g>b" % \
4690 # (t_blend.offset.x / 10 , t_blend.offset.y / 10 ,
4691 # t_blend.offset.z / 10, t_blend.scale.x ,
4692 # t_blend.scale.y , t_blend.scale.z))
4693 # using camera rotation valuesdirectly from blender seems much easier
4694 if t_blend.texture_coords == 'ANGMAP':
4695 mappingBlend = ""
4696 else:
4697 # POV-Ray "scale" is not a number of repetitions factor, but its
4698 # inverse, a standard scale factor.
4699 # 0.5 Offset is needed relatively to scale because center of the
4700 # UV scale is 0.5,0.5 in blender and 0,0 in POV
4701 # Further Scale by 2 and translate by -1 are
4702 # required for the sky_sphere not to repeat
4704 mappingBlend = (
4705 "scale 2 scale <%.4g,%.4g,%.4g> translate -1 "
4706 "translate <%.4g,%.4g,%.4g> rotate<0,0,0> "
4708 (1.0 / t_blend.scale.x),
4709 (1.0 / t_blend.scale.y),
4710 (1.0 / t_blend.scale.z),
4712 - (0.5 / t_blend.scale.x)
4713 - t_blend.offset.x,
4715 - (0.5 / t_blend.scale.y)
4716 - t_blend.offset.y,
4717 t_blend.offset.z,
4721 # The initial position and rotation of the pov camera is probably creating
4722 # the rotation offset should look into it someday but at least background
4723 # won't rotate with the camera now.
4724 # Putting the map on a plane would not introduce the skysphere distortion and
4725 # allow for better image scale matching but also some waay to chose depth and
4726 # size of the plane relative to camera.
4727 tabWrite("sky_sphere {\n")
4728 tabWrite("pigment {\n")
4729 tabWrite(
4730 "image_map{%s \"%s\" %s}\n"
4732 imageFormat(texturesBlend),
4733 texturesBlend,
4734 imgMapBG(t_blend),
4737 tabWrite("}\n")
4738 tabWrite("%s\n" % (mappingBlend))
4739 # The following layered pigment opacifies to black over the texture for
4740 # transmit below 1 or otherwise adds to itself
4741 tabWrite(
4742 "pigment {rgb 0 transmit %s}\n" % (tex.intensity)
4744 tabWrite("}\n")
4745 # tabWrite("scale 2\n")
4746 # tabWrite("translate -1\n")
4748 # For only Background gradient
4750 if worldTexCount == 0:
4751 if world.pov.use_sky_blend:
4752 tabWrite("sky_sphere {\n")
4753 tabWrite("pigment {\n")
4754 # maybe Should follow the advice of POV doc about replacing gradient
4755 # for skysphere..5.5
4756 tabWrite("gradient y\n")
4757 tabWrite("color_map {\n")
4758 # XXX Does not exists anymore
4759 # if render.alpha_mode == 'STRAIGHT':
4760 # tabWrite("[0.0 rgbt<%.3g, %.3g, %.3g, 1>]\n" % (world.pov.horizon_color[:]))
4761 # tabWrite("[1.0 rgbt<%.3g, %.3g, %.3g, 1>]\n" % (world.pov.zenith_color[:]))
4762 if render.alpha_mode == 'TRANSPARENT':
4763 tabWrite(
4764 "[0.0 rgbt<%.3g, %.3g, %.3g, 0.99>]\n"
4765 % (world.pov.horizon_color[:])
4767 # aa premult not solved with transmit 1
4768 tabWrite(
4769 "[1.0 rgbt<%.3g, %.3g, %.3g, 0.99>]\n"
4770 % (world.pov.zenith_color[:])
4772 else:
4773 tabWrite(
4774 "[0.0 rgbt<%.3g, %.3g, %.3g, 0>]\n"
4775 % (world.pov.horizon_color[:])
4777 tabWrite(
4778 "[1.0 rgbt<%.3g, %.3g, %.3g, 0>]\n"
4779 % (world.pov.zenith_color[:])
4781 tabWrite("}\n")
4782 tabWrite("}\n")
4783 tabWrite("}\n")
4784 # Sky_sphere alpha (transmit) is not translating into image alpha the same
4785 # way as 'background'
4787 # if world.pov.light_settings.use_indirect_light:
4788 # scene.pov.radio_enable=1
4790 # Maybe change the above to a function copyInternalRenderer settings when
4791 # user pushes a button, then:
4792 # scene.pov.radio_enable = world.pov.light_settings.use_indirect_light
4793 # and other such translations but maybe this would not be allowed either?
4795 ###############################################################
4797 mist = world.mist_settings
4799 if mist.use_mist:
4800 tabWrite("fog {\n")
4801 if mist.falloff == 'LINEAR':
4802 tabWrite(
4803 "distance %.6f\n" % ((mist.start + mist.depth) * 0.368)
4805 elif mist.falloff == 'QUADRATIC': # n**2 or squrt(n)?
4806 tabWrite(
4807 "distance %.6f\n" % ((mist.start + mist.depth) ** 2 * 0.368)
4809 elif mist.falloff == 'INVERSE_QUADRATIC': # n**2 or squrt(n)?
4810 tabWrite(
4811 "distance %.6f\n" % ((mist.start + mist.depth) ** 2 * 0.368)
4813 tabWrite(
4814 "color rgbt<%.3g, %.3g, %.3g, %.3g>\n"
4815 % (*world.pov.horizon_color, 1.0 - mist.intensity)
4817 # tabWrite("fog_offset %.6f\n" % mist.start) #create a pov property to prepend
4818 # tabWrite("fog_alt %.6f\n" % mist.height) #XXX right?
4819 # tabWrite("turbulence 0.2\n")
4820 # tabWrite("turb_depth 0.3\n")
4821 tabWrite("fog_type 1\n") # type2 for height
4822 tabWrite("}\n")
4823 if scene.pov.media_enable:
4824 tabWrite("media {\n")
4825 tabWrite(
4826 "scattering { %d, rgb %.12f*<%.4g, %.4g, %.4g>\n"
4828 int(scene.pov.media_scattering_type),
4829 (scene.pov.media_diffusion_scale),
4830 *(scene.pov.media_diffusion_color[:]),
4833 if scene.pov.media_scattering_type == '5':
4834 tabWrite("eccentricity %.3g\n" % scene.pov.media_eccentricity)
4835 tabWrite("}\n")
4836 tabWrite(
4837 "absorption %.12f*<%.4g, %.4g, %.4g>\n"
4839 scene.pov.media_absorption_scale,
4840 *(scene.pov.media_absorption_color[:]),
4843 tabWrite("\n")
4844 tabWrite("samples %.d\n" % scene.pov.media_samples)
4845 tabWrite("}\n")
4847 def exportGlobalSettings(scene):
4848 """write all POV global settings to exported file """
4849 tabWrite("global_settings {\n")
4850 tabWrite("assumed_gamma 1.0\n")
4851 tabWrite("max_trace_level %d\n" % scene.pov.max_trace_level)
4853 # Deprecated (autodetected in pov3.8):
4854 # if scene.pov.charset != 'ascii':
4855 # file.write(" charset %s\n" % scene.pov.charset)
4856 if scene.pov.global_settings_advanced:
4857 if scene.pov.radio_enable == False:
4858 file.write(" adc_bailout %.6f\n" % scene.pov.adc_bailout)
4859 file.write(
4860 " ambient_light <%.6f,%.6f,%.6f>\n"
4861 % scene.pov.ambient_light[:]
4863 file.write(
4864 " irid_wavelength <%.6f,%.6f,%.6f>\n"
4865 % scene.pov.irid_wavelength[:]
4867 file.write(
4868 " max_intersections %s\n" % scene.pov.max_intersections
4870 file.write(" number_of_waves %s\n" % scene.pov.number_of_waves)
4871 file.write(" noise_generator %s\n" % scene.pov.noise_generator)
4872 if scene.pov.radio_enable:
4873 tabWrite("radiosity {\n")
4874 tabWrite("adc_bailout %.4g\n" % scene.pov.radio_adc_bailout)
4875 tabWrite("brightness %.4g\n" % scene.pov.radio_brightness)
4876 tabWrite("count %d\n" % scene.pov.radio_count)
4877 tabWrite("error_bound %.4g\n" % scene.pov.radio_error_bound)
4878 tabWrite("gray_threshold %.4g\n" % scene.pov.radio_gray_threshold)
4879 tabWrite(
4880 "low_error_factor %.4g\n" % scene.pov.radio_low_error_factor
4882 tabWrite("maximum_reuse %.4g\n" % scene.pov.radio_maximum_reuse)
4883 tabWrite("minimum_reuse %.4g\n" % scene.pov.radio_minimum_reuse)
4884 tabWrite("nearest_count %d\n" % scene.pov.radio_nearest_count)
4885 tabWrite("pretrace_start %.3g\n" % scene.pov.radio_pretrace_start)
4886 tabWrite("pretrace_end %.3g\n" % scene.pov.radio_pretrace_end)
4887 tabWrite("recursion_limit %d\n" % scene.pov.radio_recursion_limit)
4888 tabWrite("always_sample %d\n" % scene.pov.radio_always_sample)
4889 tabWrite("normal %d\n" % scene.pov.radio_normal)
4890 tabWrite("media %d\n" % scene.pov.radio_media)
4891 tabWrite("subsurface %d\n" % scene.pov.radio_subsurface)
4892 tabWrite("}\n")
4893 onceSss = 1
4894 onceAmbient = 1
4895 oncePhotons = 1
4896 for material in bpy.data.materials:
4897 if material.pov_subsurface_scattering.use and onceSss:
4898 # In pov, the scale has reversed influence compared to blender. these number
4899 # should correct that
4900 tabWrite(
4901 "mm_per_unit %.6f\n"
4902 % (material.pov_subsurface_scattering.scale * 1000.0)
4904 # 1000 rather than scale * (-100.0) + 15.0))
4906 # In POV-Ray, the scale factor for all subsurface shaders needs to be the same
4908 # formerly sslt_samples were multiplied by 100 instead of 10
4909 sslt_samples = (
4910 11 - material.pov_subsurface_scattering.error_threshold
4911 ) * 10
4913 tabWrite(
4914 "subsurface { samples %d, %d }\n"
4915 % (sslt_samples, sslt_samples / 10)
4917 onceSss = 0
4919 if world and onceAmbient:
4920 tabWrite(
4921 "ambient_light rgb<%.3g, %.3g, %.3g>\n"
4922 % world.pov.ambient_color[:]
4924 onceAmbient = 0
4926 if scene.pov.photon_enable:
4927 if oncePhotons and (
4928 material.pov.refraction_type == "2"
4929 or material.pov.photons_reflection == True
4931 tabWrite("photons {\n")
4932 tabWrite("spacing %.6f\n" % scene.pov.photon_spacing)
4933 tabWrite(
4934 "max_trace_level %d\n"
4935 % scene.pov.photon_max_trace_level
4937 tabWrite(
4938 "adc_bailout %.3g\n" % scene.pov.photon_adc_bailout
4940 tabWrite(
4941 "gather %d, %d\n"
4943 scene.pov.photon_gather_min,
4944 scene.pov.photon_gather_max,
4947 if scene.pov.photon_map_file_save_load in {'save'}:
4948 filePhName = 'Photon_map_file.ph'
4949 if scene.pov.photon_map_file != '':
4950 filePhName = scene.pov.photon_map_file + '.ph'
4951 filePhDir = tempfile.gettempdir()
4952 path = bpy.path.abspath(scene.pov.photon_map_dir)
4953 if os.path.exists(path):
4954 filePhDir = path
4955 fullFileName = os.path.join(filePhDir, filePhName)
4956 tabWrite('save_file "%s"\n' % fullFileName)
4957 scene.pov.photon_map_file = fullFileName
4958 if scene.pov.photon_map_file_save_load in {'load'}:
4959 fullFileName = bpy.path.abspath(
4960 scene.pov.photon_map_file
4962 if os.path.exists(fullFileName):
4963 tabWrite('load_file "%s"\n' % fullFileName)
4964 tabWrite("}\n")
4965 oncePhotons = 0
4967 tabWrite("}\n")
4969 def exportCustomCode():
4970 """write all POV user defined custom code to exported file """
4971 # Write CurrentAnimation Frame for use in Custom POV Code
4972 file.write(
4973 "#declare CURFRAMENUM = %d;\n" % bpy.context.scene.frame_current
4975 # Change path and uncomment to add an animated include file by hand:
4976 file.write(
4977 "//#include \"/home/user/directory/animation_include_file.inc\"\n"
4979 for txt in bpy.data.texts:
4980 if txt.pov.custom_code == 'both':
4981 # Why are the newlines needed?
4982 file.write("\n")
4983 file.write(txt.as_string())
4984 file.write("\n")
4986 # sel = renderable_objects(scene) #removed for booleans
4987 if comments:
4988 file.write(
4989 "//----------------------------------------------\n"
4990 "//--Exported with POV-Ray exporter for Blender--\n"
4991 "//----------------------------------------------\n\n"
4993 file.write("#version 3.7;\n") #Switch below as soon as 3.8 beta gets easy linked
4994 #file.write("#version 3.8;\n")
4995 file.write(
4996 "#declare Default_texture = texture{pigment {rgb 0.8} "
4997 "finish {brilliance 3.8} }\n\n"
4999 if comments:
5000 file.write("\n//--Global settings--\n\n")
5002 exportGlobalSettings(scene)
5004 if comments:
5005 file.write("\n//--Custom Code--\n\n")
5006 exportCustomCode()
5008 if comments:
5009 file.write("\n//--Patterns Definitions--\n\n")
5010 LocalPatternNames = []
5011 for texture in bpy.data.textures: # ok?
5012 if texture.users > 0:
5013 currentPatName = string_strip_hyphen(
5014 bpy.path.clean_name(texture.name)
5016 # string_strip_hyphen(patternNames[texture.name]) #maybe instead of the above
5017 LocalPatternNames.append(currentPatName)
5018 # use above list to prevent writing texture instances several times and assign in mats?
5019 if (
5020 texture.type not in {'NONE', 'IMAGE'}
5021 and texture.pov.tex_pattern_type == 'emulator'
5022 ) or (
5023 texture.type in {'NONE', 'IMAGE'}
5024 and texture.pov.tex_pattern_type != 'emulator'
5026 file.write("\n#declare PAT_%s = \n" % currentPatName)
5027 file.write(shading.exportPattern(texture, string_strip_hyphen))
5028 file.write("\n")
5029 if comments:
5030 file.write("\n//--Background--\n\n")
5032 exportWorld(scene.world)
5034 if comments:
5035 file.write("\n//--Cameras--\n\n")
5037 exportCamera()
5039 if comments:
5040 file.write("\n//--Lamps--\n\n")
5042 for ob in bpy.data.objects:
5043 if ob.type == 'MESH':
5044 for mod in ob.modifiers:
5045 if mod.type == 'BOOLEAN':
5046 if mod.object not in csg_list:
5047 csg_list.append(mod.object)
5048 if csg_list != []:
5049 csg = False
5050 sel = no_renderable_objects(scene)
5051 exportMeshes(scene, sel, csg)
5053 csg = True
5054 sel = renderable_objects(scene)
5056 exportLamps(
5057 [L for L in sel if (L.type == 'LIGHT' and L.pov.object_as != 'RAINBOW')]
5060 if comments:
5061 file.write("\n//--Rainbows--\n\n")
5062 exportRainbows(
5063 [L for L in sel if (L.type == 'LIGHT' and L.pov.object_as == 'RAINBOW')]
5066 if comments:
5067 file.write("\n//--Special Curves--\n\n")
5068 for c in sel:
5069 if c.is_modified(scene, 'RENDER'):
5070 continue # don't export as pov curves objects with modifiers, but as mesh
5071 elif c.type == 'CURVE' and (
5072 c.pov.curveshape in {'lathe', 'sphere_sweep', 'loft', 'birail'}
5074 exportCurves(scene, c)
5076 if comments:
5077 file.write("\n//--Material Definitions--\n\n")
5078 # write a default pigment for objects with no material (comment out to show black)
5079 file.write("#default{ pigment{ color srgb 0.8 }}\n")
5080 # Convert all materials to strings we can access directly per vertex.
5081 # exportMaterials()
5082 shading.writeMaterial(
5083 using_uberpov,
5084 DEF_MAT_NAME,
5085 scene,
5086 tabWrite,
5087 safety,
5088 comments,
5089 uniqueName,
5090 materialNames,
5091 None,
5092 ) # default material
5093 for material in bpy.data.materials:
5094 if material.users > 0:
5095 if material.pov.material_use_nodes:
5096 ntree = material.node_tree
5097 povMatName = string_strip_hyphen(
5098 bpy.path.clean_name(material.name)
5100 if len(ntree.nodes) == 0:
5101 file.write(
5102 '#declare %s = texture {%s}\n' % (povMatName, color)
5104 else:
5105 shading.write_nodes(scene, povMatName, ntree, file)
5107 for node in ntree.nodes:
5108 if node:
5109 if node.bl_idname == "PovrayOutputNode":
5110 if node.inputs["Texture"].is_linked:
5111 for link in ntree.links:
5112 if (
5113 link.to_node.bl_idname
5114 == "PovrayOutputNode"
5116 povMatName = (
5117 string_strip_hyphen(
5118 bpy.path.clean_name(
5119 link.from_node.name
5122 + "_%s" % povMatName
5124 else:
5125 file.write(
5126 '#declare %s = texture {%s}\n'
5127 % (povMatName, color)
5129 else:
5130 shading.writeMaterial(
5131 using_uberpov,
5132 DEF_MAT_NAME,
5133 scene,
5134 tabWrite,
5135 safety,
5136 comments,
5137 uniqueName,
5138 materialNames,
5139 material,
5141 # attributes are all the variables needed by the other python file...
5142 if comments:
5143 file.write("\n")
5145 exportMeta([m for m in sel if m.type == 'META'])
5147 if comments:
5148 file.write("//--Mesh objects--\n")
5151 #tbefore = time.time()
5152 exportMeshes(scene, sel, csg)
5153 #totime = time.time() - tbefore
5154 #print("exportMeshes took" + str(totime))
5156 # What follow used to happen here:
5157 # exportCamera()
5158 # exportWorld(scene.world)
5159 # exportGlobalSettings(scene)
5160 # MR:..and the order was important for implementing pov 3.7 baking
5161 # (mesh camera) comment for the record
5162 # CR: Baking should be a special case than. If "baking", than we could change the order.
5164 # print("pov file closed %s" % file.closed)
5165 file.close()
5166 # print("pov file closed %s" % file.closed)
5169 def write_pov_ini(
5170 scene, filename_ini, filename_log, filename_pov, filename_image
5172 """Write ini file."""
5173 feature_set = bpy.context.preferences.addons[
5174 __package__
5175 ].preferences.branch_feature_set_povray
5176 using_uberpov = feature_set == 'uberpov'
5177 # scene = bpy.data.scenes[0]
5178 scene = bpy.context.scene
5179 render = scene.render
5181 x = int(render.resolution_x * render.resolution_percentage * 0.01)
5182 y = int(render.resolution_y * render.resolution_percentage * 0.01)
5184 file = open(filename_ini, "w")
5185 file.write("Version=3.8\n")
5186 # write povray text stream to temporary file of same name with _log suffix
5187 # file.write("All_File='%s'\n" % filename_log)
5188 # DEBUG.OUT log if none specified:
5189 file.write("All_File=1\n")
5191 file.write("Input_File_Name='%s'\n" % filename_pov)
5192 file.write("Output_File_Name='%s'\n" % filename_image)
5194 file.write("Width=%d\n" % x)
5195 file.write("Height=%d\n" % y)
5197 # Border render.
5198 if render.use_border:
5199 file.write("Start_Column=%4g\n" % render.border_min_x)
5200 file.write("End_Column=%4g\n" % (render.border_max_x))
5202 file.write("Start_Row=%4g\n" % (1.0 - render.border_max_y))
5203 file.write("End_Row=%4g\n" % (1.0 - render.border_min_y))
5205 file.write(
5206 "Bounding_Method=2\n"
5207 ) # The new automatic BSP is faster in most scenes
5209 # Activated (turn this back off when better live exchange is done between the two programs
5210 # (see next comment)
5211 file.write("Display=1\n")
5212 file.write("Pause_When_Done=0\n")
5213 # PNG, with POV-Ray 3.7, can show background color with alpha. In the long run using the
5214 # POV-Ray interactive preview like bishop 3D could solve the preview for all formats.
5215 file.write("Output_File_Type=N\n")
5216 # file.write("Output_File_Type=T\n") # TGA, best progressive loading
5217 file.write("Output_Alpha=1\n")
5219 if scene.pov.antialias_enable:
5220 # method 2 (recursive) with higher max subdiv forced because no mipmapping in POV-Ray
5221 # needs higher sampling.
5222 # aa_mapping = {"5": 2, "8": 3, "11": 4, "16": 5}
5223 if using_uberpov:
5224 method = {"0": 1, "1": 2, "2": 3}
5225 else:
5226 method = {"0": 1, "1": 2, "2": 2}
5227 file.write("Antialias=on\n")
5228 file.write("Antialias_Depth=%d\n" % scene.pov.antialias_depth)
5229 file.write("Antialias_Threshold=%.3g\n" % scene.pov.antialias_threshold)
5230 if using_uberpov and scene.pov.antialias_method == '2':
5231 file.write(
5232 "Sampling_Method=%s\n" % method[scene.pov.antialias_method]
5234 file.write(
5235 "Antialias_Confidence=%.3g\n" % scene.pov.antialias_confidence
5237 else:
5238 file.write(
5239 "Sampling_Method=%s\n" % method[scene.pov.antialias_method]
5241 file.write("Antialias_Gamma=%.3g\n" % scene.pov.antialias_gamma)
5242 if scene.pov.jitter_enable:
5243 file.write("Jitter=on\n")
5244 file.write("Jitter_Amount=%3g\n" % scene.pov.jitter_amount)
5245 else:
5246 file.write("Jitter=off\n") # prevent animation flicker
5248 else:
5249 file.write("Antialias=off\n")
5250 # print("ini file closed %s" % file.closed)
5251 file.close()
5252 # print("ini file closed %s" % file.closed)
5255 class PovrayRender(bpy.types.RenderEngine):
5256 """Define the external renderer"""
5258 bl_idname = 'POVRAY_RENDER'
5259 bl_label = "Persitence Of Vision"
5260 bl_use_shading_nodes_custom = False
5261 DELAY = 0.5
5263 @staticmethod
5264 def _locate_binary():
5265 """Identify POV engine"""
5266 addon_prefs = bpy.context.preferences.addons[__package__].preferences
5268 # Use the system preference if its set.
5269 pov_binary = addon_prefs.filepath_povray
5270 if pov_binary:
5271 if os.path.exists(pov_binary):
5272 return pov_binary
5273 else:
5274 print(
5275 "User Preferences path to povray %r NOT FOUND, checking $PATH"
5276 % pov_binary
5279 # Windows Only
5280 # assume if there is a 64bit binary that the user has a 64bit capable OS
5281 if sys.platform[:3] == "win":
5282 import winreg
5284 win_reg_key = winreg.OpenKey(
5285 winreg.HKEY_CURRENT_USER, "Software\\POV-Ray\\v3.7\\Windows"
5287 win_home = winreg.QueryValueEx(win_reg_key, "Home")[0]
5289 # First try 64bits UberPOV
5290 pov_binary = os.path.join(win_home, "bin", "uberpov64.exe")
5291 if os.path.exists(pov_binary):
5292 return pov_binary
5294 # Then try 64bits POV
5295 pov_binary = os.path.join(win_home, "bin", "pvengine64.exe")
5296 if os.path.exists(pov_binary):
5297 return pov_binary
5299 # Then try 32bits UberPOV
5300 pov_binary = os.path.join(win_home, "bin", "uberpov32.exe")
5301 if os.path.exists(pov_binary):
5302 return pov_binary
5304 # Then try 32bits POV
5305 pov_binary = os.path.join(win_home, "bin", "pvengine.exe")
5306 if os.path.exists(pov_binary):
5307 return pov_binary
5309 # search the path all os's
5310 pov_binary_default = "povray"
5312 os_path_ls = os.getenv("PATH").split(':') + [""]
5314 for dir_name in os_path_ls:
5315 pov_binary = os.path.join(dir_name, pov_binary_default)
5316 if os.path.exists(pov_binary):
5317 return pov_binary
5318 return ""
5320 def _export(self, depsgraph, povPath, renderImagePath):
5321 """gather all necessary output files paths user defined and auto generated and export there"""
5322 import tempfile
5324 scene = bpy.context.scene
5325 if scene.pov.tempfiles_enable:
5326 self._temp_file_in = tempfile.NamedTemporaryFile(
5327 suffix=".pov", delete=False
5328 ).name
5329 # PNG with POV 3.7, can show the background color with alpha. In the long run using the
5330 # POV-Ray interactive preview like bishop 3D could solve the preview for all formats.
5331 self._temp_file_out = tempfile.NamedTemporaryFile(
5332 suffix=".png", delete=False
5333 ).name
5334 # self._temp_file_out = tempfile.NamedTemporaryFile(suffix=".tga", delete=False).name
5335 self._temp_file_ini = tempfile.NamedTemporaryFile(
5336 suffix=".ini", delete=False
5337 ).name
5338 self._temp_file_log = os.path.join(
5339 tempfile.gettempdir(), "alltext.out"
5341 else:
5342 self._temp_file_in = povPath + ".pov"
5343 # PNG with POV 3.7, can show the background color with alpha. In the long run using the
5344 # POV-Ray interactive preview like bishop 3D could solve the preview for all formats.
5345 self._temp_file_out = renderImagePath + ".png"
5346 # self._temp_file_out = renderImagePath + ".tga"
5347 self._temp_file_ini = povPath + ".ini"
5348 logPath = bpy.path.abspath(scene.pov.scene_path).replace('\\', '/')
5349 self._temp_file_log = os.path.join(logPath, "alltext.out")
5351 self._temp_file_in = "/test.pov"
5352 # PNG with POV 3.7, can show the background color with alpha. In the long run using the
5353 # POV-Ray interactive preview like bishop 3D could solve the preview for all formats.
5354 self._temp_file_out = "/test.png"
5355 #self._temp_file_out = "/test.tga"
5356 self._temp_file_ini = "/test.ini"
5358 if scene.pov.text_block == "":
5360 def info_callback(txt):
5361 self.update_stats("", "POV-Ray 3.7: " + txt)
5363 # os.makedirs(user_dir, exist_ok=True) # handled with previews
5364 os.makedirs(preview_dir, exist_ok=True)
5366 write_pov(self._temp_file_in, scene, info_callback)
5367 else:
5368 pass
5370 def _render(self, depsgraph):
5371 """Export necessary files and render image."""
5372 scene = bpy.context.scene
5373 try:
5374 os.remove(self._temp_file_out) # so as not to load the old file
5375 except OSError:
5376 pass
5378 pov_binary = PovrayRender._locate_binary()
5379 if not pov_binary:
5380 print(
5381 "POV-Ray 3.7: could not execute povray, possibly POV-Ray isn't installed"
5383 return False
5385 write_pov_ini(
5386 scene,
5387 self._temp_file_ini,
5388 self._temp_file_log,
5389 self._temp_file_in,
5390 self._temp_file_out,
5393 print("***-STARTING-***")
5395 extra_args = []
5397 if scene.pov.command_line_switches != "":
5398 for newArg in scene.pov.command_line_switches.split(" "):
5399 extra_args.append(newArg)
5401 self._is_windows = False
5402 if sys.platform[:3] == "win":
5403 self._is_windows = True
5404 if "/EXIT" not in extra_args and not scene.pov.pov_editor:
5405 extra_args.append("/EXIT")
5406 else:
5407 # added -d option to prevent render window popup which leads to segfault on linux
5408 extra_args.append("-d")
5410 # Start Rendering!
5411 try:
5412 self._process = subprocess.Popen(
5413 [pov_binary, self._temp_file_ini] + extra_args,
5414 stdout=subprocess.PIPE,
5415 stderr=subprocess.STDOUT,
5417 except OSError:
5418 # TODO, report api
5419 print("POV-Ray 3.7: could not execute '%s'" % pov_binary)
5420 import traceback
5422 traceback.print_exc()
5423 print("***-DONE-***")
5424 return False
5426 else:
5427 print("Engine ready!...")
5428 print("Command line arguments passed: " + str(extra_args))
5429 return True
5431 # Now that we have a valid process
5433 def _cleanup(self):
5434 """Delete temp files and unpacked ones"""
5435 for f in (self._temp_file_in, self._temp_file_ini, self._temp_file_out):
5436 for i in range(5):
5437 try:
5438 os.unlink(f)
5439 break
5440 except OSError:
5441 # Wait a bit before retrying file might be still in use by Blender,
5442 # and Windows does not know how to delete a file in use!
5443 time.sleep(self.DELAY)
5444 for i in unpacked_images:
5445 for c in range(5):
5446 try:
5447 os.unlink(i)
5448 break
5449 except OSError:
5450 # Wait a bit before retrying file might be still in use by Blender,
5451 # and Windows does not know how to delete a file in use!
5452 time.sleep(self.DELAY)
5454 def render(self, depsgraph):
5455 """Export necessary files from text editor and render image."""
5456 import tempfile
5458 scene = bpy.context.scene
5459 r = scene.render
5460 x = int(r.resolution_x * r.resolution_percentage * 0.01)
5461 y = int(r.resolution_y * r.resolution_percentage * 0.01)
5462 print("***INITIALIZING***")
5464 # This makes some tests on the render, returning True if all goes good, and False if
5465 # it was finished one way or the other.
5466 # It also pauses the script (time.sleep())
5467 def _test_wait():
5468 time.sleep(self.DELAY)
5470 # User interrupts the rendering
5471 if self.test_break():
5472 try:
5473 self._process.terminate()
5474 print("***POV INTERRUPTED***")
5475 except OSError:
5476 pass
5477 return False
5479 poll_result = self._process.poll()
5480 # POV process is finisehd, one way or the other
5481 if poll_result is not None:
5482 if poll_result < 0:
5483 print("***POV PROCESS FAILED : %s ***" % poll_result)
5484 self.update_stats("", "POV-Ray 3.7: Failed")
5485 return False
5487 return True
5489 if bpy.context.scene.pov.text_block != "":
5490 if scene.pov.tempfiles_enable:
5491 self._temp_file_in = tempfile.NamedTemporaryFile(
5492 suffix=".pov", delete=False
5493 ).name
5494 self._temp_file_out = tempfile.NamedTemporaryFile(
5495 suffix=".png", delete=False
5496 ).name
5497 # self._temp_file_out = tempfile.NamedTemporaryFile(suffix=".tga", delete=False).name
5498 self._temp_file_ini = tempfile.NamedTemporaryFile(
5499 suffix=".ini", delete=False
5500 ).name
5501 self._temp_file_log = os.path.join(
5502 tempfile.gettempdir(), "alltext.out"
5504 else:
5505 povPath = scene.pov.text_block
5506 renderImagePath = os.path.splitext(povPath)[0]
5507 self._temp_file_out = os.path.join(preview_dir, renderImagePath)
5508 self._temp_file_in = os.path.join(preview_dir, povPath)
5509 self._temp_file_ini = os.path.join(
5510 preview_dir,
5511 (os.path.splitext(self._temp_file_in)[0] + ".INI"),
5513 self._temp_file_log = os.path.join(preview_dir, "alltext.out")
5516 try:
5517 os.remove(self._temp_file_in) # so as not to load the old file
5518 except OSError:
5519 pass
5521 print(scene.pov.text_block)
5522 text = bpy.data.texts[scene.pov.text_block]
5523 file = open("%s" % self._temp_file_in, "w")
5524 # Why are the newlines needed?
5525 file.write("\n")
5526 file.write(text.as_string())
5527 file.write("\n")
5528 file.close()
5530 # has to be called to update the frame on exporting animations
5531 scene.frame_set(scene.frame_current)
5533 pov_binary = PovrayRender._locate_binary()
5535 if not pov_binary:
5536 print(
5537 "POV-Ray 3.7: could not execute povray, possibly POV-Ray isn't installed"
5539 return False
5541 # start ini UI options export
5542 self.update_stats(
5543 "", "POV-Ray 3.7: Exporting ini options from Blender"
5546 write_pov_ini(
5547 scene,
5548 self._temp_file_ini,
5549 self._temp_file_log,
5550 self._temp_file_in,
5551 self._temp_file_out,
5554 print("***-STARTING-***")
5556 extra_args = []
5558 if scene.pov.command_line_switches != "":
5559 for newArg in scene.pov.command_line_switches.split(" "):
5560 extra_args.append(newArg)
5562 if sys.platform[:3] == "win":
5563 if "/EXIT" not in extra_args and not scene.pov.pov_editor:
5564 extra_args.append("/EXIT")
5565 else:
5566 # added -d option to prevent render window popup which leads to segfault on linux
5567 extra_args.append("-d")
5569 # Start Rendering!
5570 try:
5571 if (
5572 sys.platform[:3] != "win" and scene.pov.sdl_window_enable
5573 ): # segfault on linux == False !!!
5574 env = {'POV_DISPLAY_SCALED': 'off'}
5575 env.update(os.environ)
5576 self._process = subprocess.Popen(
5577 [pov_binary, self._temp_file_ini],
5578 stdout=subprocess.PIPE,
5579 stderr=subprocess.STDOUT,
5580 env=env,
5582 else:
5583 self._process = subprocess.Popen(
5584 [pov_binary, self._temp_file_ini] + extra_args,
5585 stdout=subprocess.PIPE,
5586 stderr=subprocess.STDOUT,
5588 except OSError:
5589 # TODO, report api
5590 print("POV-Ray 3.7: could not execute '%s'" % pov_binary)
5591 import traceback
5593 traceback.print_exc()
5594 print("***-DONE-***")
5595 return False
5597 else:
5598 print("Engine ready!...")
5599 print("Command line arguments passed: " + str(extra_args))
5600 # return True
5601 self.update_stats("", "POV-Ray 3.7: Parsing File")
5603 # Indented in main function now so repeated here but still not working
5604 # to bring back render result to its buffer
5606 if os.path.exists(self._temp_file_out):
5607 xmin = int(r.border_min_x * x)
5608 ymin = int(r.border_min_y * y)
5609 xmax = int(r.border_max_x * x)
5610 ymax = int(r.border_max_y * y)
5611 result = self.begin_result(0, 0, x, y)
5612 lay = result.layers[0]
5614 time.sleep(self.DELAY)
5615 try:
5616 lay.load_from_file(self._temp_file_out)
5617 except RuntimeError:
5618 print("***POV ERROR WHILE READING OUTPUT FILE***")
5619 self.end_result(result)
5620 # print(self._temp_file_log) #bring the pov log to blender console with proper path?
5621 with open(
5622 self._temp_file_log
5623 ) as f: # The with keyword automatically closes the file when you are done
5624 print(f.read())
5626 self.update_stats("", "")
5628 if scene.pov.tempfiles_enable or scene.pov.deletefiles_enable:
5629 self._cleanup()
5630 else:
5632 ##WIP output format
5633 ## if r.image_settings.file_format == 'OPENEXR':
5634 ## fformat = 'EXR'
5635 ## render.image_settings.color_mode = 'RGBA'
5636 ## else:
5637 ## fformat = 'TGA'
5638 ## r.image_settings.file_format = 'TARGA'
5639 ## r.image_settings.color_mode = 'RGBA'
5641 blendSceneName = bpy.data.filepath.split(os.path.sep)[-1].split(
5643 )[0]
5644 povSceneName = ""
5645 povPath = ""
5646 renderImagePath = ""
5648 # has to be called to update the frame on exporting animations
5649 scene.frame_set(scene.frame_current)
5651 if not scene.pov.tempfiles_enable:
5653 # check paths
5654 povPath = bpy.path.abspath(scene.pov.scene_path).replace(
5655 '\\', '/'
5657 if povPath == "":
5658 if bpy.data.is_saved:
5659 povPath = bpy.path.abspath("//")
5660 else:
5661 povPath = tempfile.gettempdir()
5662 elif povPath.endswith("/"):
5663 if povPath == "/":
5664 povPath = bpy.path.abspath("//")
5665 else:
5666 povPath = bpy.path.abspath(scene.pov.scene_path)
5668 if not os.path.exists(povPath):
5669 try:
5670 os.makedirs(povPath)
5671 except:
5672 import traceback
5674 traceback.print_exc()
5676 print(
5677 "POV-Ray 3.7: Cannot create scenes directory: %r"
5678 % povPath
5680 self.update_stats(
5682 "POV-Ray 3.7: Cannot create scenes directory %r"
5683 % povPath,
5685 time.sleep(2.0)
5686 # return
5689 # Bug in POV-Ray RC3
5690 renderImagePath = bpy.path.abspath(scene.pov.renderimage_path).replace('\\','/')
5691 if renderImagePath == "":
5692 if bpy.data.is_saved:
5693 renderImagePath = bpy.path.abspath("//")
5694 else:
5695 renderImagePath = tempfile.gettempdir()
5696 #print("Path: " + renderImagePath)
5697 elif path.endswith("/"):
5698 if renderImagePath == "/":
5699 renderImagePath = bpy.path.abspath("//")
5700 else:
5701 renderImagePath = bpy.path.abspath(scene.pov.renderimage_path)
5702 if not os.path.exists(path):
5703 print("POV-Ray 3.7: Cannot find render image directory")
5704 self.update_stats("", "POV-Ray 3.7: Cannot find render image directory")
5705 time.sleep(2.0)
5706 return
5709 # check name
5710 if scene.pov.scene_name == "":
5711 if blendSceneName != "":
5712 povSceneName = blendSceneName
5713 else:
5714 povSceneName = "untitled"
5715 else:
5716 povSceneName = scene.pov.scene_name
5717 if os.path.isfile(povSceneName):
5718 povSceneName = os.path.basename(povSceneName)
5719 povSceneName = povSceneName.split('/')[-1].split('\\')[-1]
5720 if not povSceneName:
5721 print("POV-Ray 3.7: Invalid scene name")
5722 self.update_stats("", "POV-Ray 3.7: Invalid scene name")
5723 time.sleep(2.0)
5724 # return
5725 povSceneName = os.path.splitext(povSceneName)[0]
5727 print("Scene name: " + povSceneName)
5728 print("Export path: " + povPath)
5729 povPath = os.path.join(povPath, povSceneName)
5730 povPath = os.path.realpath(povPath)
5732 # for now this has to be the same like the pov output. Bug in POV-Ray RC3.
5733 # renderImagePath = renderImagePath + "\\" + povSceneName
5734 renderImagePath = povPath # Bugfix for POV-Ray RC3 bug
5735 # renderImagePath = os.path.realpath(renderImagePath) # Bugfix for POV-Ray RC3 bug
5737 # print("Export path: %s" % povPath)
5738 # print("Render Image path: %s" % renderImagePath)
5740 # start export
5741 self.update_stats("", "POV-Ray 3.7: Exporting data from Blender")
5742 self._export(depsgraph, povPath, renderImagePath)
5743 self.update_stats("", "POV-Ray 3.7: Parsing File")
5745 if not self._render(depsgraph):
5746 self.update_stats("", "POV-Ray 3.7: Not found")
5747 # return
5749 # r = scene.render
5750 # compute resolution
5751 # x = int(r.resolution_x * r.resolution_percentage * 0.01)
5752 # y = int(r.resolution_y * r.resolution_percentage * 0.01)
5754 # Wait for the file to be created
5755 # XXX This is no more valid, as 3.7 always creates output file once render is finished!
5756 parsing = re.compile(br"= \[Parsing\.\.\.\] =")
5757 rendering = re.compile(br"= \[Rendering\.\.\.\] =")
5758 percent = re.compile(r"\(([0-9]{1,3})%\)")
5759 # print("***POV WAITING FOR FILE***")
5761 data = b""
5762 last_line = ""
5763 while _test_wait():
5764 # POV in Windows did not output its stdout/stderr, it displayed them in its GUI
5765 # But now writes file
5766 if self._is_windows:
5767 self.update_stats("", "POV-Ray 3.7: Rendering File")
5768 else:
5769 t_data = self._process.stdout.read(10000)
5770 if not t_data:
5771 continue
5773 data += t_data
5774 # XXX This is working for UNIX, not sure whether it might need adjustments for
5775 # other OSs
5776 # First replace is for windows
5777 t_data = (
5778 str(t_data)
5779 .replace('\\r\\n', '\\n')
5780 .replace('\\r', '\r')
5782 lines = t_data.split('\\n')
5783 last_line += lines[0]
5784 lines[0] = last_line
5785 print('\n'.join(lines), end="")
5786 last_line = lines[-1]
5788 if rendering.search(data):
5789 _pov_rendering = True
5790 match = percent.findall(str(data))
5791 if match:
5792 self.update_stats(
5794 "POV-Ray 3.7: Rendering File (%s%%)"
5795 % match[-1],
5797 else:
5798 self.update_stats("", "POV-Ray 3.7: Rendering File")
5800 elif parsing.search(data):
5801 self.update_stats("", "POV-Ray 3.7: Parsing File")
5803 if os.path.exists(self._temp_file_out):
5804 # print("***POV FILE OK***")
5805 # self.update_stats("", "POV-Ray 3.7: Rendering")
5807 # prev_size = -1
5809 xmin = int(r.border_min_x * x)
5810 ymin = int(r.border_min_y * y)
5811 xmax = int(r.border_max_x * x)
5812 ymax = int(r.border_max_y * y)
5814 # print("***POV UPDATING IMAGE***")
5815 result = self.begin_result(0, 0, x, y)
5816 # XXX, tests for border render.
5817 # result = self.begin_result(xmin, ymin, xmax - xmin, ymax - ymin)
5818 # result = self.begin_result(0, 0, xmax - xmin, ymax - ymin)
5819 lay = result.layers[0]
5821 # This assumes the file has been fully written We wait a bit, just in case!
5822 time.sleep(self.DELAY)
5823 try:
5824 lay.load_from_file(self._temp_file_out)
5825 # XXX, tests for border render.
5826 # lay.load_from_file(self._temp_file_out, xmin, ymin)
5827 except RuntimeError:
5828 print("***POV ERROR WHILE READING OUTPUT FILE***")
5830 # Not needed right now, might only be useful if we find a way to use temp raw output of
5831 # pov 3.7 (in which case it might go under _test_wait()).
5833 def update_image():
5834 # possible the image wont load early on.
5835 try:
5836 lay.load_from_file(self._temp_file_out)
5837 # XXX, tests for border render.
5838 #lay.load_from_file(self._temp_file_out, xmin, ymin)
5839 #lay.load_from_file(self._temp_file_out, xmin, ymin)
5840 except RuntimeError:
5841 pass
5843 # Update while POV-Ray renders
5844 while True:
5845 # print("***POV RENDER LOOP***")
5847 # test if POV-Ray exists
5848 if self._process.poll() is not None:
5849 print("***POV PROCESS FINISHED***")
5850 update_image()
5851 break
5853 # user exit
5854 if self.test_break():
5855 try:
5856 self._process.terminate()
5857 print("***POV PROCESS INTERRUPTED***")
5858 except OSError:
5859 pass
5861 break
5863 # Would be nice to redirect the output
5864 # stdout_value, stderr_value = self._process.communicate() # locks
5866 # check if the file updated
5867 new_size = os.path.getsize(self._temp_file_out)
5869 if new_size != prev_size:
5870 update_image()
5871 prev_size = new_size
5873 time.sleep(self.DELAY)
5876 self.end_result(result)
5878 else:
5879 print("***POV FILE NOT FOUND***")
5881 print("***POV FILE FINISHED***")
5883 # print(filename_log) #bring the pov log to blender console with proper path?
5884 with open(
5885 self._temp_file_log,
5886 encoding='utf-8'
5887 ) as f: # The with keyword automatically closes the file when you are done
5888 msg = f.read()
5889 #if isinstance(msg, str):
5890 #stdmsg = msg
5891 #decoded = False
5892 #else:
5893 #if type(msg) == bytes:
5894 #stdmsg = msg.split('\n')
5895 #stdmsg = msg.encode('utf-8', "replace")
5896 #stdmsg = msg.encode("utf-8", "replace")
5898 #stdmsg = msg.decode(encoding)
5899 #decoded = True
5900 #msg.encode('utf-8').decode('utf-8')
5901 print(msg)
5902 # Also print to the interactive console used in POV centric workspace
5903 # To do: get a grip on new line encoding
5904 # and make this a function to be used elsewhere
5905 for win in bpy.context.window_manager.windows:
5906 if win.screen != None:
5907 scr = win.screen
5908 for area in scr.areas:
5909 if area.type == 'CONSOLE':
5910 #context override
5911 #ctx = {'window': win, 'screen': scr, 'area':area}#bpy.context.copy()
5912 ctx = {}
5913 ctx['area'] = area
5914 ctx['region'] = area.regions[-1]
5915 ctx['space_data'] = area.spaces.active
5916 ctx['screen'] = scr#C.screen
5917 ctx['window'] = win
5919 #bpy.ops.console.banner(ctx, text = "Hello world")
5920 bpy.ops.console.clear_line(ctx)
5921 stdmsg = msg.split('\n') #XXX todo , test and see
5922 for i in stdmsg:
5923 bpy.ops.console.insert(ctx, text = i)
5925 self.update_stats("", "")
5927 if scene.pov.tempfiles_enable or scene.pov.deletefiles_enable:
5928 self._cleanup()
5930 sound_on = bpy.context.preferences.addons[
5931 __package__
5932 ].preferences.use_sounds
5934 if sys.platform[:3] == "win" and sound_on:
5935 # Could not find tts Windows command so playing beeps instead :-)
5936 # "Korobeiniki"(Коробе́йники)
5937 # aka "A-Type" Tetris theme
5938 import winsound
5939 winsound.Beep(494,250) #B
5940 winsound.Beep(370,125) #F
5941 winsound.Beep(392,125) #G
5942 winsound.Beep(440,250) #A
5943 winsound.Beep(392,125) #G
5944 winsound.Beep(370,125) #F#
5945 winsound.Beep(330,275) #E
5946 winsound.Beep(330,125) #E
5947 winsound.Beep(392,125) #G
5948 winsound.Beep(494,275) #B
5949 winsound.Beep(440,125) #A
5950 winsound.Beep(392,125) #G
5951 winsound.Beep(370,275) #F
5952 winsound.Beep(370,125) #F
5953 winsound.Beep(392,125) #G
5954 winsound.Beep(440,250) #A
5955 winsound.Beep(494,250) #B
5956 winsound.Beep(392,250) #G
5957 winsound.Beep(330,350) #E
5958 time.sleep(0.5)
5959 winsound.Beep(440,250) #A
5960 winsound.Beep(440,150) #A
5961 winsound.Beep(523,125) #D8
5962 winsound.Beep(659,250) #E8
5963 winsound.Beep(587,125) #D8
5964 winsound.Beep(523,125) #C8
5965 winsound.Beep(494,250) #B
5966 winsound.Beep(494,125) #B
5967 winsound.Beep(392,125) #G
5968 winsound.Beep(494,250) #B
5969 winsound.Beep(440,150) #A
5970 winsound.Beep(392,125) #G
5971 winsound.Beep(370,250) #F#
5972 winsound.Beep(370,125) #F#
5973 winsound.Beep(392,125) #G
5974 winsound.Beep(440,250) #A
5975 winsound.Beep(494,250) #B
5976 winsound.Beep(392,250) #G
5977 winsound.Beep(330,300) #E
5979 #Does Linux support say command?
5980 elif sys.platform[:3] != "win" :
5981 finished_render_message = "\'Render completed\'"
5982 # We don't want the say command to block Python,
5983 # so we add an ampersand after the message
5984 os.system("say %s &" % (finished_render_message))
5986 ##################################################################################
5987 #################################Operators########################################
5988 ##################################################################################
5989 class RenderPovTexturePreview(Operator):
5990 """Export only files necessary to texture preview and render image"""
5992 bl_idname = "tex.preview_update"
5993 bl_label = "Update preview"
5995 def execute(self, context):
5996 tex = (
5997 bpy.context.object.active_material.active_texture
5998 ) # context.texture
5999 texPrevName = (
6000 string_strip_hyphen(bpy.path.clean_name(tex.name)) + "_prev"
6003 ## Make sure Preview directory exists and is empty
6004 if not os.path.isdir(preview_dir):
6005 os.mkdir(preview_dir)
6007 iniPrevFile = os.path.join(preview_dir, "Preview.ini")
6008 inputPrevFile = os.path.join(preview_dir, "Preview.pov")
6009 outputPrevFile = os.path.join(preview_dir, texPrevName)
6010 ##################### ini ##########################################
6011 fileIni = open("%s" % iniPrevFile, "w")
6012 fileIni.write('Version=3.8\n')
6013 fileIni.write('Input_File_Name="%s"\n' % inputPrevFile)
6014 fileIni.write('Output_File_Name="%s.png"\n' % outputPrevFile)
6015 fileIni.write('Library_Path="%s"\n' % preview_dir)
6016 fileIni.write('Width=256\n')
6017 fileIni.write('Height=256\n')
6018 fileIni.write('Pause_When_Done=0\n')
6019 fileIni.write('Output_File_Type=N\n')
6020 fileIni.write('Output_Alpha=1\n')
6021 fileIni.write('Antialias=on\n')
6022 fileIni.write('Sampling_Method=2\n')
6023 fileIni.write('Antialias_Depth=3\n')
6024 fileIni.write('-d\n')
6025 fileIni.close()
6026 ##################### pov ##########################################
6027 filePov = open("%s" % inputPrevFile, "w")
6028 PATname = "PAT_" + string_strip_hyphen(bpy.path.clean_name(tex.name))
6029 filePov.write("#declare %s = \n" % PATname)
6030 filePov.write(shading.exportPattern(tex, string_strip_hyphen))
6032 filePov.write("#declare Plane =\n")
6033 filePov.write("mesh {\n")
6034 filePov.write(
6035 " triangle {<-2.021,-1.744,2.021>,<-2.021,-1.744,-2.021>,<2.021,-1.744,2.021>}\n"
6037 filePov.write(
6038 " triangle {<-2.021,-1.744,-2.021>,<2.021,-1.744,-2.021>,<2.021,-1.744,2.021>}\n"
6040 filePov.write(" texture{%s}\n" % PATname)
6041 filePov.write("}\n")
6042 filePov.write("object {Plane}\n")
6043 filePov.write("light_source {\n")
6044 filePov.write(" <0,4.38,-1.92e-07>\n")
6045 filePov.write(" color rgb<4, 4, 4>\n")
6046 filePov.write(" parallel\n")
6047 filePov.write(" point_at <0, 0, -1>\n")
6048 filePov.write("}\n")
6049 filePov.write("camera {\n")
6050 filePov.write(" location <0, 0, 0>\n")
6051 filePov.write(" look_at <0, 0, -1>\n")
6052 filePov.write(" right <-1.0, 0, 0>\n")
6053 filePov.write(" up <0, 1, 0>\n")
6054 filePov.write(" angle 96.805211\n")
6055 filePov.write(" rotate <-90.000003, -0.000000, 0.000000>\n")
6056 filePov.write(" translate <0.000000, 0.000000, 0.000000>\n")
6057 filePov.write("}\n")
6058 filePov.close()
6059 ##################### end write ##########################################
6061 pov_binary = PovrayRender._locate_binary()
6063 if sys.platform[:3] == "win":
6064 p1 = subprocess.Popen(
6065 ["%s" % pov_binary, "/EXIT", "%s" % iniPrevFile],
6066 stdout=subprocess.PIPE,
6067 stderr=subprocess.STDOUT,
6069 else:
6070 p1 = subprocess.Popen(
6071 ["%s" % pov_binary, "-d", "%s" % iniPrevFile],
6072 stdout=subprocess.PIPE,
6073 stderr=subprocess.STDOUT,
6075 p1.wait()
6077 tex.use_nodes = True
6078 tree = tex.node_tree
6079 links = tree.links
6080 for n in tree.nodes:
6081 tree.nodes.remove(n)
6082 im = tree.nodes.new("TextureNodeImage")
6083 pathPrev = "%s.png" % outputPrevFile
6084 im.image = bpy.data.images.load(pathPrev)
6085 name = pathPrev
6086 name = name.split("/")
6087 name = name[len(name) - 1]
6088 im.name = name
6089 im.location = 200, 200
6090 previewer = tree.nodes.new('TextureNodeOutput')
6091 previewer.label = "Preview"
6092 previewer.location = 400, 400
6093 links.new(im.outputs[0], previewer.inputs[0])
6094 # tex.type="IMAGE" # makes clip extend possible
6095 # tex.extension="CLIP"
6096 return {'FINISHED'}
6099 class RunPovTextRender(Operator):
6100 """Export files depending on text editor options and render image."""
6102 bl_idname = "text.run"
6103 bl_label = "Run"
6104 bl_context = "text"
6105 bl_description = "Run a render with this text only"
6107 def execute(self, context):
6108 scene = context.scene
6109 scene.pov.text_block = context.space_data.text.name
6111 bpy.ops.render.render()
6113 # empty text name property engain
6114 scene.pov.text_block = ""
6115 return {'FINISHED'}
6118 classes = (PovrayRender, RenderPovTexturePreview, RunPovTextRender)
6121 def register():
6122 # from bpy.utils import register_class
6124 for cls in classes:
6125 register_class(cls)
6128 def unregister():
6129 from bpy.utils import unregister_class
6131 for cls in reversed(classes):
6132 unregister_class(cls)