1 # SPDX-FileCopyrightText: 2021-2022 Blender Foundation
3 # SPDX-License-Identifier: GPL-2.0-or-later
5 """"User interface for shaders exported to POV textures."""
8 from bpy
.utils
import register_class
, unregister_class
9 from bpy
.types
import Operator
, Menu
, Panel
10 from bl_operators
.presets
import AddPresetBase
12 # Example of wrapping every class 'as is' except some
13 from bl_ui
import properties_material
15 for member
in dir(properties_material
):
16 subclass
= getattr(properties_material
, member
)
17 if hasattr(subclass
, "COMPAT_ENGINES"):
18 subclass
.COMPAT_ENGINES
.add("POVRAY_RENDER")
19 del properties_material
21 from .shading_properties
import check_material
24 def simple_material(mat
):
25 """Test if a material uses nodes"""
26 return (mat
is not None) and (not mat
.use_nodes
)
29 class MaterialButtonsPanel
:
30 """Use this class to define buttons from the material tab of properties window."""
32 bl_space_type
= "PROPERTIES"
33 bl_region_type
= "WINDOW"
34 bl_context
= "material"
35 COMPAT_ENGINES
= {"POVRAY_RENDER"}
38 def poll(cls
, context
):
39 mat
= context
.material
40 rd
= context
.scene
.render
41 return mat
and (rd
.engine
in cls
.COMPAT_ENGINES
)
44 class MATERIAL_PT_POV_shading(MaterialButtonsPanel
, Panel
):
46 COMPAT_ENGINES
= {"POVRAY_RENDER"}
49 def poll(cls
, context
):
50 mat
= context
.material
51 engine
= context
.scene
.render
.engine
54 and (mat
.pov
.type in {"SURFACE", "WIRE"})
55 and (engine
in cls
.COMPAT_ENGINES
)
58 def draw(self
, context
):
61 mat
= context
.material
# FORMERLY : #active_node_mat(context.material)
63 if mat
.pov
.type in {"SURFACE", "WIRE"}:
64 split
= layout
.split()
68 sub
.active
= not mat
.pov
.use_shadeless
69 sub
.prop(mat
.pov
, "emit")
70 sub
.prop(mat
.pov
, "ambient")
72 sub
.prop(mat
.pov
, "translucency")
75 col
.prop(mat
.pov
, "use_shadeless")
77 sub
.active
= not mat
.pov
.use_shadeless
78 sub
.prop(mat
.pov
, "use_tangent_shading")
79 sub
.prop(mat
.pov
, "use_cubic")
82 class MATERIAL_MT_POV_sss_presets(Menu
):
83 """Use this class to define pov sss preset menu."""
85 bl_label
= "SSS Presets"
86 preset_subdir
= "pov/material/sss"
87 preset_operator
= "script.execute_preset"
88 draw
= bpy
.types
.Menu
.draw_preset
91 class MATERIAL_OT_POV_sss_add_preset(AddPresetBase
, Operator
):
92 """Add an SSS Preset"""
94 bl_idname
= "material.sss_preset_add"
95 bl_label
= "Add SSS Preset"
96 preset_menu
= "MATERIAL_MT_POV_sss_presets"
98 # variable used for all preset values
99 preset_defines
= ["material = bpy.context.material"]
101 # properties to store in the preset
103 "material.pov_subsurface_scattering.radius",
104 "material.pov_subsurface_scattering.color",
107 # where to store the preset
108 preset_subdir
= "pov/material/sss"
111 class MATERIAL_PT_POV_sss(MaterialButtonsPanel
, Panel
):
112 """Use this class to define pov sss buttons panel."""
114 bl_label
= "Subsurface Scattering"
115 bl_options
= {"DEFAULT_CLOSED"}
116 COMPAT_ENGINES
= {"POVRAY_RENDER"}
119 def poll(cls
, context
):
120 mat
= context
.material
121 engine
= context
.scene
.render
.engine
124 and (mat
.pov
.type in {"SURFACE", "WIRE"})
125 and (engine
in cls
.COMPAT_ENGINES
)
128 def draw_header(self
, context
):
129 mat
= context
.material
# FORMERLY : #active_node_mat(context.material)
130 sss
= mat
.pov_subsurface_scattering
132 self
.layout
.active
= not mat
.pov
.use_shadeless
133 self
.layout
.prop(sss
, "use", text
="")
135 def draw(self
, context
):
138 mat
= context
.material
# FORMERLY : #active_node_mat(context.material)
139 sss
= mat
.pov_subsurface_scattering
141 layout
.active
= sss
.use
and (not mat
.pov
.use_shadeless
)
143 row
= layout
.row().split()
144 sub
= row
.row(align
=True).split(align
=True, factor
=0.75)
145 sub
.menu(MATERIAL_MT_POV_sss_presets
.__name
__, text
=MATERIAL_MT_POV_sss_presets
.bl_label
)
146 sub
.operator(MATERIAL_OT_POV_sss_add_preset
.bl_idname
, text
="", icon
="ADD")
148 MATERIAL_OT_POV_sss_add_preset
.bl_idname
, text
="", icon
="REMOVE"
149 ).remove_active
= True
151 split
= layout
.split()
155 col
.prop(sss
, "scale")
156 col
.prop(sss
, "color", text
="")
157 col
.prop(sss
, "radius", text
="RGB Radius", expand
=True)
160 sub
= col
.column(align
=True)
161 sub
.label(text
="Blend:")
162 sub
.prop(sss
, "color_factor", text
="Color")
163 sub
.prop(sss
, "texture_factor", text
="Texture")
164 sub
.label(text
="Scattering Weight:")
165 sub
.prop(sss
, "front")
166 sub
.prop(sss
, "back")
168 col
.prop(sss
, "error_threshold", text
="Error")
171 class MATERIAL_PT_POV_activate_node(MaterialButtonsPanel
, Panel
):
172 """Use this class to define an activate pov nodes button."""
174 bl_label
= "Activate Node Settings"
175 bl_context
= "material"
176 bl_options
= {"HIDE_HEADER"}
177 COMPAT_ENGINES
= {"POVRAY_RENDER"}
180 def poll(cls
, context
):
181 engine
= context
.scene
.render
.engine
182 mat
= context
.material
185 and mat
.pov
.type == "SURFACE"
186 and engine
in cls
.COMPAT_ENGINES
187 and not mat
.pov
.material_use_nodes
188 and not mat
.use_nodes
191 def draw(self
, context
):
193 # layout.operator("pov.material_use_nodes", icon='SOUND')#'NODETREE')
194 # the above replaced with a context hook below:
196 "WM_OT_context_toggle", text
="Use POV-Ray Nodes", icon
="NODETREE"
197 ).data_path
= "material.pov.material_use_nodes"
200 class MATERIAL_PT_POV_active_node(MaterialButtonsPanel
, Panel
):
201 """Use this class to show pov active node properties buttons."""
203 bl_label
= "Active Node Settings"
204 bl_context
= "material"
205 bl_options
= {"HIDE_HEADER"}
206 COMPAT_ENGINES
= {"POVRAY_RENDER"}
209 def poll(cls
, context
):
210 engine
= context
.scene
.render
.engine
211 mat
= context
.material
214 and mat
.pov
.type == "SURFACE"
215 and (engine
in cls
.COMPAT_ENGINES
)
216 and mat
.pov
.material_use_nodes
219 def draw(self
, context
):
220 mat
= context
.material
221 node_tree
= mat
.node_tree
222 if node_tree
and mat
.use_nodes
:
224 if node
:= node_tree
.nodes
.active
:
225 layout
.prop(mat
.pov
, "material_active_node")
226 layout
.context_pointer_set("node", node
)
227 if hasattr(node
, "draw_buttons_ext"):
228 node
.draw_buttons_ext(context
, layout
)
229 elif hasattr(node
, "draw_buttons"):
230 node
.draw_buttons(context
, layout
)
232 socket
for socket
in node
.inputs
if socket
.enabled
and not socket
.is_linked
235 layout
.label(text
="Inputs:")
236 for socket
in value_inputs
:
238 socket
.draw(context
, row
, node
, socket
.name
)
240 layout
.label(text
="No active nodes!")
243 class MATERIAL_PT_POV_specular(MaterialButtonsPanel
, Panel
):
244 """Use this class to define standard material specularity (highlights) buttons."""
246 bl_label
= "Specular"
247 COMPAT_ENGINES
= {"POVRAY_RENDER"}
250 def poll(cls
, context
):
251 mat
= context
.material
252 engine
= context
.scene
.render
.engine
255 and (mat
.pov
.type in {"SURFACE", "WIRE"})
256 and (engine
in cls
.COMPAT_ENGINES
)
259 def draw(self
, context
):
262 mat
= context
.material
264 layout
.active
= not mat
.pov
.use_shadeless
266 split
= layout
.split()
269 col
.prop(mat
, "specular_color", text
="")
270 col
.prop(mat
, "specular_intensity", text
="Intensity")
273 col
.prop(mat
.pov
, "specular_shader", text
="")
274 col
.prop(mat
.pov
, "use_specular_ramp", text
="Ramp")
276 col
= layout
.column()
277 if mat
.pov
.specular_shader
in {"COOKTORR", "PHONG"}:
278 col
.prop(mat
.pov
, "specular_hardness", text
="Hardness")
279 elif mat
.pov
.specular_shader
== "BLINN":
281 row
.prop(mat
.pov
, "specular_hardness", text
="Hardness")
282 row
.prop(mat
.pov
, "specular_ior", text
="IOR")
283 elif mat
.pov
.specular_shader
== "WARDISO":
284 col
.prop(mat
.pov
, "specular_slope", text
="Slope")
285 elif mat
.pov
.specular_shader
== "TOON":
287 row
.prop(mat
.pov
, "specular_toon_size", text
="Size")
288 row
.prop(mat
.pov
, "specular_toon_smooth", text
="Smooth")
290 if mat
.pov
.use_specular_ramp
:
292 layout
.template_color_ramp(mat
.pov
, "specular_ramp", expand
=True)
296 row
.prop(mat
, "specular_ramp_input", text
="Input")
297 row
.prop(mat
, "specular_ramp_blend", text
="Blend")
299 layout
.prop(mat
, "specular_ramp_factor", text
="Factor")
302 class MATERIAL_PT_POV_mirror(MaterialButtonsPanel
, Panel
):
303 """Use this class to define standard material reflectivity (mirror) buttons."""
306 bl_options
= {"DEFAULT_CLOSED"}
307 bl_idname
= "MATERIAL_PT_POV_raytrace_mirror"
308 COMPAT_ENGINES
= {"POVRAY_RENDER"}
311 def poll(cls
, context
):
312 mat
= context
.material
313 engine
= context
.scene
.render
.engine
316 and (mat
.pov
.type in {"SURFACE", "WIRE"})
317 and (engine
in cls
.COMPAT_ENGINES
)
320 def draw_header(self
, context
):
321 mat
= context
.material
322 raym
= mat
.pov_raytrace_mirror
324 self
.layout
.prop(raym
, "use", text
="")
326 def draw(self
, context
):
329 mat
= context
.material
# Formerly : #mat = active_node_mat(context.material)
330 raym
= mat
.pov_raytrace_mirror
332 layout
.active
= raym
.use
334 split
= layout
.split()
337 col
.prop(raym
, "reflect_factor")
338 col
.prop(raym
, "mirror_color", text
="")
341 col
.prop(raym
, "fresnel")
343 sub
.active
= raym
.fresnel
> 0.0
344 sub
.prop(raym
, "fresnel_factor", text
="Blend")
346 split
= layout
.split()
350 col
.prop(raym
, "depth")
351 col
.prop(raym
, "distance", text
="Max Dist")
353 sub
= col
.split(factor
=0.4)
354 sub
.active
= raym
.distance
> 0.0
355 sub
.label(text
="Fade To:")
356 sub
.prop(raym
, "fade_to", text
="")
359 col
.label(text
="Gloss:")
360 col
.prop(raym
, "gloss_factor", text
="Amount")
362 sub
.active
= raym
.gloss_factor
< 1.0
363 sub
.prop(raym
, "gloss_threshold", text
="Threshold")
364 sub
.prop(raym
, "gloss_samples", text
="Noise")
365 sub
.prop(raym
, "gloss_anisotropic", text
="Anisotropic")
368 class MATERIAL_PT_POV_transp(MaterialButtonsPanel
, Panel
):
369 """Use this class to define pov material transparency (alpha) buttons."""
371 bl_label
= "Transparency"
372 COMPAT_ENGINES
= {"POVRAY_RENDER"}
375 def poll(cls
, context
):
376 mat
= context
.material
377 engine
= context
.scene
.render
.engine
380 and (mat
.pov
.type in {"SURFACE", "WIRE"})
381 and (engine
in cls
.COMPAT_ENGINES
)
384 def draw_header(self
, context
):
385 mat
= context
.material
387 if simple_material(mat
):
388 self
.layout
.prop(mat
.pov
, "use_transparency", text
="")
390 def draw(self
, context
):
393 base_mat
= context
.material
394 mat
= context
.material
# FORMERLY active_node_mat(context.material)
395 rayt
= mat
.pov_raytrace_transparency
397 if simple_material(base_mat
):
399 row
.active
= mat
.pov
.use_transparency
400 row
.prop(mat
.pov
, "transparency_method", expand
=True)
402 split
= layout
.split()
403 split
.active
= base_mat
.pov
.use_transparency
406 col
.prop(mat
.pov
, "alpha")
408 row
.active
= (base_mat
.pov
.transparency_method
!= "MASK") and (not mat
.pov
.use_shadeless
)
409 row
.prop(mat
.pov
, "specular_alpha", text
="Specular")
412 col
.active
= not mat
.pov
.use_shadeless
413 col
.prop(rayt
, "fresnel")
415 sub
.active
= rayt
.fresnel
> 0.0
416 sub
.prop(rayt
, "fresnel_factor", text
="Blend")
418 if base_mat
.pov
.transparency_method
== "RAYTRACE":
420 split
= layout
.split()
421 split
.active
= base_mat
.pov
.use_transparency
424 col
.prop(rayt
, "ior")
425 col
.prop(rayt
, "filter")
426 col
.prop(rayt
, "falloff")
427 col
.prop(rayt
, "depth_max")
428 col
.prop(rayt
, "depth")
431 col
.label(text
="Gloss:")
432 col
.prop(rayt
, "gloss_factor", text
="Amount")
434 sub
.active
= rayt
.gloss_factor
< 1.0
435 sub
.prop(rayt
, "gloss_threshold", text
="Threshold")
436 sub
.prop(rayt
, "gloss_samples", text
="Samples")
439 class MATERIAL_PT_POV_reflection(MaterialButtonsPanel
, Panel
):
440 """Use this class to define more pov specific reflectivity buttons."""
442 bl_label
= "POV-Ray Reflection"
443 bl_parent_id
= "MATERIAL_PT_POV_raytrace_mirror"
444 COMPAT_ENGINES
= {"POVRAY_RENDER"}
447 def poll(cls
, context
):
448 engine
= context
.scene
.render
.engine
449 mat
= context
.material
452 and mat
.pov
.type == "SURFACE"
453 and engine
in cls
.COMPAT_ENGINES
454 and not mat
.pov
.material_use_nodes
455 and not mat
.use_nodes
458 def draw(self
, context
):
460 mat
= context
.material
461 col
= layout
.column()
462 col
.prop(mat
.pov
, "irid_enable")
463 if mat
.pov
.irid_enable
:
464 col
= layout
.column()
465 col
.prop(mat
.pov
, "irid_amount", slider
=True)
466 col
.prop(mat
.pov
, "irid_thickness", slider
=True)
467 col
.prop(mat
.pov
, "irid_turbulence", slider
=True)
468 col
.prop(mat
.pov
, "conserve_energy")
469 col2
= col
.split().column()
471 if not mat
.pov_raytrace_mirror
.use
:
472 col2
.label(text
="Please Check Mirror settings :")
473 col2
.active
= mat
.pov_raytrace_mirror
.use
474 col2
.prop(mat
.pov
, "mirror_use_IOR")
475 if mat
.pov
.mirror_use_IOR
:
476 col2
.alignment
= "CENTER"
477 col2
.label(text
="The current Raytrace ")
478 col2
.label(text
="Transparency IOR is: " + str(mat
.pov_raytrace_transparency
.ior
))
483 #group some native Blender (SSS) and POV (Fade)settings under such a parent panel?
484 class MATERIAL_PT_POV_interior(MaterialButtonsPanel, Panel):
485 bl_label = "POV-Ray Interior"
486 bl_idname = "material.pov_interior"
487 #bl_parent_id = "material.absorption"
488 COMPAT_ENGINES = {'POVRAY_RENDER'}
490 def poll(cls, context):
491 engine = context.scene.render.engine
493 return mat and mat.pov.type == "SURFACE"
494 and (engine in cls.COMPAT_ENGINES)
495 and not (mat.pov.material_use_nodes or mat.use_nodes)
498 def draw_header(self, context):
499 mat = context.material
503 class MATERIAL_PT_POV_fade_color(MaterialButtonsPanel
, Panel
):
504 """Use this class to define pov fading (absorption) color buttons."""
506 bl_label
= "POV-Ray Absorption"
507 COMPAT_ENGINES
= {"POVRAY_RENDER"}
508 # bl_parent_id = "material.pov_interior"
511 def poll(cls
, context
):
512 engine
= context
.scene
.render
.engine
513 mat
= context
.material
516 and mat
.pov
.type == "SURFACE"
517 and engine
in cls
.COMPAT_ENGINES
518 and not mat
.pov
.material_use_nodes
519 and not mat
.use_nodes
522 def draw_header(self
, context
):
523 mat
= context
.material
525 self
.layout
.prop(mat
.pov
, "interior_fade_color", text
="")
527 def draw(self
, context
):
528 mat
= context
.material
529 if mat
.pov
.interior_fade_color
!= (0.0, 0.0, 0.0):
531 # layout.active = mat.pov.interior_fade_color
532 layout
.label(text
="Raytrace transparency")
533 layout
.label(text
="depth max Limit needs")
534 layout
.label(text
="to be non zero to fade")
537 class MATERIAL_PT_POV_caustics(MaterialButtonsPanel
, Panel
):
538 """Use this class to define pov caustics buttons."""
540 bl_label
= "Caustics"
541 COMPAT_ENGINES
= {"POVRAY_RENDER"}
544 def poll(cls
, context
):
545 engine
= context
.scene
.render
.engine
546 mat
= context
.material
549 and mat
.pov
.type == "SURFACE"
550 and engine
in cls
.COMPAT_ENGINES
551 and not mat
.pov
.material_use_nodes
552 and not mat
.use_nodes
555 def draw_header(self
, context
):
556 mat
= context
.material
557 if mat
.pov
.caustics_enable
:
558 self
.layout
.prop(mat
.pov
, "caustics_enable", text
="", icon
="PMARKER_SEL")
560 self
.layout
.prop(mat
.pov
, "caustics_enable", text
="", icon
="PMARKER")
562 def draw(self
, context
):
566 mat
= context
.material
567 layout
.active
= mat
.pov
.caustics_enable
568 col
= layout
.column()
569 if mat
.pov
.caustics_enable
:
570 col
.prop(mat
.pov
, "refraction_caustics")
571 if mat
.pov
.refraction_caustics
:
573 col
.prop(mat
.pov
, "refraction_type", text
="")
575 if mat
.pov
.refraction_type
== "1":
576 col
.prop(mat
.pov
, "fake_caustics_power", slider
=True)
577 elif mat
.pov
.refraction_type
== "2":
578 col
.prop(mat
.pov
, "photons_dispersion", slider
=True)
579 col
.prop(mat
.pov
, "photons_dispersion_samples", slider
=True)
580 col
.prop(mat
.pov
, "photons_reflection")
582 if not mat
.pov
.refraction_caustics
and not mat
.pov
.photons_reflection
:
583 col
= layout
.column()
584 col
.alignment
= "CENTER"
585 col
.label(text
="Caustics override is on, ")
586 col
.label(text
="but you didn't chose any !")
589 class MATERIAL_PT_strand(MaterialButtonsPanel
, Panel
):
590 """Use this class to define Blender strand antialiasing buttons."""
593 bl_options
= {"DEFAULT_CLOSED"}
594 COMPAT_ENGINES
= {"POVRAY_RENDER"}
597 def poll(cls
, context
):
598 mat
= context
.material
599 engine
= context
.scene
.render
.engine
601 mat
and (mat
.pov
.type in {"SURFACE", "WIRE", "HALO"}) and (engine
in cls
.COMPAT_ENGINES
)
604 def draw(self
, context
):
607 mat
= context
.material
# don't use node material
610 split
= layout
.split()
613 sub
= col
.column(align
=True)
614 sub
.label(text
="Size:")
615 sub
.prop(tan
, "root_size", text
="Root")
616 sub
.prop(tan
, "tip_size", text
="Tip")
617 sub
.prop(tan
, "size_min", text
="Minimum")
618 sub
.prop(tan
, "use_blender_units")
620 sub
.active
= not mat
.pov
.use_shadeless
621 sub
.prop(tan
, "use_tangent_shading")
622 col
.prop(tan
, "shape")
625 col
.label(text
="Shading:")
626 col
.prop(tan
, "width_fade")
628 if ob
and ob
.type == "MESH":
629 col
.prop_search(tan
, "uv_layer", ob
.data
, "uv_layers", text
="")
631 col
.prop(tan
, "uv_layer", text
="")
634 sub
.active
= not mat
.pov
.use_shadeless
635 sub
.label(text
="Surface diffuse:")
637 sub
.prop(tan
, "blend_distance", text
="Distance")
640 class MATERIAL_PT_POV_replacement_text(MaterialButtonsPanel
, Panel
):
641 """Use this class to define pov custom code declared name field."""
643 bl_label
= "Custom POV Code"
644 COMPAT_ENGINES
= {"POVRAY_RENDER"}
646 def draw(self
, context
):
648 mat
= context
.material
649 col
= layout
.column()
650 col
.alignment
= "RIGHT"
651 col
.label(text
="Override properties with this")
652 col
.label(text
="text editor {pov code} block")
653 layout
.prop(mat
.pov
, "replacement_text", text
="#declare name", icon
="SYNTAX_ON")
657 MATERIAL_PT_POV_shading
,
659 MATERIAL_MT_POV_sss_presets
,
660 MATERIAL_OT_POV_sss_add_preset
,
662 MATERIAL_PT_POV_activate_node
,
663 MATERIAL_PT_POV_active_node
,
664 MATERIAL_PT_POV_specular
,
665 MATERIAL_PT_POV_mirror
,
666 MATERIAL_PT_POV_transp
,
667 MATERIAL_PT_POV_reflection
,
668 # MATERIAL_PT_POV_interior,
669 MATERIAL_PT_POV_fade_color
,
670 MATERIAL_PT_POV_caustics
,
671 MATERIAL_PT_POV_replacement_text
,
681 for cls
in reversed(classes
):
682 unregister_class(cls
)