861ad81cdccd0c7b74951306a592c82bf51a754e
[blender.git] / release / scripts / startup / bl_ui / properties_data_lamp.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
22 from rna_prop_ui import PropertyPanel
23
24
25 class LAMP_MT_sunsky_presets(Menu):
26     bl_label = "Sun & Sky Presets"
27     preset_subdir = "sunsky"
28     preset_operator = "script.execute_preset"
29     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME', 'BLENDER_CLAY', 'BLENDER_EEVEE'}
30     draw = Menu.draw_preset
31
32
33 class DataButtonsPanel:
34     bl_space_type = 'PROPERTIES'
35     bl_region_type = 'WINDOW'
36     bl_context = "data"
37
38     @classmethod
39     def poll(cls, context):
40         engine = context.scene.render.engine
41         return context.lamp and (engine in cls.COMPAT_ENGINES)
42
43
44 class DATA_PT_context_lamp(DataButtonsPanel, Panel):
45     bl_label = ""
46     bl_options = {'HIDE_HEADER'}
47     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME', 'BLENDER_CLAY', 'BLENDER_EEVEE'}
48
49     def draw(self, context):
50         layout = self.layout
51
52         ob = context.object
53         lamp = context.lamp
54         space = context.space_data
55
56         split = layout.split(percentage=0.65)
57
58         texture_count = len(lamp.texture_slots.keys())
59
60         if ob:
61             split.template_ID(ob, "data")
62         elif lamp:
63             split.template_ID(space, "pin_id")
64
65         if texture_count != 0:
66             split.label(text=str(texture_count), icon='TEXTURE')
67
68
69 class DATA_PT_preview(DataButtonsPanel, Panel):
70     bl_label = "Preview"
71     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME', 'BLENDER_CLAY', 'BLENDER_EEVEE'}
72
73     def draw(self, context):
74         self.layout.template_preview(context.lamp)
75
76
77 class DATA_PT_lamp(DataButtonsPanel, Panel):
78     bl_label = "Lamp"
79     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME', 'BLENDER_CLAY'}
80
81     def draw(self, context):
82         layout = self.layout
83
84         lamp = context.lamp
85
86         layout.row().prop(lamp, "type", expand=True)
87
88         split = layout.split()
89
90         col = split.column()
91         sub = col.column()
92         sub.prop(lamp, "color", text="")
93         sub.prop(lamp, "energy")
94
95         if lamp.type in {'POINT', 'SPOT'}:
96             sub.label(text="Falloff:")
97             sub.prop(lamp, "falloff_type", text="")
98             sub.prop(lamp, "distance")
99             sub.prop(lamp, "shadow_soft_size", text="Radius")
100
101             if lamp.falloff_type == 'LINEAR_QUADRATIC_WEIGHTED':
102                 col.label(text="Attenuation Factors:")
103                 sub = col.column(align=True)
104                 sub.prop(lamp, "linear_attenuation", slider=True, text="Linear")
105                 sub.prop(lamp, "quadratic_attenuation", slider=True, text="Quadratic")
106
107             elif lamp.falloff_type == 'INVERSE_COEFFICIENTS':
108                 col.label(text="Inverse Coefficients:")
109                 sub = col.column(align=True)
110                 sub.prop(lamp, "constant_coefficient", text="Constant")
111                 sub.prop(lamp, "linear_coefficient", text="Linear")
112                 sub.prop(lamp, "quadratic_coefficient", text="Quadratic")
113
114             col.prop(lamp, "use_sphere")
115
116         if lamp.type == 'AREA':
117             col.prop(lamp, "distance")
118             col.prop(lamp, "gamma")
119
120         col = split.column()
121         col.prop(lamp, "use_negative")
122         col.prop(lamp, "use_own_layer", text="This Layer Only")
123         col.prop(lamp, "use_specular")
124         col.prop(lamp, "use_diffuse")
125
126
127 class DATA_PT_EEVEE_lamp(DataButtonsPanel, Panel):
128     bl_label = "Lamp"
129     COMPAT_ENGINES = {'BLENDER_EEVEE'}
130
131     def draw(self, context):
132         layout = self.layout
133
134         lamp = context.lamp
135
136         layout.row().prop(lamp, "type", expand=True)
137
138         split = layout.split()
139
140         col = split.column()
141         sub = col.column()
142         sub.prop(lamp, "color", text="")
143         sub.prop(lamp, "energy")
144
145         if lamp.type in {'POINT', 'SPOT', 'SUN'}:
146             sub.prop(lamp, "shadow_soft_size", text="Radius")
147         elif lamp.type == 'AREA':
148             sub = sub.column(align=True)
149             sub.prop(lamp, "shape", text="")
150             if lamp.shape == 'SQUARE':
151                 sub.prop(lamp, "size")
152             elif lamp.shape == 'RECTANGLE':
153                 sub.prop(lamp, "size", text="Size X")
154                 sub.prop(lamp, "size_y", text="Size Y")
155
156         col = split.column()
157         col.prop(lamp, "use_specular")
158         col.prop(lamp, "use_diffuse")
159         col.separator()
160
161         if lamp.type in {'POINT', 'SPOT', 'AREA'}:
162             col.prop(lamp, "use_sphere")
163             col = col.column()
164             col.active = lamp.use_sphere
165             col.prop(lamp, "distance")
166
167
168 class DATA_PT_sunsky(DataButtonsPanel, Panel):
169     bl_label = "Sky & Atmosphere"
170     COMPAT_ENGINES = {'BLENDER_RENDER'}
171
172     @classmethod
173     def poll(cls, context):
174         lamp = context.lamp
175         engine = context.scene.render.engine
176         return (lamp and lamp.type == 'SUN') and (engine in cls.COMPAT_ENGINES)
177
178     def draw(self, context):
179         layout = self.layout
180
181         lamp = context.lamp.sky
182
183         row = layout.row(align=True)
184         row.prop(lamp, "use_sky")
185         row.menu("LAMP_MT_sunsky_presets", text=bpy.types.LAMP_MT_sunsky_presets.bl_label)
186         row.operator("lamp.sunsky_preset_add", text="", icon='ZOOMIN')
187         row.operator("lamp.sunsky_preset_add", text="", icon='ZOOMOUT').remove_active = True
188
189         row = layout.row()
190         row.active = lamp.use_sky or lamp.use_atmosphere
191         row.prop(lamp, "atmosphere_turbidity", text="Turbidity")
192
193         split = layout.split()
194
195         col = split.column()
196         col.active = lamp.use_sky
197         col.label(text="Blending:")
198         sub = col.column()
199         sub.prop(lamp, "sky_blend_type", text="")
200         sub.prop(lamp, "sky_blend", text="Factor")
201
202         col.label(text="Color Space:")
203         sub = col.column()
204         sub.row().prop(lamp, "sky_color_space", expand=True)
205         sub.prop(lamp, "sky_exposure", text="Exposure")
206
207         col = split.column()
208         col.active = lamp.use_sky
209         col.label(text="Horizon:")
210         sub = col.column()
211         sub.prop(lamp, "horizon_brightness", text="Brightness")
212         sub.prop(lamp, "spread", text="Spread")
213
214         col.label(text="Sun:")
215         sub = col.column()
216         sub.prop(lamp, "sun_brightness", text="Brightness")
217         sub.prop(lamp, "sun_size", text="Size")
218         sub.prop(lamp, "backscattered_light", slider=True, text="Back Light")
219
220         layout.separator()
221
222         layout.prop(lamp, "use_atmosphere")
223
224         split = layout.split()
225
226         col = split.column()
227         col.active = lamp.use_atmosphere
228         col.label(text="Intensity:")
229         col.prop(lamp, "sun_intensity", text="Sun")
230         col.prop(lamp, "atmosphere_distance_factor", text="Distance")
231
232         col = split.column()
233         col.active = lamp.use_atmosphere
234         col.label(text="Scattering:")
235         sub = col.column(align=True)
236         sub.prop(lamp, "atmosphere_inscattering", slider=True, text="Inscattering")
237         sub.prop(lamp, "atmosphere_extinction", slider=True, text="Extinction")
238
239
240 class DATA_PT_shadow(DataButtonsPanel, Panel):
241     bl_label = "Shadow"
242     COMPAT_ENGINES = {'BLENDER_RENDER'}
243
244     @classmethod
245     def poll(cls, context):
246         lamp = context.lamp
247         engine = context.scene.render.engine
248         return (lamp and lamp.type in {'POINT', 'SUN', 'SPOT', 'AREA'}) and (engine in cls.COMPAT_ENGINES)
249
250     def draw(self, context):
251         layout = self.layout
252
253         lamp = context.lamp
254
255         layout.row().prop(lamp, "shadow_method", expand=True)
256
257         if lamp.shadow_method == 'NOSHADOW' and lamp.type == 'AREA':
258             split = layout.split()
259
260             col = split.column()
261             col.label(text="Form Factor Sampling:")
262
263             sub = col.row(align=True)
264
265             if lamp.shape == 'SQUARE':
266                 sub.prop(lamp, "shadow_ray_samples_x", text="Samples")
267             elif lamp.shape == 'RECTANGLE':
268                 sub.prop(lamp, "shadow_ray_samples_x", text="Samples X")
269                 sub.prop(lamp, "shadow_ray_samples_y", text="Samples Y")
270
271         if lamp.shadow_method != 'NOSHADOW':
272             split = layout.split()
273
274             col = split.column()
275             col.prop(lamp, "shadow_color", text="")
276
277             col = split.column()
278             col.prop(lamp, "use_shadow_layer", text="This Layer Only")
279             col.prop(lamp, "use_only_shadow")
280
281         if lamp.shadow_method == 'RAY_SHADOW':
282             split = layout.split()
283
284             col = split.column()
285             col.label(text="Sampling:")
286
287             if lamp.type in {'POINT', 'SUN', 'SPOT'}:
288                 sub = col.row()
289
290                 sub.prop(lamp, "shadow_ray_samples", text="Samples")
291                 sub.prop(lamp, "shadow_soft_size", text="Soft Size")
292
293             elif lamp.type == 'AREA':
294                 sub = col.row(align=True)
295
296                 if lamp.shape == 'SQUARE':
297                     sub.prop(lamp, "shadow_ray_samples_x", text="Samples")
298                 elif lamp.shape == 'RECTANGLE':
299                     sub.prop(lamp, "shadow_ray_samples_x", text="Samples X")
300                     sub.prop(lamp, "shadow_ray_samples_y", text="Samples Y")
301
302             col.row().prop(lamp, "shadow_ray_sample_method", expand=True)
303
304             if lamp.shadow_ray_sample_method == 'ADAPTIVE_QMC':
305                 layout.prop(lamp, "shadow_adaptive_threshold", text="Threshold")
306
307             if lamp.type == 'AREA' and lamp.shadow_ray_sample_method == 'CONSTANT_JITTERED':
308                 row = layout.row()
309                 row.prop(lamp, "use_umbra")
310                 row.prop(lamp, "use_dither")
311                 row.prop(lamp, "use_jitter")
312
313         elif lamp.shadow_method == 'BUFFER_SHADOW':
314             col = layout.column()
315             col.label(text="Buffer Type:")
316             col.row().prop(lamp, "shadow_buffer_type", expand=True)
317
318             if lamp.shadow_buffer_type in {'REGULAR', 'HALFWAY', 'DEEP'}:
319                 split = layout.split()
320
321                 col = split.column()
322                 col.label(text="Filter Type:")
323                 col.prop(lamp, "shadow_filter_type", text="")
324                 sub = col.column(align=True)
325                 sub.prop(lamp, "shadow_buffer_soft", text="Soft")
326                 sub.prop(lamp, "shadow_buffer_bias", text="Bias")
327
328                 col = split.column()
329                 col.label(text="Sample Buffers:")
330                 col.prop(lamp, "shadow_sample_buffers", text="")
331                 sub = col.column(align=True)
332                 sub.prop(lamp, "shadow_buffer_size", text="Size")
333                 sub.prop(lamp, "shadow_buffer_samples", text="Samples")
334                 if lamp.shadow_buffer_type == 'DEEP':
335                     col.prop(lamp, "compression_threshold")
336
337             elif lamp.shadow_buffer_type == 'IRREGULAR':
338                 layout.prop(lamp, "shadow_buffer_bias", text="Bias")
339
340             split = layout.split()
341
342             col = split.column()
343             col.prop(lamp, "use_auto_clip_start", text="Autoclip Start")
344             sub = col.column()
345             sub.active = not lamp.use_auto_clip_start
346             sub.prop(lamp, "shadow_buffer_clip_start", text="Clip Start")
347
348             col = split.column()
349             col.prop(lamp, "use_auto_clip_end", text="Autoclip End")
350             sub = col.column()
351             sub.active = not lamp.use_auto_clip_end
352             sub.prop(lamp, "shadow_buffer_clip_end", text="Clip End")
353
354
355 class DATA_PT_EEVEE_shadow(DataButtonsPanel, Panel):
356     bl_label = "Shadow"
357     COMPAT_ENGINES = {'BLENDER_EEVEE'}
358
359     @classmethod
360     def poll(cls, context):
361         lamp = context.lamp
362         engine = context.scene.render.engine
363         return (lamp and lamp.type in {'POINT', 'SUN', 'SPOT', 'AREA'}) and (engine in cls.COMPAT_ENGINES)
364
365     def draw_header(self, context):
366         lamp = context.lamp
367         self.layout.prop(lamp, "use_shadow", text="")
368
369     def draw(self, context):
370         layout = self.layout
371
372         lamp = context.lamp
373
374         split = layout.split()
375         split.active = lamp.use_shadow
376
377         sub = split.column()
378         col = sub.column(align=True)
379         col.prop(lamp, "shadow_buffer_clip_start", text="Clip Start")
380         col.prop(lamp, "shadow_buffer_clip_end", text="Clip End")
381         col = sub.column()
382         col.prop(lamp, "shadow_buffer_soft", text="Soft")
383
384         col = split.column(align=True)
385         col.prop(lamp, "shadow_buffer_bias", text="Bias")
386         col.prop(lamp, "shadow_buffer_exp", text="Exponent")
387         col.prop(lamp, "shadow_buffer_bleed_bias", text="Bleed Bias")
388
389         if lamp.type == 'SUN':
390             col = layout.column()
391             col.active = lamp.use_shadow
392             col.label("Cascaded Shadow Map:")
393
394             split = col.split()
395
396             sub = split.column()
397             sub.prop(lamp, "shadow_cascade_count", text="Count")
398             sub.prop(lamp, "shadow_cascade_fade", text="Fade")
399
400             sub = split.column()
401             sub.prop(lamp, "shadow_cascade_max_distance", text="Max Distance")
402             sub.prop(lamp, "shadow_cascade_exponent", text="Distribution")
403
404
405 class DATA_PT_area(DataButtonsPanel, Panel):
406     bl_label = "Area Shape"
407     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME', 'BLENDER_CLAY'}
408
409     @classmethod
410     def poll(cls, context):
411         lamp = context.lamp
412         engine = context.scene.render.engine
413         return (lamp and lamp.type == 'AREA') and (engine in cls.COMPAT_ENGINES)
414
415     def draw(self, context):
416         layout = self.layout
417
418         lamp = context.lamp
419
420         col = layout.column()
421         col.row().prop(lamp, "shape", expand=True)
422         sub = col.row(align=True)
423
424         if lamp.shape == 'SQUARE':
425             sub.prop(lamp, "size")
426         elif lamp.shape == 'RECTANGLE':
427             sub.prop(lamp, "size", text="Size X")
428             sub.prop(lamp, "size_y", text="Size Y")
429
430
431 class DATA_PT_spot(DataButtonsPanel, Panel):
432     bl_label = "Spot Shape"
433     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME', 'BLENDER_CLAY'}
434
435     @classmethod
436     def poll(cls, context):
437         lamp = context.lamp
438         engine = context.scene.render.engine
439         return (lamp and lamp.type == 'SPOT') and (engine in cls.COMPAT_ENGINES)
440
441     def draw(self, context):
442         layout = self.layout
443
444         lamp = context.lamp
445
446         split = layout.split()
447
448         col = split.column()
449         sub = col.column()
450         sub.prop(lamp, "spot_size", text="Size")
451         sub.prop(lamp, "spot_blend", text="Blend", slider=True)
452         col.prop(lamp, "use_square")
453         col.prop(lamp, "show_cone")
454
455         col = split.column()
456
457         col.active = (lamp.shadow_method != 'BUFFER_SHADOW' or lamp.shadow_buffer_type != 'DEEP')
458         col.prop(lamp, "use_halo")
459         sub = col.column(align=True)
460         sub.active = lamp.use_halo
461         sub.prop(lamp, "halo_intensity", text="Intensity")
462         if lamp.shadow_method == 'BUFFER_SHADOW':
463             sub.prop(lamp, "halo_step", text="Step")
464
465
466 class DATA_PT_spot(DataButtonsPanel, Panel):
467     bl_label = "Spot Shape"
468     COMPAT_ENGINES = {'BLENDER_EEVEE'}
469
470     @classmethod
471     def poll(cls, context):
472         lamp = context.lamp
473         engine = context.scene.render.engine
474         return (lamp and lamp.type == 'SPOT') and (engine in cls.COMPAT_ENGINES)
475
476     def draw(self, context):
477         layout = self.layout
478
479         lamp = context.lamp
480
481         split = layout.split()
482
483         col = split.column()
484         sub = col.column()
485         sub.prop(lamp, "spot_size", text="Size")
486         sub.prop(lamp, "spot_blend", text="Blend", slider=True)
487         col = split.column()
488         col.prop(lamp, "show_cone")
489
490
491 class DATA_PT_falloff_curve(DataButtonsPanel, Panel):
492     bl_label = "Falloff Curve"
493     bl_options = {'DEFAULT_CLOSED'}
494     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME', 'BLENDER_CLAY', 'BLENDER_EEVEE'}
495
496     @classmethod
497     def poll(cls, context):
498         lamp = context.lamp
499         engine = context.scene.render.engine
500
501         return (lamp and lamp.type in {'POINT', 'SPOT'} and lamp.falloff_type == 'CUSTOM_CURVE') and (engine in cls.COMPAT_ENGINES)
502
503     def draw(self, context):
504         lamp = context.lamp
505
506         self.layout.template_curve_mapping(lamp, "falloff_curve", use_negative_slope=True)
507
508
509 class DATA_PT_custom_props_lamp(DataButtonsPanel, PropertyPanel, Panel):
510     COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_GAME', 'BLENDER_CLAY', 'BLENDER_EEVEE'}
511     _context_path = "object.data"
512     _property_type = bpy.types.Lamp
513
514
515 classes = (
516     LAMP_MT_sunsky_presets,
517     DATA_PT_context_lamp,
518     DATA_PT_preview,
519     DATA_PT_lamp,
520     DATA_PT_EEVEE_lamp,
521     DATA_PT_sunsky,
522     DATA_PT_shadow,
523     DATA_PT_EEVEE_shadow,
524     DATA_PT_area,
525     DATA_PT_spot,
526     DATA_PT_falloff_curve,
527     DATA_PT_custom_props_lamp,
528 )
529
530 if __name__ == "__main__":  # only for live edit.
531     from bpy.utils import register_class
532     for cls in classes:
533         register_class(cls)