Cleanup: unused imports
[blender.git] / release / scripts / startup / bl_ui / properties_material.py
1 # ##### BEGIN GPL LICENSE BLOCK #####
2 #
3 #  This program is free software; you can redistribute it and/or
4 #  modify it under the terms of the GNU General Public License
5 #  as published by the Free Software Foundation; either version 2
6 #  of the License, or (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software Foundation,
15 #  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
16 #
17 # ##### END GPL LICENSE BLOCK #####
18
19 # <pep8 compliant>
20 import bpy
21 from bpy.types import Menu, Panel, UIList
22 from rna_prop_ui import PropertyPanel
23 from bpy_extras.node_utils import find_node_input
24
25
26 class MATERIAL_MT_context_menu(Menu):
27     bl_label = "Material Specials"
28
29     def draw(self, context):
30         layout = self.layout
31
32         layout.operator("material.copy", icon='COPYDOWN')
33         layout.operator("object.material_slot_copy")
34         layout.operator("material.paste", icon='PASTEDOWN')
35
36
37 class MATERIAL_UL_matslots(UIList):
38
39     def draw_item(self, context, layout, data, item, icon, active_data, active_propname, index):
40         # assert(isinstance(item, bpy.types.MaterialSlot)
41         # ob = data
42         slot = item
43         ma = slot.material
44         if self.layout_type in {'DEFAULT', 'COMPACT'}:
45             if ma:
46                 layout.prop(ma, "name", text="", emboss=False, icon_value=icon)
47             else:
48                 layout.label(text="", icon_value=icon)
49         elif self.layout_type == 'GRID':
50             layout.alignment = 'CENTER'
51             layout.label(text="", icon_value=icon)
52
53
54 class MaterialButtonsPanel:
55     bl_space_type = 'PROPERTIES'
56     bl_region_type = 'WINDOW'
57     bl_context = "material"
58     # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
59
60     @classmethod
61     def poll(cls, context):
62         mat = context.material
63         return mat and (context.engine in cls.COMPAT_ENGINES) and not mat.grease_pencil
64
65
66 class MATERIAL_PT_preview(MaterialButtonsPanel, Panel):
67     bl_label = "Preview"
68     bl_options = {'DEFAULT_CLOSED'}
69     COMPAT_ENGINES = {'BLENDER_EEVEE'}
70
71     def draw(self, context):
72         self.layout.template_preview(context.material)
73
74
75 class MATERIAL_PT_custom_props(MaterialButtonsPanel, PropertyPanel, Panel):
76     COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
77     _context_path = "material"
78     _property_type = bpy.types.Material
79
80
81 class EEVEE_MATERIAL_PT_context_material(MaterialButtonsPanel, Panel):
82     bl_label = ""
83     bl_context = "material"
84     bl_options = {'HIDE_HEADER'}
85     COMPAT_ENGINES = {'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
86
87     @classmethod
88     def poll(cls, context):
89         ob = context.object
90         mat = context.material
91
92         if (ob and ob.type == 'GPENCIL') or (mat and mat.grease_pencil):
93             return False
94
95         return (ob or mat) and (context.engine in cls.COMPAT_ENGINES)
96
97     def draw(self, context):
98         layout = self.layout
99
100         mat = context.material
101         ob = context.object
102         slot = context.material_slot
103         space = context.space_data
104
105         if ob:
106             is_sortable = len(ob.material_slots) > 1
107             rows = 3
108             if (is_sortable):
109                 rows = 5
110
111             row = layout.row()
112
113             row.template_list("MATERIAL_UL_matslots", "", ob, "material_slots", ob, "active_material_index", rows=rows)
114
115             col = row.column(align=True)
116             col.operator("object.material_slot_add", icon='ADD', text="")
117             col.operator("object.material_slot_remove", icon='REMOVE', text="")
118
119             col.separator()
120
121             col.menu("MATERIAL_MT_context_menu", icon='DOWNARROW_HLT', text="")
122
123             if is_sortable:
124                 col.separator()
125
126                 col.operator("object.material_slot_move", icon='TRIA_UP', text="").direction = 'UP'
127                 col.operator("object.material_slot_move", icon='TRIA_DOWN', text="").direction = 'DOWN'
128
129         row = layout.row()
130
131         if ob:
132             row.template_ID(ob, "active_material", new="material.new")
133
134             if slot:
135                 icon_link = 'MESH_DATA' if slot.link == 'DATA' else 'OBJECT_DATA'
136                 row.prop(slot, "link", icon=icon_link, icon_only=True)
137
138             if ob.mode == 'EDIT':
139                 row = layout.row(align=True)
140                 row.operator("object.material_slot_assign", text="Assign")
141                 row.operator("object.material_slot_select", text="Select")
142                 row.operator("object.material_slot_deselect", text="Deselect")
143
144         elif mat:
145             row.template_ID(space, "pin_id")
146
147
148 def panel_node_draw(layout, ntree, output_type, input_name):
149     node = ntree.get_output_node('EEVEE')
150
151     if node:
152         input = find_node_input(node, input_name)
153         if input:
154             layout.template_node_view(ntree, node, input)
155         else:
156             layout.label(text="Incompatible output node")
157     else:
158         layout.label(text="No output node")
159
160
161 class EEVEE_MATERIAL_PT_surface(MaterialButtonsPanel, Panel):
162     bl_label = "Surface"
163     bl_context = "material"
164     COMPAT_ENGINES = {'BLENDER_EEVEE'}
165
166     def draw(self, context):
167         layout = self.layout
168
169         mat = context.material
170
171         layout.prop(mat, "use_nodes", icon='NODETREE')
172         layout.separator()
173
174         if mat.use_nodes:
175             panel_node_draw(layout, mat.node_tree, 'OUTPUT_MATERIAL', "Surface")
176         else:
177             layout.use_property_split = True
178             layout.prop(mat, "diffuse_color", text="Base Color")
179             layout.prop(mat, "metallic")
180             layout.prop(mat, "specular_intensity", text="Specular")
181             layout.prop(mat, "roughness")
182
183
184 class EEVEE_MATERIAL_PT_volume(MaterialButtonsPanel, Panel):
185     bl_label = "Volume"
186     bl_context = "material"
187     bl_options = {'DEFAULT_CLOSED'}
188     COMPAT_ENGINES = {'BLENDER_EEVEE'}
189
190     @classmethod
191     def poll(cls, context):
192         engine = context.engine
193         mat = context.material
194         return mat and mat.use_nodes and (engine in cls.COMPAT_ENGINES) and not mat.grease_pencil
195
196     def draw(self, context):
197         layout = self.layout
198
199         mat = context.material
200
201         panel_node_draw(layout, mat.node_tree, 'OUTPUT_MATERIAL', "Volume")
202
203
204 class EEVEE_MATERIAL_PT_settings(MaterialButtonsPanel, Panel):
205     bl_label = "Settings"
206     bl_context = "material"
207     COMPAT_ENGINES = {'BLENDER_EEVEE'}
208
209     @staticmethod
210     def draw_shared(self, mat):
211         layout = self.layout
212         layout.use_property_split = True
213
214         layout.prop(mat, "blend_method")
215
216         if mat.blend_method != 'OPAQUE':
217             layout.prop(mat, "transparent_shadow_method")
218
219             row = layout.row()
220             row.active = ((mat.blend_method == 'CLIP') or (mat.transparent_shadow_method == 'CLIP'))
221             row.prop(mat, "alpha_threshold")
222
223         if mat.blend_method not in {'OPAQUE', 'CLIP', 'HASHED'}:
224             layout.prop(mat, "show_transparent_back")
225
226         layout.prop(mat, "use_screen_refraction")
227         layout.prop(mat, "refraction_depth")
228         layout.prop(mat, "use_sss_translucency")
229         layout.prop(mat, "pass_index")
230
231     def draw(self, context):
232         self.draw_shared(self, context.material)
233
234
235 class MATERIAL_PT_viewport(MaterialButtonsPanel, Panel):
236     bl_label = "Viewport Display"
237     bl_context = "material"
238     bl_options = {'DEFAULT_CLOSED'}
239
240     @classmethod
241     def poll(cls, context):
242         mat = context.material
243         return mat and not mat.grease_pencil
244
245     @staticmethod
246     def draw_shared(self, mat):
247         layout = self.layout
248         layout.use_property_split = True
249
250         col = layout.column()
251         col.prop(mat, "diffuse_color", text="Color")
252         col.prop(mat, "metallic")
253         col.prop(mat, "roughness")
254
255     def draw(self, context):
256         self.draw_shared(self, context.material)
257
258
259 classes = (
260     MATERIAL_MT_context_menu,
261     MATERIAL_UL_matslots,
262     MATERIAL_PT_preview,
263     EEVEE_MATERIAL_PT_context_material,
264     EEVEE_MATERIAL_PT_surface,
265     EEVEE_MATERIAL_PT_volume,
266     EEVEE_MATERIAL_PT_settings,
267     MATERIAL_PT_viewport,
268     MATERIAL_PT_custom_props,
269 )
270
271
272 if __name__ == "__main__":  # only for live edit.
273     from bpy.utils import register_class
274     for cls in classes:
275         register_class(cls)