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