Fix add-ons with Python 3.12 by replacing "imp" with "importlib"
[blender-addons.git] / render_povray / __init__.py
blobd3de19ebd233489d346d271a3e5fb46a2295b534
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 ###########################################
14 __init__.py :
15 Provide script's metadata, Initialize addon preferences, (re)load package modules
17 ui_core.py :
18 Set up workspaces and load other ui files for the user to set up all variables
20 render_core.py :
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)
26 render_gui.py :
27 Display properties from render_properties.py for user to change them
29 render.py :
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
42 scenography_gui.py :
43 Display camera/light/environment properties from scenography_properties.py for user to change
45 scenography.py
46 Translate camera/light/environment properties to corresponding pov features
48 __------------------Z__ ____________________
49 _--¨¨] | __ __ _____________|||____________________|
50 _-¨7____/ | | °|° | □□□ □□□ □□□ ||| □□□ □□□ □□□ □□□ □□ |
51 (===========|=| | |=============|||====================|
52 `-,_(@)(@)----------------(@)(@)-'^-(@)(@)--------(@)(@)'
53 ############################################### MODEL #############################################
55 model_properties.py :
56 Initialize properties for translating Blender geometry objects parameters to pov
58 model_gui.py :
59 Display properties from model_properties.py for the user to change them
61 model_all.py :
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
73 model_primitives.py :
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
92 shading_gui.py :
93 Display variables from shading_properties.py and shading_ray_properties.py for user to change
95 shading.py
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
101 texturing_gui.py :
102 Display properties from texturing_properties.py available for user to change
104 texturing.py :
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
120 particles.py :
121 Translate to POV the particle based geometries
123 voxel_lib.py :
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
129 nodes.py :
130 Translate node trees to the pov file
132 nodes_fn.py :
133 Functions toolbox used by nodes.py to translate node trees to the pov file
135 nodes_gui.py :
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)
141 scripting_gui.py :
142 Display properties from scripting_properties.py for user to add his custom POV code
144 scripting.py :
145 Insert POV native scene description elements into blender scene or to exported POV file
147 update_files.py :
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...)
154 presets :
155 Material (sss)
156 apple.py ; chicken.py ; cream.py ; Ketchup.py ; marble.py ;
157 potato.py ; skim_milk.py ; skin1.py ; skin2.py ; whole_milk.py
158 Radiosity
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 ;
163 World
164 01_Clear_Blue_Sky.py ; 02_Partly_Hazy_Sky.py ; 03_Overcast_Sky.py ;
165 04_Cartoony_Sky.py ; 05_Under_Water.py ;
166 Light
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;
190 24_(1850K)_Candle.py
191 templates:
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
199 bl_info = {
200 'name': "POV@Ble",
201 'author': "Campbell Barton, "
202 "Maurice Raybaud, "
203 "Leonid Desyatkov, "
204 "Bastien Montagne, "
205 "Constantin Rahn, "
206 "Silvio Falcinelli,"
207 "Paco García",
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():
224 import importlib
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)
261 else:
262 import bpy
263 from bpy.utils import register_class, unregister_class
265 from bpy.props import StringProperty, BoolProperty, EnumProperty
267 from . import (
268 ui_core,
269 render_properties,
270 scenography_properties,
271 nodes_properties,
272 nodes_gui,
273 nodes,
274 shading_properties,
275 shading_ray_properties,
276 texturing_properties,
277 model_properties,
278 scripting_properties,
279 render,
280 render_core,
281 model_primitives,
282 model_primitives_topology,
283 particles_properties,
284 particles,
287 # ---------------------------------------------------------------- #
288 # Auto update.
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):
299 import os
300 import shutil
301 import tempfile
302 import urllib.error
303 import urllib.request
304 import zipfile
306 def recursive_overwrite(self, src, dest, ignore=None):
307 """Update the script automatically (along with other addons).
309 Arguments:
310 src -- path where to update from
311 dest -- storing temporary download here
312 Keyword Arguments:
313 ignore -- leave some directories alone (default: {None})
315 Returns:
316 finished flag for operator which is a set()
318 if os.path.isdir(src):
319 if not os.path.isdir(dest):
320 os.makedirs(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)
328 else:
329 shutil.copyfile(src, dest)
331 print("-" * 20)
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"
341 try:
342 print("Downloading", url)
344 with urllib.request.urlopen(url, timeout=60) as url_handle, open(
345 temp_zip_path, "wb"
346 ) as file_handle:
347 file_handle.write(url_handle.read())
348 except urllib.error.URLError as err:
349 self.report({"ERROR"}, "Could not download: %s" % err)
351 # Extract the zip
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)
365 # Skip directories
366 if not filename:
367 continue
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")
376 with source, target:
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.")
384 return {"FINISHED"}
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")
395 # remove __init__.py
396 os.remove(os.path.join(render_povray_dir, "__init__.py"))
397 # remove all folders
398 dir_names = 1
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
404 # copy __init__.py
405 shutil.copy2(
406 os.path.join(extracted_render_povray_path, "__init__.py"),
407 render_povray_dir,
409 # copy all folders
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.")
414 print("-" * 20)
415 self.report({"WARNING"}, "Restart Blender!")
416 return {"FINISHED"}
419 # ---------------------------------------------------------------- #
420 # Povray Preferences.
421 # ---------------------------------------------------------------- #
424 class PovPreferences(bpy.types.AddonPreferences):
425 """Declare preference variables to set up POV binary."""
427 bl_idname = __name__
429 branch_feature_set_povray: EnumProperty(
430 name="Feature Set",
431 description="Choose between official (POV-Ray) or (UberPOV) "
432 "development branch features to write in the pov file",
433 items=(
434 ("povray", "Official POV-Ray", "", "PLUGIN", 0),
435 ("uberpov", "Unofficial UberPOV", "", "PLUGIN", 1),
437 default="povray",
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",
446 default="",
449 use_sounds: BoolProperty(
450 name="Use Sound",
451 description="Signaling end of the render process at various"
452 "stages can help if you're away from monitor",
453 default=False,
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",
462 subtype="FILE_PATH",
465 filepath_parse_error_sound: StringProperty(
466 name="Parse Error Sound",
467 description="Path to parsing time error sound file",
468 subtype="FILE_PATH",
471 filepath_cancel_sound: StringProperty(
472 name="Cancel Render Sound",
473 description="Path to cancelled or render time error sound file",
474 subtype='FILE_PATH',
477 def draw(self, context):
478 layout = self.layout
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"
491 classes = (
492 POV_OT_update_addon,
493 PovPreferences,
497 def register():
498 for cls in classes:
499 register_class(cls)
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()
510 nodes_gui.register()
511 render.register()
512 render_core.register()
513 ui_core.register()
514 model_primitives_topology.register()
515 model_primitives.register()
518 def unregister():
519 model_primitives.unregister()
520 model_primitives_topology.unregister()
521 ui_core.unregister()
522 render_core.unregister()
523 render.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__':
540 register()
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