1 # SPDX-FileCopyrightText: 2010-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 """Import, export and render to POV engines.
7 These engines can be POV-Ray, Uberpov, HgPovray but others too, since POV is a
8 Scene Description Language. The script has been split in as few files as possible and
9 metaphorically structured as a train with some boilerplate rendering locomotive,
10 followed by its cars each representing a thematic in the 3D field:
12 ########################################### RENDERING ###########################################
15 Provide script's metadata, Initialize addon preferences, (re)load package modules
18 Set up workspaces and load other ui files for the user to set up all variables
21 Define the POV render engines declinations from generic Blender RenderEngine class
23 render_properties.py :
24 Initialize properties for render parameters (Blender generic and POV native)
27 Display properties from render_properties.py for user to change them
30 Translate render properties (Blender and POV native) to POV, ini file and CLI
32 __------------------Z__
33 _--¨¨] | __ __ _____________||
34 _-¨7____/ | | °|° | □□□ □□□ □□□ ||
35 (===========|=| | |=============||
36 `-,_(@)(@)----------------(@)(@)-'
37 ############################################# LAYOUT ##############################################
39 scenography_properties.py
40 Initialize properties for passing layout (camera/light/environment) parameters to pov
43 Display camera/light/environment properties from scenography_properties.py for user to change
46 Translate camera/light/environment properties to corresponding pov features
48 __------------------Z__ ____________________
49 _--¨¨] | __ __ _____________|||____________________|
50 _-¨7____/ | | °|° | □□□ □□□ □□□ ||| □□□ □□□ □□□ □□□ □□ |
51 (===========|=| | |=============|||====================|
52 `-,_(@)(@)----------------(@)(@)-'^-(@)(@)--------(@)(@)'
53 ############################################### MODEL #############################################
56 Initialize properties for translating Blender geometry objects parameters to pov
59 Display properties from model_properties.py for the user to change them
62 Translate to POV the object level data
64 model_poly_topology.py :
65 Translate to POV the mesh based geometries
67 model_curve_topology.py :
68 Translate to POV the curve based geometries
70 model_meta_topology.py :
71 Translate to POV the metaball based geometries
74 Display some simple POV native primitives in 3D view for input and output
76 model_primitives_topology.py :
77 Display some POV native complex or compound primitives in 3D view for input and output
79 __------------------Z__ ____________________ ____________________
80 _--¨¨] | __ __ _____________|||____________________|||____________________
81 _-¨7____/ | | °|° | □□□ □□□ □□□ ||| □□□ □□□ □□□ □□□ □□ ||| □□□ □□□ □□□ □□□ □□
82 (===========|=| | |=============|||====================|||====================
83 `-,_(@)(@)----------------(@)(@)-'^-(@)(@)--------(@)(@)-^-(@)(@)--------(@)(@)
84 ############################################ SHADING #############################################
86 shading_properties.py :
87 Initialize properties for translating Blender materials parameters to pov
89 shading_ray_properties.py :
90 Initialize properties for translating Blender ray paths relevant material parameters to pov
93 Display variables from shading_properties.py and shading_ray_properties.py for user to change
96 Translate shading properties to declared textures at the top of a pov file
98 texturing_properties.py :
99 Initialize properties for translating Blender materials/world... texture influences to pov
102 Display properties from texturing_properties.py available for user to change
105 Translate blender pixel based bitmap texture influences into POV
107 texturing_procedural.py :
108 Translate blender algorithmic procedural texture influences into POV
110 __------------------Z__ ____________________ ____________________ ________________
111 _--¨¨] | __ __ _____________|||____________________|||____________________|||________________
112 _-¨7____/ | | °|° | □□□ □□□ □□□ ||| □□□ □□□ □□□ □□□ □□ ||| □□□ □□□ □□□ □□□ □□ ||| □□□ □□□ □□□ □□□
113 (===========|=| | |=============|||====================|||====================|||================
114 `-,_(@)(@)----------------(@)(@)-'^-(@)(@)--------(@)(@)-^-(@)(@)--------(@)(@)-^-(@)(@)----------
115 ############################################ VFX/TECH #############################################
117 particles_properties.py :
118 Initialize all strands, fx, or particles based objects properties to be translated to POV
121 Translate to POV the particle based geometries
124 Render smoke to *.df3 files using an updated version of Mike Kost's df3.py legacy library
126 nodes_properties.py :
127 Define all node items and their respective variables or sockets available to POV node trees
130 Translate node trees to the pov file
133 Functions toolbox used by nodes.py to translate node trees to the pov file
136 Operators and menus to interact with POV specific node trees
138 scripting_properties.py :
139 Initialize properties for hand written scene description language fragments (POV native)
142 Display properties from scripting_properties.py for user to add his custom POV code
145 Insert POV native scene description elements into blender scene or to exported POV file
148 Update new variables to values from older API. This file needs an update
150 ######################################## PRESETS/TEMPLATES ########################################
152 Along these essential files also coexist a few additional libraries to help make
153 Blender stand up to other POV enabled IDEs (povwin, POV for Mac, QTPOV, VSCode, Vim...)
156 apple.py ; chicken.py ; cream.py ; Ketchup.py ; marble.py ;
157 potato.py ; skim_milk.py ; skin1.py ; skin2.py ; whole_milk.py
159 01_Debug.py ; 02_Fast.py ; 03_Normal.py ; 04_Two_Bounces.py ;
160 05_Final.py ; 06_Outdoor_Low_Quality.py ; 07_Outdoor_High_Quality.py ;
161 08_Outdoor(Sun)Light.py ; 09_Indoor_Low_Quality.py ;
162 10_Indoor_High_Quality.py ;
164 01_Clear_Blue_Sky.py ; 02_Partly_Hazy_Sky.py ; 03_Overcast_Sky.py ;
165 04_Cartoony_Sky.py ; 05_Under_Water.py ;
167 01_(4800K)_Direct_Sun.py ;
168 02_(5400K)_High_Noon_Sun.py ;
169 03_(6000K)_Daylight_Window.py ;
170 04_(6000K)_2500W_HMI_(Halogen_Metal_Iodide).py ;
171 05_(4000K)_100W_Metal_Halide.py ;
172 06_(3200K)_100W_Quartz_Halogen.py ;
173 07_(2850K)_100w_Tungsten.py ;
174 08_(2600K)_40w_Tungsten.py ;
175 09_(5000K)_75W_Full_Spectrum_Fluorescent_T12.py ;
176 10_(4300K)_40W_Vintage_Fluorescent_T12.py ;
177 11_(5000K)_18W_Standard_Fluorescent_T8 ;
178 12_(4200K)_18W_Cool_White_Fluorescent_T8.py ;
179 13_(3000K)_18W_Warm_Fluorescent_T8.py ;
180 14_(6500K)_54W_Grow_Light_Fluorescent_T5-HO.py ;
181 15_(3200K)_40W_Induction_Fluorescent.py ;
182 16_(2100K)_150W_High_Pressure_Sodium.py ;
183 17_(1700K)_135W_Low_Pressure_Sodium.py ;
184 18_(6800K)_175W_Mercury_Vapor.py ;
185 19_(5200K)_700W_Carbon_Arc.py ;
186 20_(6500K)_15W_LED_Spot.py ;
187 21_(2700K)_7W_OLED_Panel.py ;
188 22_(30000K)_40W_Black_Light_Fluorescent.py ;
189 23_(30000K)_40W_Black_Light_Bulb.py;
192 abyss.pov ; biscuit.pov ; bsp_Tango.pov ; chess2.pov ;
193 cornell.pov ; diffract.pov ; diffuse_back.pov ; float5 ;
194 gamma_showcase.pov ; grenadine.pov ; isocacti.pov ;
195 mediasky.pov ; patio-radio.pov ; subsurface.pov ; wallstucco.pov
201 'author': "Campbell Barton, "
208 'version': (0, 1, 3),
209 'blender': (3, 2, 0),
210 'location': "Render Properties > Render Engine > Persistence of Vision",
211 'description': "Persistence of Vision addon for Blender",
212 'doc_url': "{BLENDER_MANUAL_URL}/addons/render/povray.html",
213 'category': "Render",
214 'warning': "Co-maintainers welcome",
217 # Other occasional contributors, more or less in chronological order:
218 # Luca Bonavita ; Shane Ambler ; Brendon Murphy ; Doug Hammond ;
219 # Thomas Dinges ; Jonathan Smith ; Sebastian Nell ; Philipp Oeser ;
220 # Sybren A. Stüvel ; Dalai Felinto ; Sergey Sharybin ; Brecht Van Lommel ;
221 # Stephen Leger ; Rune Morling ; Aaron Carlisle ; Ankit Meel ;
223 if "bpy" in locals():
226 importlib
.reload(ui_core
)
227 importlib
.reload(nodes_properties
)
228 importlib
.reload(nodes_gui
)
229 importlib
.reload(nodes_fn
)
230 importlib
.reload(nodes
)
231 importlib
.reload(scenography_properties
)
232 importlib
.reload(scenography_gui
)
233 importlib
.reload(scenography
)
234 importlib
.reload(render_properties
)
235 importlib
.reload(render_gui
)
236 importlib
.reload(render_core
)
237 importlib
.reload(render
)
238 importlib
.reload(shading_properties
)
239 importlib
.reload(shading_ray_properties
)
240 importlib
.reload(shading_gui
)
241 importlib
.reload(shading
)
242 importlib
.reload(texturing_procedural
)
243 importlib
.reload(texturing_properties
)
244 importlib
.reload(texturing_gui
)
245 importlib
.reload(texturing
)
246 importlib
.reload(model_properties
)
247 importlib
.reload(model_gui
)
248 importlib
.reload(model_all
)
249 importlib
.reload(model_poly_topology
)
250 importlib
.reload(model_meta_topology
)
251 importlib
.reload(model_curve_topology
)
252 importlib
.reload(model_primitives
)
253 importlib
.reload(model_primitives_topology
)
254 importlib
.reload(particles_properties
)
255 importlib
.reload(particles
)
256 importlib
.reload(scripting_properties
)
257 importlib
.reload(scripting_gui
)
258 importlib
.reload(scripting
)
259 importlib
.reload(update_files
)
263 from bpy
.utils
import register_class
, unregister_class
265 from bpy
.props
import StringProperty
, BoolProperty
, EnumProperty
270 scenography_properties
,
275 shading_ray_properties
,
276 texturing_properties
,
278 scripting_properties
,
282 model_primitives_topology
,
283 particles_properties
,
287 # ---------------------------------------------------------------- #
289 # ---------------------------------------------------------------- #
292 class POV_OT_update_addon(bpy
.types
.Operator
):
293 """Update this addon to the latest version"""
295 bl_idname
= "pov.update_addon"
296 bl_label
= "Update POV addon"
298 def execute(self
, context
):
303 import urllib
.request
306 def recursive_overwrite(self
, src
, dest
, ignore
=None):
307 """Update the script automatically (along with other addons).
310 src -- path where to update from
311 dest -- storing temporary download here
313 ignore -- leave some directories alone (default: {None})
316 finished flag for operator which is a set()
318 if os
.path
.isdir(src
):
319 if not os
.path
.isdir(dest
):
321 files
= os
.listdir(src
)
322 ignored
= ignore(src
, files
) if ignore
is not None else set()
323 unignored_files
= (fle
for fle
in files
if fle
not in ignored
)
324 for f
in unignored_files
:
325 source
= os
.path
.join(src
, f
)
326 destination
= os
.path
.join(dest
, f
)
327 recursive_overwrite(source
, destination
, ignore
)
329 shutil
.copyfile(src
, dest
)
332 print("Updating POV addon...")
334 with tempfile
.TemporaryDirectory() as temp_dir_path
:
335 temp_zip_path
= os
.path
.join(temp_dir_path
, "master.zip")
337 # Download zip archive of latest addons master branch commit
338 # More work needed so we also get files from the shared addons presets /pov folder
339 # switch this URL back to the BF hosted one as soon as gitweb snapshot gets fixed
340 url
= "https://github.com/blender/blender-addons/archive/refs/heads/master.zip"
342 print("Downloading", url
)
344 with urllib
.request
.urlopen(url
, timeout
=60) as url_handle
, open(
347 file_handle
.write(url_handle
.read())
348 except urllib
.error
.URLError
as err
:
349 self
.report({"ERROR"}, "Could not download: %s" % err
)
352 print("Extracting ZIP archive")
353 with zipfile
.ZipFile(temp_zip_path
) as zip_archive
:
354 pov_addon_pkg
= (member
for member
in zip_archive
.namelist() if
355 "blender-addons-master/render_povray" in member
)
356 for member
in pov_addon_pkg
:
357 # Remove the first directory and the filename
358 # e.g. blender-addons-master/render_povray/nodes.py
359 # becomes render_povray/nodes.py
360 target_path
= os
.path
.join(
361 temp_dir_path
, os
.path
.join(*member
.split("/")[1:-1])
364 filename
= os
.path
.basename(member
)
369 # Create the target directory if necessary
370 if not os
.path
.exists(target_path
):
371 os
.makedirs(target_path
)
373 source
= zip_archive
.open(member
)
374 target
= open(os
.path
.join(target_path
, filename
), "wb")
377 shutil
.copyfileobj(source
, target
)
378 print("copying", source
, "to", target
)
380 extracted_render_povray_path
= os
.path
.join(temp_dir_path
, "render_povray")
382 if not os
.path
.exists(extracted_render_povray_path
):
383 self
.report({"ERROR"}, "Could not extract ZIP archive! Aborting.")
386 # Find the old POV addon files
387 render_povray_dir
= os
.path
.abspath(os
.path
.dirname(__file__
)) # Unnecessary abspath?
388 print("POV addon addon folder:", render_povray_dir
)
390 # TODO: Create backup
392 # Delete old POV addon files
393 # (only directories and *.py files, user might have other stuff in there!)
394 print("Deleting old POV addon files")
396 os
.remove(os
.path
.join(render_povray_dir
, "__init__.py"))
399 for directory
in next(os
.walk(render_povray_dir
))[dir_names
]:
400 shutil
.rmtree(os
.path
.join(render_povray_dir
, directory
))
402 print("Copying new POV addon files")
403 # copy new POV addon files
406 os
.path
.join(extracted_render_povray_path
, "__init__.py"),
410 recursive_overwrite(extracted_render_povray_path
, render_povray_dir
)
412 bpy
.ops
.preferences
.addon_refresh()
413 print("POV addon update finished, restart Blender for the changes to take effect.")
415 self
.report({"WARNING"}, "Restart Blender!")
419 # ---------------------------------------------------------------- #
420 # Povray Preferences.
421 # ---------------------------------------------------------------- #
424 class PovPreferences(bpy
.types
.AddonPreferences
):
425 """Declare preference variables to set up POV binary."""
429 branch_feature_set_povray
: EnumProperty(
431 description
="Choose between official (POV-Ray) or (UberPOV) "
432 "development branch features to write in the pov file",
434 ("povray", "Official POV-Ray", "", "PLUGIN", 0),
435 ("uberpov", "Unofficial UberPOV", "", "PLUGIN", 1),
440 filepath_povray
: StringProperty(
441 name
="Binary Location", description
="Path to renderer executable", subtype
="FILE_PATH"
444 docpath_povray
: StringProperty(
445 name
="Includes Location", description
="Path to Insert Menu files", subtype
="FILE_PATH",
449 use_sounds
: BoolProperty(
451 description
="Signaling end of the render process at various"
452 "stages can help if you're away from monitor",
456 # TODO: Auto find POV sound directory as it does for binary
457 # And implement the three cases, left uncommented for a dummy
458 # interface in case some doc screenshots get made for that area
459 filepath_complete_sound
: StringProperty(
460 name
="Finish Render Sound",
461 description
="Path to finished render sound file",
465 filepath_parse_error_sound
: StringProperty(
466 name
="Parse Error Sound",
467 description
="Path to parsing time error sound file",
471 filepath_cancel_sound
: StringProperty(
472 name
="Cancel Render Sound",
473 description
="Path to cancelled or render time error sound file",
477 def draw(self
, context
):
479 layout
.prop(self
, 'branch_feature_set_povray')
480 layout
.prop(self
, 'filepath_povray')
481 layout
.prop(self
, 'docpath_povray')
482 layout
.prop(self
, 'filepath_complete_sound')
483 layout
.prop(self
, 'filepath_parse_error_sound')
484 layout
.prop(self
, 'filepath_cancel_sound')
485 layout
.prop(self
, 'use_sounds', icon
='SOUND')
486 layout
.operator('pov.update_addon', icon
='FILE_REFRESH')
487 layout
.operator("wm.url_open", text
="Community",icon
='EVENT_F').url
= \
488 "https://www.facebook.com/povable"
501 render_properties
.register()
502 scenography_properties
.register()
503 shading_properties
.register()
504 shading_ray_properties
.register()
505 texturing_properties
.register()
506 model_properties
.register()
507 particles_properties
.register()
508 scripting_properties
.register()
509 nodes_properties
.register()
512 render_core
.register()
514 model_primitives_topology
.register()
515 model_primitives
.register()
519 model_primitives
.unregister()
520 model_primitives_topology
.unregister()
522 render_core
.unregister()
524 nodes_gui
.unregister()
525 nodes_properties
.unregister()
526 scripting_properties
.unregister()
527 particles_properties
.unregister()
528 model_properties
.unregister()
529 texturing_properties
.unregister()
530 shading_ray_properties
.unregister()
531 shading_properties
.unregister()
532 scenography_properties
.unregister()
533 render_properties
.unregister()
535 for cls
in reversed(classes
):
536 unregister_class(cls
)
539 if __name__
== '__main__':
542 # ------------8<---------[ BREAKPOINT ]--------------8<----------- # Move this snippet around
543 # __import__('code').interact(local=dict(globals(), **locals())) # < and uncomment this line
544 # ---------------------------------------------------------------- # to inspect from Terminal