object_collection_manager: use GPL headers for all files
[blender-addons.git] / lighting_dynamic_sky.py
bloba61b60e30bc9e4da7e72f3cf1c119368cf43e85e
1 # Dynamic Sky.py (c) 2015 Pratik Solanki (Draguu)
3 # ##### BEGIN GPL LICENSE BLOCK #####
5 # This program is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU General Public License
7 # as published by the Free Software Foundation; either version 2
8 # of the License, or (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software Foundation,
17 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 # ##### END GPL LICENSE BLOCK #####
22 bl_info = {
23 "name": "Dynamic Sky",
24 "author": "Pratik Solanki",
25 "version": (1, 0, 6),
26 "blender": (2, 80, 0),
27 "location": "View3D > Sidebar > Create Tab",
28 "description": "Creates Dynamic Sky for Cycles",
29 "warning": "",
30 "wiki_url": "https://docs.blender.org/manual/en/dev/addons/"
31 "lighting/dynamic_sky.html",
32 "category": "Lighting",
35 import bpy
36 from bpy.props import StringProperty
37 from bpy.types import (
38 Operator,
39 Panel,
43 # Handle error notifications
44 def error_handlers(self, error, reports="ERROR"):
45 if self and reports:
46 self.report({'WARNING'}, reports + " (See Console for more info)")
48 print("\n[Dynamic Sky]\nError: {}\n".format(error))
51 def check_world_name(name_id="Dynamic"):
52 # check if the new name pattern is in world data
53 name_list = []
54 suffix = 1
55 try:
56 name_list = [world.name for world in bpy.data.worlds if name_id in world.name]
57 new_name = "{}_{}".format(name_id, len(name_list) + suffix)
58 if new_name in name_list:
59 # KISS failed - numbering is not sequential
60 # try harvesting numbers in world names, find the rightmost ones
61 test_num = []
62 from re import findall
63 for words in name_list:
64 test_num.append(findall("\d+", words))
66 suffix += max([int(l[-1]) for l in test_num])
67 new_name = "{}_{}".format(name_id, suffix)
68 return new_name
69 except Exception as e:
70 error_handlers(False, e)
71 pass
72 return name_id
75 def check_cycles():
76 return ('cycles' in bpy.context.preferences.addons.keys())
79 class dsky(Operator):
80 bl_idname = "sky.dyn"
81 bl_label = "Make a Procedural sky"
82 bl_description = ("Make a Procedural Sky with parameters in the 3D View\n"
83 "Note: Available just for Cycles renderer\n"
84 "Only the last created Dynamic World can be accessed from this panel")
86 @classmethod
87 def poll(cls, context):
88 return check_cycles()
90 def get_node_types(self, node_tree, node_type):
91 for node in node_tree.nodes:
92 if node.type == node_type:
93 return node
94 return None
96 def execute(self, context):
97 try:
98 get_name = check_world_name()
99 context.scene.dynamic_sky_name = get_name
100 bpy.context.scene.render.engine = 'CYCLES'
102 world = bpy.data.worlds.new(get_name)
103 world.cycles.sample_as_light = True
104 world.cycles.sample_map_resolution = 2048
105 world.use_nodes = True
107 nt = world.node_tree
108 # Note: (see T52714) to avoid string localization problems, assign the name for
109 # nodes that will be exposed in the 3D view (pattern UI name with underscore)
110 bg = self.get_node_types(nt, "BACKGROUND")
111 bg.name = "Scene_Brightness"
112 bg.inputs[0].default_value[:3] = (0.5, .1, 0.6)
113 bg.inputs[1].default_value = 1
114 bg.location = (6708.3, 360)
116 ntl = nt.links.new
117 tcor = nt.nodes.new(type="ShaderNodeTexCoord")
118 tcor.location = (243.729, 1005)
120 map1 = nt.nodes.new(type="ShaderNodeMapping")
121 map1.vector_type = 'NORMAL'
122 map1.location = (786.54, 730)
124 nor = nt.nodes.new(type="ShaderNodeNormal")
125 nor.name = "Sky_normal"
126 nor.location = (1220.16, 685)
128 cr1 = nt.nodes.new(type="ShaderNodeValToRGB")
129 cr1.color_ramp.elements[0].position = 0.969
130 cr1.color_ramp.interpolation = 'EASE'
131 cr1.location = (1671.33, 415)
132 cr2 = nt.nodes.new(type="ShaderNodeValToRGB")
133 cr2.color_ramp.elements[0].position = 0.991
134 cr2.color_ramp.elements[1].position = 1
135 cr2.color_ramp.interpolation = 'EASE'
136 cr2.location = (2196.6, 415)
137 cr3 = nt.nodes.new(type="ShaderNodeValToRGB")
138 cr3.color_ramp.elements[0].position = 0.779
139 cr3.color_ramp.elements[1].position = 1
140 cr3.color_ramp.interpolation = 'EASE'
141 cr3.location = (2196.6, 415)
143 mat1 = nt.nodes.new(type="ShaderNodeMath")
144 mat1.operation = 'MULTIPLY'
145 mat1.inputs[1].default_value = 0.2
146 mat1.location = (2196.6, 685)
147 mat2 = nt.nodes.new(type="ShaderNodeMath")
148 mat2.operation = 'MULTIPLY'
149 mat2.inputs[1].default_value = 2
150 mat2.location = (3294, 685)
151 mat3 = nt.nodes.new(type="ShaderNodeMath")
152 mat3.operation = 'MULTIPLY'
153 mat3.inputs[1].default_value = 40.9
154 mat3.location = (2745.24, 415)
155 mat4 = nt.nodes.new(type="ShaderNodeMath")
156 mat4.operation = 'SUBTRACT'
157 mat4.inputs[1].default_value = 1
158 mat4.location = (3294, 415)
159 ntl(mat2.inputs[0], mat1.outputs[0])
160 ntl(mat4.inputs[0], mat3.outputs[0])
161 ntl(mat1.inputs[0], cr3.outputs[0])
162 ntl(mat3.inputs[0], cr2.outputs[0])
164 soft = nt.nodes.new(type="ShaderNodeMixRGB")
165 soft.name = "Soft_hard"
166 soft.location = (3819.3, 550)
167 soft_1 = nt.nodes.new(type="ShaderNodeMixRGB")
168 soft_1.location = (3819.3, 185)
169 soft.inputs[0].default_value = 1
170 soft_1.inputs[0].default_value = 0.466
171 ntl(soft.inputs[1], mat2.outputs[0])
172 ntl(soft.inputs[2], mat4.outputs[0])
173 ntl(soft_1.inputs[1], mat2.outputs[0])
174 ntl(soft_1.inputs[2], cr2.outputs[0])
176 mix1 = nt.nodes.new(type="ShaderNodeMixRGB")
177 mix1.blend_type = 'MULTIPLY'
178 mix1.inputs[0].default_value = 1
179 mix1.location = (4344.3, 630)
180 mix1_1 = nt.nodes.new(type="ShaderNodeMixRGB")
181 mix1_1.blend_type = 'MULTIPLY'
182 mix1_1.inputs[0].default_value = 1
183 mix1_1.location = (4344.3, 90)
185 mix2 = nt.nodes.new(type="ShaderNodeMixRGB")
186 mix2.location = (4782, 610)
187 mix2_1 = nt.nodes.new(type="ShaderNodeMixRGB")
188 mix2_1.location = (5131.8, 270)
189 mix2.inputs[1].default_value = (0, 0, 0, 1)
190 mix2.inputs[2].default_value = (32, 22, 14, 200)
191 mix2_1.inputs[1].default_value = (0, 0, 0, 1)
192 mix2_1.inputs[2].default_value = (1, 0.820, 0.650, 1)
194 ntl(mix1.inputs[1], soft.outputs[0])
195 ntl(mix1_1.inputs[1], soft_1.outputs[0])
196 ntl(mix2.inputs[0], mix1.outputs[0])
197 ntl(mix2_1.inputs[0], mix1_1.outputs[0])
199 gam = nt.nodes.new(type="ShaderNodeGamma")
200 gam.inputs[1].default_value = 2.3
201 gam.location = (5131.8, 610)
203 gam2 = nt.nodes.new(type="ShaderNodeGamma")
204 gam2.name = "Sun_value"
205 gam2.inputs[1].default_value = 1
206 gam2.location = (5524.5, 610)
208 gam3 = nt.nodes.new(type="ShaderNodeGamma")
209 gam3.name = "Shadow_color_saturation"
210 gam3.inputs[1].default_value = 1
211 gam3.location = (5524.5, 880)
213 sunopa = nt.nodes.new(type="ShaderNodeMixRGB")
214 sunopa.blend_type = 'ADD'
215 sunopa.inputs[0].default_value = 1
216 sunopa.location = (5940.6, 610)
217 sunopa_1 = nt.nodes.new(type="ShaderNodeMixRGB")
218 sunopa_1.blend_type = 'ADD'
219 sunopa_1.inputs[0].default_value = 1
220 sunopa_1.location = (5524.5, 340)
222 combine = nt.nodes.new(type="ShaderNodeMixRGB")
223 combine.location = (6313.8, 360)
224 ntl(combine.inputs[1], sunopa.outputs[0])
225 ntl(combine.inputs[2], sunopa_1.outputs[0])
226 lp = nt.nodes.new(type="ShaderNodeLightPath")
227 lp.location = (5940.6, 130)
228 ntl(combine.inputs[0], lp.outputs[0])
230 ntl(gam2.inputs[0], gam.outputs[0])
231 ntl(gam.inputs[0], mix2.outputs[0])
232 ntl(bg.inputs[0], combine.outputs[0])
234 map2 = nt.nodes.new(type="ShaderNodeMapping")
235 map2.inputs['Scale'].default_value[2] = 6.00
236 map2.inputs['Scale'].default_value[0] = 1.5
237 map2.inputs['Scale'].default_value[1] = 1.5
238 map2.location = (2196.6, 1510)
240 n1 = nt.nodes.new(type="ShaderNodeTexNoise")
241 n1.inputs['Scale'].default_value = 3.8
242 n1.inputs['Detail'].default_value = 2.4
243 n1.inputs['Distortion'].default_value = 0.5
244 n1.location = (2745.24, 1780)
246 n2 = nt.nodes.new(type="ShaderNodeTexNoise")
247 n2.inputs['Scale'].default_value = 2.0
248 n2.inputs['Detail'].default_value = 10
249 n2.inputs['Distortion'].default_value = 0.2
250 n2.location = (2745.24, 1510)
252 ntl(n2.inputs[0], map2.outputs[0])
253 ntl(n1.inputs[0], map2.outputs[0])
255 sc1 = nt.nodes.new(type="ShaderNodeValToRGB")
256 sc1.location = (3294, 1780)
257 sc2 = nt.nodes.new(type="ShaderNodeValToRGB")
258 sc2.location = (3294, 1510)
259 sc3 = nt.nodes.new(type="ShaderNodeValToRGB")
260 sc3.location = (3819.3, 820)
261 sc3_1 = nt.nodes.new(type="ShaderNodeValToRGB")
262 sc3_1.location = (4344.3, 1360)
263 sc4 = nt.nodes.new(type="ShaderNodeValToRGB")
264 sc4.location = (3819.3, 1090)
266 sc1.color_ramp.elements[1].position = 0.649
267 sc1.color_ramp.elements[0].position = 0.408
269 sc2.color_ramp.elements[1].position = 0.576
270 sc2.color_ramp.elements[0].position = 0.408
272 sc3.color_ramp.elements.new(0.5)
273 sc3.color_ramp.elements[2].position = 0.435
275 sc3.color_ramp.elements[1].position = 0.160
276 sc3.color_ramp.elements[0].position = 0.027
278 sc3.color_ramp.elements[1].color = (1, 1, 1, 1)
279 sc3.color_ramp.elements[0].color = (0.419, 0.419, 0.419, 0.419)
281 sc3.color_ramp.elements[0].position = 0.0
282 sc4.color_ramp.elements[0].position = 0.0
283 sc4.color_ramp.elements[1].position = 0.469
284 sc4.color_ramp.elements[1].color = (0, 0, 0, 1)
285 sc4.color_ramp.elements[0].color = (1, 1, 0.917412, 1)
287 sc3_1.color_ramp.elements.new(0.5)
288 sc3_1.color_ramp.elements[2].position = 0.435
290 sc3_1.color_ramp.elements[1].position = 0.187
291 sc3_1.color_ramp.elements[1].color = (1, 1, 1, 1)
292 sc3_1.color_ramp.elements[0].color = (0, 0, 0, 0)
293 sc3_1.color_ramp.elements[0].position = 0.0
295 smix1 = nt.nodes.new(type="ShaderNodeMixRGB")
296 smix1.location = (3819.3, 1550)
297 smix1.name = "Cloud_color"
298 smix2 = nt.nodes.new(type="ShaderNodeMixRGB")
299 smix2.location = (4344.3, 1630)
300 smix2.name = "Cloud_density"
301 smix2_1 = nt.nodes.new(type="ShaderNodeMixRGB")
302 smix2_1.location = (4782, 1360)
304 smix3 = nt.nodes.new(type="ShaderNodeMixRGB")
305 smix3.location = (4344.3, 1090)
306 smix3.name = "Sky_and_Horizon_colors"
308 smix4 = nt.nodes.new(type="ShaderNodeMixRGB")
309 smix4.location = (4782, 880)
311 smix5 = nt.nodes.new(type="ShaderNodeMixRGB")
312 smix5.name = "Cloud_opacity"
313 smix5.location = (5131.8, 880)
315 smix1.inputs[1].default_value = (1, 1, 1, 1)
316 smix1.inputs[2].default_value = (0, 0, 0, 1)
317 smix2.inputs[0].default_value = 0.267
318 smix2.blend_type = 'MULTIPLY'
319 smix2_1.inputs[0].default_value = 1
320 smix2_1.blend_type = 'MULTIPLY'
322 smix3.inputs[1].default_value = (0.434, 0.838, 1, 1)
323 smix3.inputs[2].default_value = (0.962, 0.822, 0.822, 1)
324 smix4.blend_type = 'MULTIPLY'
325 smix4.inputs[0].default_value = 1
326 smix5.blend_type = 'SCREEN'
327 smix5.inputs[0].default_value = 1
329 srgb = nt.nodes.new(type="ShaderNodeSeparateRGB")
330 srgb.location = (786.54, 1370)
331 aniadd = nt.nodes.new(type="ShaderNodeMath")
332 aniadd.location = (1220.16, 1235)
333 crgb = nt.nodes.new(type="ShaderNodeCombineRGB")
334 crgb.location = (1671.33, 1510)
335 sunrgb = nt.nodes.new(type="ShaderNodeMixRGB")
336 sunrgb.name = "Sun_color"
338 sunrgb.blend_type = 'MULTIPLY'
339 sunrgb.inputs[2].default_value = (32, 30, 30, 200)
340 sunrgb.inputs[0].default_value = 1
341 sunrgb.location = (4344.3, 360)
343 ntl(mix2.inputs[2], sunrgb.outputs[0])
345 ntl(smix1.inputs[0], sc2.outputs[0])
346 ntl(smix2.inputs[1], smix1.outputs[0])
347 ntl(smix2.inputs[2], sc1.outputs[0])
348 ntl(smix2_1.inputs[2], sc3_1.outputs[0])
349 ntl(smix3.inputs[0], sc4.outputs[0])
350 ntl(smix4.inputs[2], smix3.outputs[0])
351 ntl(smix4.inputs[1], sc3.outputs[0])
352 ntl(smix5.inputs[1], smix4.outputs[0])
353 ntl(smix2_1.inputs[1], smix2.outputs[0])
354 ntl(smix5.inputs[2], smix2_1.outputs[0])
355 ntl(sunopa.inputs[1], gam3.outputs[0])
356 ntl(gam3.inputs[0], smix5.outputs[0])
357 ntl(mix1.inputs[2], sc3.outputs[0])
358 ntl(sunopa.inputs[2], gam2.outputs[0])
360 ntl(sc1.inputs[0], n1.outputs['Fac'])
361 ntl(sc2.inputs[0], n2.outputs['Fac'])
363 skynor = nt.nodes.new(type="ShaderNodeNormal")
364 skynor.location = (3294, 1070)
366 ntl(sc3.inputs[0], skynor.outputs[1])
367 ntl(sc4.inputs[0], skynor.outputs[1])
368 ntl(sc3_1.inputs[0], skynor.outputs[1])
369 ntl(map2.inputs[0], crgb.outputs[0])
370 ntl(skynor.inputs[0], tcor.outputs[0])
371 ntl(mix1_1.inputs[2], sc3.outputs[0])
372 ntl(srgb.inputs[0], tcor.outputs[0])
373 ntl(crgb.inputs[1], srgb.outputs[1])
374 ntl(crgb.inputs[2], srgb.outputs[2])
375 ntl(aniadd.inputs[1], srgb.outputs[0])
376 ntl(crgb.inputs[0], aniadd.outputs[0])
378 ntl(cr1.inputs[0], nor.outputs[1])
379 ntl(cr2.inputs[0], cr1.outputs[0])
380 ntl(cr3.inputs[0], nor.outputs[1])
381 ntl(nor.inputs[0], map1.outputs[0])
382 ntl(map1.inputs[0], tcor.outputs[0])
383 ntl(sunopa_1.inputs[1], smix5.outputs[0])
384 ntl(sunopa_1.inputs[2], mix2_1.outputs[0])
386 world_out = self.get_node_types(nt, "OUTPUT_WORLD")
387 world_out.location = (7167.3, 360)
389 except Exception as e:
390 error_handlers(self, e, "Make a Procedural sky has failed")
392 return {"CANCELLED"}
394 return {'FINISHED'}
397 def draw_world_settings(col, context):
398 get_world = context.scene.world
399 stored_name = context.scene.dynamic_sky_name
400 get_world_keys = bpy.data.worlds.keys()
402 if stored_name not in get_world_keys or len(get_world_keys) < 1:
403 col.label(text="The {} World could not".format(stored_name),
404 icon="INFO")
405 col.label(text="be found in the Worlds' Data", icon="BLANK1")
406 return
408 elif not (get_world and get_world.name == stored_name):
409 col.label(text="Please select the World", icon="INFO")
410 col.label(text="named {}".format(stored_name), icon="BLANK1")
411 col.label(text="from the Properties > World", icon="BLANK1")
412 return
414 pick_world = bpy.data.worlds[stored_name]
415 try:
416 m = pick_world.node_tree.nodes[28]
418 m = pick_world.node_tree.nodes['Sky_and_Horizon_colors'].inputs[1]
419 n = pick_world.node_tree.nodes['Sky_and_Horizon_colors'].inputs[2]
420 c = pick_world.node_tree.nodes['Cloud_color'].inputs[1]
421 o = pick_world.node_tree.nodes['Cloud_opacity'].inputs[0]
422 d = pick_world.node_tree.nodes['Cloud_density'].inputs[0]
423 so = pick_world.node_tree.nodes['Sun_value'].inputs[1]
424 so2 = pick_world.node_tree.nodes['Shadow_color_saturation'].inputs[1]
425 no = pick_world.node_tree.nodes['Sky_normal'].outputs[0]
426 sof = pick_world.node_tree.nodes['Soft_hard'].inputs[0]
427 bgp = pick_world.node_tree.nodes['Scene_Brightness'].inputs[1]
429 suc = pick_world.node_tree.nodes['Sun_color'].inputs[1]
430 except:
431 col.label(text="Please Create a new World", icon="INFO")
432 col.label(text="seems that there was already", icon="BLANK1")
433 col.label(text="one called {}".format(stored_name), icon="BLANK1")
434 return
436 col.label(text="World: %s" % stored_name)
437 col.separator()
439 col.label(text="Scene Control")
440 col.prop(bgp, "default_value", text="Brightness")
441 col.prop(so2, "default_value", text="Shadow color saturation")
443 col.label(text="Sky Control")
444 col.prop(m, "default_value", text="Sky color")
445 col.prop(n, "default_value", text="Horizon Color")
446 col.prop(c, "default_value", text="Cloud color")
447 col.prop(o, "default_value", text="Cloud opacity")
448 col.prop(d, "default_value", text="Cloud density")
450 col.label(text="Sun Control")
451 col.prop(suc, "default_value", text="")
452 col.prop(so, "default_value", text="Sun value")
453 col.prop(sof, "default_value", text="Soft hard")
455 col.prop(no, "default_value", text="")
458 class Dynapanel(Panel):
459 bl_label = "Dynamic sky"
460 bl_idname = "DYNSKY_PT_tools"
461 bl_space_type = 'VIEW_3D'
462 bl_region_type = 'UI'
463 bl_context = "objectmode"
464 bl_category = "Create"
465 bl_options = {'DEFAULT_CLOSED'}
467 def draw(self, context):
468 layout = self.layout
469 layout.operator("sky.dyn", text="Create", icon='MAT_SPHERE_SKY')
471 col = layout.column()
472 draw_world_settings(col, context)
475 def register():
476 bpy.utils.register_class(Dynapanel)
477 bpy.utils.register_class(dsky)
478 bpy.types.Scene.dynamic_sky_name = StringProperty(
479 name="",
480 default="Dynamic"
484 def unregister():
485 bpy.utils.unregister_class(Dynapanel)
486 bpy.utils.unregister_class(dsky)
487 del bpy.types.Scene.dynamic_sky_name
490 if __name__ == "__main__":
491 register()