glTF exporter: fix division by zero in some specular approximation
[blender-addons.git] / lighting_dynamic_sky.py
blob5be1a438caf4301349378511fb6c1e34424ca98e
1 # SPDX-License-Identifier: GPL-2.0-or-later
2 # Copyright 2015 Pratik Solanki (Draguu)
4 bl_info = {
5 "name": "Dynamic Sky",
6 "author": "Pratik Solanki",
7 "version": (1, 0, 6),
8 "blender": (2, 80, 0),
9 "location": "View3D > Sidebar > Create Tab",
10 "description": "Creates Dynamic Sky for Cycles",
11 "warning": "",
12 "doc_url": "{BLENDER_MANUAL_URL}/addons/lighting/dynamic_sky.html",
13 "category": "Lighting",
16 import bpy
17 from bpy.props import StringProperty
18 from bpy.types import (
19 Operator,
20 Panel,
24 # Handle error notifications
25 def error_handlers(self, error, reports="ERROR"):
26 if self and reports:
27 self.report({'WARNING'}, reports + " (See Console for more info)")
29 print("\n[Dynamic Sky]\nError: {}\n".format(error))
32 def check_world_name(name_id="Dynamic"):
33 # check if the new name pattern is in world data
34 name_list = []
35 suffix = 1
36 try:
37 name_list = [world.name for world in bpy.data.worlds if name_id in world.name]
38 new_name = "{}_{}".format(name_id, len(name_list) + suffix)
39 if new_name in name_list:
40 # KISS failed - numbering is not sequential
41 # try harvesting numbers in world names, find the rightmost ones
42 test_num = []
43 from re import findall
44 for words in name_list:
45 test_num.append(findall(r"\d+", words))
47 suffix += max([int(l[-1]) for l in test_num])
48 new_name = "{}_{}".format(name_id, suffix)
49 return new_name
50 except Exception as e:
51 error_handlers(False, e)
52 pass
53 return name_id
56 def check_cycles():
57 return ('cycles' in bpy.context.preferences.addons.keys())
60 class dsky(Operator):
61 bl_idname = "sky.dyn"
62 bl_label = "Make a Procedural sky"
63 bl_description = ("Make a Procedural Sky with parameters in the 3D View\n"
64 "Note: Available just for Cycles renderer\n"
65 "Only the last created Dynamic World can be accessed from this panel")
67 @classmethod
68 def poll(cls, context):
69 return check_cycles()
71 def get_node_types(self, node_tree, node_type):
72 for node in node_tree.nodes:
73 if node.type == node_type:
74 return node
75 return None
77 def execute(self, context):
78 try:
79 get_name = check_world_name()
80 context.scene.dynamic_sky_name = get_name
81 bpy.context.scene.render.engine = 'CYCLES'
83 world = bpy.data.worlds.new(get_name)
84 world.cycles.sample_as_light = True
85 world.cycles.sample_map_resolution = 2048
86 world.use_nodes = True
88 nt = world.node_tree
89 # Note: (see T52714) to avoid string localization problems, assign the name for
90 # nodes that will be exposed in the 3D view (pattern UI name with underscore)
91 bg = self.get_node_types(nt, "BACKGROUND")
92 bg.name = "Scene_Brightness"
93 bg.inputs[0].default_value[:3] = (0.5, .1, 0.6)
94 bg.inputs[1].default_value = 1
95 bg.location = (6708.3, 360)
97 ntl = nt.links.new
98 tcor = nt.nodes.new(type="ShaderNodeTexCoord")
99 tcor.location = (243.729, 1005)
101 map1 = nt.nodes.new(type="ShaderNodeMapping")
102 map1.vector_type = 'NORMAL'
103 map1.location = (786.54, 730)
105 nor = nt.nodes.new(type="ShaderNodeNormal")
106 nor.name = "Sky_normal"
107 nor.location = (1220.16, 685)
109 cr1 = nt.nodes.new(type="ShaderNodeValToRGB")
110 cr1.color_ramp.elements[0].position = 0.969
111 cr1.color_ramp.interpolation = 'EASE'
112 cr1.location = (1671.33, 415)
113 cr2 = nt.nodes.new(type="ShaderNodeValToRGB")
114 cr2.color_ramp.elements[0].position = 0.991
115 cr2.color_ramp.elements[1].position = 1
116 cr2.color_ramp.interpolation = 'EASE'
117 cr2.location = (2196.6, 415)
118 cr3 = nt.nodes.new(type="ShaderNodeValToRGB")
119 cr3.color_ramp.elements[0].position = 0.779
120 cr3.color_ramp.elements[1].position = 1
121 cr3.color_ramp.interpolation = 'EASE'
122 cr3.location = (2196.6, 415)
124 mat1 = nt.nodes.new(type="ShaderNodeMath")
125 mat1.operation = 'MULTIPLY'
126 mat1.inputs[1].default_value = 0.2
127 mat1.location = (2196.6, 685)
128 mat2 = nt.nodes.new(type="ShaderNodeMath")
129 mat2.operation = 'MULTIPLY'
130 mat2.inputs[1].default_value = 2
131 mat2.location = (3294, 685)
132 mat3 = nt.nodes.new(type="ShaderNodeMath")
133 mat3.operation = 'MULTIPLY'
134 mat3.inputs[1].default_value = 40.9
135 mat3.location = (2745.24, 415)
136 mat4 = nt.nodes.new(type="ShaderNodeMath")
137 mat4.operation = 'SUBTRACT'
138 mat4.inputs[1].default_value = 1
139 mat4.location = (3294, 415)
140 ntl(mat2.inputs[0], mat1.outputs[0])
141 ntl(mat4.inputs[0], mat3.outputs[0])
142 ntl(mat1.inputs[0], cr3.outputs[0])
143 ntl(mat3.inputs[0], cr2.outputs[0])
145 soft = nt.nodes.new(type="ShaderNodeMixRGB")
146 soft.name = "Soft_hard"
147 soft.location = (3819.3, 550)
148 soft_1 = nt.nodes.new(type="ShaderNodeMixRGB")
149 soft_1.location = (3819.3, 185)
150 soft.inputs[0].default_value = 1
151 soft_1.inputs[0].default_value = 0.466
152 ntl(soft.inputs[1], mat2.outputs[0])
153 ntl(soft.inputs[2], mat4.outputs[0])
154 ntl(soft_1.inputs[1], mat2.outputs[0])
155 ntl(soft_1.inputs[2], cr2.outputs[0])
157 mix1 = nt.nodes.new(type="ShaderNodeMixRGB")
158 mix1.blend_type = 'MULTIPLY'
159 mix1.inputs[0].default_value = 1
160 mix1.location = (4344.3, 630)
161 mix1_1 = nt.nodes.new(type="ShaderNodeMixRGB")
162 mix1_1.blend_type = 'MULTIPLY'
163 mix1_1.inputs[0].default_value = 1
164 mix1_1.location = (4344.3, 90)
166 mix2 = nt.nodes.new(type="ShaderNodeMixRGB")
167 mix2.location = (4782, 610)
168 mix2_1 = nt.nodes.new(type="ShaderNodeMixRGB")
169 mix2_1.location = (5131.8, 270)
170 mix2.inputs[1].default_value = (0, 0, 0, 1)
171 mix2.inputs[2].default_value = (32, 22, 14, 200)
172 mix2_1.inputs[1].default_value = (0, 0, 0, 1)
173 mix2_1.inputs[2].default_value = (1, 0.820, 0.650, 1)
175 ntl(mix1.inputs[1], soft.outputs[0])
176 ntl(mix1_1.inputs[1], soft_1.outputs[0])
177 ntl(mix2.inputs[0], mix1.outputs[0])
178 ntl(mix2_1.inputs[0], mix1_1.outputs[0])
180 gam = nt.nodes.new(type="ShaderNodeGamma")
181 gam.inputs[1].default_value = 2.3
182 gam.location = (5131.8, 610)
184 gam2 = nt.nodes.new(type="ShaderNodeGamma")
185 gam2.name = "Sun_value"
186 gam2.inputs[1].default_value = 1
187 gam2.location = (5524.5, 610)
189 gam3 = nt.nodes.new(type="ShaderNodeGamma")
190 gam3.name = "Shadow_color_saturation"
191 gam3.inputs[1].default_value = 1
192 gam3.location = (5524.5, 880)
194 sunopa = nt.nodes.new(type="ShaderNodeMixRGB")
195 sunopa.blend_type = 'ADD'
196 sunopa.inputs[0].default_value = 1
197 sunopa.location = (5940.6, 610)
198 sunopa_1 = nt.nodes.new(type="ShaderNodeMixRGB")
199 sunopa_1.blend_type = 'ADD'
200 sunopa_1.inputs[0].default_value = 1
201 sunopa_1.location = (5524.5, 340)
203 combine = nt.nodes.new(type="ShaderNodeMixRGB")
204 combine.location = (6313.8, 360)
205 ntl(combine.inputs[1], sunopa.outputs[0])
206 ntl(combine.inputs[2], sunopa_1.outputs[0])
207 lp = nt.nodes.new(type="ShaderNodeLightPath")
208 lp.location = (5940.6, 130)
209 ntl(combine.inputs[0], lp.outputs[0])
211 ntl(gam2.inputs[0], gam.outputs[0])
212 ntl(gam.inputs[0], mix2.outputs[0])
213 ntl(bg.inputs[0], combine.outputs[0])
215 map2 = nt.nodes.new(type="ShaderNodeMapping")
216 map2.inputs['Scale'].default_value[2] = 6.00
217 map2.inputs['Scale'].default_value[0] = 1.5
218 map2.inputs['Scale'].default_value[1] = 1.5
219 map2.location = (2196.6, 1510)
221 n1 = nt.nodes.new(type="ShaderNodeTexNoise")
222 n1.inputs['Scale'].default_value = 3.8
223 n1.inputs['Detail'].default_value = 2.4
224 n1.inputs['Distortion'].default_value = 0.5
225 n1.location = (2745.24, 1780)
227 n2 = nt.nodes.new(type="ShaderNodeTexNoise")
228 n2.inputs['Scale'].default_value = 2.0
229 n2.inputs['Detail'].default_value = 10
230 n2.inputs['Distortion'].default_value = 0.2
231 n2.location = (2745.24, 1510)
233 ntl(n2.inputs[0], map2.outputs[0])
234 ntl(n1.inputs[0], map2.outputs[0])
236 sc1 = nt.nodes.new(type="ShaderNodeValToRGB")
237 sc1.location = (3294, 1780)
238 sc2 = nt.nodes.new(type="ShaderNodeValToRGB")
239 sc2.location = (3294, 1510)
240 sc3 = nt.nodes.new(type="ShaderNodeValToRGB")
241 sc3.location = (3819.3, 820)
242 sc3_1 = nt.nodes.new(type="ShaderNodeValToRGB")
243 sc3_1.location = (4344.3, 1360)
244 sc4 = nt.nodes.new(type="ShaderNodeValToRGB")
245 sc4.location = (3819.3, 1090)
247 sc1.color_ramp.elements[1].position = 0.649
248 sc1.color_ramp.elements[0].position = 0.408
250 sc2.color_ramp.elements[1].position = 0.576
251 sc2.color_ramp.elements[0].position = 0.408
253 sc3.color_ramp.elements.new(0.5)
254 sc3.color_ramp.elements[2].position = 0.435
256 sc3.color_ramp.elements[1].position = 0.160
257 sc3.color_ramp.elements[0].position = 0.027
259 sc3.color_ramp.elements[1].color = (1, 1, 1, 1)
260 sc3.color_ramp.elements[0].color = (0.419, 0.419, 0.419, 0.419)
262 sc3.color_ramp.elements[0].position = 0.0
263 sc4.color_ramp.elements[0].position = 0.0
264 sc4.color_ramp.elements[1].position = 0.469
265 sc4.color_ramp.elements[1].color = (0, 0, 0, 1)
266 sc4.color_ramp.elements[0].color = (1, 1, 0.917412, 1)
268 sc3_1.color_ramp.elements.new(0.5)
269 sc3_1.color_ramp.elements[2].position = 0.435
271 sc3_1.color_ramp.elements[1].position = 0.187
272 sc3_1.color_ramp.elements[1].color = (1, 1, 1, 1)
273 sc3_1.color_ramp.elements[0].color = (0, 0, 0, 0)
274 sc3_1.color_ramp.elements[0].position = 0.0
276 smix1 = nt.nodes.new(type="ShaderNodeMixRGB")
277 smix1.location = (3819.3, 1550)
278 smix1.name = "Cloud_color"
279 smix2 = nt.nodes.new(type="ShaderNodeMixRGB")
280 smix2.location = (4344.3, 1630)
281 smix2.name = "Cloud_density"
282 smix2_1 = nt.nodes.new(type="ShaderNodeMixRGB")
283 smix2_1.location = (4782, 1360)
285 smix3 = nt.nodes.new(type="ShaderNodeMixRGB")
286 smix3.location = (4344.3, 1090)
287 smix3.name = "Sky_and_Horizon_colors"
289 smix4 = nt.nodes.new(type="ShaderNodeMixRGB")
290 smix4.location = (4782, 880)
292 smix5 = nt.nodes.new(type="ShaderNodeMixRGB")
293 smix5.name = "Cloud_opacity"
294 smix5.location = (5131.8, 880)
296 smix1.inputs[1].default_value = (1, 1, 1, 1)
297 smix1.inputs[2].default_value = (0, 0, 0, 1)
298 smix2.inputs[0].default_value = 0.267
299 smix2.blend_type = 'MULTIPLY'
300 smix2_1.inputs[0].default_value = 1
301 smix2_1.blend_type = 'MULTIPLY'
303 smix3.inputs[1].default_value = (0.434, 0.838, 1, 1)
304 smix3.inputs[2].default_value = (0.962, 0.822, 0.822, 1)
305 smix4.blend_type = 'MULTIPLY'
306 smix4.inputs[0].default_value = 1
307 smix5.blend_type = 'SCREEN'
308 smix5.inputs[0].default_value = 1
310 srgb = nt.nodes.new(type="ShaderNodeSeparateRGB")
311 srgb.location = (786.54, 1370)
312 aniadd = nt.nodes.new(type="ShaderNodeMath")
313 aniadd.location = (1220.16, 1235)
314 crgb = nt.nodes.new(type="ShaderNodeCombineRGB")
315 crgb.location = (1671.33, 1510)
316 sunrgb = nt.nodes.new(type="ShaderNodeMixRGB")
317 sunrgb.name = "Sun_color"
319 sunrgb.blend_type = 'MULTIPLY'
320 sunrgb.inputs[2].default_value = (32, 30, 30, 200)
321 sunrgb.inputs[0].default_value = 1
322 sunrgb.location = (4344.3, 360)
324 ntl(mix2.inputs[2], sunrgb.outputs[0])
326 ntl(smix1.inputs[0], sc2.outputs[0])
327 ntl(smix2.inputs[1], smix1.outputs[0])
328 ntl(smix2.inputs[2], sc1.outputs[0])
329 ntl(smix2_1.inputs[2], sc3_1.outputs[0])
330 ntl(smix3.inputs[0], sc4.outputs[0])
331 ntl(smix4.inputs[2], smix3.outputs[0])
332 ntl(smix4.inputs[1], sc3.outputs[0])
333 ntl(smix5.inputs[1], smix4.outputs[0])
334 ntl(smix2_1.inputs[1], smix2.outputs[0])
335 ntl(smix5.inputs[2], smix2_1.outputs[0])
336 ntl(sunopa.inputs[1], gam3.outputs[0])
337 ntl(gam3.inputs[0], smix5.outputs[0])
338 ntl(mix1.inputs[2], sc3.outputs[0])
339 ntl(sunopa.inputs[2], gam2.outputs[0])
341 ntl(sc1.inputs[0], n1.outputs['Fac'])
342 ntl(sc2.inputs[0], n2.outputs['Fac'])
344 skynor = nt.nodes.new(type="ShaderNodeNormal")
345 skynor.location = (3294, 1070)
347 ntl(sc3.inputs[0], skynor.outputs[1])
348 ntl(sc4.inputs[0], skynor.outputs[1])
349 ntl(sc3_1.inputs[0], skynor.outputs[1])
350 ntl(map2.inputs[0], crgb.outputs[0])
351 ntl(skynor.inputs[0], tcor.outputs[0])
352 ntl(mix1_1.inputs[2], sc3.outputs[0])
353 ntl(srgb.inputs[0], tcor.outputs[0])
354 ntl(crgb.inputs[1], srgb.outputs[1])
355 ntl(crgb.inputs[2], srgb.outputs[2])
356 ntl(aniadd.inputs[1], srgb.outputs[0])
357 ntl(crgb.inputs[0], aniadd.outputs[0])
359 ntl(cr1.inputs[0], nor.outputs[1])
360 ntl(cr2.inputs[0], cr1.outputs[0])
361 ntl(cr3.inputs[0], nor.outputs[1])
362 ntl(nor.inputs[0], map1.outputs[0])
363 ntl(map1.inputs[0], tcor.outputs[0])
364 ntl(sunopa_1.inputs[1], smix5.outputs[0])
365 ntl(sunopa_1.inputs[2], mix2_1.outputs[0])
367 world_out = self.get_node_types(nt, "OUTPUT_WORLD")
368 world_out.location = (7167.3, 360)
370 except Exception as e:
371 error_handlers(self, e, "Make a Procedural sky has failed")
373 return {"CANCELLED"}
375 return {'FINISHED'}
378 def draw_world_settings(col, context):
379 get_world = context.scene.world
380 stored_name = context.scene.dynamic_sky_name
381 get_world_keys = bpy.data.worlds.keys()
383 if stored_name not in get_world_keys or len(get_world_keys) < 1:
384 col.label(text="The {} World could not".format(stored_name),
385 icon="INFO")
386 col.label(text="be found in the Worlds' Data", icon="BLANK1")
387 return
389 elif not (get_world and get_world.name == stored_name):
390 col.label(text="Please select the World", icon="INFO")
391 col.label(text="named {}".format(stored_name), icon="BLANK1")
392 col.label(text="from the Properties > World", icon="BLANK1")
393 return
395 pick_world = bpy.data.worlds[stored_name]
396 try:
397 m = pick_world.node_tree.nodes[28]
399 m = pick_world.node_tree.nodes['Sky_and_Horizon_colors'].inputs[1]
400 n = pick_world.node_tree.nodes['Sky_and_Horizon_colors'].inputs[2]
401 c = pick_world.node_tree.nodes['Cloud_color'].inputs[1]
402 o = pick_world.node_tree.nodes['Cloud_opacity'].inputs[0]
403 d = pick_world.node_tree.nodes['Cloud_density'].inputs[0]
404 so = pick_world.node_tree.nodes['Sun_value'].inputs[1]
405 so2 = pick_world.node_tree.nodes['Shadow_color_saturation'].inputs[1]
406 no = pick_world.node_tree.nodes['Sky_normal'].outputs[0]
407 sof = pick_world.node_tree.nodes['Soft_hard'].inputs[0]
408 bgp = pick_world.node_tree.nodes['Scene_Brightness'].inputs[1]
410 suc = pick_world.node_tree.nodes['Sun_color'].inputs[1]
411 except:
412 col.label(text="Please Create a new World", icon="INFO")
413 col.label(text="seems that there was already", icon="BLANK1")
414 col.label(text="one called {}".format(stored_name), icon="BLANK1")
415 return
417 col.label(text="World: %s" % stored_name)
418 col.separator()
420 col.label(text="Scene Control")
421 col.prop(bgp, "default_value", text="Brightness")
422 col.prop(so2, "default_value", text="Shadow color saturation")
424 col.label(text="Sky Control")
425 col.prop(m, "default_value", text="Sky color")
426 col.prop(n, "default_value", text="Horizon Color")
427 col.prop(c, "default_value", text="Cloud color")
428 col.prop(o, "default_value", text="Cloud opacity")
429 col.prop(d, "default_value", text="Cloud density")
431 col.label(text="Sun Control")
432 col.prop(suc, "default_value", text="")
433 col.prop(so, "default_value", text="Sun value")
434 col.prop(sof, "default_value", text="Soft hard")
436 col.prop(no, "default_value", text="")
439 class Dynapanel(Panel):
440 bl_label = "Dynamic sky"
441 bl_idname = "DYNSKY_PT_tools"
442 bl_space_type = 'VIEW_3D'
443 bl_region_type = 'UI'
444 bl_context = "objectmode"
445 bl_category = "Create"
446 bl_options = {'DEFAULT_CLOSED'}
448 def draw(self, context):
449 layout = self.layout
450 layout.operator("sky.dyn", text="Create", icon='MAT_SPHERE_SKY')
452 col = layout.column()
453 draw_world_settings(col, context)
456 def register():
457 bpy.utils.register_class(Dynapanel)
458 bpy.utils.register_class(dsky)
459 bpy.types.Scene.dynamic_sky_name = StringProperty(
460 name="",
461 default="Dynamic"
465 def unregister():
466 bpy.utils.unregister_class(Dynapanel)
467 bpy.utils.unregister_class(dsky)
468 del bpy.types.Scene.dynamic_sky_name
471 if __name__ == "__main__":
472 register()