1 # SPDX-FileCopyrightText: 2015 Pratik Solanki (Draguu)
3 # SPDX-License-Identifier: GPL-2.0-or-later
7 "author": "Pratik Solanki",
10 "location": "View3D > Sidebar > Create Tab",
11 "description": "Creates Dynamic Sky for Cycles",
13 "doc_url": "{BLENDER_MANUAL_URL}/addons/lighting/dynamic_sky.html",
14 "category": "Lighting",
18 from bpy
.props
import StringProperty
19 from bpy
.types
import (
25 # Handle error notifications
26 def error_handlers(self
, error
, reports
="ERROR"):
28 self
.report({'WARNING'}, reports
+ " (See Console for more info)")
30 print("\n[Dynamic Sky]\nError: {}\n".format(error
))
33 def check_world_name(name_id
="Dynamic"):
34 # check if the new name pattern is in world data
38 name_list
= [world
.name
for world
in bpy
.data
.worlds
if name_id
in world
.name
]
39 new_name
= "{}_{}".format(name_id
, len(name_list
) + suffix
)
40 if new_name
in name_list
:
41 # KISS failed - numbering is not sequential
42 # try harvesting numbers in world names, find the rightmost ones
44 from re
import findall
45 for words
in name_list
:
46 test_num
.append(findall(r
"\d+", words
))
48 suffix
+= max([int(l
[-1]) for l
in test_num
])
49 new_name
= "{}_{}".format(name_id
, suffix
)
51 except Exception as e
:
52 error_handlers(False, e
)
58 return ('cycles' in bpy
.context
.preferences
.addons
.keys())
63 bl_label
= "Make a Procedural sky"
64 bl_description
= ("Make a Procedural Sky with parameters in the 3D View\n"
65 "Note: Available just for Cycles renderer\n"
66 "Only the last created Dynamic World can be accessed from this panel")
69 def poll(cls
, context
):
72 def get_node_types(self
, node_tree
, node_type
):
73 for node
in node_tree
.nodes
:
74 if node
.type == node_type
:
78 def execute(self
, context
):
80 get_name
= check_world_name()
81 context
.scene
.dynamic_sky_name
= get_name
82 bpy
.context
.scene
.render
.engine
= 'CYCLES'
84 world
= bpy
.data
.worlds
.new(get_name
)
85 world
.cycles
.sample_as_light
= True
86 world
.cycles
.sample_map_resolution
= 2048
87 world
.use_nodes
= True
90 # Note: (see T52714) to avoid string localization problems, assign the name for
91 # nodes that will be exposed in the 3D view (pattern UI name with underscore)
92 bg
= self
.get_node_types(nt
, "BACKGROUND")
93 bg
.name
= "Scene_Brightness"
94 bg
.inputs
[0].default_value
[:3] = (0.5, .1, 0.6)
95 bg
.inputs
[1].default_value
= 1
96 bg
.location
= (6708.3, 360)
99 tcor
= nt
.nodes
.new(type="ShaderNodeTexCoord")
100 tcor
.location
= (243.729, 1005)
102 map1
= nt
.nodes
.new(type="ShaderNodeMapping")
103 map1
.vector_type
= 'NORMAL'
104 map1
.location
= (786.54, 730)
106 nor
= nt
.nodes
.new(type="ShaderNodeNormal")
107 nor
.name
= "Sky_normal"
108 nor
.location
= (1220.16, 685)
110 cr1
= nt
.nodes
.new(type="ShaderNodeValToRGB")
111 cr1
.color_ramp
.elements
[0].position
= 0.969
112 cr1
.color_ramp
.interpolation
= 'EASE'
113 cr1
.location
= (1671.33, 415)
114 cr2
= nt
.nodes
.new(type="ShaderNodeValToRGB")
115 cr2
.color_ramp
.elements
[0].position
= 0.991
116 cr2
.color_ramp
.elements
[1].position
= 1
117 cr2
.color_ramp
.interpolation
= 'EASE'
118 cr2
.location
= (2196.6, 415)
119 cr3
= nt
.nodes
.new(type="ShaderNodeValToRGB")
120 cr3
.color_ramp
.elements
[0].position
= 0.779
121 cr3
.color_ramp
.elements
[1].position
= 1
122 cr3
.color_ramp
.interpolation
= 'EASE'
123 cr3
.location
= (2196.6, 415)
125 mat1
= nt
.nodes
.new(type="ShaderNodeMath")
126 mat1
.operation
= 'MULTIPLY'
127 mat1
.inputs
[1].default_value
= 0.2
128 mat1
.location
= (2196.6, 685)
129 mat2
= nt
.nodes
.new(type="ShaderNodeMath")
130 mat2
.operation
= 'MULTIPLY'
131 mat2
.inputs
[1].default_value
= 2
132 mat2
.location
= (3294, 685)
133 mat3
= nt
.nodes
.new(type="ShaderNodeMath")
134 mat3
.operation
= 'MULTIPLY'
135 mat3
.inputs
[1].default_value
= 40.9
136 mat3
.location
= (2745.24, 415)
137 mat4
= nt
.nodes
.new(type="ShaderNodeMath")
138 mat4
.operation
= 'SUBTRACT'
139 mat4
.inputs
[1].default_value
= 1
140 mat4
.location
= (3294, 415)
141 ntl(mat2
.inputs
[0], mat1
.outputs
[0])
142 ntl(mat4
.inputs
[0], mat3
.outputs
[0])
143 ntl(mat1
.inputs
[0], cr3
.outputs
[0])
144 ntl(mat3
.inputs
[0], cr2
.outputs
[0])
146 soft
= nt
.nodes
.new(type="ShaderNodeMixRGB")
147 soft
.name
= "Soft_hard"
148 soft
.location
= (3819.3, 550)
149 soft_1
= nt
.nodes
.new(type="ShaderNodeMixRGB")
150 soft_1
.location
= (3819.3, 185)
151 soft
.inputs
[0].default_value
= 1
152 soft_1
.inputs
[0].default_value
= 0.466
153 ntl(soft
.inputs
[1], mat2
.outputs
[0])
154 ntl(soft
.inputs
[2], mat4
.outputs
[0])
155 ntl(soft_1
.inputs
[1], mat2
.outputs
[0])
156 ntl(soft_1
.inputs
[2], cr2
.outputs
[0])
158 mix1
= nt
.nodes
.new(type="ShaderNodeMixRGB")
159 mix1
.blend_type
= 'MULTIPLY'
160 mix1
.inputs
[0].default_value
= 1
161 mix1
.location
= (4344.3, 630)
162 mix1_1
= nt
.nodes
.new(type="ShaderNodeMixRGB")
163 mix1_1
.blend_type
= 'MULTIPLY'
164 mix1_1
.inputs
[0].default_value
= 1
165 mix1_1
.location
= (4344.3, 90)
167 mix2
= nt
.nodes
.new(type="ShaderNodeMixRGB")
168 mix2
.location
= (4782, 610)
169 mix2_1
= nt
.nodes
.new(type="ShaderNodeMixRGB")
170 mix2_1
.location
= (5131.8, 270)
171 mix2
.inputs
[1].default_value
= (0, 0, 0, 1)
172 mix2
.inputs
[2].default_value
= (32, 22, 14, 200)
173 mix2_1
.inputs
[1].default_value
= (0, 0, 0, 1)
174 mix2_1
.inputs
[2].default_value
= (1, 0.820, 0.650, 1)
176 ntl(mix1
.inputs
[1], soft
.outputs
[0])
177 ntl(mix1_1
.inputs
[1], soft_1
.outputs
[0])
178 ntl(mix2
.inputs
[0], mix1
.outputs
[0])
179 ntl(mix2_1
.inputs
[0], mix1_1
.outputs
[0])
181 gam
= nt
.nodes
.new(type="ShaderNodeGamma")
182 gam
.inputs
[1].default_value
= 2.3
183 gam
.location
= (5131.8, 610)
185 gam2
= nt
.nodes
.new(type="ShaderNodeGamma")
186 gam2
.name
= "Sun_value"
187 gam2
.inputs
[1].default_value
= 1
188 gam2
.location
= (5524.5, 610)
190 gam3
= nt
.nodes
.new(type="ShaderNodeGamma")
191 gam3
.name
= "Shadow_color_saturation"
192 gam3
.inputs
[1].default_value
= 1
193 gam3
.location
= (5524.5, 880)
195 sunopa
= nt
.nodes
.new(type="ShaderNodeMixRGB")
196 sunopa
.blend_type
= 'ADD'
197 sunopa
.inputs
[0].default_value
= 1
198 sunopa
.location
= (5940.6, 610)
199 sunopa_1
= nt
.nodes
.new(type="ShaderNodeMixRGB")
200 sunopa_1
.blend_type
= 'ADD'
201 sunopa_1
.inputs
[0].default_value
= 1
202 sunopa_1
.location
= (5524.5, 340)
204 combine
= nt
.nodes
.new(type="ShaderNodeMixRGB")
205 combine
.location
= (6313.8, 360)
206 ntl(combine
.inputs
[1], sunopa
.outputs
[0])
207 ntl(combine
.inputs
[2], sunopa_1
.outputs
[0])
208 lp
= nt
.nodes
.new(type="ShaderNodeLightPath")
209 lp
.location
= (5940.6, 130)
210 ntl(combine
.inputs
[0], lp
.outputs
[0])
212 ntl(gam2
.inputs
[0], gam
.outputs
[0])
213 ntl(gam
.inputs
[0], mix2
.outputs
[0])
214 ntl(bg
.inputs
[0], combine
.outputs
[0])
216 map2
= nt
.nodes
.new(type="ShaderNodeMapping")
217 map2
.inputs
['Scale'].default_value
[2] = 6.00
218 map2
.inputs
['Scale'].default_value
[0] = 1.5
219 map2
.inputs
['Scale'].default_value
[1] = 1.5
220 map2
.location
= (2196.6, 1510)
222 n1
= nt
.nodes
.new(type="ShaderNodeTexNoise")
223 n1
.inputs
['Scale'].default_value
= 3.8
224 n1
.inputs
['Detail'].default_value
= 2.4
225 n1
.inputs
['Distortion'].default_value
= 0.5
226 n1
.location
= (2745.24, 1780)
228 n2
= nt
.nodes
.new(type="ShaderNodeTexNoise")
229 n2
.inputs
['Scale'].default_value
= 2.0
230 n2
.inputs
['Detail'].default_value
= 10
231 n2
.inputs
['Distortion'].default_value
= 0.2
232 n2
.location
= (2745.24, 1510)
234 ntl(n2
.inputs
[0], map2
.outputs
[0])
235 ntl(n1
.inputs
[0], map2
.outputs
[0])
237 sc1
= nt
.nodes
.new(type="ShaderNodeValToRGB")
238 sc1
.location
= (3294, 1780)
239 sc2
= nt
.nodes
.new(type="ShaderNodeValToRGB")
240 sc2
.location
= (3294, 1510)
241 sc3
= nt
.nodes
.new(type="ShaderNodeValToRGB")
242 sc3
.location
= (3819.3, 820)
243 sc3_1
= nt
.nodes
.new(type="ShaderNodeValToRGB")
244 sc3_1
.location
= (4344.3, 1360)
245 sc4
= nt
.nodes
.new(type="ShaderNodeValToRGB")
246 sc4
.location
= (3819.3, 1090)
248 sc1
.color_ramp
.elements
[1].position
= 0.649
249 sc1
.color_ramp
.elements
[0].position
= 0.408
251 sc2
.color_ramp
.elements
[1].position
= 0.576
252 sc2
.color_ramp
.elements
[0].position
= 0.408
254 sc3
.color_ramp
.elements
.new(0.5)
255 sc3
.color_ramp
.elements
[2].position
= 0.435
257 sc3
.color_ramp
.elements
[1].position
= 0.160
258 sc3
.color_ramp
.elements
[0].position
= 0.027
260 sc3
.color_ramp
.elements
[1].color
= (1, 1, 1, 1)
261 sc3
.color_ramp
.elements
[0].color
= (0.419, 0.419, 0.419, 0.419)
263 sc3
.color_ramp
.elements
[0].position
= 0.0
264 sc4
.color_ramp
.elements
[0].position
= 0.0
265 sc4
.color_ramp
.elements
[1].position
= 0.469
266 sc4
.color_ramp
.elements
[1].color
= (0, 0, 0, 1)
267 sc4
.color_ramp
.elements
[0].color
= (1, 1, 0.917412, 1)
269 sc3_1
.color_ramp
.elements
.new(0.5)
270 sc3_1
.color_ramp
.elements
[2].position
= 0.435
272 sc3_1
.color_ramp
.elements
[1].position
= 0.187
273 sc3_1
.color_ramp
.elements
[1].color
= (1, 1, 1, 1)
274 sc3_1
.color_ramp
.elements
[0].color
= (0, 0, 0, 0)
275 sc3_1
.color_ramp
.elements
[0].position
= 0.0
277 smix1
= nt
.nodes
.new(type="ShaderNodeMixRGB")
278 smix1
.location
= (3819.3, 1550)
279 smix1
.name
= "Cloud_color"
280 smix2
= nt
.nodes
.new(type="ShaderNodeMixRGB")
281 smix2
.location
= (4344.3, 1630)
282 smix2
.name
= "Cloud_density"
283 smix2_1
= nt
.nodes
.new(type="ShaderNodeMixRGB")
284 smix2_1
.location
= (4782, 1360)
286 smix3
= nt
.nodes
.new(type="ShaderNodeMixRGB")
287 smix3
.location
= (4344.3, 1090)
288 smix3
.name
= "Sky_and_Horizon_colors"
290 smix4
= nt
.nodes
.new(type="ShaderNodeMixRGB")
291 smix4
.location
= (4782, 880)
293 smix5
= nt
.nodes
.new(type="ShaderNodeMixRGB")
294 smix5
.name
= "Cloud_opacity"
295 smix5
.location
= (5131.8, 880)
297 smix1
.inputs
[1].default_value
= (1, 1, 1, 1)
298 smix1
.inputs
[2].default_value
= (0, 0, 0, 1)
299 smix2
.inputs
[0].default_value
= 0.267
300 smix2
.blend_type
= 'MULTIPLY'
301 smix2_1
.inputs
[0].default_value
= 1
302 smix2_1
.blend_type
= 'MULTIPLY'
304 smix3
.inputs
[1].default_value
= (0.434, 0.838, 1, 1)
305 smix3
.inputs
[2].default_value
= (0.962, 0.822, 0.822, 1)
306 smix4
.blend_type
= 'MULTIPLY'
307 smix4
.inputs
[0].default_value
= 1
308 smix5
.blend_type
= 'SCREEN'
309 smix5
.inputs
[0].default_value
= 1
311 srgb
= nt
.nodes
.new(type="ShaderNodeSeparateRGB")
312 srgb
.location
= (786.54, 1370)
313 aniadd
= nt
.nodes
.new(type="ShaderNodeMath")
314 aniadd
.location
= (1220.16, 1235)
315 crgb
= nt
.nodes
.new(type="ShaderNodeCombineRGB")
316 crgb
.location
= (1671.33, 1510)
317 sunrgb
= nt
.nodes
.new(type="ShaderNodeMixRGB")
318 sunrgb
.name
= "Sun_color"
320 sunrgb
.blend_type
= 'MULTIPLY'
321 sunrgb
.inputs
[2].default_value
= (32, 30, 30, 200)
322 sunrgb
.inputs
[0].default_value
= 1
323 sunrgb
.location
= (4344.3, 360)
325 ntl(mix2
.inputs
[2], sunrgb
.outputs
[0])
327 ntl(smix1
.inputs
[0], sc2
.outputs
[0])
328 ntl(smix2
.inputs
[1], smix1
.outputs
[0])
329 ntl(smix2
.inputs
[2], sc1
.outputs
[0])
330 ntl(smix2_1
.inputs
[2], sc3_1
.outputs
[0])
331 ntl(smix3
.inputs
[0], sc4
.outputs
[0])
332 ntl(smix4
.inputs
[2], smix3
.outputs
[0])
333 ntl(smix4
.inputs
[1], sc3
.outputs
[0])
334 ntl(smix5
.inputs
[1], smix4
.outputs
[0])
335 ntl(smix2_1
.inputs
[1], smix2
.outputs
[0])
336 ntl(smix5
.inputs
[2], smix2_1
.outputs
[0])
337 ntl(sunopa
.inputs
[1], gam3
.outputs
[0])
338 ntl(gam3
.inputs
[0], smix5
.outputs
[0])
339 ntl(mix1
.inputs
[2], sc3
.outputs
[0])
340 ntl(sunopa
.inputs
[2], gam2
.outputs
[0])
342 ntl(sc1
.inputs
[0], n1
.outputs
['Fac'])
343 ntl(sc2
.inputs
[0], n2
.outputs
['Fac'])
345 skynor
= nt
.nodes
.new(type="ShaderNodeNormal")
346 skynor
.location
= (3294, 1070)
348 ntl(sc3
.inputs
[0], skynor
.outputs
[1])
349 ntl(sc4
.inputs
[0], skynor
.outputs
[1])
350 ntl(sc3_1
.inputs
[0], skynor
.outputs
[1])
351 ntl(map2
.inputs
[0], crgb
.outputs
[0])
352 ntl(skynor
.inputs
[0], tcor
.outputs
[0])
353 ntl(mix1_1
.inputs
[2], sc3
.outputs
[0])
354 ntl(srgb
.inputs
[0], tcor
.outputs
[0])
355 ntl(crgb
.inputs
[1], srgb
.outputs
[1])
356 ntl(crgb
.inputs
[2], srgb
.outputs
[2])
357 ntl(aniadd
.inputs
[1], srgb
.outputs
[0])
358 ntl(crgb
.inputs
[0], aniadd
.outputs
[0])
360 ntl(cr1
.inputs
[0], nor
.outputs
[1])
361 ntl(cr2
.inputs
[0], cr1
.outputs
[0])
362 ntl(cr3
.inputs
[0], nor
.outputs
[1])
363 ntl(nor
.inputs
[0], map1
.outputs
[0])
364 ntl(map1
.inputs
[0], tcor
.outputs
[0])
365 ntl(sunopa_1
.inputs
[1], smix5
.outputs
[0])
366 ntl(sunopa_1
.inputs
[2], mix2_1
.outputs
[0])
368 world_out
= self
.get_node_types(nt
, "OUTPUT_WORLD")
369 world_out
.location
= (7167.3, 360)
371 except Exception as e
:
372 error_handlers(self
, e
, "Make a Procedural sky has failed")
379 def draw_world_settings(col
, context
):
380 get_world
= context
.scene
.world
381 stored_name
= context
.scene
.dynamic_sky_name
382 get_world_keys
= bpy
.data
.worlds
.keys()
384 if stored_name
not in get_world_keys
or len(get_world_keys
) < 1:
385 col
.label(text
="The {} World could not".format(stored_name
),
387 col
.label(text
="be found in the Worlds' Data", icon
="BLANK1")
390 elif not (get_world
and get_world
.name
== stored_name
):
391 col
.label(text
="Please select the World", icon
="INFO")
392 col
.label(text
="named {}".format(stored_name
), icon
="BLANK1")
393 col
.label(text
="from the Properties > World", icon
="BLANK1")
396 pick_world
= bpy
.data
.worlds
[stored_name
]
398 m
= pick_world
.node_tree
.nodes
[28]
400 m
= pick_world
.node_tree
.nodes
['Sky_and_Horizon_colors'].inputs
[1]
401 n
= pick_world
.node_tree
.nodes
['Sky_and_Horizon_colors'].inputs
[2]
402 c
= pick_world
.node_tree
.nodes
['Cloud_color'].inputs
[1]
403 o
= pick_world
.node_tree
.nodes
['Cloud_opacity'].inputs
[0]
404 d
= pick_world
.node_tree
.nodes
['Cloud_density'].inputs
[0]
405 so
= pick_world
.node_tree
.nodes
['Sun_value'].inputs
[1]
406 so2
= pick_world
.node_tree
.nodes
['Shadow_color_saturation'].inputs
[1]
407 no
= pick_world
.node_tree
.nodes
['Sky_normal'].outputs
[0]
408 sof
= pick_world
.node_tree
.nodes
['Soft_hard'].inputs
[0]
409 bgp
= pick_world
.node_tree
.nodes
['Scene_Brightness'].inputs
[1]
411 suc
= pick_world
.node_tree
.nodes
['Sun_color'].inputs
[1]
413 col
.label(text
="Please Create a new World", icon
="INFO")
414 col
.label(text
="seems that there was already", icon
="BLANK1")
415 col
.label(text
="one called {}".format(stored_name
), icon
="BLANK1")
418 col
.label(text
="World: %s" % stored_name
)
421 col
.label(text
="Scene Control")
422 col
.prop(bgp
, "default_value", text
="Brightness")
423 col
.prop(so2
, "default_value", text
="Shadow color saturation")
425 col
.label(text
="Sky Control")
426 col
.prop(m
, "default_value", text
="Sky color")
427 col
.prop(n
, "default_value", text
="Horizon Color")
428 col
.prop(c
, "default_value", text
="Cloud color")
429 col
.prop(o
, "default_value", text
="Cloud opacity")
430 col
.prop(d
, "default_value", text
="Cloud density")
432 col
.label(text
="Sun Control")
433 col
.prop(suc
, "default_value", text
="")
434 col
.prop(so
, "default_value", text
="Sun value")
435 col
.prop(sof
, "default_value", text
="Soft hard")
437 col
.prop(no
, "default_value", text
="")
440 class Dynapanel(Panel
):
441 bl_label
= "Dynamic sky"
442 bl_idname
= "DYNSKY_PT_tools"
443 bl_space_type
= 'VIEW_3D'
444 bl_region_type
= 'UI'
445 bl_context
= "objectmode"
446 bl_category
= "Create"
447 bl_options
= {'DEFAULT_CLOSED'}
449 def draw(self
, context
):
451 layout
.operator("sky.dyn", text
="Create", icon
='MAT_SPHERE_SKY')
453 col
= layout
.column()
454 draw_world_settings(col
, context
)
458 bpy
.utils
.register_class(Dynapanel
)
459 bpy
.utils
.register_class(dsky
)
460 bpy
.types
.Scene
.dynamic_sky_name
= StringProperty(
467 bpy
.utils
.unregister_class(Dynapanel
)
468 bpy
.utils
.unregister_class(dsky
)
469 del bpy
.types
.Scene
.dynamic_sky_name
472 if __name__
== "__main__":