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>
20import bpy
21from bpy.types import Panel
22from rna_prop_ui import PropertyPanel
23
24
25class DataButtonsPanel:
26    bl_space_type = 'PROPERTIES'
27    bl_region_type = 'WINDOW'
28    bl_context = "data"
29
30    @classmethod
31    def poll(cls, context):
32        engine = context.engine
33        return context.light and (engine in cls.COMPAT_ENGINES)
34
35
36class DATA_PT_context_light(DataButtonsPanel, Panel):
37    bl_label = ""
38    bl_options = {'HIDE_HEADER'}
39    COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
40
41    def draw(self, context):
42        layout = self.layout
43
44        ob = context.object
45        light = context.light
46        space = context.space_data
47
48        if ob:
49            layout.template_ID(ob, "data")
50        elif light:
51            layout.template_ID(space, "pin_id")
52
53
54class DATA_PT_preview(DataButtonsPanel, Panel):
55    bl_label = "Preview"
56    bl_options = {'DEFAULT_CLOSED'}
57    COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
58
59    def draw(self, context):
60        self.layout.template_preview(context.light)
61
62
63class DATA_PT_light(DataButtonsPanel, Panel):
64    bl_label = "Light"
65    COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_WORKBENCH'}
66
67    def draw(self, context):
68        layout = self.layout
69
70        light = context.light
71
72        # Compact layout for node editor.
73        if self.bl_space_type == 'PROPERTIES':
74            layout.row().prop(light, "type", expand=True)
75            layout.use_property_split = True
76        else:
77            layout.use_property_split = True
78            layout.row().prop(light, "type")
79
80
81class DATA_PT_EEVEE_light(DataButtonsPanel, Panel):
82    bl_label = "Light"
83    COMPAT_ENGINES = {'BLENDER_EEVEE'}
84
85    def draw(self, context):
86        layout = self.layout
87        light = context.light
88
89        # Compact layout for node editor.
90        if self.bl_space_type == 'PROPERTIES':
91            layout.row().prop(light, "type", expand=True)
92            layout.use_property_split = True
93        else:
94            layout.use_property_split = True
95            layout.row().prop(light, "type")
96
97        col = layout.column()
98        col.prop(light, "color")
99        col.prop(light, "energy")
100        col.prop(light, "specular_factor", text="Specular")
101
102        col.separator()
103
104        if light.type in {'POINT', 'SPOT'}:
105            col.prop(light, "shadow_soft_size", text="Radius")
106        elif light.type == 'SUN':
107            col.prop(light, "angle")
108        elif light.type == 'AREA':
109            col.prop(light, "shape")
110
111            sub = col.column(align=True)
112
113            if light.shape in {'SQUARE', 'DISK'}:
114                sub.prop(light, "size")
115            elif light.shape in {'RECTANGLE', 'ELLIPSE'}:
116                sub.prop(light, "size", text="Size X")
117                sub.prop(light, "size_y", text="Y")
118
119
120class DATA_PT_EEVEE_light_distance(DataButtonsPanel, Panel):
121    bl_label = "Custom Distance"
122    bl_parent_id = "DATA_PT_EEVEE_light"
123    bl_options = {'DEFAULT_CLOSED'}
124    COMPAT_ENGINES = {'BLENDER_EEVEE'}
125
126    @classmethod
127    def poll(cls, context):
128        light = context.light
129        engine = context.engine
130
131        return (light and light.type != 'SUN') and (engine in cls.COMPAT_ENGINES)
132
133    def draw_header(self, context):
134        light = context.light
135
136        layout = self.layout
137        layout.prop(light, "use_custom_distance", text="")
138
139    def draw(self, context):
140        layout = self.layout
141        light = context.light
142        layout.active = light.use_custom_distance
143        layout.use_property_split = True
144
145        layout.prop(light, "cutoff_distance", text="Distance")
146
147
148class DATA_PT_EEVEE_shadow(DataButtonsPanel, Panel):
149    bl_label = "Shadow"
150    bl_options = {'DEFAULT_CLOSED'}
151    COMPAT_ENGINES = {'BLENDER_EEVEE'}
152
153    @classmethod
154    def poll(cls, context):
155        light = context.light
156        engine = context.engine
157        return (
158            (light and light.type in {'POINT', 'SUN', 'SPOT', 'AREA'}) and
159            (engine in cls.COMPAT_ENGINES)
160        )
161
162    def draw_header(self, context):
163        light = context.light
164        self.layout.prop(light, "use_shadow", text="")
165
166    def draw(self, context):
167        layout = self.layout
168        layout.use_property_split = True
169
170        light = context.light
171
172        layout.active = light.use_shadow
173
174        col = layout.column()
175        sub = col.column(align=True)
176        if light.type != 'SUN':
177            sub.prop(light, "shadow_buffer_clip_start", text="Clip Start")
178
179        col.prop(light, "shadow_buffer_bias", text="Bias")
180
181
182class DATA_PT_EEVEE_shadow_cascaded_shadow_map(DataButtonsPanel, Panel):
183    bl_label = "Cascaded Shadow Map"
184    bl_parent_id = "DATA_PT_EEVEE_shadow"
185    bl_options = {'DEFAULT_CLOSED'}
186    COMPAT_ENGINES = {'BLENDER_EEVEE'}
187
188    @classmethod
189    def poll(cls, context):
190        light = context.light
191        engine = context.engine
192
193        return (light and light.type == 'SUN') and (engine in cls.COMPAT_ENGINES)
194
195    def draw(self, context):
196        layout = self.layout
197        light = context.light
198        layout.use_property_split = True
199
200        col = layout.column()
201
202        col.prop(light, "shadow_cascade_count", text="Count")
203        col.prop(light, "shadow_cascade_fade", text="Fade")
204
205        col.prop(light, "shadow_cascade_max_distance", text="Max Distance")
206        col.prop(light, "shadow_cascade_exponent", text="Distribution")
207
208
209class DATA_PT_EEVEE_shadow_contact(DataButtonsPanel, Panel):
210    bl_label = "Contact Shadows"
211    bl_parent_id = "DATA_PT_EEVEE_shadow"
212    COMPAT_ENGINES = {'BLENDER_EEVEE'}
213
214    @classmethod
215    def poll(cls, context):
216        light = context.light
217        engine = context.engine
218        return (
219            (light and light.type in {'POINT', 'SUN', 'SPOT', 'AREA'}) and
220            (engine in cls.COMPAT_ENGINES)
221        )
222
223    def draw_header(self, context):
224        light = context.light
225
226        layout = self.layout
227        layout.active = light.use_shadow
228        layout.prop(light, "use_contact_shadow", text="")
229
230    def draw(self, context):
231        layout = self.layout
232        light = context.light
233        layout.use_property_split = True
234
235        col = layout.column()
236        col.active = light.use_shadow and light.use_contact_shadow
237
238        col.prop(light, "contact_shadow_distance", text="Distance")
239        col.prop(light, "contact_shadow_bias", text="Bias")
240        col.prop(light, "contact_shadow_thickness", text="Thickness")
241
242
243class DATA_PT_area(DataButtonsPanel, Panel):
244    bl_label = "Area Shape"
245    COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_WORKBENCH'}
246
247    @classmethod
248    def poll(cls, context):
249        light = context.light
250        engine = context.engine
251        return (light and light.type == 'AREA') and (engine in cls.COMPAT_ENGINES)
252
253    def draw(self, context):
254        layout = self.layout
255
256        light = context.light
257
258        col = layout.column()
259        col.row().prop(light, "shape", expand=True)
260        sub = col.row(align=True)
261
262        if light.shape in {'SQUARE', 'DISK'}:
263            sub.prop(light, "size")
264        elif light.shape in {'RECTANGLE', 'ELLIPSE'}:
265            sub.prop(light, "size", text="Size X")
266            sub.prop(light, "size_y", text="Size Y")
267
268
269class DATA_PT_spot(DataButtonsPanel, Panel):
270    bl_label = "Spot Shape"
271    bl_parent_id = "DATA_PT_EEVEE_light"
272    COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
273
274    @classmethod
275    def poll(cls, context):
276        light = context.light
277        engine = context.engine
278        return (light and light.type == 'SPOT') and (engine in cls.COMPAT_ENGINES)
279
280    def draw(self, context):
281        layout = self.layout
282        layout.use_property_split = True
283
284        light = context.light
285
286        col = layout.column()
287
288        col.prop(light, "spot_size", text="Size")
289        col.prop(light, "spot_blend", text="Blend", slider=True)
290
291        col.prop(light, "show_cone")
292
293
294class DATA_PT_falloff_curve(DataButtonsPanel, Panel):
295    bl_label = "Falloff Curve"
296    bl_options = {'DEFAULT_CLOSED'}
297    COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE'}
298
299    @classmethod
300    def poll(cls, context):
301        light = context.light
302        engine = context.engine
303
304        return (
305            (light and light.type in {'POINT', 'SPOT'} and light.falloff_type == 'CUSTOM_CURVE') and
306            (engine in cls.COMPAT_ENGINES)
307        )
308
309    def draw(self, context):
310        light = context.light
311
312        self.layout.template_curve_mapping(
313            light, "falloff_curve", use_negative_slope=True)
314
315
316class DATA_PT_custom_props_light(DataButtonsPanel, PropertyPanel, Panel):
317    COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
318    _context_path = "object.data"
319    _property_type = bpy.types.Light
320
321
322classes = (
323    DATA_PT_context_light,
324    DATA_PT_preview,
325    DATA_PT_light,
326    DATA_PT_EEVEE_light,
327    DATA_PT_EEVEE_light_distance,
328    DATA_PT_EEVEE_shadow,
329    DATA_PT_EEVEE_shadow_cascaded_shadow_map,
330    DATA_PT_EEVEE_shadow_contact,
331    DATA_PT_area,
332    DATA_PT_spot,
333    DATA_PT_falloff_curve,
334    DATA_PT_custom_props_light,
335)
336
337if __name__ == "__main__":  # only for live edit.
338    from bpy.utils import register_class
339    for cls in classes:
340        register_class(cls)
341