400d6dac4540ee78697c9aee9c1e613e95bc77b5
[blender.git] / intern / cycles / blender / addon / version_update.py
1 #
2 # Copyright 2011-2014 Blender Foundation
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 #
16
17 # <pep8 compliant>
18
19 import bpy
20
21 from bpy.app.handlers import persistent
22
23
24 def check_is_new_shading_ntree(node_tree):
25     for node in node_tree.nodes:
26         # If material has any node with ONLY new shading system
27         # compatibility then it's considered a Cycles material
28         # and versioning code would need to perform on it.
29         #
30         # We can not check for whether NEW_SHADING in compatibility
31         # because some nodes could have compatibility with both old
32         # and new shading system and they can't be used for any
33         # decision here.
34         if node.shading_compatibility == {'NEW_SHADING'}:
35             return True
36
37         # If node is only compatible with old shading system
38         # then material can not be Cycles material and we
39         # can stopiterating nodes now.
40         if node.shading_compatibility == {'OLD_SHADING'}:
41             return False
42     return False
43
44
45 def check_is_new_shading_material(material):
46     if not material.node_tree:
47         return False
48     return check_is_new_shading_ntree(material.node_tree)
49
50
51 def check_is_new_shading_world(world):
52     if not world.node_tree:
53         return False
54     return check_is_new_shading_ntree(world.node_tree)
55
56
57 def check_is_new_shading_lamp(lamp):
58     if not lamp.node_tree:
59         return False
60     return check_is_new_shading_ntree(lamp.node_tree)
61
62
63 def foreach_notree_node(nodetree, callback, traversed):
64     if nodetree in traversed:
65         return
66     traversed.add(nodetree)
67     for node in nodetree.nodes:
68         callback(node)
69         if node.bl_idname == 'ShaderNodeGroup':
70             foreach_notree_node(node.node_tree, callback, traversed)
71
72
73 def foreach_cycles_node(callback):
74     traversed = set()
75     for material in bpy.data.materials:
76         if check_is_new_shading_material(material):
77                 foreach_notree_node(material.node_tree,
78                                     callback,
79                                     traversed)
80     for world in bpy.data.worlds:
81         if check_is_new_shading_world(world):
82                 foreach_notree_node(world.node_tree,
83                                     callback,
84                                     traversed)
85     for lamp in bpy.data.lamps:
86         if check_is_new_shading_world(lamp):
87                 foreach_notree_node(lamp.node_tree,
88                                     callback,
89                                     traversed)
90
91
92 def displacement_node_insert(material, nodetree, traversed):
93     if nodetree in traversed:
94         return
95     traversed.add(nodetree)
96
97     for node in nodetree.nodes:
98         if node.bl_idname == 'ShaderNodeGroup':
99             displacement_node_insert(material, node.node_tree, traversed)
100
101     # Gather links to replace
102     displacement_links = []
103     for link in nodetree.links:
104         if link.to_node.bl_idname == 'ShaderNodeOutputMaterial' and \
105            link.from_node.bl_idname != 'ShaderNodeDisplacement' and \
106            link.to_socket.identifier == 'Displacement':
107            displacement_links.append(link)
108
109     # Replace links with displacement node
110     for link in displacement_links:
111         from_node = link.from_node
112         from_socket = link.from_socket
113         to_node = link.to_node
114         to_socket = link.to_socket
115
116         nodetree.links.remove(link)
117
118         node = nodetree.nodes.new(type='ShaderNodeDisplacement')
119         node.location[0] = 0.5 * (from_node.location[0] + to_node.location[0]);
120         node.location[1] = 0.5 * (from_node.location[1] + to_node.location[1]);
121         node.inputs['Scale'].default_value = 0.1
122         node.inputs['Midlevel'].default_value = 0.0
123
124         nodetree.links.new(from_socket, node.inputs['Height'])
125         nodetree.links.new(node.outputs['Displacement'], to_socket)
126
127 def displacement_nodes_insert():
128     traversed = set()
129     for material in bpy.data.materials:
130         if check_is_new_shading_material(material):
131             displacement_node_insert(material, material.node_tree, traversed)
132
133 def displacement_node_space(node):
134     if node.bl_idname == 'ShaderNodeDisplacement':
135         if node.space != 'WORLD':
136             node.space = 'OBJECT'
137
138
139 def mapping_node_order_flip(node):
140     """
141     Flip euler order of mapping shader node
142     """
143     if node.bl_idname == 'ShaderNodeMapping':
144         rot = node.rotation.copy()
145         rot.order = 'ZYX'
146         quat = rot.to_quaternion()
147         node.rotation = quat.to_euler('XYZ')
148
149
150 def vector_curve_node_remap(node):
151     """
152     Remap values of vector curve node from normalized to absolute values
153     """
154     if node.bl_idname == 'ShaderNodeVectorCurve':
155         node.mapping.use_clip = False
156         for curve in node.mapping.curves:
157             for point in curve.points:
158                 point.location.x = (point.location.x * 2.0) - 1.0
159                 point.location.y = (point.location.y - 0.5) * 2.0
160         node.mapping.update()
161
162
163 def custom_bake_remap(scene):
164     """
165     Remap bake types into the new types and set the flags accordingly
166     """
167     bake_lookup = (
168         'COMBINED',
169         'AO',
170         'SHADOW',
171         'NORMAL',
172         'UV',
173         'EMIT',
174         'ENVIRONMENT',
175         'DIFFUSE_DIRECT',
176         'DIFFUSE_INDIRECT',
177         'DIFFUSE_COLOR',
178         'GLOSSY_DIRECT',
179         'GLOSSY_INDIRECT',
180         'GLOSSY_COLOR',
181         'TRANSMISSION_DIRECT',
182         'TRANSMISSION_INDIRECT',
183         'TRANSMISSION_COLOR',
184         'SUBSURFACE_DIRECT',
185         'SUBSURFACE_INDIRECT',
186         'SUBSURFACE_COLOR')
187
188     diffuse_direct_idx = bake_lookup.index('DIFFUSE_DIRECT')
189
190     cscene = scene.cycles
191
192     # Old bake type
193     bake_type_idx = cscene.get("bake_type")
194
195     if bake_type_idx is None:
196         cscene.bake_type = 'COMBINED'
197         return
198
199     # File doesn't need versioning
200     if bake_type_idx < diffuse_direct_idx:
201         return
202
203     # File needs versioning
204     bake_type = bake_lookup[bake_type_idx]
205     cscene.bake_type, end = bake_type.split('_')
206
207     if end == 'DIRECT':
208         scene.render.bake.use_pass_indirect = False
209         scene.render.bake.use_pass_color = False
210
211     elif end == 'INDIRECT':
212         scene.render.bake.use_pass_direct = False
213         scene.render.bake.use_pass_color = False
214
215     elif end == 'COLOR':
216         scene.render.bake.use_pass_direct = False
217         scene.render.bake.use_pass_indirect = False
218
219
220 @persistent
221 def do_versions(self):
222     if bpy.context.user_preferences.version <= (2, 78, 1):
223         prop = bpy.context.user_preferences.addons[__package__].preferences
224         system = bpy.context.user_preferences.system
225         if not prop.is_property_set("compute_device_type"):
226             # Device might not currently be available so this can fail
227             try:
228                 if system.legacy_compute_device_type == 1:
229                     prop.compute_device_type = 'OPENCL'
230                 elif system.legacy_compute_device_type == 2:
231                     prop.compute_device_type = 'CUDA'
232                 else:
233                     prop.compute_device_type = 'NONE'
234             except:
235                 pass
236
237             # Init device list for UI
238             prop.get_devices()
239
240     # We don't modify startup file because it assumes to
241     # have all the default values only.
242     if not bpy.data.is_saved:
243         return
244
245     # Clamp Direct/Indirect separation in 270
246     if bpy.data.version <= (2, 70, 0):
247         for scene in bpy.data.scenes:
248             cscene = scene.cycles
249             sample_clamp = cscene.get("sample_clamp", False)
250             if (sample_clamp and
251                 not cscene.is_property_set("sample_clamp_direct") and
252                 not cscene.is_property_set("sample_clamp_indirect")):
253
254                 cscene.sample_clamp_direct = sample_clamp
255                 cscene.sample_clamp_indirect = sample_clamp
256
257     # Change of Volume Bounces in 271
258     if bpy.data.version <= (2, 71, 0):
259         for scene in bpy.data.scenes:
260             cscene = scene.cycles
261             if not cscene.is_property_set("volume_bounces"):
262                 cscene.volume_bounces = 1
263
264     # Caustics Reflective/Refractive separation in 272
265     if bpy.data.version <= (2, 72, 0):
266         for scene in bpy.data.scenes:
267             cscene = scene.cycles
268             if (cscene.get("no_caustics", False) and
269                 not cscene.is_property_set("caustics_reflective") and
270                 not cscene.is_property_set("caustics_refractive")):
271
272                 cscene.caustics_reflective = False
273                 cscene.caustics_refractive = False
274
275     # Euler order was ZYX in previous versions.
276     if bpy.data.version <= (2, 73, 4):
277         foreach_cycles_node(mapping_node_order_flip)
278
279     if bpy.data.version <= (2, 76, 5):
280         foreach_cycles_node(vector_curve_node_remap)
281
282     # Baking types changed
283     if bpy.data.version <= (2, 76, 6):
284         for scene in bpy.data.scenes:
285             custom_bake_remap(scene)
286
287     # Several default changes for 2.77
288     if bpy.data.version <= (2, 76, 8):
289         for scene in bpy.data.scenes:
290             cscene = scene.cycles
291
292             # Samples
293             if not cscene.is_property_set("samples"):
294                 cscene.samples = 10
295
296             # Preview Samples
297             if not cscene.is_property_set("preview_samples"):
298                 cscene.preview_samples = 10
299
300             # Filter
301             if not cscene.is_property_set("filter_type"):
302                 cscene.pixel_filter_type = 'GAUSSIAN'
303
304             # Tile Order
305             if not cscene.is_property_set("tile_order"):
306                 cscene.tile_order = 'CENTER'
307
308         for lamp in bpy.data.lamps:
309             clamp = lamp.cycles
310
311             # MIS
312             if not clamp.is_property_set("use_multiple_importance_sampling"):
313                 clamp.use_multiple_importance_sampling = False
314
315         for mat in bpy.data.materials:
316             cmat = mat.cycles
317
318             # Volume Sampling
319             if not cmat.is_property_set("volume_sampling"):
320                 cmat.volume_sampling = 'DISTANCE'
321
322     if bpy.data.version <= (2, 76, 9):
323         for world in bpy.data.worlds:
324             cworld = world.cycles
325
326             # World MIS
327             if not cworld.is_property_set("sample_as_light"):
328                 cworld.sample_as_light = False
329
330             # World MIS Samples
331             if not cworld.is_property_set("samples"):
332                 cworld.samples = 4
333
334             # World MIS Resolution
335             if not cworld.is_property_set("sample_map_resolution"):
336                 cworld.sample_map_resolution = 256
337
338     if bpy.data.version <= (2, 76, 10):
339         for scene in bpy.data.scenes:
340             cscene = scene.cycles
341             if cscene.is_property_set("filter_type"):
342                 if not cscene.is_property_set("pixel_filter_type"):
343                     cscene.pixel_filter_type = cscene.filter_type
344                 if cscene.filter_type == 'BLACKMAN_HARRIS':
345                     cscene.filter_type = 'GAUSSIAN'
346
347     if bpy.data.version <= (2, 78, 2):
348         for scene in bpy.data.scenes:
349             cscene = scene.cycles
350             if not cscene.is_property_set("light_sampling_threshold"):
351                 cscene.light_sampling_threshold = 0.0
352
353     if bpy.data.version <= (2, 79, 0):
354         for scene in bpy.data.scenes:
355             cscene = scene.cycles
356             # Default changes
357             if not cscene.is_property_set("aa_samples"):
358                 cscene.aa_samples = 4
359             if not cscene.is_property_set("preview_aa_samples"):
360                 cscene.preview_aa_samples = 4
361             if not cscene.is_property_set("blur_glossy"):
362                 cscene.blur_glossy = 0.0
363             if not cscene.is_property_set("sample_clamp_indirect"):
364                 cscene.sample_clamp_indirect = 0.0
365
366     if bpy.data.version <= (2, 79, 1):
367         displacement_nodes_insert()
368
369     if bpy.data.version <= (2, 79, 2):
370         for mat in bpy.data.materials:
371             cmat = mat.cycles
372             if not cmat.is_property_set("displacement_method"):
373                 cmat.displacement_method = 'BUMP'
374
375         foreach_cycles_node(displacement_node_space)