Fix T104270: Error in materials_utils "unregister" function
[blender-addons.git] / render_povray / shading_gui.py
blob0c16aa73bc41c2796c32cba074d6f317c2ceed85
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 """"User interface for shaders exported to POV textures."""
5 import bpy
6 from bpy.utils import register_class, unregister_class
7 from bpy.types import Operator, Menu, Panel
8 from bl_operators.presets import AddPresetBase
10 # Example of wrapping every class 'as is' except some
11 from bl_ui import properties_material
13 for member in dir(properties_material):
14 subclass = getattr(properties_material, member)
15 if hasattr(subclass, "COMPAT_ENGINES"):
16 subclass.COMPAT_ENGINES.add("POVRAY_RENDER")
17 del properties_material
19 from .shading_properties import check_material
22 def simple_material(mat):
23 """Test if a material uses nodes"""
24 return (mat is not None) and (not mat.use_nodes)
27 class MaterialButtonsPanel:
28 """Use this class to define buttons from the material tab of properties window."""
30 bl_space_type = "PROPERTIES"
31 bl_region_type = "WINDOW"
32 bl_context = "material"
33 COMPAT_ENGINES = {"POVRAY_RENDER"}
35 @classmethod
36 def poll(cls, context):
37 mat = context.material
38 rd = context.scene.render
39 return mat and (rd.engine in cls.COMPAT_ENGINES)
42 class MATERIAL_PT_POV_shading(MaterialButtonsPanel, Panel):
43 bl_label = "Shading"
44 COMPAT_ENGINES = {"POVRAY_RENDER"}
46 @classmethod
47 def poll(cls, context):
48 mat = context.material
49 engine = context.scene.render.engine
50 return (
51 check_material(mat)
52 and (mat.pov.type in {"SURFACE", "WIRE"})
53 and (engine in cls.COMPAT_ENGINES)
56 def draw(self, context):
57 layout = self.layout
59 mat = context.material # FORMERLY : #active_node_mat(context.material)
61 if mat.pov.type in {"SURFACE", "WIRE"}:
62 split = layout.split()
64 col = split.column()
65 sub = col.column()
66 sub.active = not mat.pov.use_shadeless
67 sub.prop(mat.pov, "emit")
68 sub.prop(mat.pov, "ambient")
69 sub = col.column()
70 sub.prop(mat.pov, "translucency")
72 col = split.column()
73 col.prop(mat.pov, "use_shadeless")
74 sub = col.column()
75 sub.active = not mat.pov.use_shadeless
76 sub.prop(mat.pov, "use_tangent_shading")
77 sub.prop(mat.pov, "use_cubic")
80 class MATERIAL_MT_POV_sss_presets(Menu):
81 """Use this class to define pov sss preset menu."""
83 bl_label = "SSS Presets"
84 preset_subdir = "pov/material/sss"
85 preset_operator = "script.execute_preset"
86 draw = bpy.types.Menu.draw_preset
89 class MATERIAL_OT_POV_sss_add_preset(AddPresetBase, Operator):
90 """Add an SSS Preset"""
92 bl_idname = "material.sss_preset_add"
93 bl_label = "Add SSS Preset"
94 preset_menu = "MATERIAL_MT_POV_sss_presets"
96 # variable used for all preset values
97 preset_defines = ["material = bpy.context.material"]
99 # properties to store in the preset
100 preset_values = [
101 "material.pov_subsurface_scattering.radius",
102 "material.pov_subsurface_scattering.color",
105 # where to store the preset
106 preset_subdir = "pov/material/sss"
109 class MATERIAL_PT_POV_sss(MaterialButtonsPanel, Panel):
110 """Use this class to define pov sss buttons panel."""
112 bl_label = "Subsurface Scattering"
113 bl_options = {"DEFAULT_CLOSED"}
114 COMPAT_ENGINES = {"POVRAY_RENDER"}
116 @classmethod
117 def poll(cls, context):
118 mat = context.material
119 engine = context.scene.render.engine
120 return (
121 check_material(mat)
122 and (mat.pov.type in {"SURFACE", "WIRE"})
123 and (engine in cls.COMPAT_ENGINES)
126 def draw_header(self, context):
127 mat = context.material # FORMERLY : #active_node_mat(context.material)
128 sss = mat.pov_subsurface_scattering
130 self.layout.active = not mat.pov.use_shadeless
131 self.layout.prop(sss, "use", text="")
133 def draw(self, context):
134 layout = self.layout
136 mat = context.material # FORMERLY : #active_node_mat(context.material)
137 sss = mat.pov_subsurface_scattering
139 layout.active = sss.use and (not mat.pov.use_shadeless)
141 row = layout.row().split()
142 sub = row.row(align=True).split(align=True, factor=0.75)
143 sub.menu(MATERIAL_MT_POV_sss_presets.__name__, text=MATERIAL_MT_POV_sss_presets.bl_label)
144 sub.operator(MATERIAL_OT_POV_sss_add_preset.bl_idname, text="", icon="ADD")
145 sub.operator(
146 MATERIAL_OT_POV_sss_add_preset.bl_idname, text="", icon="REMOVE"
147 ).remove_active = True
149 split = layout.split()
151 col = split.column()
152 col.prop(sss, "ior")
153 col.prop(sss, "scale")
154 col.prop(sss, "color", text="")
155 col.prop(sss, "radius", text="RGB Radius", expand=True)
157 col = split.column()
158 sub = col.column(align=True)
159 sub.label(text="Blend:")
160 sub.prop(sss, "color_factor", text="Color")
161 sub.prop(sss, "texture_factor", text="Texture")
162 sub.label(text="Scattering Weight:")
163 sub.prop(sss, "front")
164 sub.prop(sss, "back")
165 col.separator()
166 col.prop(sss, "error_threshold", text="Error")
169 class MATERIAL_PT_POV_activate_node(MaterialButtonsPanel, Panel):
170 """Use this class to define an activate pov nodes button."""
172 bl_label = "Activate Node Settings"
173 bl_context = "material"
174 bl_options = {"HIDE_HEADER"}
175 COMPAT_ENGINES = {"POVRAY_RENDER"}
177 @classmethod
178 def poll(cls, context):
179 engine = context.scene.render.engine
180 mat = context.material
181 return (
183 and mat.pov.type == "SURFACE"
184 and engine in cls.COMPAT_ENGINES
185 and not mat.pov.material_use_nodes
186 and not mat.use_nodes
189 def draw(self, context):
190 layout = self.layout
191 # layout.operator("pov.material_use_nodes", icon='SOUND')#'NODETREE')
192 # the above replaced with a context hook below:
193 layout.operator(
194 "WM_OT_context_toggle", text="Use POV-Ray Nodes", icon="NODETREE"
195 ).data_path = "material.pov.material_use_nodes"
198 class MATERIAL_PT_POV_active_node(MaterialButtonsPanel, Panel):
199 """Use this class to show pov active node properties buttons."""
201 bl_label = "Active Node Settings"
202 bl_context = "material"
203 bl_options = {"HIDE_HEADER"}
204 COMPAT_ENGINES = {"POVRAY_RENDER"}
206 @classmethod
207 def poll(cls, context):
208 engine = context.scene.render.engine
209 mat = context.material
210 return (
212 and mat.pov.type == "SURFACE"
213 and (engine in cls.COMPAT_ENGINES)
214 and mat.pov.material_use_nodes
217 def draw(self, context):
218 mat = context.material
219 node_tree = mat.node_tree
220 if node_tree and mat.use_nodes:
221 layout = self.layout
222 if node := node_tree.nodes.active:
223 layout.prop(mat.pov, "material_active_node")
224 layout.context_pointer_set("node", node)
225 if hasattr(node, "draw_buttons_ext"):
226 node.draw_buttons_ext(context, layout)
227 elif hasattr(node, "draw_buttons"):
228 node.draw_buttons(context, layout)
229 if value_inputs := [
230 socket for socket in node.inputs if socket.enabled and not socket.is_linked
232 layout.separator()
233 layout.label(text="Inputs:")
234 for socket in value_inputs:
235 row = layout.row()
236 socket.draw(context, row, node, socket.name)
237 else:
238 layout.label(text="No active nodes!")
241 class MATERIAL_PT_POV_specular(MaterialButtonsPanel, Panel):
242 """Use this class to define standard material specularity (highlights) buttons."""
244 bl_label = "Specular"
245 COMPAT_ENGINES = {"POVRAY_RENDER"}
247 @classmethod
248 def poll(cls, context):
249 mat = context.material
250 engine = context.scene.render.engine
251 return (
252 check_material(mat)
253 and (mat.pov.type in {"SURFACE", "WIRE"})
254 and (engine in cls.COMPAT_ENGINES)
257 def draw(self, context):
258 layout = self.layout
260 mat = context.material
262 layout.active = not mat.pov.use_shadeless
264 split = layout.split()
266 col = split.column()
267 col.prop(mat, "specular_color", text="")
268 col.prop(mat, "specular_intensity", text="Intensity")
270 col = split.column()
271 col.prop(mat.pov, "specular_shader", text="")
272 col.prop(mat.pov, "use_specular_ramp", text="Ramp")
274 col = layout.column()
275 if mat.pov.specular_shader in {"COOKTORR", "PHONG"}:
276 col.prop(mat.pov, "specular_hardness", text="Hardness")
277 elif mat.pov.specular_shader == "BLINN":
278 row = col.row()
279 row.prop(mat.pov, "specular_hardness", text="Hardness")
280 row.prop(mat.pov, "specular_ior", text="IOR")
281 elif mat.pov.specular_shader == "WARDISO":
282 col.prop(mat.pov, "specular_slope", text="Slope")
283 elif mat.pov.specular_shader == "TOON":
284 row = col.row()
285 row.prop(mat.pov, "specular_toon_size", text="Size")
286 row.prop(mat.pov, "specular_toon_smooth", text="Smooth")
288 if mat.pov.use_specular_ramp:
289 layout.separator()
290 layout.template_color_ramp(mat.pov, "specular_ramp", expand=True)
291 layout.separator()
293 row = layout.row()
294 row.prop(mat, "specular_ramp_input", text="Input")
295 row.prop(mat, "specular_ramp_blend", text="Blend")
297 layout.prop(mat, "specular_ramp_factor", text="Factor")
300 class MATERIAL_PT_POV_mirror(MaterialButtonsPanel, Panel):
301 """Use this class to define standard material reflectivity (mirror) buttons."""
303 bl_label = "Mirror"
304 bl_options = {"DEFAULT_CLOSED"}
305 bl_idname = "MATERIAL_PT_POV_raytrace_mirror"
306 COMPAT_ENGINES = {"POVRAY_RENDER"}
308 @classmethod
309 def poll(cls, context):
310 mat = context.material
311 engine = context.scene.render.engine
312 return (
313 check_material(mat)
314 and (mat.pov.type in {"SURFACE", "WIRE"})
315 and (engine in cls.COMPAT_ENGINES)
318 def draw_header(self, context):
319 mat = context.material
320 raym = mat.pov_raytrace_mirror
322 self.layout.prop(raym, "use", text="")
324 def draw(self, context):
325 layout = self.layout
327 mat = context.material # Formerly : #mat = active_node_mat(context.material)
328 raym = mat.pov_raytrace_mirror
330 layout.active = raym.use
332 split = layout.split()
334 col = split.column()
335 col.prop(raym, "reflect_factor")
336 col.prop(raym, "mirror_color", text="")
338 col = split.column()
339 col.prop(raym, "fresnel")
340 sub = col.column()
341 sub.active = raym.fresnel > 0.0
342 sub.prop(raym, "fresnel_factor", text="Blend")
344 split = layout.split()
346 col = split.column()
347 col.separator()
348 col.prop(raym, "depth")
349 col.prop(raym, "distance", text="Max Dist")
350 col.separator()
351 sub = col.split(factor=0.4)
352 sub.active = raym.distance > 0.0
353 sub.label(text="Fade To:")
354 sub.prop(raym, "fade_to", text="")
356 col = split.column()
357 col.label(text="Gloss:")
358 col.prop(raym, "gloss_factor", text="Amount")
359 sub = col.column()
360 sub.active = raym.gloss_factor < 1.0
361 sub.prop(raym, "gloss_threshold", text="Threshold")
362 sub.prop(raym, "gloss_samples", text="Noise")
363 sub.prop(raym, "gloss_anisotropic", text="Anisotropic")
366 class MATERIAL_PT_POV_transp(MaterialButtonsPanel, Panel):
367 """Use this class to define pov material transparency (alpha) buttons."""
369 bl_label = "Transparency"
370 COMPAT_ENGINES = {"POVRAY_RENDER"}
372 @classmethod
373 def poll(cls, context):
374 mat = context.material
375 engine = context.scene.render.engine
376 return (
377 check_material(mat)
378 and (mat.pov.type in {"SURFACE", "WIRE"})
379 and (engine in cls.COMPAT_ENGINES)
382 def draw_header(self, context):
383 mat = context.material
385 if simple_material(mat):
386 self.layout.prop(mat.pov, "use_transparency", text="")
388 def draw(self, context):
389 layout = self.layout
391 base_mat = context.material
392 mat = context.material # FORMERLY active_node_mat(context.material)
393 rayt = mat.pov_raytrace_transparency
395 if simple_material(base_mat):
396 row = layout.row()
397 row.active = mat.pov.use_transparency
398 row.prop(mat.pov, "transparency_method", expand=True)
400 split = layout.split()
401 split.active = base_mat.pov.use_transparency
403 col = split.column()
404 col.prop(mat.pov, "alpha")
405 row = col.row()
406 row.active = (base_mat.pov.transparency_method != "MASK") and (not mat.pov.use_shadeless)
407 row.prop(mat.pov, "specular_alpha", text="Specular")
409 col = split.column()
410 col.active = not mat.pov.use_shadeless
411 col.prop(rayt, "fresnel")
412 sub = col.column()
413 sub.active = rayt.fresnel > 0.0
414 sub.prop(rayt, "fresnel_factor", text="Blend")
416 if base_mat.pov.transparency_method == "RAYTRACE":
417 layout.separator()
418 split = layout.split()
419 split.active = base_mat.pov.use_transparency
421 col = split.column()
422 col.prop(rayt, "ior")
423 col.prop(rayt, "filter")
424 col.prop(rayt, "falloff")
425 col.prop(rayt, "depth_max")
426 col.prop(rayt, "depth")
428 col = split.column()
429 col.label(text="Gloss:")
430 col.prop(rayt, "gloss_factor", text="Amount")
431 sub = col.column()
432 sub.active = rayt.gloss_factor < 1.0
433 sub.prop(rayt, "gloss_threshold", text="Threshold")
434 sub.prop(rayt, "gloss_samples", text="Samples")
437 class MATERIAL_PT_POV_reflection(MaterialButtonsPanel, Panel):
438 """Use this class to define more pov specific reflectivity buttons."""
440 bl_label = "POV-Ray Reflection"
441 bl_parent_id = "MATERIAL_PT_POV_raytrace_mirror"
442 COMPAT_ENGINES = {"POVRAY_RENDER"}
444 @classmethod
445 def poll(cls, context):
446 engine = context.scene.render.engine
447 mat = context.material
448 return (
450 and mat.pov.type == "SURFACE"
451 and engine in cls.COMPAT_ENGINES
452 and not mat.pov.material_use_nodes
453 and not mat.use_nodes
456 def draw(self, context):
457 layout = self.layout
458 mat = context.material
459 col = layout.column()
460 col.prop(mat.pov, "irid_enable")
461 if mat.pov.irid_enable:
462 col = layout.column()
463 col.prop(mat.pov, "irid_amount", slider=True)
464 col.prop(mat.pov, "irid_thickness", slider=True)
465 col.prop(mat.pov, "irid_turbulence", slider=True)
466 col.prop(mat.pov, "conserve_energy")
467 col2 = col.split().column()
469 if not mat.pov_raytrace_mirror.use:
470 col2.label(text="Please Check Mirror settings :")
471 col2.active = mat.pov_raytrace_mirror.use
472 col2.prop(mat.pov, "mirror_use_IOR")
473 if mat.pov.mirror_use_IOR:
474 col2.alignment = "CENTER"
475 col2.label(text="The current Raytrace ")
476 col2.label(text="Transparency IOR is: " + str(mat.pov_raytrace_transparency.ior))
481 #group some native Blender (SSS) and POV (Fade)settings under such a parent panel?
482 class MATERIAL_PT_POV_interior(MaterialButtonsPanel, Panel):
483 bl_label = "POV-Ray Interior"
484 bl_idname = "material.pov_interior"
485 #bl_parent_id = "material.absorption"
486 COMPAT_ENGINES = {'POVRAY_RENDER'}
487 @classmethod
488 def poll(cls, context):
489 engine = context.scene.render.engine
490 mat=context.material
491 return mat and mat.pov.type == "SURFACE"
492 and (engine in cls.COMPAT_ENGINES)
493 and not (mat.pov.material_use_nodes or mat.use_nodes)
496 def draw_header(self, context):
497 mat = context.material
501 class MATERIAL_PT_POV_fade_color(MaterialButtonsPanel, Panel):
502 """Use this class to define pov fading (absorption) color buttons."""
504 bl_label = "POV-Ray Absorption"
505 COMPAT_ENGINES = {"POVRAY_RENDER"}
506 # bl_parent_id = "material.pov_interior"
508 @classmethod
509 def poll(cls, context):
510 engine = context.scene.render.engine
511 mat = context.material
512 return (
514 and mat.pov.type == "SURFACE"
515 and engine in cls.COMPAT_ENGINES
516 and not mat.pov.material_use_nodes
517 and not mat.use_nodes
520 def draw_header(self, context):
521 mat = context.material
523 self.layout.prop(mat.pov, "interior_fade_color", text="")
525 def draw(self, context):
526 mat = context.material
527 if mat.pov.interior_fade_color != (0.0, 0.0, 0.0):
528 layout = self.layout
529 # layout.active = mat.pov.interior_fade_color
530 layout.label(text="Raytrace transparency")
531 layout.label(text="depth max Limit needs")
532 layout.label(text="to be non zero to fade")
535 class MATERIAL_PT_POV_caustics(MaterialButtonsPanel, Panel):
536 """Use this class to define pov caustics buttons."""
538 bl_label = "Caustics"
539 COMPAT_ENGINES = {"POVRAY_RENDER"}
541 @classmethod
542 def poll(cls, context):
543 engine = context.scene.render.engine
544 mat = context.material
545 return (
547 and mat.pov.type == "SURFACE"
548 and engine in cls.COMPAT_ENGINES
549 and not mat.pov.material_use_nodes
550 and not mat.use_nodes
553 def draw_header(self, context):
554 mat = context.material
555 if mat.pov.caustics_enable:
556 self.layout.prop(mat.pov, "caustics_enable", text="", icon="PMARKER_SEL")
557 else:
558 self.layout.prop(mat.pov, "caustics_enable", text="", icon="PMARKER")
560 def draw(self, context):
562 layout = self.layout
564 mat = context.material
565 layout.active = mat.pov.caustics_enable
566 col = layout.column()
567 if mat.pov.caustics_enable:
568 col.prop(mat.pov, "refraction_caustics")
569 if mat.pov.refraction_caustics:
571 col.prop(mat.pov, "refraction_type", text="")
573 if mat.pov.refraction_type == "1":
574 col.prop(mat.pov, "fake_caustics_power", slider=True)
575 elif mat.pov.refraction_type == "2":
576 col.prop(mat.pov, "photons_dispersion", slider=True)
577 col.prop(mat.pov, "photons_dispersion_samples", slider=True)
578 col.prop(mat.pov, "photons_reflection")
580 if not mat.pov.refraction_caustics and not mat.pov.photons_reflection:
581 col = layout.column()
582 col.alignment = "CENTER"
583 col.label(text="Caustics override is on, ")
584 col.label(text="but you didn't chose any !")
587 class MATERIAL_PT_strand(MaterialButtonsPanel, Panel):
588 """Use this class to define Blender strand antialiasing buttons."""
590 bl_label = "Strand"
591 bl_options = {"DEFAULT_CLOSED"}
592 COMPAT_ENGINES = {"POVRAY_RENDER"}
594 @classmethod
595 def poll(cls, context):
596 mat = context.material
597 engine = context.scene.render.engine
598 return (
599 mat and (mat.pov.type in {"SURFACE", "WIRE", "HALO"}) and (engine in cls.COMPAT_ENGINES)
602 def draw(self, context):
603 layout = self.layout
605 mat = context.material # don't use node material
606 tan = mat.strand
608 split = layout.split()
610 col = split.column()
611 sub = col.column(align=True)
612 sub.label(text="Size:")
613 sub.prop(tan, "root_size", text="Root")
614 sub.prop(tan, "tip_size", text="Tip")
615 sub.prop(tan, "size_min", text="Minimum")
616 sub.prop(tan, "use_blender_units")
617 sub = col.column()
618 sub.active = not mat.pov.use_shadeless
619 sub.prop(tan, "use_tangent_shading")
620 col.prop(tan, "shape")
622 col = split.column()
623 col.label(text="Shading:")
624 col.prop(tan, "width_fade")
625 ob = context.object
626 if ob and ob.type == "MESH":
627 col.prop_search(tan, "uv_layer", ob.data, "uv_layers", text="")
628 else:
629 col.prop(tan, "uv_layer", text="")
630 col.separator()
631 sub = col.column()
632 sub.active = not mat.pov.use_shadeless
633 sub.label(text="Surface diffuse:")
634 sub = col.column()
635 sub.prop(tan, "blend_distance", text="Distance")
638 class MATERIAL_PT_POV_replacement_text(MaterialButtonsPanel, Panel):
639 """Use this class to define pov custom code declared name field."""
641 bl_label = "Custom POV Code"
642 COMPAT_ENGINES = {"POVRAY_RENDER"}
644 def draw(self, context):
645 layout = self.layout
646 mat = context.material
647 col = layout.column()
648 col.alignment = "RIGHT"
649 col.label(text="Override properties with this")
650 col.label(text="text editor {pov code} block")
651 layout.prop(mat.pov, "replacement_text", text="#declare name", icon="SYNTAX_ON")
654 classes = (
655 MATERIAL_PT_POV_shading,
656 MATERIAL_PT_POV_sss,
657 MATERIAL_MT_POV_sss_presets,
658 MATERIAL_OT_POV_sss_add_preset,
659 MATERIAL_PT_strand,
660 MATERIAL_PT_POV_activate_node,
661 MATERIAL_PT_POV_active_node,
662 MATERIAL_PT_POV_specular,
663 MATERIAL_PT_POV_mirror,
664 MATERIAL_PT_POV_transp,
665 MATERIAL_PT_POV_reflection,
666 # MATERIAL_PT_POV_interior,
667 MATERIAL_PT_POV_fade_color,
668 MATERIAL_PT_POV_caustics,
669 MATERIAL_PT_POV_replacement_text,
673 def register():
674 for cls in classes:
675 register_class(cls)
678 def unregister():
679 for cls in reversed(classes):
680 unregister_class(cls)