Bugfixing for OSL script nodes with internal scripts.
[blender-addons-contrib.git] / online_mat_lib / __init__.py
1 #  ***** BEGIN GPL LICENSE BLOCK *****
2 #
3 #  This program is free software: you can redistribute it and/or modify
4 #  it under the terms of the GNU General Public License as published by
5 #  the Free Software Foundation, either version 3 of the License, or
6 #  (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, see http://www.gnu.org/licenses/
15 #  or write to the Free Software Foundation, Inc., 51 Franklin Street,
16 #  Fifth Floor, Boston, MA 02110-1301, USA.
17 #
18 #  The Original Code is Copyright (C) 2012 by Peter Cassetta    ###
19 #  All rights reserved.
20 #
21 #  Contact:                        matlib@peter.cassetta.info   ###
22 #  Information:  http://peter.cassetta.info/material-library/   ###
23 #
24 #  The Original Code is: all of this file.
25 #
26 #  Contributor(s): Peter Cassetta.
27 #
28 #  ***** END GPL LICENSE BLOCK *****
29
30 bl_info = {
31     "name": "Online Material Library",
32     "author": "Peter Cassetta",
33     "version": (0, 5),
34     "blender": (2, 6, 3),
35     "location": "Properties > Material > Online Material Library",
36     "description": "Browse and download materials from online CC0 libraries.",
37     "warning": "Beta version",
38     "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/Material/Online_Material_Library",
39     "tracker_url": "http://projects.blender.org/tracker/index.php?func=detail&aid=31802",
40     "category": "Material"}
41
42 import bpy
43 from bpy_extras.io_utils import ExportHelper
44 import os.path
45 import http.client
46 import xml.dom.minidom
47
48 library = ""
49 library_data = []
50 update_data = ["Up-to-date.", ""]
51
52 library_enum_items = [("peter.cassetta.info/material-library/release/", "Peter's Library - Release", "Stable library hosted on peter.cassetta.info (Default)"),
53                       ("peter.cassetta.info/material-library/testing/", "Peter's Library - Testing", "Continually updated library hosted on peter.cassetta.info (Online only)"),
54                       ("bundled", "Bundled Library", "The library bundled with this add-on (Offline only)")]
55 bpy.types.Scene.mat_lib_library = bpy.props.EnumProperty(name = "Select library:", items = library_enum_items, description = "Choose a library", options = {'SKIP_SAVE'})
56
57 working_mode = "none"
58 mat_lib_host = ""
59 mat_lib_location = ""
60 mat_lib_cached_files = -1
61 if os.path.exists(os.path.join(bpy.context.user_preferences.filepaths.script_directory, "addons", "online_mat_lib", "material-library")):
62     mat_lib_folder = os.path.join(bpy.context.user_preferences.filepaths.script_directory, "addons", "online_mat_lib", "material-library")
63 elif os.path.exists(os.path.join(bpy.utils.script_path_user(), "addons", "online_mat_lib", "material-library")):
64     mat_lib_folder = os.path.join(bpy.utils.script_path_user(), "addons", "online_mat_lib", "material-library")
65 elif os.path.exists(os.path.join(bpy.utils.script_paths()[0], "addons", "online_mat_lib", "material-library")):
66     mat_lib_folder = os.path.join(bpy.utils.script_paths()[0], "addons", "online_mat_lib", "material-library")
67 elif os.path.exists(os.path.join(bpy.utils.script_paths()[0], "addons_contrib", "online_mat_lib", "material-library")):
68     mat_lib_folder = os.path.join(bpy.utils.script_paths()[0], "addons_contrib", "online_mat_lib", "material-library")
69 else:
70     print("ONLINE MATERIAL LIBRARY -- MAJOR PROBLEM:"\
71     "COULD NOT LOCATE ADD-ON INSTALLATION PATH.")
72     mat_lib_folder = "error"
73
74 mat_lib_contents = "Please refresh."
75 mat_lib_category_filenames = []
76 mat_lib_category_types = []
77 mat_lib_category_names = []
78 mat_lib_categories = 0
79
80 category_contents = "None"
81 category_name = ""
82 category_filename = ""
83 category_materials = 0
84
85 category_type = "none"
86
87 sub_category_contents = "None"
88 sub_category_name = ""
89 sub_category_filename = ""
90 sub_category_categories = 0
91 sub_category_names = []
92 sub_category_filenames = []
93
94 material_names = []
95 material_filenames = []
96 material_contributors = []
97 material_ratings = []
98 material_fireflies = []
99 material_speeds = []
100 material_complexities = []
101 material_scripts = []
102 material_images = []
103
104 material_file_contents = ""
105
106 current_material_number = -1
107 current_material_cached = False
108 current_material_previewed = False
109 material_detail_view = "RENDER"
110 preview_message = []
111 node_message = []
112 save_filename = ""
113 script_stack = []
114 osl_scripts = []
115
116 bpy.types.Scene.mat_lib_auto_preview = bpy.props.BoolProperty(name = "Auto-download previews", description = "Automatically download material previews in online mode", default = True, options = {'SKIP_SAVE'})
117 bpy.types.Scene.mat_lib_show_osl_materials = bpy.props.BoolProperty(name = "Show OSL materials", description = "Enable to show materials with OSL shading scripts", default = False, options = {'SKIP_SAVE'})
118 bpy.types.Scene.mat_lib_show_textured_materials = bpy.props.BoolProperty(name = "Show textured materials", description = "Enable to show materials with image textures", default = True, options = {'SKIP_SAVE'})
119 bpy.types.Scene.mat_lib_osl_only_trusted = bpy.props.BoolProperty(name = "Use OSL scripts from trusted sources only", description = "Disable to allow downloading OSL scripts from anywhere on the web (Not recommended)", default = True, options = {'SKIP_SAVE'})
120 bpy.types.Scene.mat_lib_images_only_trusted = bpy.props.BoolProperty(name = "Use image textures from trusted sources only", description = "Disable to allow downloading image textures from anywhere on the web (Not recommended)", default = True, options = {'SKIP_SAVE'})
121
122 bpy.types.Scene.mat_lib_bcm_write = bpy.props.StringProperty(name = "Text Datablock", description = "Name of text datablock to write .bcm data to", default="bcm_file", options = {'SKIP_SAVE'})
123 bpy.types.Scene.mat_lib_bcm_read = bpy.props.StringProperty(name = "Text Datablock", description = "Name of text datablock to read .bcm data from", default="bcm_file", options = {'SKIP_SAVE'})
124 bpy.types.Scene.mat_lib_bcm_name = bpy.props.StringProperty(name = "Material Name", description = "Specify a name for the new material", default="Untitled", options = {'SKIP_SAVE'})
125
126 bpy.types.Scene.mat_lib_bcm_save_location = bpy.props.StringProperty(name = "Save location", description = "Directory to save .bcm files in", default=mat_lib_folder + os.sep + "my-materials" + os.sep, options = {'SKIP_SAVE'}, subtype="DIR_PATH")
127 bpy.types.Scene.mat_lib_bcm_open_location = bpy.props.StringProperty(name = "Open location", description = "Location of .bcm file to open", default=mat_lib_folder + os.sep + "my-materials" + os.sep + "untitled.bcm", options = {'SKIP_SAVE'})
128
129 category_enum_items = [("None0", "None", "No category selected")]
130 bpy.types.Scene.mat_lib_material_category = bpy.props.EnumProperty(name = "", items = category_enum_items, description = "Choose a category", options = {'SKIP_SAVE'})
131 prev_category = "None0"
132
133 subcategory_enum_items = [("None0", "None", "No Subcategory Selected")]
134 bpy.types.Scene.mat_lib_material_subcategory = bpy.props.EnumProperty(name = "", items = subcategory_enum_items, description = "Choose a subcategory", options = {'SKIP_SAVE'})
135
136 class OnlineMaterialLibraryPanel(bpy.types.Panel):
137     """Creates a Panel in the Object properties window"""
138     bl_label = "Online Material Library"
139     bl_idname = "OnlineMaterialLibraryPanel"
140     bl_space_type = "PROPERTIES"
141     bl_region_type = "WINDOW"
142     bl_context = "material"
143     
144     def draw(self, context):
145         global show_success_message
146         global show_success_message_timeout
147         global current_material_number
148         global mat_lib_contents
149         global prev_category
150         global save_filename
151         
152         layout = self.layout
153         
154         if context.scene.render.engine == "CYCLES":
155             #Cycles is enabled!
156             row = layout.row()
157             
158             if category_type is not "info" and category_type is not "settings" and category_type is not "tools":
159                 if mat_lib_contents == "" or mat_lib_contents == "Please refresh.":
160                     if mat_lib_folder == "error":
161                         row.label(text="ERROR: Could not find installation path!", icon='ERROR')
162                     else:
163                         #Material Library Contents variable is empty -- show welcome message
164                         row.label(text="Online Material Library Add-on -- Version 0.5", icon='SMOOTH')
165                         
166                         row = layout.row()
167                         rowcol = row.column(align=True)
168                         rowcol.alignment = 'EXPAND'
169                         rowcol.prop(context.scene, "mat_lib_library", text="")
170                         
171                         rowcolrow = rowcol.row(align=True)
172                         rowcolrow.alignment = 'EXPAND'
173                         if "bundled" not in context.scene.mat_lib_library:
174                             rowcolrow.operator("material.libraryconnect", text="Connect", icon='WORLD').mode = "online"
175                         if "testing" not in context.scene.mat_lib_library:
176                             rowcolrow.operator("material.libraryconnect", text="Work Offline", icon='DISK_DRIVE').mode = "offline"
177                     
178                 elif working_mode is not "none":
179                     #We have a valid material library
180                     row = layout.row(align=True)
181                     row.alignment = 'EXPAND'
182                     row.prop(bpy.context.scene, "mat_lib_material_category")
183                 else:
184                     #Could not retrive a valid material library
185                     row.label(text="Could not retrieve material library.", icon='CANCEL')
186                     row = layout.row()
187                     row.label(text=str(mat_lib_contents))
188                     row = layout.row()
189                     row.label(text="..." + str(mat_lib_contents)[-50:])
190                     
191                     row = layout.row()
192                     rowcol = row.column(align=True)
193                     rowcol.alignment = 'EXPAND'
194                     rowcol.prop(context.scene, "mat_lib_library", text="")
195                     
196                     rowcolrow = rowcol.row(align=True)
197                     rowcolrow.alignment = 'EXPAND'
198                     if "bundled" not in context.scene.mat_lib_library:
199                         rowcolrow.operator("material.libraryconnect", text="Attempt Reconnect", icon='WORLD').mode = "online"
200                     if "testing" not in context.scene.mat_lib_library:
201                         rowcolrow.operator("material.libraryconnect", text="Work Offline", icon='DISK_DRIVE').mode = "offline"
202             
203             #Here we check if the user has changed the category, if so, update.
204             if prev_category != bpy.context.scene.mat_lib_material_category:
205                 prev_category = bpy.context.scene.mat_lib_material_category
206                 libraryCategoryUpdate()
207             
208             if category_type == "none":
209                 #Not browsing category
210                 if working_mode is not "none":
211                     row = layout.row()
212                     rowcol = row.column(align=True)
213                     rowcol.alignment = 'EXPAND'
214                     rowcol.prop(context.scene, "mat_lib_library", text="")
215                     
216                     rowcolrow = rowcol.row(align=True)
217                     rowcolrow.alignment = 'EXPAND'
218                     if "bundled" not in context.scene.mat_lib_library:
219                         if working_mode == "online":
220                             rowcolrow.operator("material.libraryconnect", text="Reconnect", icon='WORLD').mode = "online"
221                         else:
222                             rowcolrow.operator("material.libraryconnect", text="Connect", icon='WORLD').mode = "online"
223                     if "testing" not in context.scene.mat_lib_library:
224                         if working_mode == "offline":
225                             rowcolrow.operator("material.libraryconnect", text="Reload Library", icon='DISK_DRIVE').mode = "offline"
226                         else:
227                             rowcolrow.operator("material.libraryconnect", text="Work Offline", icon='DISK_DRIVE').mode = "offline"
228                 
229                 row = layout.row(align=True)
230                 row.alignment = 'EXPAND'
231                 row.operator("material.libraryinfo", text="Info", icon='INFO')
232                 row.operator("material.librarytools", text="Tools", icon='MODIFIER')
233                 row.operator("material.librarysettings", text="Settings", icon='SETTINGS')
234                 
235                 if "Up-to-date." not in update_data[0]:
236                     row = layout.row()
237                     row.label(text=update_data[0])
238                     row.operator("wm.url_open", text="Get latest version", icon='WORLD').url = update_data[1]
239                     
240             elif category_type == "info":
241                 row.label(text="Add-on Info", icon='INFO')
242                 row.operator("material.libraryhome", text="", icon='LOOP_BACK')
243                 
244                 row = layout.row()
245                 row.operator("wm.url_open", text="All materials are CC0 - learn more.", emboss=False).url = "http://creativecommons.org/publicdomain/zero/1.0/"
246                 
247                 row = layout.row()
248                 row.operator("wm.url_open", text="Material previews generated with B.M.P.S.", emboss=False).url = "https://svn.blender.org/svnroot/bf-blender/trunk/lib/tests/rendering/cycles/bmps.blend"
249                 row = layout.row()
250                 row.operator("wm.url_open", text="B.M.P.S. created by Robin \"tuqueque\" MarĂ­n", emboss=False).url = "http://blenderartists.org/forum/showthread.php?151903-b.m.p.s.-1.5!"
251             
252             elif category_type == "settings":
253                 row.label(text="Library Settings", icon='SETTINGS')
254                 row.operator("material.libraryhome", text="", icon='LOOP_BACK')
255                 
256                 row = layout.row()
257                 row.prop(bpy.context.scene, "mat_lib_auto_preview")
258                 row = layout.row()
259                 row.prop(bpy.context.scene, "mat_lib_osl_only_trusted")
260                 row = layout.row()
261                 row.prop(bpy.context.scene, "mat_lib_images_only_trusted")
262                 
263                 row = layout.row()
264                 row.label(text="Cached data for active library:")
265                 
266                 row = layout.row()
267                 if mat_lib_cached_files == 0:
268                     row.label(text="No cached files.")
269                 elif mat_lib_cached_files == -2:
270                     row.label(text="The Bundled library contains no cached files.")
271                 elif mat_lib_cached_files == 1:
272                     row.label(text="1 cached file.")
273                     row.operator("material.libraryclearcache", text="Clear Cache", icon='CANCEL')
274                 elif mat_lib_cached_files != -1:
275                     row.label(text=str(mat_lib_cached_files) + " cached files.")
276                     row.operator("material.libraryclearcache", text="Clear Cache", icon='CANCEL')
277                 else:
278                     row.label(text="Please select a library first.", icon="ERROR")
279             
280             elif category_type == "tools":
281                 row.label(text="Material Tools", icon='MODIFIER')
282                 row.operator("material.libraryhome", text="", icon='LOOP_BACK')
283                 
284                 row = layout.row()
285                 row.label(text="Write material data to text as .bcm:")
286                 
287                 row = layout.row(align=True)
288                 row.alignment = 'EXPAND'
289                 row.prop(bpy.context.scene, "mat_lib_bcm_write", text="", icon="TEXT")
290                 row.operator("material.libraryconvert", text="Write to text", icon='MATERIAL_DATA')
291                 
292                 row = layout.row()
293                 row.label(text="Save material(s) as .bcm files:")
294                 
295                 row = layout.row()
296                 col = row.column(align=True)
297                 col.alignment = 'EXPAND'
298                 colrow = col.row()
299                 colrow.prop(context.scene, "mat_lib_bcm_save_location", text="")
300                 colrow = col.row(align=True)
301                 colrow.alignment = 'EXPAND'
302                 colrow.operator("material.libraryconvert", text="Save active", icon='DISK_DRIVE').save_location = context.scene.mat_lib_bcm_save_location
303                 save_button = colrow.operator("material.libraryconvert", text="Save all materials", icon='DISK_DRIVE')
304                 save_button.save_location = context.scene.mat_lib_bcm_save_location
305                 save_button.all_materials = True
306                 
307                 row = layout.row()
308                 row.label(text="Open a local .bcm file:")
309                  
310                 row = layout.row()
311                 col = row.column(align=True)
312                 col.alignment = 'EXPAND'
313                 colrow = col.row()
314                 colrow.prop(context.scene, "mat_lib_bcm_open_location", text="")
315                 colrow.operator("buttons.file_browse", text="", icon='FILESEL').relative_path = False
316                 colrow = col.row(align=True)
317                 colrow.alignment = 'EXPAND'
318                 colrow.operator("material.libraryadd", text="Add to materials", icon='ZOOMIN').open_location = context.scene.mat_lib_bcm_open_location
319                 colrow.operator("material.libraryapply", text="Apply to active", icon='PASTEDOWN').open_location = context.scene.mat_lib_bcm_open_location
320                 
321                 row = layout.row()
322                 row.label(text="Read .bcm data in a text block to a material:")
323                  
324                 row = layout.row()
325                 col = row.column(align=True)
326                 col.alignment = 'EXPAND'
327                 colrow = col.row()
328                 colrow.prop(context.scene, "mat_lib_bcm_read", text="", icon='TEXT')
329                 colrow = col.row()
330                 colrow.prop(context.scene, "mat_lib_bcm_name", text="", icon='MATERIAL')
331                 colrow = col.row(align=True)
332                 colrow.alignment = 'EXPAND'
333                 colrow.operator("material.libraryadd", text="Add to materials", icon='ZOOMIN').text_block = context.scene.mat_lib_bcm_read
334                 colrow.operator("material.libraryapply", text="Apply to active", icon='PASTEDOWN').text_block = context.scene.mat_lib_bcm_read
335                 
336             elif category_type == "category":
337                 #Browsing category - show materials
338                 row = layout.row()
339                 matwrap = row.box()
340                 i = 0
341                 while i < category_materials:
342                     if i == current_material_number:
343                         matwraprow = matwrap.row()
344                         matwrapbox = matwraprow.box()
345                         matwrapboxrow = matwrapbox.row()
346                         matwrapboxrow.operator("material.libraryviewmaterial", text=material_names[i], icon='MATERIAL', emboss=False).material = i
347                         matwrapboxrowcol = matwrapboxrow.column()
348                         matwrapboxrowcolrow = matwrapboxrowcol.row()
349                         matwrapboxrowcolrow.operator("material.libraryviewmaterial", text="", icon='BLANK1', emboss=False).material = i
350                         matwrapboxrowcolrow.operator("material.libraryviewmaterial", text="", icon='BLANK1', emboss=False).material = i
351                         matwrapboxrowcol = matwrapboxrow.column()
352                         matwrapboxrowcolrow = matwrapboxrowcol.row()
353                         matwrapboxrowcolrowsplit = matwrapboxrowcolrow.split(percentage=0.8)
354                         matwrapboxrowcolrowsplitrow = matwrapboxrowcolrowsplit.row()
355                         
356                         #Ratings
357                         if material_ratings[i] == 0:
358                             matwrapboxrowcolrowsplitrow.operator("material.libraryviewmaterial", text="Unrated", emboss=False).material = i
359                         else:
360                             e = 0
361                             while e < material_ratings[i]:
362                                 matwrapboxrowcolrowsplitrow.operator("material.libraryviewmaterial", text="", icon='SOLO_ON', emboss=False).material = i
363                                 e = e + 1
364                             
365                             if material_ratings[i] is not 5:    
366                                 e = 0
367                                 while e < (5 - material_ratings[i]):
368                                     matwrapboxrowcolrowsplitrow.operator("material.libraryviewmaterial", text="", icon='SOLO_OFF', emboss=False).material = i
369                                     e = e + 1
370                     else:
371                         matwraprow = matwrap.row()
372                         matwrapcol = matwraprow.column()
373                         matwrapcolrow = matwrapcol.row()
374                         matwrapcolrow.operator("material.libraryviewmaterial", text=material_names[i], icon='MATERIAL', emboss=False).material = i
375                         matwrapcolrowcol = matwrapcolrow.column()
376                         matwrapcolrowcolrow = matwrapcolrowcol.row()
377                         matwrapcolrowcolrow.operator("material.libraryviewmaterial", text="", icon='BLANK1', emboss=False).material = i
378                         matwrapcolrowcolrow.operator("material.libraryviewmaterial", text="", icon='BLANK1', emboss=False).material = i
379                         matwrapcolrowcol = matwrapcolrow.column()
380                         matwrapcolrowcolrow = matwrapcolrowcol.row()
381                         matwrapcolrowcolrowsplit = matwrapcolrowcolrow.split(percentage=0.8)
382                         matwrapcolrowcolrowsplitrow = matwrapcolrowcolrowsplit.row()
383                         
384                         #Ratings
385                         if material_ratings[i] == 0:
386                             matwrapcolrowcolrowsplitrow.operator("material.libraryviewmaterial", text="Unrated", emboss=False).material = i
387                         else:
388                             e = 0
389                             while e < material_ratings[i]:
390                                 matwrapcolrowcolrowsplitrow.operator("material.libraryviewmaterial", text="", icon='SOLO_ON', emboss=False).material = i
391                                 e = e + 1
392                             
393                             if material_ratings[i] is not 5:    
394                                 e = 0
395                                 while e < (5 - material_ratings[i]):
396                                     matwrapcolrowcolrowsplitrow.operator("material.libraryviewmaterial", text="", icon='SOLO_OFF', emboss=False).material = i
397                                     e = e + 1
398                     i = i + 1
399                 
400                 if current_material_number is not -1:
401                     #Display selected material's info
402                     row = layout.row()
403                     infobox = row.box()
404                     inforow = infobox.row()
405                     
406                     #Material name
407                     inforow.label(text=(material_names[current_material_number]))
408                     
409                     #Preview download button
410                     if current_material_previewed == False:
411                         preview_button = inforow.operator("material.librarypreview", text="", icon='COLOR', emboss=False)
412                         preview_button.name = material_names[current_material_number]
413                         preview_button.filename = material_filenames[current_material_number]
414                     
415                     if library == "release":
416                         #Cache indicator/button
417                         if current_material_cached:
418                             inforow.label(text="", icon="SAVE_COPY")
419                         else:
420                             inforow.operator("material.librarycache", text="", icon="LONGDISPLAY", emboss=False).filename = material_filenames[current_material_number]
421                     
422                     #Close button
423                     inforow.operator("material.libraryviewmaterial", text="", icon='PANEL_CLOSE').material = -1
424                     
425                     #inforow = infobox.row()
426                     inforowsplit = infobox.split(percentage=0.5)
427                     
428                     #Display a preview
429                     if bpy.data.textures.find("mat_lib_preview_texture") == -1:
430                         bpy.data.textures.new("mat_lib_preview_texture", "IMAGE")
431                         preview_texture = bpy.data.textures["mat_lib_preview_texture"]
432                         inforowcol = inforowsplit.column()
433                         
434                         if material_contributors[current_material_number] != "Unknown" and material_contributors[current_material_number] != "Anonymous":
435                             inforowcolrow = inforowcol.row()
436                             inforowcolrow.label(text="By %s." % material_contributors[current_material_number])
437                         else:
438                             inforowcolrow = inforowcol.row()
439                             inforowcolrow.label(text="Author unknown.")
440                         inforowcolrow = inforowcol.row()
441                         inforowcolrow.template_preview(preview_texture)
442                     else:
443                         preview_texture = bpy.data.textures['mat_lib_preview_texture']
444                         inforowcol = inforowsplit.column()
445                         if material_contributors[current_material_number] != "Unknown" and material_contributors[current_material_number] != "Anonymous":
446                             inforowcolrow = inforowcol.row()
447                             inforowcolrow.label(text="By %s." % material_contributors[current_material_number])
448                         else:
449                             inforowcolrow = inforowcol.row()
450                             inforowcolrow.label(text="Author unknown.")
451                         inforowcolrow = inforowcol.row()
452                         inforowcolrow.template_preview(preview_texture)
453                     
454                     inforowcol = inforowsplit.column()
455                     inforowcolrow = inforowcol.row()
456                     inforowcolcol = inforowcol.column(align=True)
457                     inforowcolcol.alignment = 'EXPAND'
458                     inforowcolcolrow = inforowcolcol.row()
459                     if material_detail_view == 'RENDER':
460                         if material_fireflies[current_material_number] == "high":
461                             inforowcolcolrow.label(text="Firefly Level: High")
462                             inforowcolcolrow.label(text="", icon='PARTICLES')
463                         elif material_fireflies[current_material_number] == "medium":
464                             inforowcolcolrow.label(text="Firefly Level: Medium")
465                             inforowcolcolrow.label(text="", icon='MOD_PARTICLES')
466                         else:
467                             inforowcolcolrow.label(text="Firefly Level: Low")
468                         inforowcolcolrow = inforowcolcol.row()
469                             
470                         if material_complexities[current_material_number] == "simple":
471                             inforowcolcolrow.label(text="Complexity: Simple")
472                         elif material_complexities[current_material_number] == "intermediate":
473                             inforowcolcolrow.label(text="Complexity: Intermediate")
474                         elif material_complexities[current_material_number] == "complex":
475                             inforowcolcolrow.label(text="Complexity: Complex")
476                         inforowcolcolrow = inforowcolcol.row()
477                         
478                         if material_speeds[current_material_number] == "slow":
479                             inforowcolcolrow.label(text="Render Speed: Slow")
480                             inforowcolcolrow.label(text="", icon='PREVIEW_RANGE')
481                         elif material_speeds[current_material_number] == "fair":
482                             inforowcolcolrow.label(text="Render Speed: Fair")
483                             inforowcolcolrow.label(text="", icon='TIME')
484                         else:
485                             inforowcolcolrow.label(text="Render Speed: Good")
486                         inforowcolcolrow = inforowcolcol.row()
487                         details = inforowcolcolrow.row(align=True)
488                         details.alignment = 'RIGHT'
489                         detailshidden = details.row()
490                         detailshidden.enabled = False
491                         detailshidden.operator("material.librarydetailview", text="", icon='PLAY_REVERSE').mode = "PREVIOUS"
492                         details.operator("material.librarydetailview", text="", icon='PLAY').mode = "NEXT"
493                     elif material_detail_view == "DATA":
494                         if material_scripts[current_material_number] == "0":
495                             inforowcolcolrow.label(text="OSL Scripts: 0")
496                         else:
497                             inforowcolcolrow.label(text="OSL Scripts: %s" % material_scripts[current_material_number])
498                             inforowcolcolrow.label(text="", icon='TEXT')
499                         inforowcolcolrow = inforowcolcol.row()
500                     
501                         if material_images[current_material_number] == "0":
502                             inforowcolcolrow.label(text="Images: 0")
503                         else:
504                             inforowcolcolrow.label(text="Images: %s" % material_images[current_material_number])
505                             inforowcolcolrow.label(text="", icon='IMAGE_RGB')
506                             
507                         inforowcolcolrow = inforowcolcol.row()
508                         inforowcolcolrow.label(text=" ")
509                         
510                         inforowcolcolrow = inforowcolcol.row()
511                         details = inforowcolcolrow.row(align=True)
512                         details.alignment = 'RIGHT'
513                         details.operator("material.librarydetailview", text="", icon='PLAY_REVERSE').mode = "PREVIOUS"
514                         detailshidden = details.row()
515                         detailshidden.enabled = False
516                         detailshidden.operator("material.librarydetailview", text="", icon='PLAY').mode = "NEXT"
517                         
518                     inforowcolcolcol = inforowcolcol.column(align=True)
519                     inforowcolcolcol.alignment = 'EXPAND'
520                     functions = inforowcolcolcol.row()
521                     #Display "Add" button
522                     mat_button = functions.operator("material.libraryadd", text="Add to materials", icon='ZOOMIN')
523                     mat_button.mat_name = material_names[current_material_number]
524                     mat_button.filename = material_filenames[current_material_number]
525                     functions = inforowcolcolcol.row()
526                     
527                     #Display "Apply" button
528                     mat_button = functions.operator("material.libraryapply", text="Apply to active", icon='PASTEDOWN')
529                     mat_button.mat_name = material_names[current_material_number]
530                     mat_button.filename = material_filenames[current_material_number]
531                     functions = inforowcolcolcol.row()
532                     
533                     #Display "Save" button
534                     mat_button = functions.operator("material.librarysave", text="Save as...", icon='DISK_DRIVE')
535                     mat_button.filepath = mat_lib_folder + os.sep + "my-materials" + os.sep + material_filenames[current_material_number] + ".bcm"
536                     mat_button.filename = material_filenames[current_material_number]
537                     save_filename = material_filenames[current_material_number]
538                 
539             elif category_type == "subcategory":
540                 #Browsing subcategory - show materials
541                 row = layout.row()
542                 matwrap = row.box()
543                 i = 0
544                 while i < category_materials:
545                     if (i == current_material_number):
546                         matwraprow = matwrap.row()
547                         matwrapbox = matwraprow.box()
548                         matwrapboxrow = matwrapbox.row()
549                         matwrapboxrow.operator("material.libraryviewmaterial", text=material_names[i], icon='MATERIAL', emboss=False).material = i
550                         matwrapboxrowcol = matwrapboxrow.column()
551                         matwrapboxrowcolrow = matwrapboxrowcol.row()
552                         matwrapboxrowcolrow.operator("material.libraryviewmaterial", text="", icon='BLANK1', emboss=False).material = i
553                         matwrapboxrowcolrow.operator("material.libraryviewmaterial", text="", icon='BLANK1', emboss=False).material = i
554                         matwrapboxrowcol = matwrapboxrow.column()
555                         matwrapboxrowcolrow = matwrapboxrowcol.row()
556                         matwrapboxrowcolrowsplit = matwrapboxrowcolrow.split(percentage=0.8)
557                         matwrapboxrowcolrowsplitrow = matwrapboxrowcolrowsplit.row()
558                         
559                         #Ratings
560                         e = 0
561                         while e < material_ratings[i]:
562                             matwrapboxrowcolrowsplitrow.operator("material.libraryviewmaterial", text="", icon='SOLO_ON', emboss=False).material = i
563                             e = e + 1
564                         
565                         if material_ratings[i] is not 5:    
566                             e = 0
567                             while e < (5 - material_ratings[i]):
568                                 matwrapboxrowcolrowsplitrow.operator("material.libraryviewmaterial", text="", icon='SOLO_OFF', emboss=False).material = i
569                                 e = e + 1
570                     else:
571                         matwraprow = matwrap.row()
572                         matwrapcol = matwraprow.column()
573                         matwrapcolrow = matwrapcol.row()
574                         matwrapcolrow.operator("material.libraryviewmaterial", text=material_names[i], icon='MATERIAL', emboss=False).material = i
575                         matwrapcolrowcol = matwrapcolrow.column()
576                         matwrapcolrowcolrow = matwrapcolrowcol.row()
577                         matwrapcolrowcolrow.operator("material.libraryviewmaterial", text="", icon='BLANK1', emboss=False).material = i
578                         matwrapcolrowcolrow.operator("material.libraryviewmaterial", text="", icon='BLANK1', emboss=False).material = i
579                         matwrapcolrowcol = matwrapcolrow.column()
580                         matwrapcolrowcolrow = matwrapcolrowcol.row()
581                         matwrapcolrowcolrowsplit = matwrapcolrowcolrow.split(percentage=0.8)
582                         matwrapcolrowcolrowsplitrow = matwrapcolrowcolrowsplit.row()
583                         
584                         #Ratings
585                         e = 0
586                         while e < material_ratings[i]:
587                             matwrapcolrowcolrowsplitrow.operator("material.libraryviewmaterial", text="", icon='SOLO_ON', emboss=False).material = i
588                             e = e + 1
589                         
590                         if material_ratings[i] is not 5:    
591                             e = 0
592                             while e < (5 - material_ratings[i]):
593                                 matwrapcolrowcolrowsplitrow.operator("material.libraryviewmaterial", text="", icon='SOLO_OFF', emboss=False).material = i
594                                 e = e + 1
595                     i = i + 1
596                     
597                 if current_material_number is not -1:
598                     #Display selected material's info
599                     row = layout.row()
600                     infobox = row.box()
601                     inforow = infobox.row()
602                    
603                     inforow.label(text=material_names[current_material_number])
604                     if bpy.context.scene.mat_lib_auto_preview == False:
605                         mat_button = inforow.operator("material.librarypreview", text="", icon='IMAGE_COL')
606                         mat_button.name = material_names[current_material_number]
607                         mat_button.filename = material_filenames[current_material_number]
608                     inforow.operator("material.viewmaterial", text="", icon='PANEL_CLOSE').material = -1
609                     
610                     inforow = infobox.row()
611                     
612                     #Display a preview
613                     if bpy.data.textures.find("mat_lib_preview_texture") == -1:
614                         bpy.data.textures.new("mat_lib_preview_texture", "IMAGE")
615                         preview_texture = bpy.data.textures["mat_lib_preview_texture"]
616                         inforowcol = inforow.column(align=True)
617                         inforowcol.alignment = 'EXPAND'
618                         inforowcolrow = inforowcol.row()
619                         inforowcolrow.operator("material.librarypreview", text="Preview Material", icon='IMAGE_COL')
620                         inforowcolrow = inforowcol.row()
621                         inforowcolrow.template_preview(preview_texture)
622                     else:
623                         preview_texture = bpy.data.textures['mat_lib_preview_texture']
624                         inforowcol = inforow.column(align=True)
625                         inforowcol.alignment = 'EXPAND'
626                         inforowcolrow = inforowcol.row()
627                         inforowcolrow.operator("material.librarypreview", text="Preview Material", icon='IMAGE_COL')
628                         inforowcolrow = inforowcol.row()
629                         inforowcolrow.template_preview(preview_texture)
630                     
631                     inforowcol = inforow.column()
632                     inforowcolrow = inforowcol.row()
633                     if bpy.data.materials.find(material_names[current_material_number]) is not -1:
634                         inforowcolrow.label(text="Material exists", icon='ERROR')
635                     if material_contributors[current_material_number] == "Unknown" or material_contributors[current_material_number] == "Anonymous":
636                         inforowcolrow = inforowcol.row()
637                     else:
638                         inforowcolrow = inforowcol.row()
639                         inforowcolrow.label(text="By %s." % material_contributors[current_material_number])
640                         inforowcolrow = inforowcol.row()
641                     
642                     if bpy.data.materials.find(material_names[current_material_number]) is not -1:
643                         inforowcolrow.label(text="\"Add\" will overwrite.")
644                     inforowcolcol = inforowcol.column(align=True)
645                     inforowcolcol.alignment = 'EXPAND'
646                     
647                     
648                     #Display "Add" or "Overwrite" button
649                     mat_button = inforowcolcol.operator("material.libraryadd", text="Add to materials", icon='ZOOMIN')
650                     mat_button.name = material_names[current_material_number]
651                     mat_button.filename = material_filenames[current_material_number]
652                     
653                     #Display "Paste" button
654                     mat_button = inforowcolcol.operator("material.libraryapply", text="Apply to active", icon='PASTEDOWN')
655                     mat_button.name = bpy.context.object.active_material.name
656                     mat_button.filename = material_filenames[current_material_number]
657                     
658                     #Display "Save" button
659                     mat_button = inforowcolcol.operator("material.librarysave", text="Save to disk", icon='DISK_DRIVE')
660                     mat_button.name = bpy.context.object.active_material.name
661                     mat_button.filename = material_filenames[current_material_number]
662                     save_filename = material_filenames[current_material_number]
663         else:
664             #Dude, you gotta switch to Cycles to use this.
665             row = layout.row()
666             row.label(text="Sorry, Cycles only at the moment.",icon='ERROR')
667
668 class libraryCategory:
669     def __init__(self, title, folder):
670         self.title = title
671         self.folder = folder
672         self.materials = []
673
674 class libraryMaterial:
675     def __init__(self, name, href, contrib, stars, fireflies, speed, complexity, scripts, images):
676         self.name = name
677         self.href = href
678         self.contrib = contrib
679         self.stars = stars
680         self.fireflies = fireflies
681         self.speed = speed
682         self.complexity = complexity
683         self.scripts = scripts
684         self.images = images
685
686 def handleCategories(categories):
687     for index, category in enumerate(categories):
688         handleCategory(category, index)
689
690 def handleCategory(category, index):
691     if 'addon' in category.attributes:
692         needed_version = float(category.attributes['addon'].value)
693         this_version = bl_info["version"][0] + (bl_info["version"][1] / 10.0)
694         
695         #Check this addon's compatibility with this category
696         if needed_version > this_version:
697             print('\n\n-Category "' + category.attributes['title'].value + '" not used; its materials are for a newer version of this add-on.')
698             return
699     
700     if 'bl' in category.attributes:
701         bl_version = bpy.app.version[0] + (bpy.app.version[1] / 100)
702         
703         #Check Blender's compatibility with this category
704         if category.attributes['bl'].value[-1] == "-":
705             #This option is for if Blender's compatiblity
706             #with a category started only with a specific
707             #version, but has not yet ended; this will
708             #look like the following: bl="2.64-"
709             bl_lower = float(category.attributes['bl'].value[:-1])
710             if bl_lower > bl_version:
711                 print('\n\n-Category "' + category.attributes['title'].value + '" was not used; its materials are not compatible with this version of Blender.')
712                 return
713             
714         elif category.attributes['bl'].value[0] == "-":
715             #This option is for if Blender's compatiblity
716             #with a category ended at some point, and will
717             #look like the following: bl="-2.73"
718             bl_upper = float(category.attributes['bl'].value[1:])
719             if bl_upper < bl_version:
720                 print('\n\n-Category "' + category.attributes['title'].value + '" was not used; its materials are not compatible with this version of Blender.')
721                 return
722             
723         else:
724             #This option is for if Blender's compatiblity
725             #with a category started with a certain version,
726             #then ended with another; it will look
727             #like the following: bl="2.64-2.73"
728             bl_lower = float(category.attributes['bl'].value.split('-')[0])
729             bl_upper = float(category.attributes['bl'].value.split('-')[1])
730             if bl_upper < bl_version:
731                 print('\n\n-Category "' + category.attributes['title'].value + '" was not used; its materials are not compatible with this version of Blender.')
732                 return
733             elif bl_lower > bl_version:
734                 print('\n\n-Category "' + category.attributes['title'].value + '" was not used; its materials are not compatible with this version of Blender.')
735                 return
736     
737     if library is not "bundled":
738         if not os.path.exists(mat_lib_folder + os.sep + mat_lib_host + os.sep + library + os.sep + "cycles" + os.sep + category.attributes['folder'].value):
739             print("Folder \"/" + category.attributes['folder'].value + "/\" does not exist; creating now.")
740             if library == "composite":
741                 os.mkdir(mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep + category.attributes['folder'].value)
742             else:
743                 os.mkdir(mat_lib_folder + os.sep + mat_lib_host + os.sep + library + os.sep + "cycles" + os.sep + category.attributes['folder'].value)
744         
745         if 'remove' in category.attributes:
746             if library == "composite":
747                 if os.path.exists(mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep + category.attributes['folder'].value):
748                     for the_file in os.listdir(mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep + category.attributes['folder'].value):
749                         file_path = mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep + category.attributes['folder'].value + os.sep + the_file
750                         if os.path.isfile(file_path):
751                             os.remove(file_path)
752                         elif os.path.isdir(file_path):
753                             for sub_file in os.listdir(file_path):
754                                 if os.path.isfile(file_path + sub_file):
755                                     os.remove(file_path + sub_file)
756                             os.rmdir(file_path)
757                     os.rmdir(mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep + category.attributes['folder'].value)
758             else:
759                 if os.path.exists(mat_lib_folder + os.sep + mat_lib_host + os.sep + library + os.sep + "cycles" + os.sep + category.attributes['folder'].value):
760                     for the_file in os.listdir(mat_lib_folder + os.sep + mat_lib_host + os.sep + library + os.sep + "cycles" + os.sep + category.attributes['folder'].value):
761                         file_path = mat_lib_folder + os.sep + mat_lib_host + os.sep + library + os.sep + "cycles" + os.sep + category.attributes['folder'].value + os.sep + the_file
762                         if os.path.isfile(file_path):
763                             os.remove(file_path)
764                         elif os.path.isdir(file_path):
765                             for sub_file in os.listdir(file_path):
766                                 if os.path.isfile(file_path + sub_file):
767                                     os.remove(file_path + sub_file)
768                             os.rmdir(file_path)
769                     os.rmdir(mat_lib_folder + os.sep + mat_lib_host + os.sep + library + os.sep + "cycles" + os.sep + category.attributes['folder'].value)
770                 return
771     
772     print ('\n\n-Category "' + category.attributes['title'].value + '"; located in folder "/' + category.attributes['folder'].value + '/".')
773     library_data.append(libraryCategory(category.attributes['title'].value, category.attributes['folder'].value))
774     handleMaterials(category.getElementsByTagName("material"), index)
775
776 def handleMaterials(materials, index):
777     for material in materials:
778         handleMaterial(material, index)
779
780 def handleMaterial(material, index):
781     if 'addon' in material.attributes:
782         needed_version = float(material.attributes['addon'].value)
783         this_version = bl_info["version"][0] + (bl_info["version"][1] / 10.0)
784         
785         #Check this addon's compatibility with this material
786         if needed_version > this_version:
787             print('\n  -Material "' + material.attributes['name'].value + '" was not used, and is for a newer version of this add-on.')
788             return
789     
790     if 'bl' in material.attributes:
791         bl_version = bpy.app.version[0] + (bpy.app.version[1] / 100)
792         
793         #Check Blender's compatibility with this material
794         if material.attributes['bl'].value[-1] == "-":
795             #This option is for if Blender's compatiblity
796             #with a material started only with a specific
797             #version, but has not yet ended; this will
798             #look like the following: bl="2.64-"
799             bl_lower = float(material.attributes['bl'].value[:-1])
800             if bl_lower > bl_version:
801                 print('\n  -Material "' + material.attributes['name'].value + '" was not used, and is not compatible with this version of Blender.')
802                 return
803             
804         elif material.attributes['bl'].value[0] == "-":
805             #This option is for if Blender's compatiblity
806             #with a material ended at some point, and will
807             #look like the following: bl="-2.73"
808             bl_upper = float(material.attributes['bl'].value[1:])
809             if bl_upper < bl_version:
810                 print('\n  -Material "' + material.attributes['name'].value + '" was not used, and is not compatible with this version of Blender.')
811                 return
812             
813         else:
814             #This option is for if Blender's compatiblity
815             #with a material started with a certain version,
816             #then ended with another; it will
817             #look like the following: bl="2.64-2.73"
818             bl_lower = float(material.attributes['bl'].value.split('-')[0])
819             bl_upper = float(material.attributes['bl'].value.split('-')[1])
820             if bl_upper < bl_version:
821                 print('\n  -Material "' + material.attributes['name'].value + '" was not used, and is not compatible with this version of Blender.')
822                 return
823             elif bl_lower > bl_version:
824                 print('\n  -Material "' + material.attributes['name'].value + '" was not used, and is not compatible with this version of Blender.')
825                 return
826     
827     if 'by' in material.attributes:
828         contributor = material.attributes['by'].value
829     else:
830         contributor = "Unknown"
831     
832     if 'stars' in material.attributes:
833         stars = material.attributes['stars'].value
834     else:
835         stars = '0'
836     
837     if 'fireflies' in material.attributes:
838         fireflies = material.attributes['fireflies'].value
839     else:
840         fireflies = 'low'
841     
842     if 'speed' in material.attributes:
843         speed = material.attributes['speed'].value
844     else:
845         speed = 'good'
846     
847     if 'complexity' in material.attributes:
848         complexity = material.attributes['complexity'].value
849     else:
850         complexity = 'simple'
851     
852     if 'scripts' in material.attributes:
853         scripts = material.attributes['scripts'].value
854     else:
855         scripts = '0'
856     
857     if 'images' in material.attributes:
858         images = material.attributes['images'].value
859     else:
860         images = '0'
861     
862     library_data[index].materials.append(
863         libraryMaterial(
864         material.attributes['name'].value,
865         material.attributes['href'].value,
866         contributor,
867         int(stars),
868         fireflies,
869         speed,
870         complexity,
871         scripts,
872         images))
873     print ('\n  -Material "' + 
874         material.attributes['name'].value + 
875         '"\n    -Filename: "' + 
876         material.attributes['href'].value + 
877         '.bcm"\n    -Rating: ' + stars + 
878         ' stars\n    -Contributed by "' + 
879         contributor + '"')
880
881 class LibraryConnect(bpy.types.Operator):
882     '''Connect to the material library'''
883     bl_idname = "material.libraryconnect"
884     bl_label = "Connect to the material library"
885     mode = bpy.props.StringProperty()
886
887     def execute(self, context):
888         global library_data
889         global library
890         global update_data
891         
892         global mat_lib_contents
893         global mat_lib_categories
894         global mat_lib_category_names
895         global mat_lib_category_types
896         global mat_lib_category_filenames
897         
898         global category_enum_items
899         global subcategory_enum_items
900         
901         global show_success_message
902         global show_success_message_timeout
903         
904         global prev_category
905         global mat_lib_host
906         global mat_lib_location
907         global working_mode
908         
909         if self.mode == "online":
910             mat_lib_host = context.scene.mat_lib_library[:context.scene.mat_lib_library.index("/")]
911             mat_lib_location = context.scene.mat_lib_library[(context.scene.mat_lib_library.index(mat_lib_host) + len(mat_lib_host)):]
912             print(mat_lib_host)
913             print(mat_lib_location)
914         elif "bundled" not in context.scene.mat_lib_library:
915             mat_lib_host = context.scene.mat_lib_library[:context.scene.mat_lib_library.index("/")]
916         
917         #Pre-create preview image
918         if not os.path.exists(mat_lib_folder + os.sep + "mat_lib_preview_image.jpg"):
919             f = open(mat_lib_folder + os.sep + "mat_lib_preview_image.jpg", 'w+b')
920             f.close()
921         
922         if self.mode == "online":
923             #Connect and download
924             connection = http.client.HTTPConnection(mat_lib_host)
925             connection.request("GET", mat_lib_location + "cycles/index.xml")
926             
927             if "release" in context.scene.mat_lib_library:
928                 response = connection.getresponse().read()
929                 
930                 #Cache the index.xml file for offline use
931                 library_file = open(os.path.join(mat_lib_folder, mat_lib_host, "release", "cycles", "index.xml"), mode="w+b")
932                 library_file.write(response)
933                 library_file.close()
934                 
935                 #Create /textures/ folder
936                 if not os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, "release", "cycles", "textures")):
937                     os.mkdir(os.path.join(mat_lib_folder, mat_lib_host, "release", "cycles", "textures"))
938                 
939                 #Create /scripts/ folder
940                 if not os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, "release", "cycles", "scripts")):
941                     os.mkdir(os.path.join(mat_lib_folder, mat_lib_host, "release", "cycles", "scripts"))
942                 library = "release"
943             elif "testing" in context.scene.mat_lib_library:
944                 response = connection.getresponse().read()
945                 
946                 #Create /textures/ folder
947                 if not os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, "testing", "cycles", "textures")):
948                     os.mkdir(os.path.join(mat_lib_folder, mat_lib_host, "testing", "cycles", "textures"))
949                 
950                 #Create /scripts/ folder
951                 if not os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, "testing", "cycles", "scripts")):
952                     os.mkdir(os.path.join(mat_lib_folder, mat_lib_host, "testing", "cycles", "scripts"))
953                 library = "testing"
954             else:
955                 response = connection.getresponse().read()
956                 
957                 #Cache the index.xml file for offline use
958                 library_file = open(mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep + "index.xml", mode="w+b")
959                 library_file.write(response)
960                 library_file.close()
961                 
962                 #Create /textures/ folder
963                 if not os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures")):
964                     os.mkdir(os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures"))
965                 
966                 #Create /scripts/ folder
967                 if not os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, "cycles", "scripts")):
968                     os.mkdir(os.path.join(mat_lib_folder, mat_lib_host, "cycles", "scripts"))
969                 library = "composite"
970                 
971             #Convert the response to a string
972             mat_lib_contents = str(response)
973             
974             #Check for connection errors
975             if "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" not in mat_lib_contents:
976                 self.report({'ERROR'}, "Error connecting; see console for details.")
977                 print("Received following response from server:\n" + mat_lib_contents)
978                 return {'CANCELLED'}
979             
980             #Format nicely
981             mat_lib_contents = mat_lib_contents.replace("b'<?xml version=\"1.0\" encoding=\"UTF-8\"?>",'')
982             mat_lib_contents = mat_lib_contents.replace("\\r\\n",'')
983             mat_lib_contents = mat_lib_contents.replace("\\t",'')[:-1]
984             mat_lib_contents = mat_lib_contents.replace("\\",'')
985             
986         else:
987             if "release" in context.scene.mat_lib_library:
988                 #Check for cached index.xml file
989                 if os.path.exists(mat_lib_folder + os.sep + mat_lib_host + os.sep + "release" + os.sep + "cycles" + os.sep + "index.xml"):
990                     library_file = open(mat_lib_folder + os.sep + mat_lib_host + os.sep + "release" + os.sep + "cycles" + os.sep + "index.xml", mode="r", encoding="UTF-8")
991                     mat_lib_contents = library_file.read()
992                     library_file.close()
993                 else:
994                     self.report({'ERROR'}, "No cached library exists!")
995                     return {'CANCELLED'}
996                 
997                 #Create /textures/ folder
998                 if not os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, "release", "cycles", "textures")):
999                     os.mkdir(os.path.join(mat_lib_folder, mat_lib_host, "release", "cycles", "textures"))
1000                 
1001                 #Create /scripts/ folder
1002                 if not os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, "release", "cycles", "scripts")):
1003                     os.mkdir(os.path.join(mat_lib_folder, mat_lib_host, "release", "cycles", "scripts"))
1004                 library = "release"
1005             elif context.scene.mat_lib_library == "bundled":
1006                 #Check for index.xml file
1007                 if os.path.exists(mat_lib_folder + os.sep + "bundled" + os.sep + "cycles" + os.sep + "index.xml"):
1008                     library_file = open(mat_lib_folder + os.sep + "bundled" + os.sep + "cycles" + os.sep + "index.xml", mode="r", encoding="UTF-8")
1009                     mat_lib_contents = library_file.read()
1010                     library_file.close()
1011                 else:
1012                     self.report({'ERROR'}, "Bundled library does not exist!")
1013                     return {'CANCELLED'}
1014                 library = "bundled"
1015             else:
1016                 #Check for cached index.xml file
1017                 if os.path.exists(mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep + "index.xml"):
1018                     library_file = open(mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep + "index.xml", mode="r", encoding="UTF-8")
1019                     mat_lib_contents = library_file.read()
1020                     library_file.close()
1021                 else:
1022                     self.report({'ERROR'}, "No cached library exists!")
1023                     return {'CANCELLED'}
1024                 
1025                 #Create /textures/ folder
1026                 if not os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures")):
1027                     os.mkdir(os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures"))
1028                 
1029                 #Create /scripts/ folder
1030                 if not os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, "cycles", "scripts")):
1031                     os.mkdir(os.path.join(mat_lib_folder, mat_lib_host, "cycles", "scripts"))
1032                 library = "composite"
1033             
1034             if '<?xml version="1.0" encoding="UTF-8"?>' not in mat_lib_contents:
1035                 self.report({'ERROR'}, "Cached XML file is invalid!")
1036                 return {'CANCELLED'}
1037             
1038             #Format nicely
1039             mat_lib_contents = mat_lib_contents.replace('<?xml version="1.0" encoding="UTF-8"?>', '')
1040             mat_lib_contents = mat_lib_contents.replace("\r\n",'')
1041             mat_lib_contents = mat_lib_contents.replace("\n",'')
1042             mat_lib_contents = mat_lib_contents.replace("\t",'')
1043             mat_lib_contents = mat_lib_contents.replace("\\",'')
1044         
1045         #Clear important lists
1046         library_data = []
1047         mat_lib_category_names = []
1048         mat_lib_category_types = []
1049         mat_lib_category_filenames = []
1050             
1051         dom = xml.dom.minidom.parseString(mat_lib_contents)
1052         
1053         if self.mode == "online":
1054             if library == "composite":
1055                 rev_file = open(mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep + "revision_data.ini", mode="r", encoding="UTF-8")
1056                 rev_data = rev_file.read()
1057                 rev_file.close()
1058             else:
1059                 rev_file = open(mat_lib_folder + os.sep + mat_lib_host + os.sep + library + os.sep + "cycles" + os.sep + "revision_data.ini", mode="r", encoding="UTF-8")
1060                 rev_data = rev_file.read()
1061                 rev_file.close()
1062             
1063             if "revision=" in rev_data:
1064                 revision = int(rev_data[9:])
1065             else:
1066                 revision = -1
1067                 print("The revision_data.ini file is invalid; clearing cache and re-creating.")
1068             
1069             if revision is not int(dom.getElementsByTagName("library")[0].attributes['rev'].value):
1070                 bpy.ops.material.libraryclearcache()
1071             
1072             if library == "composite":
1073                 rev_file = open(mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep + "revision_data.ini", mode="w", encoding="UTF-8")
1074             else:
1075                 rev_file = open(mat_lib_folder + os.sep + mat_lib_host + os.sep + library + os.sep + "cycles" + os.sep + "revision_data.ini", mode="w", encoding="UTF-8")
1076             rev_file.write("revision=" + dom.getElementsByTagName("library")[0].attributes['rev'].value)
1077             rev_file.close()
1078             
1079             if 'addon' in dom.getElementsByTagName("library")[0].attributes:
1080                 current_version = float(dom.getElementsByTagName("library")[0].attributes['addon'].value)
1081                 this_version = bl_info["version"][0] + (bl_info["version"][1] / 10.0)
1082                 if current_version > this_version:
1083                     update_data = ["Add-on is outdated.", dom.getElementsByTagName("library")[0].attributes['download'].value]
1084         
1085         print ("\n\n---Material Library---")
1086         categories = dom.getElementsByTagName("category")
1087         handleCategories(categories)
1088         
1089         for cat in library_data:
1090             #Find category names
1091             mat_lib_category_names.append(cat.title)
1092             #Find category types
1093             #NOTE: Will have to redo this.
1094             #mat_lib_category_types = safeEval(mat_lib_contents[(mat_lib_contents.index('[types]') + 7):mat_lib_contents.index('[/types]')])
1095             #Get category filenames
1096             mat_lib_category_filenames.append(cat.folder)
1097             
1098         #Find amount of categories
1099         mat_lib_categories = len(mat_lib_category_names)
1100         
1101         #Set enum items for category dropdown
1102         category_enum_items = [("None0", "None", "No category selected")]
1103         
1104         i = 0
1105         while i < mat_lib_categories:
1106             print ("Adding category #%d" % (i + 1))
1107             category_enum_items.append(((mat_lib_category_names[i] + str(i + 1)), mat_lib_category_names[i], (mat_lib_category_names[i] + " category")))
1108             i = i + 1
1109         bpy.types.Scene.mat_lib_material_category = bpy.props.EnumProperty(name = "", items = category_enum_items, description = "Choose a category")
1110         bpy.context.scene.mat_lib_material_category = "None0";
1111         
1112         #No errors - set working mode
1113         working_mode = self.mode
1114         
1115         self.report({'INFO'}, "Retrieved library!")
1116         
1117         return {'FINISHED'}
1118
1119 class LibraryInfo(bpy.types.Operator):
1120     '''Display add-on info'''
1121     bl_idname = "material.libraryinfo"
1122     bl_label = "Display add-on info"
1123
1124     def execute(self, context):
1125         global category_type
1126         
1127         category_type = "info"
1128         
1129         return {'FINISHED'}
1130
1131 class LibraryTools(bpy.types.Operator):
1132     '''Display material tools'''
1133     bl_idname = "material.librarytools"
1134     bl_label = "Display material tools"
1135
1136     def execute(self, context):
1137         global category_type
1138         
1139         category_type = "tools"
1140         
1141         return {'FINISHED'}
1142
1143 class LibrarySettings(bpy.types.Operator):
1144     '''Display add-on settings'''
1145     bl_idname = "material.librarysettings"
1146     bl_label = "Display add-on settings"
1147
1148     def execute(self, context):
1149         global category_type
1150         global mat_lib_cached_files
1151         
1152         category_type = "settings"
1153         if library == "":
1154             return {'FINISHED'}
1155         elif library == "bundled":
1156             mat_lib_cached_files = -2
1157             return {'FINISHED'}
1158         elif library == "composite":
1159             cached_data_path = mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep
1160         else:
1161             cached_data_path = mat_lib_folder + os.sep + mat_lib_host + os.sep + library + os.sep + "cycles" + os.sep
1162         mat_lib_cached_files = 0
1163         for root, dirs, files in os.walk(cached_data_path):
1164             for name in files:
1165                 if ".jpg" in name.lower():
1166                     mat_lib_cached_files += 1
1167                 elif ".png" in name.lower():
1168                     mat_lib_cached_files += 1
1169                 elif ".osl" in name.lower():
1170                     mat_lib_cached_files += 1
1171                 elif ".osl" in name.lower():
1172                     mat_lib_cached_files += 1
1173                 elif ".bcm" in name:
1174                     mat_lib_cached_files += 1
1175         
1176         return {'FINISHED'}
1177
1178 class LibraryHome(bpy.types.Operator):
1179     '''Go back'''
1180     bl_idname = "material.libraryhome"
1181     bl_label = "Go back"
1182
1183     def execute(self, context):
1184         global category_type
1185         
1186         category_type = "none"
1187         
1188         return {'FINISHED'}
1189
1190 def libraryCategoryUpdate():
1191     print("Updating Material Category.")
1192     #Check if the category is None
1193     if bpy.context.scene.mat_lib_material_category != "None0":
1194         #Selected category is not None; select category.        
1195         
1196         global category_contents
1197         global category_name
1198         global category_filename
1199         global category_materials
1200                 
1201         global material_names
1202         global material_filenames
1203         global material_contributors
1204         global material_ratings
1205         global material_fireflies
1206         global material_speeds
1207         global material_complexities
1208         global material_scripts
1209         global material_images
1210         
1211         global current_material_number
1212                 
1213         global category_type
1214     
1215         i = 0
1216         while i < len(category_enum_items):
1217             if category_enum_items[i][0] == bpy.context.scene.mat_lib_material_category:
1218                 #Set category filename for refresh button
1219                 category_filename = mat_lib_category_filenames[i - 1]
1220                 #Set category name for header
1221                 category_name = mat_lib_category_names[i - 1]
1222                 category_index = i - 1
1223             i = i + 1
1224         
1225         if True:
1226             current_material_number = -1
1227             
1228             material_names = []
1229             material_filenames = []
1230             material_contributors = []
1231             material_ratings = []
1232             material_fireflies = []
1233             material_speeds = []
1234             material_complexities = []
1235             material_scripts = []
1236             material_images = []
1237             
1238             for mat in library_data[category_index].materials:
1239                 #Get material names
1240                 material_names.append(mat.name)
1241                 #Get material filenames
1242                 material_filenames.append(mat.href)
1243                 #Get material contributors
1244                 material_contributors.append(mat.contrib)
1245                 #Get material ratings
1246                 material_ratings.append(mat.stars)
1247                 #Get material firefly levels
1248                 material_fireflies.append(mat.fireflies)
1249                 #Get material render speeds
1250                 material_speeds.append(mat.speed)
1251                 #Get material complexities
1252                 material_complexities.append(mat.complexity)
1253                 #Get material image textures
1254                 material_images.append(mat.images)
1255                 #Get material OSL scripts
1256                 material_scripts.append(mat.scripts)
1257             
1258             #Set amount of materials in selected category
1259             category_materials = len(material_names)
1260         
1261             category_type = "category"
1262         
1263         elif "parent" == "parent":
1264             current_material_number = -1
1265             #REWRITE, REWRITE...
1266             #Find category names
1267             #parent_category_names = safeEval(parent_category_contents[(parent_category_contents.index('[names]') + 7):parent_category_contents.index('[/names]')])
1268             #
1269             #Get category filenames
1270             #parent_category_filenames = safeEval(parent_category_contents[(parent_category_contents.index('[filenames]') + 11):parent_category_contents.index('[/filenames]')])
1271             #
1272             #Set parent category name for header
1273             #parent_category_name = self.name
1274             #
1275             #Set parent category filename
1276             #parent_category_filename = self.filename
1277             #
1278             #Set amount of categories in parent category
1279             #parent_category_categories = len(parent_category_names)
1280             
1281             category_type = "parent"
1282         
1283         elif "subcategory" == "subcategory":
1284             current_material_number = -1
1285             #ANOTHER REWRITE.
1286             #Get material names
1287             #material_names = safeEval(category_contents[(category_contents.index("[names]") + 7):category_contents.index("[/names]")])
1288             #
1289             #Get material filenames
1290             #material_filenames = safeEval(category_contents[(category_contents.index("[filenames]") + 11):category_contents.index("[/filenames]")])
1291             #
1292             #Get material contributors
1293             #material_contributors = safeEval(category_contents[(category_contents.index("[contributors]") + 14):category_contents.index("[/contributors]")])
1294             #
1295             #Get material ratings
1296             #material_ratings = safeEval(category_contents[(category_contents.index("[ratings]") + 9):category_contents.index("[/ratings]")])
1297             #
1298             #Set category name for header
1299             #category_name = self.name
1300             #
1301             #Set category filename for refresh button
1302             #category_filename = self.filename
1303             
1304             #Set amount of materials in selected category
1305             #category_materials = len(material_names)
1306             
1307             category_type = "subcategory"
1308         
1309         else:
1310             self.report({'ERROR'}, "Invalid category! See console for details.")
1311             print ("Invalid category!")
1312             print (category_contents)
1313     else:
1314         #Selected category is None
1315         parent_category_contents = "None"
1316         category_contents = "None"
1317         current_material_number = -1
1318         category_type = "none"
1319
1320 class ViewMaterial(bpy.types.Operator):
1321     '''View material details'''
1322     bl_idname = "material.libraryviewmaterial"
1323     bl_label = "view material details"
1324     material = bpy.props.IntProperty()
1325     
1326     def execute(self, context):
1327         global current_material_number
1328         global current_material_cached
1329         global current_material_previewed
1330         
1331         if current_material_number == self.material:
1332             if current_material_previewed:
1333                 current_material_previewed = True
1334             else:
1335                 current_material_previewed = False
1336         else:
1337             current_material_previewed = False
1338         
1339         current_material_number = self.material
1340         current_material_cached = False
1341         
1342         if self.material == -1:
1343             return {'FINISHED'}
1344         
1345         if library == "composite":
1346             if os.path.exists(mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep + category_filename + os.sep + material_filenames[self.material] + ".bcm"):
1347                 current_material_cached = True
1348         elif library != "bundled":
1349             if os.path.exists(mat_lib_folder + os.sep + mat_lib_host + os.sep + library + os.sep + "cycles" + os.sep + category_filename + os.sep + material_filenames[self.material] + ".bcm"):
1350                 current_material_cached = True
1351         
1352         if context.scene.mat_lib_auto_preview == True:
1353             print("Auto-download previews on.")
1354             bpy.ops.material.librarypreview(name=material_names[self.material], filename=material_filenames[self.material])
1355             self.report({preview_message[0]}, preview_message[1])
1356         elif working_mode == "offline":
1357             bpy.ops.material.librarypreview(name=material_names[self.material], filename=material_filenames[self.material])
1358             self.report({preview_message[0]}, preview_message[1])
1359         elif library == "composite":
1360             if os.path.exists(mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep  + category_filename + os.sep + material_filenames[self.material] + ".jpg"):
1361                 bpy.ops.material.librarypreview(name=material_names[self.material], filename=material_filenames[self.material])
1362                 self.report({preview_message[0]}, preview_message[1])
1363         else:
1364             if os.path.exists(mat_lib_folder + os.sep + mat_lib_host + os.sep + library + os.sep + "cycles" + os.sep  + category_filename + os.sep + material_filenames[self.material] + ".jpg"):
1365                 bpy.ops.material.librarypreview(name=material_names[self.material], filename=material_filenames[self.material])
1366                 self.report({preview_message[0]}, preview_message[1])
1367         return {'FINISHED'}
1368
1369 class MaterialDetailView(bpy.types.Operator):
1370     '''Change detail view'''
1371     bl_idname = "material.librarydetailview"
1372     bl_label = "change detail view"
1373     mode = bpy.props.StringProperty()
1374
1375     def execute(self, context):
1376         global material_detail_view
1377         
1378         if self.mode == "NEXT":
1379             if material_detail_view == "RENDER":
1380                 material_detail_view = "DATA"
1381         else:
1382             if material_detail_view == "DATA":
1383                 material_detail_view = "RENDER"
1384         return {'FINISHED'}
1385             
1386
1387 class LibraryClearCache(bpy.types.Operator):
1388     '''Delete active library's cached previews and/or materials'''
1389     bl_idname = "material.libraryclearcache"
1390     bl_label = "delete cached previews and materials"
1391     
1392     def execute(self, context):
1393         global mat_lib_cached_files
1394         
1395         
1396         if library == "bundled":
1397             self.report({'ERROR'}, "The bundled library is local only and contains no cached online data.")
1398             return {'CANCELLED'}
1399         if library == "composite":
1400             for root, dirs, files in os.walk(mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep):
1401                 for name in files:
1402                     if name[-4:].lower() == ".jpg":
1403                         print("Deleting \"" + os.path.join(root, name) + "\".")
1404                         os.remove(os.path.join(root, name))
1405                     elif name[-4:].lower() == ".png":
1406                         print("Deleting \"" + os.path.join(root, name) + "\".")
1407                         os.remove(os.path.join(root, name))
1408                     elif name[-4:].lower() == ".osl":
1409                         print("Deleting \"" + os.path.join(root, name) + "\".")
1410                         os.remove(os.path.join(root, name))
1411                     elif name[-4:].lower() == ".oso":
1412                         print("Deleting \"" + os.path.join(root, name) + "\".")
1413                         os.remove(os.path.join(root, name))
1414                     elif name[-4:].lower() == ".bcm":
1415                         print("Deleting \"" + os.path.join(root, name) + "\".")
1416                         os.remove(os.path.join(root, name))
1417         else:
1418             for root, dirs, files in os.walk(mat_lib_folder + os.sep + mat_lib_host + os.sep + library + os.sep + "cycles" + os.sep):
1419                 for name in files:
1420                     if name[-4:].lower() == ".jpg":
1421                         print("Deleting \"" + os.path.join(root, name) + "\".")
1422                         os.remove(os.path.join(root, name))
1423                     elif name[-4:].lower() == ".png":
1424                         print("Deleting \"" + os.path.join(root, name) + "\".")
1425                         os.remove(os.path.join(root, name))
1426                     elif name[-4:].lower() == ".osl":
1427                         print("Deleting \"" + os.path.join(root, name) + "\".")
1428                         os.remove(os.path.join(root, name))
1429                     elif name[-4:].lower() == ".oso":
1430                         print("Deleting \"" + os.path.join(root, name) + "\".")
1431                         os.remove(os.path.join(root, name))
1432                     elif name[-4:].lower() == ".bcm":
1433                         print("Deleting \"" + os.path.join(root, name) + "\".")
1434                         os.remove(os.path.join(root, name))
1435         
1436         if library == "":
1437             return {'FINISHED'}
1438         elif library == "bundled":
1439             return {'FINISHED'}
1440         elif library == "composite":
1441             cached_data_path = mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep
1442         else:
1443             cached_data_path = mat_lib_folder + os.sep + mat_lib_host + os.sep + library + os.sep + "cycles" + os.sep
1444         mat_lib_cached_files = 0
1445         for root, dirs, files in os.walk(cached_data_path):
1446             for name in files:
1447                 if ".jpg" in name:
1448                     mat_lib_cached_files += 1
1449                 elif ".bcm" in name:
1450                     mat_lib_cached_files += 1
1451         
1452         self.report({'INFO'}, "Preview cache cleared.")
1453         return {'FINISHED'}
1454     
1455 class LibraryPreview(bpy.types.Operator):
1456     '''Download preview'''
1457     bl_idname = "material.librarypreview"
1458     bl_label = "preview material"
1459     name = bpy.props.StringProperty()
1460     filename = bpy.props.StringProperty()
1461         
1462     def execute(self, context):
1463         global parent_category_filename
1464         global category_filename
1465         global preview_message
1466         global library
1467         global current_material_previewed
1468         
1469         #Check for a cached preview
1470         if library == "bundled":
1471             image_path = mat_lib_folder + os.sep + "bundled" + os.sep + "cycles" + os.sep  + category_filename + os.sep + self.filename + ".jpg"
1472         elif library == "composite":
1473             image_path = mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep  + category_filename + os.sep + self.filename + ".jpg"
1474         else:
1475             image_path = mat_lib_folder + os.sep + mat_lib_host + os.sep + library + os.sep + "cycles" + os.sep  + category_filename + os.sep + self.filename + ".jpg"
1476         
1477         if os.path.exists(image_path):
1478             #Cached preview exists
1479             cached_image = open(image_path, 'r+b')
1480             response = cached_image.read()
1481             cached_image.close()
1482             f = open(mat_lib_folder + os.sep + "mat_lib_preview_image.jpg", 'w+b')
1483             f.write(response)
1484             f.close()
1485             
1486         elif working_mode == "online":
1487             #This preview doesn't exist yet; let's download it.
1488             connection = http.client.HTTPConnection(mat_lib_host)
1489             connection.request("GET", mat_lib_location + "cycles/" + category_filename + "/" + self.filename + ".jpg")
1490             response = connection.getresponse().read()
1491             f = open(mat_lib_folder + os.sep + "mat_lib_preview_image.jpg", 'w+b')
1492             f.write(response)
1493             f.close()
1494             
1495             #Cache this preview
1496             f = open(image_path, 'w+b')
1497             f.write(response)
1498             f.close()
1499             
1500         else:
1501             self.report({'WARNING'}, "Preview does not exist; cannot download in offline mode.")
1502             preview_message = ['WARNING', "Preview does not exist; cannot download in offline mode."]
1503             return {'CANCELLED'}
1504         
1505         #Check if has texture
1506         if bpy.data.images.find("mat_lib_preview_image.jpg") == -1:
1507             bpy.ops.image.open(filepath=os.path.join(mat_lib_folder, "mat_lib_preview_image.jpg"))
1508         
1509         if "mat_lib_preview_texture" not in bpy.data.textures:
1510              bpy.data.textures.new("mat_lib_preview_texture", "IMAGE")
1511         
1512         if bpy.data.textures["mat_lib_preview_texture"].image != bpy.data.images["mat_lib_preview_image.jpg"]:
1513             bpy.data.textures["mat_lib_preview_texture"].image = bpy.data.images["mat_lib_preview_image.jpg"]
1514         
1515         if bpy.data.images["mat_lib_preview_image.jpg"].filepath != os.path.join(mat_lib_folder, "mat_lib_preview_image.jpg"):
1516             bpy.data.images["mat_lib_preview_image.jpg"].filepath != os.path.join(mat_lib_folder, "mat_lib_preview_image.jpg")
1517             
1518         #Do everything possible to get Blender to reload the preview.
1519         bpy.data.images["mat_lib_preview_image.jpg"].reload()
1520         bpy.ops.wm.redraw_timer()
1521         bpy.data.scenes[bpy.context.scene.name].update()
1522         bpy.data.scenes[bpy.context.scene.name].frame_set(bpy.data.scenes[bpy.context.scene.name].frame_current)
1523         
1524         self.report({'INFO'}, "Preview applied.")
1525         preview_message = ['INFO', "Preview applied."]
1526         current_material_previewed = True
1527         
1528         return {'FINISHED'}
1529
1530 class AddLibraryMaterial(bpy.types.Operator):
1531     '''Add material to scene'''
1532     bl_idname = "material.libraryadd"
1533     bl_label = "add material to scene"
1534     mat_name = bpy.props.StringProperty()
1535     filename = bpy.props.StringProperty()
1536     open_location = bpy.props.StringProperty()
1537     text_block = bpy.props.StringProperty()
1538     
1539     def execute(self, context):
1540         global material_file_contents
1541         global library
1542         global node_message
1543         global current_material_cached
1544         
1545         if not bpy.context.active_object:
1546             self.report({'ERROR'}, "No object selected!")
1547         if self.open_location == "" and self.text_block == "":
1548             if library == "composite" and os.path.exists(mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep + category_filename + os.sep + self.filename + ".bcm"):
1549                 bcm_file = open(mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep + category_filename + os.sep + self.filename + ".bcm", mode="r", encoding="UTF-8")
1550                 material_file_contents = ""
1551                 material_file_contents = bcm_file.read()
1552                 bcm_file.close()
1553             elif library != "bundled" and os.path.exists(mat_lib_folder + os.sep + mat_lib_host + os.sep + library + os.sep + "cycles" + os.sep + category_filename + os.sep + self.filename + ".bcm"):
1554                 bcm_file = open(mat_lib_folder + os.sep + mat_lib_host + os.sep + library + os.sep + "cycles" + os.sep + category_filename + os.sep + self.filename + ".bcm", mode="r", encoding="UTF-8")
1555                 material_file_contents = ""
1556                 material_file_contents = bcm_file.read()
1557                 bcm_file.close()
1558             elif library == "bundled" and os.path.exists(mat_lib_folder + os.sep + "bundled" + os.sep + "cycles" + os.sep + category_filename + os.sep + self.filename + ".bcm"):
1559                 bcm_file = open(mat_lib_folder + os.sep + "bundled" + os.sep + "cycles" + os.sep + category_filename + os.sep + self.filename + ".bcm", mode="r", encoding="UTF-8")
1560                 material_file_contents = bcm_file.read()
1561                 bcm_file.close()
1562             elif working_mode == "online":
1563                 connection = http.client.HTTPConnection(mat_lib_host)
1564                 connection.request("GET", mat_lib_location + "cycles/" + category_filename + "/" + self.filename + ".bcm")
1565                 response = connection.getresponse().read()
1566                 
1567                 #Check file for validitity
1568                 if '<?xml version="1.0" encoding="UTF-8"?>' not in str(response)[2:40]:
1569                     self.report({'ERROR'}, "Material file is either outdated or invalid.")
1570                     self.filename = ""
1571                     return {'CANCELLED'}
1572                 
1573                 #Cache material
1574                 if library == "composite":
1575                     bcm_file = open(mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep + category_filename + os.sep + self.filename + ".bcm", mode="w+b")
1576                     bcm_file.write(response)
1577                     bcm_file.close()
1578                 else:
1579                     bcm_file = open(mat_lib_folder + os.sep + mat_lib_host + os.sep + library + os.sep + "cycles" + os.sep + category_filename + os.sep + self.filename + ".bcm", mode="w+b")
1580                     bcm_file.write(response)
1581                     bcm_file.close()
1582                 
1583                 material_file_contents = str(response)
1584             else:
1585                 self.report({'ERROR'}, "Material is not cached; cannot download in offline mode!")
1586                 return {'CANCELLED'}
1587             print (material_file_contents)
1588             mat_name = self.mat_name
1589         elif self.open_location is not "":
1590             if ".bcm" in self.open_location:
1591                 bcm_file = open(self.open_location, mode="r", encoding="UTF-8")
1592                 material_file_contents = bcm_file.read()
1593                 bcm_file.close()
1594                 
1595                 #Check file for validitity
1596                 if '<?xml version="1.0" encoding="UTF-8"?>' not in material_file_contents:
1597                     self.open_location = ""
1598                     self.report({'ERROR'}, "Material file is either outdated or invalid.")
1599                     return {'CANCELLED'}
1600                 mat_name = ""
1601                 for word in self.open_location.split(os.sep)[-1][:-4].split("_"):
1602                     if mat_name is not "":
1603                         mat_name += " "
1604                     mat_name += word.capitalize()
1605             else:
1606                 self.report({'ERROR'}, "Not a .bcm file.")
1607                 self.open_location = ""
1608                 return {'CANCELLED'}
1609         else:
1610             if self.text_block in bpy.data.texts:
1611                 #Read from a text datablock
1612                 material_file_contents = bpy.data.texts[self.text_block].as_string()
1613             else:
1614                 self.report({'ERROR'}, "Requested text block does not exist.")
1615                 self.text_block = ""
1616                 return {'CANCELLED'}
1617                 
1618             #Check file for validitity
1619             if '<?xml version="1.0" encoding="UTF-8"?>' not in material_file_contents[0:38]:
1620                 self.report({'ERROR'}, "Material data is either outdated or invalid.")
1621                 self.text_block = ""
1622                 return {'CANCELLED'}
1623             mat_name = ""
1624             
1625             separator = ""
1626             if context.scene.mat_lib_bcm_name is "":
1627                 separator = ""
1628                 if "_" in self.text_block:
1629                     separator = "_"
1630                 elif "-" in self.text_block:
1631                     separator = "-"
1632                 elif " " in self.text_block:
1633                     separator = " "
1634                     
1635                 if separator is not "":
1636                     for word in self.text_block.split(separator):
1637                         if mat_name is not "":
1638                             mat_name += " "
1639                         mat_name += word.capitalize()
1640                 else:
1641                     mat_name = self.text_block
1642             else:
1643                 mat_name = context.scene.mat_lib_bcm_name
1644         
1645         if '<?xml version="1.0" encoding="UTF-8"?>' in material_file_contents[0:40]:
1646             material_file_contents = material_file_contents[material_file_contents.index("<material"):(material_file_contents.rindex("</material>") + 11)]
1647         else:
1648             self.mat_name = ""
1649             self.filename = ""
1650             self.text_block = ""
1651             self.open_location = ""
1652             self.report({'ERROR'}, "Material file is either invalid or outdated.")
1653             print(material_file_contents)
1654             return {'CANCELLED'}
1655         
1656         #Create new material
1657         new_mat = bpy.data.materials.new(mat_name)
1658         new_mat.use_nodes = True
1659         new_mat.node_tree.nodes.clear()
1660         
1661         #Parse file
1662         dom = xml.dom.minidom.parseString(material_file_contents)
1663         
1664         #Create internal OSL scripts
1665         scripts = dom.getElementsByTagName("script")
1666         for s in scripts:
1667             osl_datablock = bpy.data.texts.new(name=s.attributes['name'].value)
1668             osl_text = s.toxml()[s.toxml().index(">"):s.toxml().rindex("<")]
1669             osl_text = osl_text[1:].replace("<br/>","\n").replace("&lt;", "<").replace("&gt;", ">").replace("&quot;", "\"").replace("&amp;", "&")
1670             osl_datablock.write(osl_text)
1671             osl_scripts.append(osl_datablock)
1672         
1673         #Add nodes
1674         nodes = dom.getElementsByTagName("node")
1675         addNodes(nodes, new_mat)
1676         if node_message:
1677             self.report({node_message[0]}, node_message[1])
1678             node_message = []
1679             self.mat_name = ""
1680             self.filename = ""
1681             self.open_location = ""
1682             self.text_block = ""
1683             return {'CANCELLED'}
1684             
1685         #Create links
1686         links = dom.getElementsByTagName("link")
1687         createLinks(links, new_mat)
1688         
1689         m = dom.getElementsByTagName("material")[0]
1690         
1691         #Set viewport color
1692         new_mat.diffuse_color = color(m.attributes["view_color"].value)
1693             
1694         #Set sample-as-lamp-ness
1695         if m.attributes["sample_lamp"].value == "True":
1696             sample_lamp = True
1697         else:
1698             sample_lamp = False
1699         new_mat.cycles.sample_as_light = sample_lamp
1700         
1701         self.mat_name = ""
1702         self.filename = ""
1703         self.open_location = ""
1704         self.text_block = ""
1705         self.report({'INFO'}, "Material added.")
1706         current_material_cached = True
1707         
1708         return {'FINISHED'}
1709
1710 class ApplyLibraryMaterial(bpy.types.Operator):
1711     '''Apply to active material'''
1712     bl_idname = "material.libraryapply"
1713     bl_label = "Apply to active material"
1714     mat_name = bpy.props.StringProperty()
1715     filename = bpy.props.StringProperty()
1716     open_location = bpy.props.StringProperty()
1717     text_block = bpy.props.StringProperty()
1718     
1719     def execute(self, context):
1720         global material_file_contents
1721         global library
1722         global node_message
1723         global current_material_cached
1724         global osl_scripts
1725         
1726         mat_name = ""
1727         material_file_contents = ""
1728         if not bpy.context.active_object:
1729             self.report({'ERROR'}, "No object selected!")
1730             return {'CANCELLED'}
1731         if self.filename is not "":
1732             if library == "composite" and os.path.exists(mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep + category_filename + os.sep + self.filename + ".bcm"):
1733                 bcm_file = open(mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep + category_filename + os.sep + self.filename + ".bcm", mode="r", encoding="UTF-8")
1734                 material_file_contents = bcm_file.read()
1735                 bcm_file.close()
1736             elif library != "bundled" and os.path.exists(mat_lib_folder + os.sep + mat_lib_host + os.sep + library + os.sep + "cycles" + os.sep + category_filename + os.sep + self.filename + ".bcm"):
1737                 bcm_file = open(mat_lib_folder + os.sep + mat_lib_host + os.sep + library + os.sep + "cycles" + os.sep + category_filename + os.sep + self.filename + ".bcm", mode="r", encoding="UTF-8")
1738                 material_file_contents = bcm_file.read()
1739                 bcm_file.close()
1740             elif library == "bundled" and os.path.exists(mat_lib_folder + os.sep + "bundled" + os.sep + "cycles" + os.sep + category_filename + os.sep + self.filename + ".bcm"):
1741                 bcm_file = open(mat_lib_folder + os.sep + "bundled" + os.sep + "cycles" + os.sep + category_filename + os.sep + self.filename + ".bcm", mode="r", encoding="UTF-8")
1742                 material_file_contents = bcm_file.read()
1743                 bcm_file.close()
1744             elif working_mode == "online":
1745                 connection = http.client.HTTPConnection(mat_lib_host)
1746                 connection.request("GET", mat_lib_location + "cycles/" + category_filename + "/" + self.filename + ".bcm")
1747                 response = connection.getresponse().read()
1748                 
1749                 #Check file for validitity
1750                 if '<?xml version="1.0" encoding="UTF-8"?>' not in str(response)[2:40]:
1751                     self.report({'ERROR'}, "Material file is either outdated or invalid.")
1752                     self.mat_name = ""
1753                     self.filename = ""
1754                     return {'CANCELLED'}
1755                 
1756                 #Cache material
1757                 if library == "composite":
1758                     bcm_file = open(mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep + category_filename + os.sep + self.filename + ".bcm", mode="w+b")
1759                     bcm_file.write(response)
1760                     bcm_file.close()
1761                 else:
1762                     bcm_file = open(mat_lib_folder + os.sep + mat_lib_host + os.sep + library + os.sep + "cycles" + os.sep + category_filename + os.sep + self.filename + ".bcm", mode="w+b")
1763                     bcm_file.write(response)
1764                     bcm_file.close()
1765                 
1766                 material_file_contents = str(response)
1767             else:
1768                 self.report({'ERROR'}, "Material is not cached; cannot download in offline mode!")
1769                 self.mat_name = ""
1770                 self.filename = ""
1771                 return {'CANCELLED'}
1772             mat_name = self.mat_name
1773         elif self.open_location is not "":
1774             if ".bcm" in self.open_location:
1775                 material_file_contents = ""
1776                 bcm_file = open(self.open_location, mode="r", encoding="UTF-8")
1777                 material_file_contents = bcm_file.read()
1778                 bcm_file.close()
1779                 
1780                 mat_name = ""
1781                 for word in self.open_location.split(os.sep)[-1][:-4].split("_"):
1782                     if mat_name is not "":
1783                         mat_name += " "
1784                     mat_name += word.capitalize()
1785             else:
1786                 self.open_location = ""
1787                 self.report({'ERROR'}, "Not a .bcm file.")
1788                 return {'CANCELLED'}
1789         else:
1790             if self.text_block in bpy.data.texts:
1791                 #Read from a text datablock
1792                 material_file_contents = ""
1793                 material_file_contents = bpy.data.texts[self.text_block].as_string()
1794             else:
1795                 self.report({'ERROR'}, "Requested text block does not exist.")
1796                 self.text_block = "";
1797                 return {'CANCELLED'}
1798             
1799             if context.scene.mat_lib_bcm_name is "":
1800                 separator = ""
1801                 if "_" in self.text_block:
1802                     separator = "_"
1803                 elif "-" in self.text_block:
1804                     separator = "-"
1805                 elif " " in self.text_block:
1806                     separator = " "
1807                     
1808                 if separator is not "":
1809                     for word in self.text_block.split(separator):
1810                         if mat_name is not "":
1811                             mat_name += " "
1812                         mat_name += word.capitalize()
1813                 else:
1814                     mat_name = self.text_block
1815             else:
1816                 mat_name = context.scene.mat_lib_bcm_name
1817         
1818         
1819         if context.active_object.active_material:
1820             context.active_object.active_material.name = mat_name
1821         else:
1822             new_material = bpy.data.materials.new(mat_name)
1823             if len(context.active_object.material_slots.keys()) is 0:
1824                 bpy.ops.object.material_slot_add()
1825             context.active_object.material_slots[context.active_object.active_material_index].material = new_material
1826         
1827         #Prepare material for new nodes
1828         context.active_object.active_material.use_nodes = True
1829         context.active_object.active_material.node_tree.nodes.clear()
1830         
1831         if '<?xml version="1.0" encoding="UTF-8"?>' in material_file_contents[0:40]:
1832             material_file_contents = material_file_contents[material_file_contents.index("<material"):(material_file_contents.rindex("</material>") + 11)]
1833         else:
1834             self.mat_name = ""
1835             self.filename = ""
1836             self.text_block = ""
1837             self.open_location = ""
1838             self.report({'ERROR'}, "Material file is either invalid or outdated.")
1839             print(material_file_contents)
1840             return {'CANCELLED'}
1841         
1842         #Parse file
1843         dom = xml.dom.minidom.parseString(material_file_contents)
1844         
1845         #Create internal OSL scripts
1846         scripts = dom.getElementsByTagName("script")
1847         for s in scripts:
1848             osl_datablock = bpy.data.texts.new(name=s.attributes['name'].value)
1849             osl_text = s.toxml()[s.toxml().index(">"):s.toxml().rindex("<")]
1850             osl_text = osl_text[1:].replace("<br/>","\n").replace("&lt;", "<").replace("&gt;", ">").replace("&quot;", "\"").replace("&amp;", "&")
1851             osl_datablock.write(osl_text)
1852             osl_scripts.append(osl_datablock)
1853         
1854         #Add nodes
1855         nodes = dom.getElementsByTagName("node")
1856         addNodes(nodes, context.active_object.active_material)
1857         if node_message:
1858             self.report({node_message[0]}, node_message[1])
1859             node_message = []
1860             self.mat_name = ""
1861             self.filename = ""
1862             self.open_location = ""
1863             self.text_block = ""
1864             return {'CANCELLED'}
1865             
1866         #Create links
1867         links = dom.getElementsByTagName("link")
1868         createLinks(links, context.active_object.active_material)
1869         
1870         m = dom.getElementsByTagName("material")[0]
1871         
1872         #Set viewport color
1873         context.active_object.active_material.diffuse_color = color(m.attributes["view_color"].value)
1874             
1875         #Set sample-as-lamp-ness
1876         if boolean(m.attributes["sample_lamp"].value):
1877             sample_lamp = True
1878         else:
1879             sample_lamp = False
1880         context.active_object.active_material.cycles.sample_as_light = sample_lamp
1881         
1882         self.mat_name = ""
1883         self.filename = ""
1884         self.open_location = ""
1885         self.text_block = ""
1886         self.report({'INFO'}, "Material applied.")
1887         current_material_cached = True
1888         
1889         return {'FINISHED'}
1890
1891 class CacheLibraryMaterial(bpy.types.Operator):
1892     '''Cache material to disk'''
1893     bl_idname = "material.librarycache"
1894     bl_label = "cache material to disk"
1895     filename = bpy.props.StringProperty()
1896     
1897     def execute(self, context):
1898         global material_file_contents
1899         global current_material_cached
1900         
1901         if working_mode == "online":
1902             connection = http.client.HTTPConnection(mat_lib_host)
1903             connection.request("GET", mat_lib_location + "cycles/" + category_filename + "/" + self.filename + ".bcm")
1904             response = connection.getresponse().read()
1905         else:
1906             self.report({'ERROR'}, "Cannot cache material in offline mode.")
1907             return {'CANCELLED'}
1908         
1909         material_file_contents = str(response)
1910         if '<?xml version="1.0" encoding="UTF-8"?>' in material_file_contents[2:40]:
1911             material_file_contents = material_file_contents[material_file_contents.index("<material"):(material_file_contents.rindex("</material>") + 11)]
1912         else:
1913             self.report({'ERROR'}, "Invalid material file.")
1914             print(material_file_contents)
1915             return {'CANCELLED'}
1916         
1917         #Parse file
1918         dom = xml.dom.minidom.parseString(material_file_contents)
1919         
1920         #Create internal OSL scripts and cache image textures
1921         nodes = dom.getElementsByTagName("node")
1922         for node in nodes:
1923             node_data = node.attributes
1924             if node_data['type'].value == "TEX_IMAGE":
1925                 if node_data['image'].value:
1926                     if "file://" in node_data['image'].value:
1927                         self.report({'ERROR'}, "Cannot cache image texture located at %s." % node_data['image'].value)
1928                     elif "http://" in node_data['image'].value:
1929                         self.report({'ERROR'}, "Cannot cache image texture hosted at %s." % node_data['image'].value)
1930                     else:
1931                         ext = "." + node_data['image'].value.split(".")[-1]
1932                         image_name = node_data['image'].value[:-4]
1933                         
1934                         if ext.lower() != ".jpg" and ext.lower() != ".png":
1935                             node_message = ['ERROR', "The image file referenced by this image texture node is not .jpg or .png; not downloading."]
1936                             return
1937                             
1938                         if library == "composite" and os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures", image_name + ext)):
1939                             image_filepath = os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures", image_name + ext)
1940                         elif library != "bundled" and os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "textures", image_name + ext)):
1941                             image_filepath = os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "textures", image_name + ext)
1942                         elif library == "bundled" and os.path.exists(os.path.join(mat_lib_folder, "bundled", "cycles", "textures", image_name + ext)):
1943                             image_filepath = os.path.join(mat_lib_folder, "bundled", "cycles", "textures", image_name + ext)
1944                         elif working_mode == "online":
1945                             connection = http.client.HTTPConnection(mat_lib_host)
1946                             connection.request("GET", mat_lib_location + "cycles/textures/" + image_name + ext)
1947                             response = connection.getresponse().read()
1948                             
1949                             #Cache image texture
1950                             if library == "composite":
1951                                 image_filepath = os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures", image_name + ext)
1952                                 image_file = open(image_filepath, mode="w+b")
1953                                 image_file.write(response)
1954                                 image_file.close()
1955                             else:
1956                                 image_filepath = os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "textures", image_name + ext)
1957                                 image_file = open(image_filepath, mode="w+b")
1958                                 image_file.write(response)
1959                                 image_file.close()
1960                         else:
1961                             node_message = ['ERROR', "The image texture, \"%s\", is not cached; cannot download in offline mode." % (image_name + ext)]
1962                             image_filepath = ""
1963             elif node_data['type'].value == "SCRIPT":
1964                 if node_data['script'].value:
1965                     if "file://" in node_data['script'].value:
1966                         self.report({'ERROR'}, "Cannot cache OSL script located at %s." % node_data['script'].value)
1967                     elif "http://" in node_data['script'].value:
1968                         self.report({'ERROR'}, "Cannot cache OSL script hosted at %s." % node_data['script'].value)
1969                     else:
1970                         ext = "." + node_data['script'].value.split(".")[-1]
1971                         script_name = node_data['script'].value[:-4]
1972                         
1973                         if ext.lower() != ".osl" and ext.lower() != ".oso":
1974                             node_message = ['ERROR', "The OSL script file referenced by this script node is not .osl or .oso; not downloading."]
1975                             return
1976                             
1977                         if library == "composite" and os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, "cycles", "scripts", script_name + ext)):
1978                             script_filepath = os.path.join(mat_lib_folder, mat_lib_host, "cycles", "scripts", script_name + ext)
1979                         elif library != "bundled" and os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "scripts", script_name + ext)):
1980                             script_filepath = os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "scripts", script_name + ext)
1981                         elif library == "bundled" and os.path.exists(os.path.join(mat_lib_folder, "bundled", "cycles", "scripts", script_name + ext)):
1982                             script_filepath = os.path.join(mat_lib_folder, "bundled", "cycles", "scripts", script_name + ext)
1983                         elif working_mode == "online":
1984                             connection = http.client.HTTPConnection(mat_lib_host)
1985                             connection.request("GET", mat_lib_location + "cycles/scripts/" + script_name + ext)
1986                             response = connection.getresponse().read()
1987                             
1988                             #Cache image texture
1989                             if library == "composite":
1990                                 script_filepath = os.path.join(mat_lib_folder, mat_lib_host, "cycles", "scripts", script_name + ext)
1991                                 script_file = open(script_filepath, mode="w+b")
1992                                 script_file.write(response)
1993                                 script_file.close()
1994                             else:
1995                                 script_filepath = os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "scripts", script_name + ext)
1996                                 script_file = open(script_filepath, mode="w+b")
1997                                 script_file.write(response)
1998                                 script_file.close()
1999                         else:
2000                             node_message = ['ERROR', "The OSL script, \"%s\", is not cached; cannot download in offline mode." % (script_name + ext)]
2001                             script_filepath = ""
2002                     
2003         
2004         if library == "composite":
2005             bcm_file = open(mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep + category_filename + os.sep + self.filename + ".bcm", mode="w+b")
2006             bcm_file.write(response)
2007             bcm_file.close()
2008         elif library != "bundled":
2009             bcm_file = open(mat_lib_folder + os.sep + mat_lib_host + os.sep + library + os.sep + "cycles" + os.sep + category_filename + os.sep + self.filename + ".bcm", mode="w+b")
2010             bcm_file.write(response)
2011             bcm_file.close()
2012         else:
2013             self.report({'ERROR'}, "Cannot cache materials from this library.")
2014             return {'CANCELLED'}
2015             
2016         current_material_cached = True
2017         self.report({'INFO'}, "Material cached.")
2018         return {'FINISHED'}
2019
2020 class SaveLibraryMaterial(bpy.types.Operator, ExportHelper):
2021     '''Save material to disk'''
2022     bl_idname = "material.librarysave"
2023     bl_label = "Save material to disk"
2024     filepath = bpy.props.StringProperty()
2025     filename = bpy.props.StringProperty()
2026     
2027     #ExportHelper uses this
2028     filename_ext = ".bcm"
2029
2030     filter_glob = bpy.props.StringProperty(
2031             default="*.bcm",
2032             options={'HIDDEN'},
2033             )
2034     
2035     def execute(self, context):
2036         global material_file_contents
2037         global save_filename
2038         global current_material_cached
2039         
2040         if library == "composite" and os.path.exists(mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep + category_filename + os.sep + save_filename):
2041             bcm_file = open(mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep + category_filename + os.sep + self.filename, mode="r+b")
2042             response = bcm_file.read()
2043             bcm_file.close()
2044         elif library != "bundled" and os.path.exists(mat_lib_folder + os.sep + mat_lib_host + os.sep + library + os.sep + "cycles" + os.sep + category_filename + os.sep + save_filename):
2045             bcm_file = open(mat_lib_folder + os.sep + mat_lib_host + os.sep + library + os.sep + "cycles" + os.sep + category_filename + os.sep + self.filename, mode="r+b")
2046             response = bcm_file.read()
2047             bcm_file.close()
2048         elif library == "bundled" and os.path.exists(mat_lib_folder + os.sep + "bundled" + os.sep + "cycles" + os.sep + category_filename + os.sep + save_filename):
2049             bcm_file = open(mat_lib_folder + os.sep + "bundled" + os.sep + "cycles" + os.sep + category_filename + os.sep + self.filename, mode="r+b")
2050             response = bcm_file.read()
2051             bcm_file.close()
2052         elif working_mode == "online":
2053             connection = http.client.HTTPConnection(mat_lib_host)
2054             connection.request("GET", mat_lib_location + "cycles/" + category_filename + "/" + self.filename)
2055             response = connection.getresponse().read()
2056             
2057             #Cache material
2058             if library == "composite":
2059                 bcm_file = open(mat_lib_folder + os.sep + mat_lib_host + os.sep + "cycles" + os.sep + category_filename + os.sep + self.filename, mode="w+b")
2060                 bcm_file.write(response)
2061                 bcm_file.close()
2062             else:
2063                 bcm_file = open(mat_lib_folder + os.sep + mat_lib_host + os.sep + library + os.sep + "cycles" + os.sep + category_filename + os.sep + self.filename, mode="w+b")
2064                 bcm_file.write(response)
2065                 bcm_file.close()
2066         else:
2067             self.report({'ERROR'}, "Material is not cached; cannot download in offline mode.")
2068             return {'FINISHED'}
2069         
2070         material_file_contents = str(response)
2071         
2072         bcm_file = open(self.filepath, mode="w+b")
2073         bcm_file.write(response)
2074         bcm_file.close()
2075         
2076         if '<?xml version="1.0" encoding="UTF-8"?>' in material_file_contents[0:40]:
2077             material_file_contents = material_file_contents[material_file_contents.index("<material"):(material_file_contents.rindex("</material>") + 11)]
2078         else:
2079             self.report({'ERROR'}, "Invalid material file.")
2080             print(material_file_contents)
2081             return {'CANCELLED'}
2082         
2083         #Parse file
2084         dom = xml.dom.minidom.parseString(material_file_contents)
2085         
2086         bcm_file = open(self.filepath, mode="r", encoding="UTF-8")
2087         material_file_contents = bcm_file.read()
2088         bcm_file.close()
2089         
2090         #Create internal OSL scripts and cache image textures
2091         nodes = dom.getElementsByTagName("node")
2092         for node in nodes:
2093             node_data = node.attributes
2094             if node_data['type'].value == "TEX_IMAGE":
2095                 if node_data['image'].value:
2096                     node_attributes = (
2097                         node_data['image'].value,
2098                         node_data['source'].value,
2099                         node_data['color_space'].value,
2100                         node_data['projection'].value,
2101                         node_data['loc'].value)
2102                     original_xml = ("<node type=\"TEX_IMAGE\" image=\"%s\" source=\"%s\" color_space=\"%s\" projection=\"%s\" loc=\"%s\" />" % node_attributes)
2103                     if "file://" in node_data['image'].value:
2104                         if os.path.exists(node_data['image'].value[7:]):
2105                             image_file = open(node_data['image'].value[7:], mode="r+b")
2106                             image_data = image_file.read()
2107                             image_file.close()
2108                             copied_image = open(self.filepath[:-len(self.filename)] + node_data['image'].value.split(os.sep)[-1], mode="w+b")
2109                             copied_image.write(image_data)
2110                             copied_image.close()
2111                             image_location = ("file://" + self.filepath[:-len(self.filename)] + node_data['image'].value.split(os.sep)[-1])
2112                         else:
2113                             image_location = ""
2114                     elif "http://" in node_data['image'].value:
2115                         self.report({'ERROR'}, "Cannot save image texture hosted at %s." % node_data['image'].value)
2116                         image_location = ""
2117                     else:
2118                         ext = "." + node_data['image'].value.split(".")[-1]
2119                         image_name = node_data['image'].value[:-4]
2120                         
2121                         if ext.lower() != ".jpg" and ext.lower() != ".png":
2122                             node_message = ['ERROR', "The image file referenced by this image texture node is not .jpg or .png; not downloading."]
2123                             return
2124                             
2125                         if library == "composite" and os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures", image_name + ext)):
2126                             image_filepath = os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures", image_name + ext)
2127                         elif library != "bundled" and os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "textures", image_name + ext)):
2128                             image_filepath = os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "textures", image_name + ext)
2129                         elif library == "bundled" and os.path.exists(os.path.join(mat_lib_folder, "bundled", "cycles", "textures", image_name + ext)):
2130                             image_filepath = os.path.join(mat_lib_folder, "bundled", "cycles", "textures", image_name + ext)
2131                         elif working_mode == "online":
2132                             connection = http.client.HTTPConnection(mat_lib_host)
2133                             connection.request("GET", mat_lib_location + "cycles/textures/" + image_name + ext)
2134                             response = connection.getresponse().read()
2135                             
2136                             #Cache image texture
2137                             if library == "composite":
2138                                 image_filepath = os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures", image_name + ext)
2139                                 image_file = open(image_filepath, mode="w+b")
2140                                 image_file.write(response)
2141                                 image_file.close()
2142                             else:
2143                                 image_filepath = os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "textures", image_name + ext)
2144                                 image_file = open(image_filepath, mode="w+b")
2145                                 image_file.write(response)
2146                                 image_file.close()
2147                         else:
2148                             node_message = ['ERROR', "The image texture, \"%s\", is not cached; cannot download in offline mode." % (image_name + ext)]
2149                             image_filepath = ""
2150                         image_location = ("file://" + self.filepath[:-len(self.filename)] + node_data['image'].value)
2151                         
2152                         if image_filepath:
2153                             print(image_filepath)
2154                             image_file = open(image_filepath, mode="r+b")
2155                             image_data = image_file.read()
2156                             image_file.close()
2157                             saved_image = open(self.filepath[:-len(self.filename)] + node_data['image'].value, mode="w+b")
2158                             saved_image.write(image_data)
2159                             saved_image.close()
2160                 
2161                     updated_xml = original_xml.replace(node_data['image'].value, image_location)
2162                     material_file_contents = material_file_contents.replace(original_xml, updated_xml)
2163             elif node_data['type'].value == "SCRIPT":
2164                 if node_data['script'].value:
2165                     node_attributes = (
2166                         node_data['mode'].value,
2167                         node_data['script'].value,
2168                         node_data['loc'].value)
2169                     original_xml = ("<node type=\"SCRIPT\" mode=\"%s\" script=\"%s\" loc=\"%s\" />" % node_attributes)
2170                     if "file://" in node_data['script'].value:
2171                         if os.path.exists(node_data['script'].value[7:]):
2172                             script_file = open(node_data['script'].value[7:], mode="r+b")
2173                             script_data = script_file.read()
2174                             script_file.close()
2175                             copied_script = open(self.filepath[:-len(self.filename)] + node_data['script'].value.split(os.sep)[-1], mode="w+b")
2176                             copied_script.write(script_data)
2177                             copied_script.close()
2178                             script_location = ("file://" + self.filepath[:-len(self.filename)] + node_data['script'].value.split(os.sep)[-1])
2179                         else:
2180                             script_location = ""
2181                     elif "http://" in node_data['script'].value:
2182                         self.report({'ERROR'}, "Cannot save OSL script hosted at %s." % node_data['script'].value)
2183                         script_location = ""
2184                     else:
2185                         ext = "." + node_data['script'].value.split(".")[-1]
2186                         script_name = node_data['script'].value[:-4]
2187                         
2188                         if ext.lower() != ".osl" and ext.lower() != ".oso":
2189                             node_message = ['ERROR', "The OSL script file referenced by this script node is not .osl or .oso; not downloading."]
2190                             return
2191                             
2192                         if library == "composite" and os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, "cycles", "scripts", script_name + ext)):
2193                             script_filepath = os.path.join(mat_lib_folder, mat_lib_host, "cycles", "scripts", script_name + ext)
2194                         elif library != "bundled" and os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "scripts", script_name + ext)):
2195                             script_filepath = os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "scripts", script_name + ext)
2196                         elif library == "bundled" and os.path.exists(os.path.join(mat_lib_folder, "bundled", "cycles", "scripts", script_name + ext)):
2197                             script_filepath = os.path.join(mat_lib_folder, "bundled", "cycles", "scripts", script_name + ext)
2198                         elif working_mode == "online":
2199                             connection = http.client.HTTPConnection(mat_lib_host)
2200                             connection.request("GET", mat_lib_location + "cycles/scripts/" + script_name + ext)
2201                             response = connection.getresponse().read()
2202                             
2203                             #Cache OSL script
2204                             if library == "composite":
2205                                 script_filepath = os.path.join(mat_lib_folder, mat_lib_host, "cycles", "scripts", script_name + ext)
2206                                 script_file = open(script_filepath, mode="w+b")
2207                                 script_file.write(response)
2208                                 script_file.close()
2209                             else:
2210                                 script_filepath = os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "scripts", script_name + ext)
2211                                 script_file = open(script_filepath, mode="w+b")
2212                                 script_file.write(response)
2213                                 script_file.close()
2214                         else:
2215                             node_message = ['ERROR', "The OSL script, \"%s\", is not cached; cannot download in offline mode." % (script_name + ext)]
2216                             script_filepath = ""
2217                         
2218                         if script_filepath:
2219                             print(script_filepath)
2220                             script_file = open(script_filepath, mode="r+b")
2221                             script_data = script_file.read()
2222                             script_file.close()
2223                             saved_script = open(self.filepath[:-len(self.filename)] + node_data['script'].value, mode="w+b")
2224                             saved_script.write(script_data)
2225                             saved_script.close()
2226                     
2227                     updated_xml = original_xml.replace(node_data['script'].value, script_location)
2228                     material_file_contents = material_file_contents.replace(original_xml, updated_xml)
2229         
2230         bcm_file = open(self.filepath, mode="w", encoding="UTF-8")
2231         bcm_file.write(material_file_contents)
2232         bcm_file.close()
2233         
2234         self.report({'INFO'}, "Material saved.")
2235         current_material_cached = True
2236         
2237         return {'FINISHED'}
2238
2239 def createLinks(links, mat):
2240     node_tree = mat.node_tree
2241     for dom_link in links:
2242         link = {
2243     "to": int(dom_link.attributes['to'].value),
2244     "input": int(dom_link.attributes['input'].value),
2245     "from": int(dom_link.attributes['from'].value),
2246     "output": int(dom_link.attributes['output'].value)}
2247         node_tree.links.new(
2248     node_tree.nodes[link["to"]].inputs[link["input"]],
2249     node_tree.nodes[link["from"]].outputs[link["output"]])
2250
2251 def addNodes(nodes, mat):
2252     global node_message
2253     global osl_scripts
2254     
2255     for dom_node in nodes:
2256         node_type = dom_node.attributes['type'].value
2257         loc = dom_node.attributes['loc'].value
2258         node_location = [int(loc[:loc.index(",")]), int(loc[(loc.index(",") + 1):])]
2259         node_tree = mat.node_tree
2260         node_data = dom_node.attributes
2261         
2262         #Below here checks the type of the node and adds the correct type
2263         
2264         #INPUT TYPES
2265         #This is totally crafty, but some of these nodes actually
2266         # store their values as their output's default value!
2267         if node_type == "ATTRIBUTE":
2268             print ("ATTRIBUTE")
2269             node = node_tree.nodes.new(node_type)
2270             node.attribute_name = node_data['attribute'].value
2271         
2272         elif node_type == "CAMERA":
2273             print ("CAMERA")
2274             node = node_tree.nodes.new(node_type)
2275         
2276         elif node_type == "FRESNEL":
2277             print ("FRESNEL")
2278             node = node_tree.nodes.new(node_type)
2279             node.inputs['IOR'].default_value = float(node_data['ior'].value)
2280                 
2281         elif node_type == "LAYER_WEIGHT":
2282             print ("LAYER_WEIGHT")
2283             node = node_tree.nodes.new(node_type)
2284             node.inputs['Blend'].default_value = float(node_data['blend'].value)
2285                 
2286         elif node_type == "LIGHT_PATH":
2287             print ("LIGHT_PATH")
2288             node = node_tree.nodes.new(node_type)
2289         
2290         elif node_type == "NEW_GEOMETRY":
2291             print ("NEW_GEOMETRY")
2292             node = node_tree.nodes.new(node_type)
2293         
2294         elif node_type == "OBJECT_INFO":
2295             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) < 2.64:
2296                 node_message = ['ERROR', """The material file contains the node \"%s\".
2297 This node is not available in the Blender version you are currently using.
2298 You may need a newer version of Blender for this material to work properly.""" % node_type]
2299                 return
2300             print ("OBJECT_INFO")
2301             node = node_tree.nodes.new(node_type)
2302         
2303         elif node_type == "PARTICLE_INFO":
2304             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) < 2.64:
2305                 node_message = ['ERROR', """The material file contains the node \"%s\".
2306 This node is not available in the Blender version you are currently using.
2307 You may need a newer version of Blender for this material to work properly.""" % node_type]
2308                 return
2309             print ("PARTICLE_INFO")
2310             node = node_tree.nodes.new(node_type)
2311         
2312         elif node_type == "RGB":
2313             print ("RGB")
2314             node = node_tree.nodes.new(node_type)
2315             node.outputs['Color'].default_value = color(node_data['color'].value)
2316         
2317         elif node_type == "TANGENT":
2318             print ("TANGENT")
2319             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) < 2.65:
2320                 node_message = ['ERROR', """The material file contains the node \"%s\".
2321 This node is not available in the Blender version you are currently using.
2322 You may need a newer version of Blender for this material to work properly.""" % node_type]
2323                 return
2324             node = node_tree.nodes.new(node_type)
2325             node.direction_type = node_data['direction'].value
2326             node.axis = node_data['axis'].value
2327         
2328         elif node_type == "TEX_COORD":
2329             print ("TEX_COORD")
2330             node = node_tree.nodes.new(node_type)
2331             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) > 2.64 and "dupli" in node_data:
2332                 node.from_dupli = boolean(node_data['dupli'].value)
2333         
2334         elif node_type == "VALUE":
2335             print ("VALUE")
2336             node = node_tree.nodes.new(node_type)
2337             node.outputs['Value'].default_value = float(node_data['value'].value)
2338             
2339             #OUTPUT TYPES
2340         elif node_type == "OUTPUT_LAMP":
2341             print ("OUTPUT_LAMP")
2342             node = node_tree.nodes.new(node_type)
2343         
2344         elif node_type == "OUTPUT_MATERIAL":
2345             print ("OUTPUT_MATERIAL")
2346             node = node_tree.nodes.new(node_type)
2347         
2348         elif node_type == "OUTPUT_WORLD":
2349             print ("OUTPUT_WORLD")
2350             node = node_tree.nodes.new(node_type)
2351         
2352             #SHADER TYPES
2353         elif node_type == "ADD_SHADER":
2354             print ("ADD_SHADER")
2355             node = node_tree.nodes.new(node_type)
2356             
2357         elif node_type == "AMBIENT_OCCLUSION":
2358             print ("AMBIENT_OCCLUSION")
2359             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) < 2.65:
2360                 node_message = ['ERROR', """The material file contains the node \"%s\".
2361 This node is not available in the Blender version you are currently using.
2362 You may need a newer version of Blender for this material to work properly.""" % node_type]
2363                 return
2364             node = node_tree.nodes.new(node_type)
2365             node.inputs['Color'].default_value = color(node_data['color'].value)
2366         
2367         elif node_type == "BACKGROUND":
2368             print ("BACKGROUND")
2369             node = node_tree.nodes.new(node_type)
2370             node.inputs['Color'].default_value = color(node_data['color'].value)
2371             node.inputs['Strength'].default_value = float(node_data['strength'].value)
2372             
2373         elif node_type == "BSDF_ANISOTROPIC":
2374             print ("BSDF_ANISOTROPIC")
2375             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) < 2.65:
2376                 node_message = ['ERROR', """The material file contains the node \"%s\".
2377 This node is not available in the Blender version you are currently using.
2378 You may need a newer version of Blender for this material to work properly.""" % node_type]
2379                 return
2380             node = node_tree.nodes.new(node_type)
2381             node.inputs['Color'].default_value = color(node_data['color'].value)
2382             node.inputs['Roughness'].default_value = float(node_data['roughness'].value)
2383             node.inputs['Anisotropy'].default_value = float(node_data['anisotropy'].value)
2384             node.inputs['Rotation'].default_value = float(node_data['rotation'].value)
2385             
2386         elif node_type == "BSDF_DIFFUSE":
2387             print ("BSDF_DIFFUSE")
2388             node = node_tree.nodes.new(node_type)
2389             node.inputs['Color'].default_value = color(node_data['color'].value)
2390             node.inputs['Roughness'].default_value = float(node_data['roughness'].value)
2391         
2392         elif node_type == "BSDF_GLASS":
2393             print ("BSDF_GLASS")
2394             node = node_tree.nodes.new(node_type)
2395             node.distribution = node_data['distribution'].value
2396             node.inputs['Color'].default_value = color(node_data['color'].value)
2397             node.inputs['Roughness'].default_value = float(node_data['roughness'].value)
2398             node.inputs['IOR'].default_value = float(node_data['ior'].value)
2399             
2400         elif node_type == "BSDF_GLOSSY":
2401             print ("BSDF_GLOSSY")
2402             node = node_tree.nodes.new(node_type)
2403             node.distribution = node_data['distribution'].value
2404             node.inputs['Color'].default_value = color(node_data['color'].value)
2405             node.inputs['Roughness'].default_value = float(node_data['roughness'].value)
2406         
2407         elif node_type == "BSDF_REFRACTION":
2408             print ("BSDF_REFRACTION")
2409             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) < 2.65:
2410                 node_message = ['ERROR', """The material file contains the node \"%s\".
2411 This node is not available in the Blender version you are currently using.
2412 You may need a newer version of Blender for this material to work properly.""" % node_type]
2413                 return
2414             node = node_tree.nodes.new(node_type)
2415             node.distribution = node_data['distribution'].value
2416             node.inputs['Color'].default_value = color(node_data['color'].value)
2417             node.inputs['Roughness'].default_value = float(node_data['roughness'].value)
2418             node.inputs['IOR'].default_value = float(node_data['ior'].value)
2419         
2420         elif node_type == "BSDF_TRANSLUCENT":
2421             print ("BSDF_TRANSLUCENT")
2422             node = node_tree.nodes.new(node_type)
2423             node.inputs['Color'].default_value = color(node_data['color'].value)
2424         
2425         elif node_type == "BSDF_TRANSPARENT":
2426             print ("BSDF_TRANSPARENT")
2427             node = node_tree.nodes.new(node_type)
2428             node.inputs['Color'].default_value = color(node_data['color'].value)
2429         
2430         elif node_type == "BSDF_VELVET":
2431             print ("BSDF_VELVET")
2432             node = node_tree.nodes.new(node_type)
2433             node.inputs['Color'].default_value = color(node_data['color'].value)
2434             node.inputs['Sigma'].default_value = float(node_data['sigma'].value)
2435         
2436         elif node_type == "EMISSION":
2437             print ("EMISSION")
2438             node = node_tree.nodes.new(node_type)
2439             node.inputs['Color'].default_value = color(node_data['color'].value)
2440             node.inputs['Strength'].default_value = float(node_data['strength'].value)
2441         
2442         elif node_type == "HOLDOUT":
2443             print ("HOLDOUT")
2444             node = node_tree.nodes.new(node_type)
2445         
2446         elif node_type == "MIX_SHADER":
2447             print ("MIX_SHADER")
2448             node = node_tree.nodes.new(node_type)
2449             node.inputs['Fac'].default_value = float(node_data['fac'].value)
2450         
2451             #TEXTURE TYPES
2452         elif node_type == "TEX_BRICK":
2453             print ("TEX_BRICK")
2454             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) < 2.64:
2455                 node_message = ['ERROR', """The material file contains the node \"%s\".
2456 This node is not available in the Blender version you are currently using.
2457 You may need a newer version of Blender for this material to work properly.""" % node_type]
2458                 return
2459             node = node_tree.nodes.new(node_type)
2460             node.offset = float(node_data['offset'].value)
2461             node.offset_frequency = float(node_data['offset_freq'].value)
2462             node.squash = float(node_data['squash'].value)
2463             node.squash_frequency = float(node_data['squash_freq'].value)
2464             node.inputs['Color1'].default_value = color(node_data['color1'].value)
2465             node.inputs['Color2'].default_value = color(node_data['color2'].value)
2466             node.inputs['Mortar'].default_value = color(node_data['mortar'].value)
2467             node.inputs['Scale'].default_value = float(node_data['scale'].value)
2468             node.inputs['Mortar Size'].default_value = float(node_data['mortar_size'].value)
2469             node.inputs['Bias'].default_value = float(node_data['bias'].value)
2470             node.inputs['Brick Width'].default_value = float(node_data['width'].value)
2471             node.inputs['Row Height'].default_value = float(node_data['height'].value)
2472             
2473         elif node_type == "TEX_CHECKER":
2474             print ("TEX_CHECKER")
2475             node = node_tree.nodes.new(node_type)
2476             node.inputs['Color1'].default_value = color(node_data['color1'].value)
2477             node.inputs['Color2'].default_value = color(node_data['color2'].value)
2478             node.inputs['Scale'].default_value = float(node_data['scale'].value)
2479             
2480         elif node_type == "TEX_ENVIRONMENT":
2481             print ("TEX_ENVIRONMENT")
2482             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) < 2.64:
2483                 node_message = ['ERROR', """The material file contains the node \"%s\".
2484 This node is not available in the Blender version you are currently using.
2485 You may need a newer version of Blender for this material to work properly.""" % node_type]
2486                 return
2487             node = node_tree.nodes.new(node_type)
2488             node.color_space = node_data['color_space'].value
2489             node.projection = node_data['projection'].value
2490             if 'image' in node_data:
2491                 if "file://" in node_data['image'].value:
2492                     image_filepath = node_data['image'].value[7:]
2493                     image_name = node_data['image'].value.split(os.sep)[-1]
2494                     image_datablock = bpy.data.images.new(name=image_name, width=4, height=4)
2495                     image_datablock.source = node_data['source'].value
2496                     image_datablock.filepath = image_filepath
2497                     node.image = image_datablock
2498                     if node_data['source'].value == 'MOVIE' or node_data['source'].value == 'SEQUENCE':
2499                         node.image_user.frame_duration = int(node_data['frame_duration'].value)
2500                         node.image_user.frame_start = int(node_data['frame_start'].value)
2501                         node.image_user.frame_offset = int(node_data['frame_offset'].value)
2502                         node.image_user.use_cyclic = boolean(node_data['cyclic'].value)
2503                         node.image_user.use_auto_refresh = boolean(node_data['auto_refresh'].value)
2504                 elif "http://" in node_data['image'].value and bpy.context.scene.mat_lib_images_only_trusted == False:
2505                     ext = "." + node_data['image'].value.split(".")[-1]
2506                     image_name = node_data['image'].value.split("/")[-1][:-4]
2507                     image_host = node_data['image'].value[7:].split("/")[0]
2508                     image_location = node_data['image'].value[(7 + len(image_host)):]
2509                     
2510                     if ext.lower() != ".jpg" and ext.lower() != ".png":
2511                         node_message = ['ERROR', "The image file referenced by this image texture node is not .jpg or .png; not downloading."]
2512                         return
2513                     
2514                     connection = http.client.HTTPConnection(image_host)
2515                     connection.request("GET", image_location)
2516                     response = connection.getresponse().read()
2517                     #Save image texture
2518                     image_filepath = os.path.join(mat_lib_folder, "my-materials", image_name + ext)
2519                     image_file = open(image_filepath, mode="w+b")
2520                     image_file.write(response)
2521                     image_file.close()
2522                     image_datablock = bpy.data.images.new(name=(image_name + ext), width=4, height=4)
2523                     image_datablock.source = node_data['source'].value
2524                     image_datablock.filepath = image_filepath
2525                     node.image = image_datablock
2526                     if node_data['source'].value == 'MOVIE' or node_data['source'].value == 'SEQUENCE':
2527                         node.image_user.frame_duration = int(node_data['frame_duration'].value)
2528                         node.image_user.frame_start = int(node_data['frame_start'].value)
2529                         node.image_user.frame_offset = int(node_data['frame_offset'].value)
2530                         node.image_user.use_cyclic = boolean(node_data['cyclic'].value)
2531                         node.image_user.use_auto_refresh = boolean(node_data['auto_refresh'].value)
2532                 else:
2533                     ext = "." + node_data['image'].value.split(".")[-1]
2534                     image_name = node_data['image'].value[:-4]
2535                     
2536                     if ext.lower() != ".jpg" and ext.lower() != ".png":
2537                         node_message = ['ERROR', "The image file referenced by this image texture node is not .jpg or .png; not downloading."]
2538                         return
2539                         
2540                     if library == "composite" and os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures", image_name + ext)):
2541                         image_filepath = os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures", image_name + ext)
2542                     elif library != "bundled" and os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "textures", image_name + ext)):
2543                         image_filepath = os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "textures", image_name + ext)
2544                     elif library == "bundled" and os.path.exists(os.path.join(mat_lib_folder, "bundled", "cycles", "textures", image_name + ext)):
2545                         image_filepath = os.path.join(mat_lib_folder, "bundled", "cycles", "textures", image_name + ext)
2546                     elif working_mode == "online":
2547                         connection = http.client.HTTPConnection(mat_lib_host)
2548                         connection.request("GET", mat_lib_location + "cycles/textures/" + image_name + ext)
2549                         response = connection.getresponse().read()
2550                         
2551                         #Cache image texture
2552                         if library == "composite":
2553                             image_filepath = os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures", image_name + ext)
2554                             image_file = open(image_filepath, mode="w+b")
2555                             image_file.write(response)
2556                             image_file.close()
2557                         else:
2558                             image_filepath = os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "textures", image_name + ext)
2559                             image_file = open(image_filepath, mode="w+b")
2560                             image_file.write(response)
2561                             image_file.close()
2562                     else:
2563                         node_message = ['ERROR', "The image texture, \"%s\", is not cached; cannot download in offline mode." % (image_name + ext)]
2564                         image_filepath = ""
2565                     if image_filepath != "":
2566                         image_datablock = bpy.data.images.new(name=(image_name + ext), width=4, height=4)
2567                         image_datablock.source = node_data['source'].value
2568                         image_datablock.filepath = image_filepath
2569                         node.image = image_datablock
2570                         if node_data['source'].value == 'MOVIE' or node_data['source'].value == 'SEQUENCE':
2571                             node.image_user.frame_duration = int(node_data['frame_duration'].value)
2572                             node.image_user.frame_start = int(node_data['frame_start'].value)
2573                             node.image_user.frame_offset = int(node_data['frame_offset'].value)
2574                             node.image_user.use_cyclic = boolean(node_data['cyclic'].value)
2575                             node.image_user.use_auto_refresh = boolean(node_data['auto_refresh'].value)
2576             
2577         elif node_type == "TEX_GRADIENT":
2578             print ("TEX_GRADIENT")
2579             node = node_tree.nodes.new(node_type)
2580             node.gradient_type = node_data['gradient'].value
2581         
2582         elif node_type == "TEX_IMAGE":
2583             print ("TEX_IMAGE")
2584             node = node_tree.nodes.new(node_type)
2585             node.color_space = node_data['color_space'].value
2586             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) > 2.63 and "projection" in node_data:
2587                 node.projection = node_data['projection'].value
2588             if 'image' in node_data:
2589                 if "file://" in node_data['image'].value:
2590                     image_filepath = node_data['image'].value[7:]
2591                     image_name = node_data['image'].value.split(os.sep)[-1]
2592                     image_datablock = bpy.data.images.new(name=image_name, width=4, height=4)
2593                     image_datablock.source = node_data['source'].value
2594                     image_datablock.filepath = image_filepath
2595                     node.image = image_datablock
2596                     if node_data['source'].value == 'MOVIE' or node_data['source'].value == 'SEQUENCE':
2597                         node.image_user.frame_duration = int(node_data['frame_duration'].value)
2598                         node.image_user.frame_start = int(node_data['frame_start'].value)
2599                         node.image_user.frame_offset = int(node_data['frame_offset'].value)
2600                         node.image_user.use_cyclic = boolean(node_data['cyclic'].value)
2601                         node.image_user.use_auto_refresh = boolean(node_data['auto_refresh'].value)
2602                 elif "http://" in node_data['image'].value and bpy.context.scene.mat_lib_images_only_trusted == False:
2603                     ext = "." + node_data['image'].value.split(".")[-1]
2604                     image_name = node_data['image'].value.split("/")[-1][:-4]
2605                     image_host = node_data['image'].value[7:].split("/")[0]
2606                     image_location = node_data['image'].value[(7 + len(image_host)):]
2607                     
2608                     if ext.lower() != ".jpg" and ext.lower() != ".png":
2609                         node_message = ['ERROR', "The image file referenced by this image texture node is not .jpg or .png; not downloading."]
2610                         return
2611                     
2612                     connection = http.client.HTTPConnection(image_host)
2613                     connection.request("GET", image_location)
2614                     response = connection.getresponse().read()
2615                     #Save image texture
2616                     image_filepath = os.path.join(mat_lib_folder, "my-materials", image_name + ext)
2617                     image_file = open(image_filepath, mode="w+b")
2618                     image_file.write(response)
2619                     image_file.close()
2620                     image_datablock = bpy.data.images.new(name=(image_name + ext), width=4, height=4)
2621                     image_datablock.source = node_data['source'].value
2622                     image_datablock.filepath = image_filepath
2623                     node.image = image_datablock
2624                     if node_data['source'].value == 'MOVIE' or node_data['source'].value == 'SEQUENCE':
2625                         node.image_user.frame_duration = int(node_data['frame_duration'].value)
2626                         node.image_user.frame_start = int(node_data['frame_start'].value)
2627                         node.image_user.frame_offset = int(node_data['frame_offset'].value)
2628                         node.image_user.use_cyclic = boolean(node_data['cyclic'].value)
2629                         node.image_user.use_auto_refresh = boolean(node_data['auto_refresh'].value)
2630                 else:
2631                     ext = "." + node_data['image'].value.split(".")[-1]
2632                     image_name = node_data['image'].value[:-4]
2633                     
2634                     if ext.lower() != ".jpg" and ext.lower() != ".png":
2635                         node_message = ['ERROR', "The image file referenced by this image texture node is not .jpg or .png; not downloading."]
2636                         return
2637                         
2638                     if library == "composite" and os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures", image_name + ext)):
2639                         image_filepath = os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures", image_name + ext)
2640                     elif library != "bundled" and os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "textures", image_name + ext)):
2641                         image_filepath = os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "textures", image_name + ext)
2642                     elif library == "bundled" and os.path.exists(os.path.join(mat_lib_folder, "bundled", "cycles", "textures", image_name + ext)):
2643                         image_filepath = os.path.join(mat_lib_folder, "bundled", "cycles", "textures", image_name + ext)
2644                     elif working_mode == "online":
2645                         connection = http.client.HTTPConnection(mat_lib_host)
2646                         connection.request("GET", mat_lib_location + "cycles/textures/" + image_name + ext)
2647                         response = connection.getresponse().read()
2648                         
2649                         #Cache image texture
2650                         if library == "composite":
2651                             image_filepath = os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures", image_name + ext)
2652                             image_file = open(image_filepath, mode="w+b")
2653                             image_file.write(response)
2654                             image_file.close()
2655                         else:
2656                             image_filepath = os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "textures", image_name + ext)
2657                             image_file = open(image_filepath, mode="w+b")
2658                             image_file.write(response)
2659                             image_file.close()
2660                     else:
2661                         node_message = ['ERROR', "The image texture, \"%s\", is not cached; cannot download in offline mode." % (image_name + ext)]
2662                         image_filepath = ""
2663                     if image_filepath != "":
2664                         image_datablock = bpy.data.images.new(name=(image_name + ext), width=4, height=4)
2665                         image_datablock.source = node_data['source'].value
2666                         image_datablock.filepath = image_filepath
2667                         node.image = image_datablock
2668                         if node_data['source'].value == 'MOVIE' or node_data['source'].value == 'SEQUENCE':
2669                             node.image_user.frame_duration = int(node_data['frame_duration'].value)
2670                             node.image_user.frame_start = int(node_data['frame_start'].value)
2671                             node.image_user.frame_offset = int(node_data['frame_offset'].value)
2672                             node.image_user.use_cyclic = boolean(node_data['cyclic'].value)
2673                             node.image_user.use_auto_refresh = boolean(node_data['auto_refresh'].value)
2674                 
2675         elif node_type == "TEX_MAGIC":
2676             print ("TEX_MAGIC")
2677             node = node_tree.nodes.new(node_type)
2678             node.turbulence_depth = int(node_data['depth'].value)
2679             node.inputs['Scale'].default_value = float(node_data['scale'].value)
2680             node.inputs['Distortion'].default_value = float(node_data['distortion'].value)
2681         
2682         elif node_type == "TEX_MUSGRAVE":
2683             print ("TEX_MUSGRAVE")
2684             node = node_tree.nodes.new(node_type)
2685             node.musgrave_type = node_data['musgrave'].value
2686             node.inputs['Scale'].default_value = float(node_data['scale'].value)
2687             node.inputs['Detail'].default_value = float(node_data['detail'].value)
2688             node.inputs['Dimension'].default_value = float(node_data['dimension'].value)
2689             node.inputs['Lacunarity'].default_value = float(node_data['lacunarity'].value)
2690             node.inputs['Offset'].default_value = float(node_data['offset'].value)
2691             node.inputs['Gain'].default_value = float(node_data['gain'].value)
2692         
2693         elif node_type == "TEX_NOISE":
2694             print ("TEX_NOISE")
2695             node = node_tree.nodes.new(node_type)
2696             node.inputs['Scale'].default_value = float(node_data['scale'].value)
2697             node.inputs['Detail'].default_value = float(node_data['detail'].value)
2698             node.inputs['Distortion'].default_value = float(node_data['distortion'].value)
2699                         
2700         elif node_type == "TEX_SKY":
2701             print ("TEX_SKY")
2702             node = node_tree.nodes.new(node_type)
2703             node.sun_direction = vector(node_data['sun_direction'].value)
2704             node.turbidity = float(node_data['turbidity'].value)
2705         
2706         elif node_type == "TEX_VORONOI":
2707             print ("TEX_VORONOI")
2708             node = node_tree.nodes.new(node_type)
2709             node.coloring = node_data['coloring'].value
2710             node.inputs['Scale'].default_value = float(node_data['scale'].value)
2711         
2712         elif node_type == "TEX_WAVE":
2713             print ("TEX_WAVE")
2714             node = node_tree.nodes.new(node_type)
2715             node.wave_type = node_data['wave'].value
2716             node.inputs['Scale'].default_value = float(node_data['scale'].value)
2717             node.inputs['Distortion'].default_value = float(node_data['distortion'].value)
2718             node.inputs['Detail'].default_value = float(node_data['detail'].value)
2719             node.inputs['Detail Scale'].default_value = float(node_data['detail_scale'].value)
2720         
2721             #COLOR TYPES
2722         elif node_type == "BRIGHTCONTRAST":
2723             print ("BRIGHTCONTRAST")
2724             node = node_tree.nodes.new(node_type)
2725             node.inputs['Color'].default_value = color(node_data['color'].value)
2726             node.inputs['Bright'].default_value = float(node_data['bright'].value)
2727             node.inputs['Contrast'].default_value = float(node_data['contrast'].value)
2728         
2729         elif node_type == "GAMMA":
2730             print ("GAMMA")
2731             node = node_tree.nodes.new(node_type)
2732             node.inputs['Color'].default_value = color(node_data['color'].value)
2733             node.inputs['Gamma'].default_value = float(node_data['gamma'].value)
2734         
2735         elif node_type == "HUE_SAT":
2736             print ("HUE_SAT")
2737             node = node_tree.nodes.new(node_type)
2738             node.inputs['Hue'].default_value = float(node_data['hue'].value)
2739             node.inputs['Saturation'].default_value = float(node_data['saturation'].value)
2740             node.inputs['Value'].default_value = float(node_data['value'].value)
2741             node.inputs['Fac'].default_value = float(node_data['fac'].value)
2742             node.inputs['Color'].default_value = color(node_data['color'].value)
2743             
2744         elif node_type == "INVERT":
2745             print ("INVERT")
2746             node = node_tree.nodes.new(node_type)
2747             node.inputs['Fac'].default_value = float(node_data['fac'].value)
2748             node.inputs['Color'].default_value = color(node_data['color'].value)
2749         
2750         elif node_type == "LIGHT_FALLOFF":
2751             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) < 2.64:
2752                 node_message = ['ERROR', """The material file contains the node \"%s\".
2753 This node is not available in the Blender version you are currently using.
2754 You may need a newer version of Blender for this material to work properly.""" % node_type]
2755                 return
2756             print ("LIGHT_FALLOFF")
2757             node = node_tree.nodes.new(node_type)
2758             node.inputs['Strength'].default_value = float(node_data['strength'].value)
2759             node.inputs['Smooth'].default_value = float(node_data['smooth'].value)
2760         
2761         elif node_type == "MIX_RGB":
2762             print ("MIX_RGB")
2763             node = node_tree.nodes.new(node_type)
2764             node.blend_type = node_data['blend_type'].value
2765             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) > 2.63 and "clamp" in node_data:
2766                 node.use_clamp = boolean(node_data['clamp'].value)
2767             node.inputs['Fac'].default_value = float(node_data['fac'].value)
2768             node.inputs['Color1'].default_value = color(node_data['color1'].value)
2769             node.inputs['Color2'].default_value = color(node_data['color2'].value)
2770         
2771             #VECTOR TYPES
2772         elif node_type == "BUMP":
2773             print ("BUMP")
2774             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) < 2.65:
2775                 node_message = ['ERROR', """The material file contains the node \"%s\".
2776 This node is not available in the Blender version you are currently using.
2777 You may need a newer version of Blender for this material to work properly.""" % node_type]
2778                 return
2779             node = node_tree.nodes.new(node_type)
2780             node.inputs["Strength"].default_value = float(node_data['strength'].value)
2781             
2782         elif node_type == "MAPPING":
2783             print ("MAPPING")
2784             node = node_tree.nodes.new(node_type)
2785             node.translation = vector(node_data['translation'].value)
2786             node.rotation = vector(node_data['rotation'].value)
2787             node.scale = vector(node_data['scale'].value)
2788             if boolean(node_data['use_min'].value):
2789                 node.use_min = True
2790                 node.min = vector(node_data['min'].value)
2791             if boolean(node_data['use_max'].value):
2792                 node.use_max = True
2793                 node.max = vector(node_data['max'].value)
2794             node.inputs['Vector'].default_value = vector(node_data['vector'].value)
2795         
2796         elif node_type == "NORMAL":
2797             print ("NORMAL")
2798             node = node_tree.nodes.new(node_type)
2799             node.outputs['Normal'].default_value = vector(node_data['vector_output'].value)
2800             node.inputs['Normal'].default_value = vector(node_data['vector_input'].value)
2801             
2802         elif node_type == "NORMAL_MAP":
2803             print ("NORMAL_MAP")
2804             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) < 2.65:
2805                 node_message = ['ERROR', """The material file contains the node \"%s\".
2806 This node is not available in the Blender version you are currently using.
2807 You may need a newer version of Blender for this material to work properly.""" % node_type]
2808                 return
2809             node = node_tree.nodes.new(node_type)
2810             node.space = node_data['space'].value
2811             node.uv_map = node_data['uv_map'].value
2812             node.inputs["Strength"].default_value = float(node_data['strength'].value)
2813             node.inputs['Color'].default_value = color(node_data['color'].value)
2814             
2815             #CONVERTOR TYPES
2816         elif node_type == "COMBRGB":
2817             print ("COMBRGB")
2818             node = node_tree.nodes.new(node_type)
2819             node.inputs['R'].default_value = float(node_data['red'].value)
2820             node.inputs['G'].default_value = float(node_data['green'].value)
2821             node.inputs['B'].default_value = float(node_data['blue'].value)
2822         
2823         elif node_type == "MATH":
2824             print ("MATH")
2825             node = node_tree.nodes.new(node_type)
2826             node.operation = node_data['operation'].value
2827             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) > 2.63 and "clamp" in node_data:
2828                 node.use_clamp = boolean(node_data['clamp'].value)
2829             node.inputs[0].default_value = float(node_data['value1'].value)
2830             node.inputs[1].default_value = float(node_data['value2'].value)
2831         
2832         elif node_type == "RGBTOBW":
2833             print ("RGBTOBW")
2834             node = node_tree.nodes.new(node_type)
2835             node.inputs['Color'].default_value = color(node_data['color'].value)
2836         
2837         elif node_type == "SEPRGB":
2838             print ("SEPRGB")
2839             node = node_tree.nodes.new(node_type)
2840             node.inputs['Image'].default_value = color(node_data['image'].value)
2841         
2842         elif node_type == "VALTORGB":
2843             print ("VALTORGB")
2844             node = node_tree.nodes.new(node_type)
2845             node.color_ramp.interpolation = node_data['interpolation'].value
2846             node.inputs['Fac'].default_value = float(node_data['fac'].value)
2847             
2848             #Delete the first stop which comes with the ramp by default
2849             node.color_ramp.elements.remove(node.color_ramp.elements[0])
2850             
2851             # The first stop will be "stop1", so set i to 1
2852             i = 1
2853             while i <= int(node_data['stops'].value):
2854                 #Each color stop element is formatted like this:
2855                 #            stop1="0.35|rgba(1, 0.5, 0.8, 0.5)"
2856                 #The "|" separates the stop's position and color.
2857                 element_data = node_data[("stop" + str(i))].value.split('|')
2858                 if i == 1:
2859                     element = node.color_ramp.elements[0]
2860                &n