1 # SPDX-License-Identifier: GPL-2.0-or-later
3 """Import, export and render to POV engines.
5 These engines can be POV-Ray, Uberpov, HgPovray but others too, since POV is a
6 Scene Description Language. The script has been split in as few files as possible and
7 metaphorically structured as a train with some boilerplate rendering locomotive,
8 followed by its cars each representing a thematic in the 3D field:
10 ########################################### RENDERING ###########################################
13 Provide script's metadata, Initialize addon preferences, (re)load package modules
16 Set up workspaces and load other ui files for the user to set up all variables
19 Define the POV render engines declinations from generic Blender RenderEngine class
21 render_properties.py :
22 Initialize properties for render parameters (Blender generic and POV native)
25 Display properties from render_properties.py for user to change them
28 Translate render properties (Blender and POV native) to POV, ini file and CLI
30 __------------------Z__
31 _--¨¨] | __ __ _____________||
32 _-¨7____/ | | °|° | □□□ □□□ □□□ ||
33 (===========|=| | |=============||
34 `-,_(@)(@)----------------(@)(@)-'
35 ############################################# LAYOUT ##############################################
37 scenography_properties.py
38 Initialize properties for passing layout (camera/light/environment) parameters to pov
41 Display camera/light/environment properties from scenography_properties.py for user to change
44 Translate camera/light/environment properties to corresponding pov features
46 __------------------Z__ ____________________
47 _--¨¨] | __ __ _____________|||____________________|
48 _-¨7____/ | | °|° | □□□ □□□ □□□ ||| □□□ □□□ □□□ □□□ □□ |
49 (===========|=| | |=============|||====================|
50 `-,_(@)(@)----------------(@)(@)-'^-(@)(@)--------(@)(@)'
51 ############################################### MODEL #############################################
54 Initialize properties for translating Blender geometry objects parameters to pov
57 Display properties from model_properties.py for the user to change them
60 Translate to POV the object level data
62 model_poly_topology.py :
63 Translate to POV the mesh based geometries
65 model_curve_topology.py :
66 Translate to POV the curve based geometries
68 model_meta_topology.py :
69 Translate to POV the metaball based geometries
72 Display some simple POV native primitives in 3D view for input and output
74 model_primitives_topology.py :
75 Display some POV native complex or compound primitives in 3D view for input and output
77 __------------------Z__ ____________________ ____________________
78 _--¨¨] | __ __ _____________|||____________________|||____________________
79 _-¨7____/ | | °|° | □□□ □□□ □□□ ||| □□□ □□□ □□□ □□□ □□ ||| □□□ □□□ □□□ □□□ □□
80 (===========|=| | |=============|||====================|||====================
81 `-,_(@)(@)----------------(@)(@)-'^-(@)(@)--------(@)(@)-^-(@)(@)--------(@)(@)
82 ############################################ SHADING #############################################
84 shading_properties.py :
85 Initialize properties for translating Blender materials parameters to pov
87 shading_ray_properties.py :
88 Initialize properties for translating Blender ray paths relevant material parameters to pov
91 Display variables from shading_properties.py and shading_ray_properties.py for user to change
94 Translate shading properties to declared textures at the top of a pov file
96 texturing_properties.py :
97 Initialize properties for translating Blender materials/world... texture influences to pov
100 Display properties from texturing_properties.py available for user to change
103 Translate blender pixel based bitmap texture influences into POV
105 texturing_procedural.py :
106 Translate blender algorithmic procedural texture influences into POV
108 __------------------Z__ ____________________ ____________________ ________________
109 _--¨¨] | __ __ _____________|||____________________|||____________________|||________________
110 _-¨7____/ | | °|° | □□□ □□□ □□□ ||| □□□ □□□ □□□ □□□ □□ ||| □□□ □□□ □□□ □□□ □□ ||| □□□ □□□ □□□ □□□
111 (===========|=| | |=============|||====================|||====================|||================
112 `-,_(@)(@)----------------(@)(@)-'^-(@)(@)--------(@)(@)-^-(@)(@)--------(@)(@)-^-(@)(@)----------
113 ############################################ VFX/TECH #############################################
115 particles_properties.py :
116 Initialize all strands, fx, or particles based objects properties to be translated to POV
119 Translate to POV the particle based geometries
122 Render smoke to *.df3 files using an updated version of Mike Kost's df3.py legacy library
124 nodes_properties.py :
125 Define all node items and their respective variables or sockets available to POV node trees
128 Translate node trees to the pov file
131 Functions toolbox used by nodes.py to translate node trees to the pov file
134 Operators and menus to interact with POV specific node trees
136 scripting_properties.py :
137 Initialize properties for hand written scene description language fragments (POV native)
140 Display properties from scripting_properties.py for user to add his custom POV code
143 Insert POV native scene description elements into blender scene or to exported POV file
146 Update new variables to values from older API. This file needs an update
148 ######################################## PRESETS/TEMPLATES ########################################
150 Along these essential files also coexist a few additional libraries to help make
151 Blender stand up to other POV enabled IDEs (povwin, POV for Mac, QTPOV, VSCode, Vim...)
154 apple.py ; chicken.py ; cream.py ; Ketchup.py ; marble.py ;
155 potato.py ; skim_milk.py ; skin1.py ; skin2.py ; whole_milk.py
157 01_Debug.py ; 02_Fast.py ; 03_Normal.py ; 04_Two_Bounces.py ;
158 05_Final.py ; 06_Outdoor_Low_Quality.py ; 07_Outdoor_High_Quality.py ;
159 08_Outdoor(Sun)Light.py ; 09_Indoor_Low_Quality.py ;
160 10_Indoor_High_Quality.py ;
162 01_Clear_Blue_Sky.py ; 02_Partly_Hazy_Sky.py ; 03_Overcast_Sky.py ;
163 04_Cartoony_Sky.py ; 05_Under_Water.py ;
165 01_(4800K)_Direct_Sun.py ;
166 02_(5400K)_High_Noon_Sun.py ;
167 03_(6000K)_Daylight_Window.py ;
168 04_(6000K)_2500W_HMI_(Halogen_Metal_Iodide).py ;
169 05_(4000K)_100W_Metal_Halide.py ;
170 06_(3200K)_100W_Quartz_Halogen.py ;
171 07_(2850K)_100w_Tungsten.py ;
172 08_(2600K)_40w_Tungsten.py ;
173 09_(5000K)_75W_Full_Spectrum_Fluorescent_T12.py ;
174 10_(4300K)_40W_Vintage_Fluorescent_T12.py ;
175 11_(5000K)_18W_Standard_Fluorescent_T8 ;
176 12_(4200K)_18W_Cool_White_Fluorescent_T8.py ;
177 13_(3000K)_18W_Warm_Fluorescent_T8.py ;
178 14_(6500K)_54W_Grow_Light_Fluorescent_T5-HO.py ;
179 15_(3200K)_40W_Induction_Fluorescent.py ;
180 16_(2100K)_150W_High_Pressure_Sodium.py ;
181 17_(1700K)_135W_Low_Pressure_Sodium.py ;
182 18_(6800K)_175W_Mercury_Vapor.py ;
183 19_(5200K)_700W_Carbon_Arc.py ;
184 20_(6500K)_15W_LED_Spot.py ;
185 21_(2700K)_7W_OLED_Panel.py ;
186 22_(30000K)_40W_Black_Light_Fluorescent.py ;
187 23_(30000K)_40W_Black_Light_Bulb.py;
190 abyss.pov ; biscuit.pov ; bsp_Tango.pov ; chess2.pov ;
191 cornell.pov ; diffract.pov ; diffuse_back.pov ; float5 ;
192 gamma_showcase.pov ; grenadine.pov ; isocacti.pov ;
193 mediasky.pov ; patio-radio.pov ; subsurface.pov ; wallstucco.pov
199 'author': "Campbell Barton, "
206 'version': (0, 1, 3),
207 'blender': (3, 2, 0),
208 'location': "Render Properties > Render Engine > Persistence of Vision",
209 'description': "Persistence of Vision addon for Blender",
210 'doc_url': "{BLENDER_MANUAL_URL}/addons/render/povray.html",
211 'category': "Render",
212 'warning': "Co-maintainers welcome",
215 # Other occasional contributors, more or less in chronological order:
216 # Luca Bonavita ; Shane Ambler ; Brendon Murphy ; Doug Hammond ;
217 # Thomas Dinges ; Jonathan Smith ; Sebastian Nell ; Philipp Oeser ;
218 # Sybren A. Stüvel ; Dalai Felinto ; Sergey Sharybin ; Brecht Van Lommel ;
219 # Stephen Leger ; Rune Morling ; Aaron Carlisle ; Ankit Meel ;
221 if "bpy" in locals():
224 importlib
.reload(ui_core
)
225 importlib
.reload(nodes_properties
)
226 importlib
.reload(nodes_gui
)
227 importlib
.reload(nodes_fn
)
228 importlib
.reload(nodes
)
229 importlib
.reload(scenography_properties
)
230 importlib
.reload(scenography_gui
)
231 importlib
.reload(scenography
)
232 importlib
.reload(render_properties
)
233 importlib
.reload(render_gui
)
234 importlib
.reload(render_core
)
235 importlib
.reload(render
)
236 importlib
.reload(shading_properties
)
237 importlib
.reload(shading_ray_properties
)
238 importlib
.reload(shading_gui
)
239 importlib
.reload(shading
)
240 importlib
.reload(texturing_procedural
)
241 importlib
.reload(texturing_properties
)
242 importlib
.reload(texturing_gui
)
243 importlib
.reload(texturing
)
244 importlib
.reload(model_properties
)
245 importlib
.reload(model_gui
)
246 importlib
.reload(model_all
)
247 importlib
.reload(model_poly_topology
)
248 importlib
.reload(model_meta_topology
)
249 importlib
.reload(model_curve_topology
)
250 importlib
.reload(model_primitives
)
251 importlib
.reload(model_primitives_topology
)
252 importlib
.reload(particles_properties
)
253 importlib
.reload(particles
)
254 importlib
.reload(scripting_properties
)
255 importlib
.reload(scripting_gui
)
256 importlib
.reload(scripting
)
257 importlib
.reload(update_files
)
261 from bpy
.utils
import register_class
, unregister_class
263 from bpy
.props
import StringProperty
, BoolProperty
, EnumProperty
268 scenography_properties
,
273 shading_ray_properties
,
274 texturing_properties
,
276 scripting_properties
,
280 model_primitives_topology
,
281 particles_properties
,
285 # ---------------------------------------------------------------- #
287 # ---------------------------------------------------------------- #
290 class POV_OT_update_addon(bpy
.types
.Operator
):
291 """Update this addon to the latest version"""
293 bl_idname
= "pov.update_addon"
294 bl_label
= "Update POV addon"
296 def execute(self
, context
):
301 import urllib
.request
304 def recursive_overwrite(self
, src
, dest
, ignore
=None):
305 """Update the script automatically (along with other addons).
308 src -- path where to update from
309 dest -- storing temporary download here
311 ignore -- leave some directories alone (default: {None})
314 finished flag for operator which is a set()
316 if os
.path
.isdir(src
):
317 if not os
.path
.isdir(dest
):
319 files
= os
.listdir(src
)
320 ignored
= ignore(src
, files
) if ignore
is not None else set()
321 unignored_files
= (fle
for fle
in files
if fle
not in ignored
)
322 for f
in unignored_files
:
323 source
= os
.path
.join(src
, f
)
324 destination
= os
.path
.join(dest
, f
)
325 recursive_overwrite(source
, destination
, ignore
)
327 shutil
.copyfile(src
, dest
)
330 print("Updating POV addon...")
332 with tempfile
.TemporaryDirectory() as temp_dir_path
:
333 temp_zip_path
= os
.path
.join(temp_dir_path
, "master.zip")
335 # Download zip archive of latest addons master branch commit
336 # More work needed so we also get files from the shared addons presets /pov folder
337 # switch this URL back to the BF hosted one as soon as gitweb snapshot gets fixed
338 url
= "https://github.com/blender/blender-addons/archive/refs/heads/master.zip"
340 print("Downloading", url
)
342 with urllib
.request
.urlopen(url
, timeout
=60) as url_handle
, open(
345 file_handle
.write(url_handle
.read())
346 except urllib
.error
.URLError
as err
:
347 self
.report({"ERROR"}, "Could not download: %s" % err
)
350 print("Extracting ZIP archive")
351 with zipfile
.ZipFile(temp_zip_path
) as zip_archive
:
352 pov_addon_pkg
= (member
for member
in zip_archive
.namelist() if
353 "blender-addons-master/render_povray" in member
)
354 for member
in pov_addon_pkg
:
355 # Remove the first directory and the filename
356 # e.g. blender-addons-master/render_povray/nodes.py
357 # becomes render_povray/nodes.py
358 target_path
= os
.path
.join(
359 temp_dir_path
, os
.path
.join(*member
.split("/")[1:-1])
362 filename
= os
.path
.basename(member
)
367 # Create the target directory if necessary
368 if not os
.path
.exists(target_path
):
369 os
.makedirs(target_path
)
371 source
= zip_archive
.open(member
)
372 target
= open(os
.path
.join(target_path
, filename
), "wb")
375 shutil
.copyfileobj(source
, target
)
376 print("copying", source
, "to", target
)
378 extracted_render_povray_path
= os
.path
.join(temp_dir_path
, "render_povray")
380 if not os
.path
.exists(extracted_render_povray_path
):
381 self
.report({"ERROR"}, "Could not extract ZIP archive! Aborting.")
384 # Find the old POV addon files
385 render_povray_dir
= os
.path
.abspath(os
.path
.dirname(__file__
)) # Unnecessary abspath?
386 print("POV addon addon folder:", render_povray_dir
)
388 # TODO: Create backup
390 # Delete old POV addon files
391 # (only directories and *.py files, user might have other stuff in there!)
392 print("Deleting old POV addon files")
394 os
.remove(os
.path
.join(render_povray_dir
, "__init__.py"))
397 for directory
in next(os
.walk(render_povray_dir
))[dir_names
]:
398 shutil
.rmtree(os
.path
.join(render_povray_dir
, directory
))
400 print("Copying new POV addon files")
401 # copy new POV addon files
404 os
.path
.join(extracted_render_povray_path
, "__init__.py"),
408 recursive_overwrite(extracted_render_povray_path
, render_povray_dir
)
410 bpy
.ops
.preferences
.addon_refresh()
411 print("POV addon update finished, restart Blender for the changes to take effect.")
413 self
.report({"WARNING"}, "Restart Blender!")
417 # ---------------------------------------------------------------- #
418 # Povray Preferences.
419 # ---------------------------------------------------------------- #
422 class PovPreferences(bpy
.types
.AddonPreferences
):
423 """Declare preference variables to set up POV binary."""
427 branch_feature_set_povray
: EnumProperty(
429 description
="Choose between official (POV-Ray) or (UberPOV) "
430 "development branch features to write in the pov file",
432 ("povray", "Official POV-Ray", "", "PLUGIN", 0),
433 ("uberpov", "Unofficial UberPOV", "", "PLUGIN", 1),
438 filepath_povray
: StringProperty(
439 name
="Binary Location", description
="Path to renderer executable", subtype
="FILE_PATH"
442 docpath_povray
: StringProperty(
443 name
="Includes Location", description
="Path to Insert Menu files", subtype
="FILE_PATH",
447 use_sounds
: BoolProperty(
449 description
="Signaling end of the render process at various"
450 "stages can help if you're away from monitor",
454 # TODO: Auto find POV sound directory as it does for binary
455 # And implement the three cases, left uncommented for a dummy
456 # interface in case some doc screenshots get made for that area
457 filepath_complete_sound
: StringProperty(
458 name
="Finish Render Sound",
459 description
="Path to finished render sound file",
463 filepath_parse_error_sound
: StringProperty(
464 name
="Parse Error Sound",
465 description
="Path to parsing time error sound file",
469 filepath_cancel_sound
: StringProperty(
470 name
="Cancel Render Sound",
471 description
="Path to cancelled or render time error sound file",
475 def draw(self
, context
):
477 layout
.prop(self
, 'branch_feature_set_povray')
478 layout
.prop(self
, 'filepath_povray')
479 layout
.prop(self
, 'docpath_povray')
480 layout
.prop(self
, 'filepath_complete_sound')
481 layout
.prop(self
, 'filepath_parse_error_sound')
482 layout
.prop(self
, 'filepath_cancel_sound')
483 layout
.prop(self
, 'use_sounds', icon
='SOUND')
484 layout
.operator('pov.update_addon', icon
='FILE_REFRESH')
485 layout
.operator("wm.url_open", text
="Community",icon
='EVENT_F').url
= \
486 "https://www.facebook.com/povable"
499 render_properties
.register()
500 scenography_properties
.register()
501 shading_properties
.register()
502 shading_ray_properties
.register()
503 texturing_properties
.register()
504 model_properties
.register()
505 particles_properties
.register()
506 scripting_properties
.register()
507 nodes_properties
.register()
510 render_core
.register()
512 model_primitives_topology
.register()
513 model_primitives
.register()
517 model_primitives
.unregister()
518 model_primitives_topology
.unregister()
520 render_core
.unregister()
522 nodes_gui
.unregister()
523 nodes_properties
.unregister()
524 scripting_properties
.unregister()
525 particles_properties
.unregister()
526 model_properties
.unregister()
527 texturing_properties
.unregister()
528 shading_ray_properties
.unregister()
529 shading_properties
.unregister()
530 scenography_properties
.unregister()
531 render_properties
.unregister()
533 for cls
in reversed(classes
):
534 unregister_class(cls
)
537 if __name__
== '__main__':
540 # ------------8<---------[ BREAKPOINT ]--------------8<----------- # Move this snippet around
541 # __import__('code').interact(local=dict(globals(), **locals())) # < and uncomment this line
542 # ---------------------------------------------------------------- # to inspect from Terminal