29e5f266eb5d15d4cf5b6bea02a1a0b8b4e4413f
[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                 #Check file for validitity
1554                 if '<?xml version="1.0" encoding="UTF-8"?>' not in material_file_contents:
1555                     self.filename = ""
1556                     self.mat_name = ""
1557                     self.report({'ERROR'}, "Material file is either outdated or invalid.")
1558                     return {'CANCELLED'}
1559             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"):
1560                 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")
1561                 material_file_contents = ""
1562                 material_file_contents = bcm_file.read()
1563                 bcm_file.close()
1564                 #Check file for validitity
1565                 if '<?xml version="1.0" encoding="UTF-8"?>' not in material_file_contents:
1566                     self.filename = ""
1567                     self.mat_name = ""
1568                     self.report({'ERROR'}, "Material file is either outdated or invalid.")
1569                     return {'CANCELLED'}
1570             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"):
1571                 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")
1572                 material_file_contents = bcm_file.read()
1573                 bcm_file.close()
1574                 #Check file for validitity
1575                 if '<?xml version="1.0" encoding="UTF-8"?>' not in material_file_contents:
1576                     self.filename = ""
1577                     self.mat_name = ""
1578                     self.report({'ERROR'}, "Material file is either outdated or invalid.")
1579                     return {'CANCELLED'}
1580             elif working_mode == "online":
1581                 connection = http.client.HTTPConnection(mat_lib_host)
1582                 connection.request("GET", mat_lib_location + "cycles/" + category_filename + "/" + self.filename + ".bcm")
1583                 response = connection.getresponse().read()
1584                 
1585                 #Cache material
1586                 if library == "composite":
1587                     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")
1588                     bcm_file.write(response)
1589                     bcm_file.close()
1590                 else:
1591                     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")
1592                     bcm_file.write(response)
1593                     bcm_file.close()
1594                 
1595                 #Check file for validitity
1596                 if '<?xml version="1.0" encoding="UTF-8"?>' not in str(response)[2:40]:
1597                     self.report({'ERROR'}, "Material file is either outdated or invalid.")
1598                     self.filename = ""
1599                     return {'CANCELLED'}
1600                 material_file_contents = ""
1601                 material_file_contents = str(response)[str(response).index("<material"):str(response).index("/material>") + 10]
1602             else:
1603                 self.report({'ERROR'}, "Material is not cached; cannot download in offline mode!")
1604                 return {'CANCELLED'}
1605             print (material_file_contents)
1606             mat_name = self.mat_name
1607         elif self.open_location is not "":
1608             if ".bcm" in self.open_location:
1609                 bcm_file = open(self.open_location, mode="r", encoding="UTF-8")
1610                 material_file_contents = bcm_file.read()
1611                 bcm_file.close()
1612                 
1613                 #Check file for validitity
1614                 if '<?xml version="1.0" encoding="UTF-8"?>' not in material_file_contents:
1615                     self.open_location = ""
1616                     self.report({'ERROR'}, "Material file is either outdated or invalid.")
1617                     return {'CANCELLED'}
1618                 mat_name = ""
1619                 for word in self.open_location.split(os.sep)[-1][:-4].split("_"):
1620                     if mat_name is not "":
1621                         mat_name += " "
1622                     mat_name += word.capitalize()
1623             else:
1624                 self.report({'ERROR'}, "Not a .bcm file.")
1625                 self.open_location = ""
1626                 return {'CANCELLED'}
1627         else:
1628             if self.text_block in bpy.data.texts:
1629                 #Read from a text datablock
1630                 material_file_contents = bpy.data.texts[self.text_block].as_string()
1631             else:
1632                 self.report({'ERROR'}, "Requested text block does not exist.")
1633                 self.text_block = ""
1634                 return {'CANCELLED'}
1635                 
1636             #Check file for validitity
1637             if '<?xml version="1.0" encoding="UTF-8"?>' not in material_file_contents[0:38]:
1638                 self.report({'ERROR'}, "Material data is either outdated or invalid.")
1639                 self.text_block = ""
1640                 return {'CANCELLED'}
1641             mat_name = ""
1642             
1643             separator = ""
1644             if context.scene.mat_lib_bcm_name is "":
1645                 separator = ""
1646                 if "_" in self.text_block:
1647                     separator = "_"
1648                 elif "-" in self.text_block:
1649                     separator = "-"
1650                 elif " " in self.text_block:
1651                     separator = " "
1652                     
1653                 if separator is not "":
1654                     for word in self.text_block.split(separator):
1655                         if mat_name is not "":
1656                             mat_name += " "
1657                         mat_name += word.capitalize()
1658                 else:
1659                     mat_name = self.text_block
1660             else:
1661                 mat_name = context.scene.mat_lib_bcm_name
1662         
1663         #Format nicely
1664         material_file_contents = material_file_contents.replace('<?xml version="1.0" encoding="UTF-8"?>', '')
1665         material_file_contents = material_file_contents.replace("\r\n",'')
1666         material_file_contents = material_file_contents.replace("\n",'')
1667         material_file_contents = material_file_contents.replace("\t",'')
1668         material_file_contents = material_file_contents.replace("\\",'')
1669         
1670         #Create new material
1671         new_mat = bpy.data.materials.new(mat_name)
1672         new_mat.use_nodes = True
1673         new_mat.node_tree.nodes.clear()
1674         
1675         #Parse file
1676         dom = xml.dom.minidom.parseString(material_file_contents)
1677         
1678         #Create internal OSL scripts
1679         scripts = dom.getElementsByTagName("script")
1680         for s in scripts:
1681             osl_datablock = bpy.data.texts.new(name=s.attributes['name'].value)
1682             osl_text = s.toxml()[s.toxml().index(">"):s.toxml().rindex("<")]
1683             osl_text = osl_text[1:].replace("<br/>","\n").replace("lt;", "<").replace("gt;", ">")
1684             osl_datablock.write(osl_text)
1685             osl_scripts.append(osl_datablock)
1686         
1687         #Add nodes
1688         nodes = dom.getElementsByTagName("node")
1689         addNodes(nodes, new_mat)
1690         if node_message:
1691             self.report({node_message[0]}, node_message[1])
1692             node_message = []
1693             self.mat_name = ""
1694             self.filename = ""
1695             self.open_location = ""
1696             self.text_block = ""
1697             return {'CANCELLED'}
1698             
1699         #Create links
1700         links = dom.getElementsByTagName("link")
1701         createLinks(links, new_mat)
1702         
1703         m = dom.getElementsByTagName("material")[0]
1704         
1705         #Set viewport color
1706         new_mat.diffuse_color = color(m.attributes["view_color"].value)
1707             
1708         #Set sample-as-lamp-ness
1709         if m.attributes["sample_lamp"].value == "True":
1710             sample_lamp = True
1711         else:
1712             sample_lamp = False
1713         new_mat.cycles.sample_as_light = sample_lamp
1714         
1715         self.mat_name = ""
1716         self.filename = ""
1717         self.open_location = ""
1718         self.text_block = ""
1719         self.report({'INFO'}, "Material added.")
1720         current_material_cached = True
1721         
1722         return {'FINISHED'}
1723
1724 class ApplyLibraryMaterial(bpy.types.Operator):
1725     '''Apply to active material'''
1726     bl_idname = "material.libraryapply"
1727     bl_label = "Apply to active material"
1728     mat_name = bpy.props.StringProperty()
1729     filename = bpy.props.StringProperty()
1730     open_location = bpy.props.StringProperty()
1731     text_block = bpy.props.StringProperty()
1732     
1733     def execute(self, context):
1734         global material_file_contents
1735         global library
1736         global node_message
1737         global current_material_cached
1738         global osl_scripts
1739         
1740         mat_name = ""
1741         material_file_contents = ""
1742         if not bpy.context.active_object:
1743             self.report({'ERROR'}, "No object selected!")
1744             return {'CANCELLED'}
1745         if self.filename is not "":
1746             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"):
1747                 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")
1748                 material_file_contents = bcm_file.read()
1749                 bcm_file.close()
1750                 #Check file for validitity
1751                 if '<?xml version="1.0" encoding="UTF-8"?>' not in material_file_contents:
1752                     self.mat_name = ""
1753                     self.filename = ""
1754                     self.report({'ERROR'}, "Material file is either outdated or invalid.")
1755                     return {'CANCELLED'}
1756             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"):
1757                 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")
1758                 material_file_contents = bcm_file.read()
1759                 bcm_file.close()
1760                 #Check file for validitity
1761                 if '<?xml version="1.0" encoding="UTF-8"?>' not in material_file_contents:
1762                     self.mat_name = ""
1763                     self.filename = ""
1764                     self.report({'ERROR'}, "Material file is either outdated or invalid.")
1765                     return {'CANCELLED'}
1766             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"):
1767                 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")
1768                 material_file_contents = bcm_file.read()
1769                 bcm_file.close()
1770                 #Check file for validitity
1771                 if '<?xml version="1.0" encoding="UTF-8"?>' not in material_file_contents:
1772                     self.mat_name = ""
1773                     self.filename = ""
1774                     self.report({'ERROR'}, "Material file is either outdated or invalid.")
1775                     return {'CANCELLED'}
1776             elif working_mode == "online":
1777                 connection = http.client.HTTPConnection(mat_lib_host)
1778                 connection.request("GET", mat_lib_location + "cycles/" + category_filename + "/" + self.filename + ".bcm")
1779                 response = connection.getresponse().read()
1780                 
1781                 #Check file for validitity
1782                 if '<?xml version="1.0" encoding="UTF-8"?>' not in str(response)[2:40]:
1783                     self.report({'ERROR'}, "Material file is either outdated or invalid.")
1784                     self.mat_name = ""
1785                     self.filename = ""
1786                     return {'CANCELLED'}
1787                 
1788                 #Cache material
1789                 if library == "composite":
1790                     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")
1791                     bcm_file.write(response)
1792                     bcm_file.close()
1793                 else:
1794                     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")
1795                     bcm_file.write(response)
1796                     bcm_file.close()
1797                 
1798                 material_file_contents = ""
1799                 material_file_contents = str(response)[str(response).index("<material"):str(response).index("/material>") + 10]
1800             else:
1801                 self.report({'ERROR'}, "Material is not cached; cannot download in offline mode!")
1802                 self.mat_name = ""
1803                 self.filename = ""
1804                 return {'CANCELLED'}
1805             mat_name = self.mat_name
1806         elif self.open_location is not "":
1807             if ".bcm" in self.open_location:
1808                 material_file_contents = ""
1809                 bcm_file = open(self.open_location, mode="r", encoding="UTF-8")
1810                 material_file_contents = bcm_file.read()
1811                 bcm_file.close()
1812                 
1813                 #Check file for validitity
1814                 if '<?xml version="1.0" encoding="UTF-8"?>' not in material_file_contents:
1815                     self.open_location = ""
1816                     self.report({'ERROR'}, "Material file is either outdated or invalid.")
1817                     return {'CANCELLED'}
1818                 mat_name = ""
1819                 for word in self.open_location.split(os.sep)[-1][:-4].split("_"):
1820                     if mat_name is not "":
1821                         mat_name += " "
1822                     mat_name += word.capitalize()
1823             else:
1824                 self.open_location = ""
1825                 self.report({'ERROR'}, "Not a .bcm file.")
1826                 return {'CANCELLED'}
1827         else:
1828             if self.text_block in bpy.data.texts:
1829                 #Read from a text datablock
1830                 material_file_contents = ""
1831                 material_file_contents = bpy.data.texts[self.text_block].as_string()
1832             else:
1833                 self.report({'ERROR'}, "Requested text block does not exist.")
1834                 self.text_block = "";
1835                 return {'CANCELLED'}
1836             
1837             #Check file for validitity
1838             if '<?xml version="1.0" encoding="UTF-8"?>' not in material_file_contents[0:38]:
1839                 self.report({'ERROR'}, "Material data is either outdated or invalid.")
1840                 self.text_block = ""
1841                 return {'CANCELLED'}
1842             if context.scene.mat_lib_bcm_name is "":
1843                 separator = ""
1844                 if "_" in self.text_block:
1845                     separator = "_"
1846                 elif "-" in self.text_block:
1847                     separator = "-"
1848                 elif " " in self.text_block:
1849                     separator = " "
1850                     
1851                 if separator is not "":
1852                     for word in self.text_block.split(separator):
1853                         if mat_name is not "":
1854                             mat_name += " "
1855                         mat_name += word.capitalize()
1856                 else:
1857                     mat_name = self.text_block
1858             else:
1859                 mat_name = context.scene.mat_lib_bcm_name
1860         
1861         
1862         if context.active_object.active_material:
1863             context.active_object.active_material.name = mat_name
1864         else:
1865             new_material = bpy.data.materials.new(mat_name)
1866             if len(context.active_object.material_slots.keys()) is 0:
1867                 bpy.ops.object.material_slot_add()
1868             context.active_object.material_slots[context.active_object.active_material_index].material = new_material
1869         
1870         #Prepare material for new nodes
1871         context.active_object.active_material.use_nodes = True
1872         context.active_object.active_material.node_tree.nodes.clear()
1873         
1874         #Format nicely
1875         material_file_contents = material_file_contents.replace("<?xml version=\"1.0\" encoding=\"UTF-8\"?>", '')
1876         material_file_contents = material_file_contents.replace("\r\n",'')
1877         material_file_contents = material_file_contents.replace("\n",'')
1878         material_file_contents = material_file_contents.replace("\t",'')
1879         material_file_contents = material_file_contents.replace("\\",'')
1880         
1881         #Parse file
1882         dom = xml.dom.minidom.parseString(material_file_contents)
1883         
1884         #Create internal OSL scripts
1885         scripts = dom.getElementsByTagName("script")
1886         for s in scripts:
1887             osl_datablock = bpy.data.texts.new(name=s.attributes['name'].value)
1888             osl_text = s.toxml()[s.toxml().index(">"):s.toxml().rindex("<")]
1889             osl_text = osl_text[1:].replace("<br/>","\n").replace("lt;", "<").replace("gt;", ">")
1890             osl_datablock.write(osl_text)
1891             osl_scripts.append(osl_datablock)
1892         
1893         #Add nodes
1894         nodes = dom.getElementsByTagName("node")
1895         addNodes(nodes, context.active_object.active_material)
1896         if node_message:
1897             self.report({node_message[0]}, node_message[1])
1898             node_message = []
1899             self.mat_name = ""
1900             self.filename = ""
1901             self.open_location = ""
1902             self.text_block = ""
1903             return {'CANCELLED'}
1904             
1905         #Create links
1906         links = dom.getElementsByTagName("link")
1907         createLinks(links, context.active_object.active_material)
1908         
1909         m = dom.getElementsByTagName("material")[0]
1910         
1911         #Set viewport color
1912         context.active_object.active_material.diffuse_color = color(m.attributes["view_color"].value)
1913             
1914         #Set sample-as-lamp-ness
1915         if boolean(m.attributes["sample_lamp"].value):
1916             sample_lamp = True
1917         else:
1918             sample_lamp = False
1919         context.active_object.active_material.cycles.sample_as_light = sample_lamp
1920         
1921         self.mat_name = ""
1922         self.filename = ""
1923         self.open_location = ""
1924         self.text_block = ""
1925         self.report({'INFO'}, "Material applied.")
1926         current_material_cached = True
1927         
1928         return {'FINISHED'}
1929
1930 class CacheLibraryMaterial(bpy.types.Operator):
1931     '''Cache material to disk'''
1932     bl_idname = "material.librarycache"
1933     bl_label = "cache material to disk"
1934     filename = bpy.props.StringProperty()
1935     
1936     def execute(self, context):
1937         global material_file_contents
1938         global current_material_cached
1939         
1940         if working_mode == "online":
1941             connection = http.client.HTTPConnection(mat_lib_host)
1942             connection.request("GET", mat_lib_location + "cycles/" + category_filename + "/" + self.filename + ".bcm")
1943             response = connection.getresponse().read()
1944         else:
1945             self.report({'ERROR'}, "Cannot cache material in offline mode.")
1946             return {'CANCELLED'}
1947         
1948         material_file_contents = str(response)
1949         if '<?xml version="1.0" encoding="UTF-8"?>' in material_file_contents[2:40]:
1950             material_file_contents = material_file_contents[material_file_contents.index("<material"):(material_file_contents.rindex("</material>") + 11)]
1951         else:
1952             self.report({'ERROR'}, "Invalid material file.")
1953             print(material_file_contents)
1954             return {'CANCELLED'}
1955         
1956         #Parse file
1957         dom = xml.dom.minidom.parseString(material_file_contents)
1958         
1959         #Create internal OSL scripts and cache image textures
1960         nodes = dom.getElementsByTagName("node")
1961         for node in nodes:
1962             node_data = node.attributes
1963             if node_data['type'].value == "TEX_IMAGE":
1964                 if node_data['image'].value:
1965                     if "file://" in node_data['image'].value:
1966                         self.report({'ERROR'}, "Cannot cache image texture located at %s." % node_data['image'].value)
1967                     elif "http://" in node_data['image'].value:
1968                         self.report({'ERROR'}, "Cannot cache image texture hosted at %s." % node_data['image'].value)
1969                     else:
1970                         ext = "." + node_data['image'].value.split(".")[-1]
1971                         image_name = node_data['image'].value[:-4]
1972                         
1973                         if ext.lower() != ".jpg" and ext.lower() != ".png":
1974                             node_message = ['ERROR', "The image file referenced by this image texture node is not .jpg or .png; not downloading."]
1975                             return
1976                             
1977                         if library == "composite" and os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures", image_name + ext)):
1978                             image_filepath = os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures", image_name + ext)
1979                         elif library != "bundled" and os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "textures", image_name + ext)):
1980                             image_filepath = os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "textures", image_name + ext)
1981                         elif library == "bundled" and os.path.exists(os.path.join(mat_lib_folder, "bundled", "cycles", "textures", image_name + ext)):
1982                             image_filepath = os.path.join(mat_lib_folder, "bundled", "cycles", "textures", image_name + ext)
1983                         elif working_mode == "online":
1984                             connection = http.client.HTTPConnection(mat_lib_host)
1985                             connection.request("GET", mat_lib_location + "cycles/textures/" + image_name + ext)
1986                             response = connection.getresponse().read()
1987                             
1988                             #Cache image texture
1989                             if library == "composite":
1990                                 image_filepath = os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures", image_name + ext)
1991                                 image_file = open(image_filepath, mode="w+b")
1992                                 image_file.write(response)
1993                                 image_file.close()
1994                             else:
1995                                 image_filepath = os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "textures", image_name + ext)
1996                                 image_file = open(image_filepath, mode="w+b")
1997                                 image_file.write(response)
1998                                 image_file.close()
1999                         else:
2000                             node_message = ['ERROR', "The image texture, \"%s\", is not cached; cannot download in offline mode." % (image_name + ext)]
2001                             image_filepath = ""
2002             elif node_data['type'].value == "SCRIPT":
2003                 if node_data['script'].value:
2004                     if "file://" in node_data['script'].value:
2005                         self.report({'ERROR'}, "Cannot cache OSL script located at %s." % node_data['script'].value)
2006                     elif "http://" in node_data['script'].value:
2007                         self.report({'ERROR'}, "Cannot cache OSL script hosted at %s." % node_data['script'].value)
2008                     else:
2009                         ext = "." + node_data['script'].value.split(".")[-1]
2010                         script_name = node_data['script'].value[:-4]
2011                         
2012                         if ext.lower() != ".osl" and ext.lower() != ".oso":
2013                             node_message = ['ERROR', "The OSL script file referenced by this script node is not .osl or .oso; not downloading."]
2014                             return
2015                             
2016                         if library == "composite" and os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, "cycles", "scripts", script_name + ext)):
2017                             script_filepath = os.path.join(mat_lib_folder, mat_lib_host, "cycles", "scripts", script_name + ext)
2018                         elif library != "bundled" and os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "scripts", script_name + ext)):
2019                             script_filepath = os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "scripts", script_name + ext)
2020                         elif library == "bundled" and os.path.exists(os.path.join(mat_lib_folder, "bundled", "cycles", "scripts", script_name + ext)):
2021                             script_filepath = os.path.join(mat_lib_folder, "bundled", "cycles", "scripts", script_name + ext)
2022                         elif working_mode == "online":
2023                             connection = http.client.HTTPConnection(mat_lib_host)
2024                             connection.request("GET", mat_lib_location + "cycles/scripts/" + script_name + ext)
2025                             response = connection.getresponse().read()
2026                             
2027                             #Cache image texture
2028                             if library == "composite":
2029                                 script_filepath = os.path.join(mat_lib_folder, mat_lib_host, "cycles", "scripts", script_name + ext)
2030                                 script_file = open(script_filepath, mode="w+b")
2031                                 script_file.write(response)
2032                                 script_file.close()
2033                             else:
2034                                 script_filepath = os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "scripts", script_name + ext)
2035                                 script_file = open(script_filepath, mode="w+b")
2036                                 script_file.write(response)
2037                                 script_file.close()
2038                         else:
2039                             node_message = ['ERROR', "The OSL script, \"%s\", is not cached; cannot download in offline mode." % (script_name + ext)]
2040                             script_filepath = ""
2041                     
2042         
2043         if library == "composite":
2044             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")
2045             bcm_file.write(response)
2046             bcm_file.close()
2047         elif library != "bundled":
2048             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")
2049             bcm_file.write(response)
2050             bcm_file.close()
2051         else:
2052             self.report({'ERROR'}, "Cannot cache materials from this library.")
2053             return {'CANCELLED'}
2054             
2055         current_material_cached = True
2056         self.report({'INFO'}, "Material cached.")
2057         return {'FINISHED'}
2058
2059 class SaveLibraryMaterial(bpy.types.Operator, ExportHelper):
2060     '''Save material to disk'''
2061     bl_idname = "material.librarysave"
2062     bl_label = "Save material to disk"
2063     filepath = bpy.props.StringProperty()
2064     filename = bpy.props.StringProperty()
2065     
2066     #ExportHelper uses this
2067     filename_ext = ".bcm"
2068
2069     filter_glob = bpy.props.StringProperty(
2070             default="*.bcm",
2071             options={'HIDDEN'},
2072             )
2073     
2074     def execute(self, context):
2075         global material_file_contents
2076         global save_filename
2077         global current_material_cached
2078         
2079         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):
2080             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")
2081             response = bcm_file.read()
2082             bcm_file.close()
2083         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):
2084             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")
2085             response = bcm_file.read()
2086             bcm_file.close()
2087         elif library == "bundled" and os.path.exists(mat_lib_folder + os.sep + "bundled" + os.sep + "cycles" + os.sep + category_filename + os.sep + save_filename):
2088             bcm_file = open(mat_lib_folder + os.sep + "bundled" + os.sep + "cycles" + os.sep + category_filename + os.sep + self.filename, mode="r+b")
2089             response = bcm_file.read()
2090             bcm_file.close()
2091         elif working_mode == "online":
2092             connection = http.client.HTTPConnection(mat_lib_host)
2093             connection.request("GET", mat_lib_location + "cycles/" + category_filename + "/" + self.filename)
2094             response = connection.getresponse().read()
2095             
2096             #Cache material
2097             if library == "composite":
2098                 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")
2099                 bcm_file.write(response)
2100                 bcm_file.close()
2101             else:
2102                 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")
2103                 bcm_file.write(response)
2104                 bcm_file.close()
2105         else:
2106             self.report({'ERROR'}, "Material is not cached; cannot download in offline mode.")
2107             return {'FINISHED'}
2108         
2109         material_file_contents = str(response)
2110         
2111         bcm_file = open(self.filepath, mode="w+b")
2112         bcm_file.write(response)
2113         bcm_file.close()
2114         
2115         if '<?xml version="1.0" encoding="UTF-8"?>' in material_file_contents[2:40]:
2116             material_file_contents = material_file_contents[material_file_contents.index("<material"):(material_file_contents.rindex("</material>") + 11)]
2117         else:
2118             self.report({'ERROR'}, "Invalid material file.")
2119             print(material_file_contents)
2120             return {'CANCELLED'}
2121         
2122         #Parse file
2123         dom = xml.dom.minidom.parseString(material_file_contents)
2124         
2125         bcm_file = open(self.filepath, mode="r", encoding="UTF-8")
2126         material_file_contents = bcm_file.read()
2127         bcm_file.close()
2128         
2129         #Create internal OSL scripts and cache image textures
2130         nodes = dom.getElementsByTagName("node")
2131         for node in nodes:
2132             node_data = node.attributes
2133             if node_data['type'].value == "TEX_IMAGE":
2134                 if node_data['image'].value:
2135                     node_attributes = (
2136                         node_data['image'].value,
2137                         node_data['source'].value,
2138                         node_data['color_space'].value,
2139                         node_data['projection'].value,
2140                         node_data['loc'].value)
2141                     original_xml = ("<node type=\"TEX_IMAGE\" image=\"%s\" source=\"%s\" color_space=\"%s\" projection=\"%s\" loc=\"%s\" />" % node_attributes)
2142                     if "file://" in node_data['image'].value:
2143                         if os.path.exists(node_data['image'].value[7:]):
2144                             image_file = open(node_data['image'].value[7:], mode="r+b")
2145                             image_data = image_file.read()
2146                             image_file.close()
2147                             copied_image = open(self.filepath[:-len(self.filename)] + node_data['image'].value.split(os.sep)[-1], mode="w+b")
2148                             copied_image.write(image_data)
2149                             copied_image.close()
2150                             image_location = ("file://" + self.filepath[:-len(self.filename)] + node_data['image'].value.split(os.sep)[-1])
2151                         else:
2152                             image_location = ""
2153                     elif "http://" in node_data['image'].value:
2154                         self.report({'ERROR'}, "Cannot save image texture hosted at %s." % node_data['image'].value)
2155                         image_location = ""
2156                     else:
2157                         ext = "." + node_data['image'].value.split(".")[-1]
2158                         image_name = node_data['image'].value[:-4]
2159                         
2160                         if ext.lower() != ".jpg" and ext.lower() != ".png":
2161                             node_message = ['ERROR', "The image file referenced by this image texture node is not .jpg or .png; not downloading."]
2162                             return
2163                             
2164                         if library == "composite" and os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures", image_name + ext)):
2165                             image_filepath = os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures", image_name + ext)
2166                         elif library != "bundled" and os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "textures", image_name + ext)):
2167                             image_filepath = os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "textures", image_name + ext)
2168                         elif library == "bundled" and os.path.exists(os.path.join(mat_lib_folder, "bundled", "cycles", "textures", image_name + ext)):
2169                             image_filepath = os.path.join(mat_lib_folder, "bundled", "cycles", "textures", image_name + ext)
2170                         elif working_mode == "online":
2171                             connection = http.client.HTTPConnection(mat_lib_host)
2172                             connection.request("GET", mat_lib_location + "cycles/textures/" + image_name + ext)
2173                             response = connection.getresponse().read()
2174                             
2175                             #Cache image texture
2176                             if library == "composite":
2177                                 image_filepath = os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures", image_name + ext)
2178                                 image_file = open(image_filepath, mode="w+b")
2179                                 image_file.write(response)
2180                                 image_file.close()
2181                             else:
2182                                 image_filepath = os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "textures", image_name + ext)
2183                                 image_file = open(image_filepath, mode="w+b")
2184                                 image_file.write(response)
2185                                 image_file.close()
2186                         else:
2187                             node_message = ['ERROR', "The image texture, \"%s\", is not cached; cannot download in offline mode." % (image_name + ext)]
2188                             image_filepath = ""
2189                         image_location = ("file://" + self.filepath[:-len(self.filename)] + node_data['image'].value)
2190                         
2191                         if image_filepath:
2192                             print(image_filepath)
2193                             image_file = open(image_filepath, mode="r+b")
2194                             image_data = image_file.read()
2195                             image_file.close()
2196                             saved_image = open(self.filepath[:-len(self.filename)] + node_data['image'].value, mode="w+b")
2197                             saved_image.write(image_data)
2198                             saved_image.close()
2199                 
2200                     updated_xml = original_xml.replace(node_data['image'].value, image_location)
2201                     material_file_contents = material_file_contents.replace(original_xml, updated_xml)
2202             elif node_data['type'].value == "SCRIPT":
2203                 if node_data['script'].value:
2204                     node_attributes = (
2205                         node_data['mode'].value,
2206                         node_data['script'].value,
2207                         node_data['loc'].value)
2208                     original_xml = ("<node type=\"SCRIPT\" mode=\"%s\" script=\"%s\" loc=\"%s\" />" % node_attributes)
2209                     if "file://" in node_data['script'].value:
2210                         if os.path.exists(node_data['script'].value[7:]):
2211                             script_file = open(node_data['script'].value[7:], mode="r+b")
2212                             script_data = script_file.read()
2213                             script_file.close()
2214                             copied_script = open(self.filepath[:-len(self.filename)] + node_data['script'].value.split(os.sep)[-1], mode="w+b")
2215                             copied_script.write(script_data)
2216                             copied_script.close()
2217                             script_location = ("file://" + self.filepath[:-len(self.filename)] + node_data['script'].value.split(os.sep)[-1])
2218                         else:
2219                             script_location = ""
2220                     elif "http://" in node_data['script'].value:
2221                         self.report({'ERROR'}, "Cannot save OSL script hosted at %s." % node_data['script'].value)
2222                         script_location = ""
2223                     else:
2224                         ext = "." + node_data['script'].value.split(".")[-1]
2225                         script_name = node_data['script'].value[:-4]
2226                         
2227                         if ext.lower() != ".osl" and ext.lower() != ".oso":
2228                             node_message = ['ERROR', "The OSL script file referenced by this script node is not .osl or .oso; not downloading."]
2229                             return
2230                             
2231                         if library == "composite" and os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, "cycles", "scripts", script_name + ext)):
2232                             script_filepath = os.path.join(mat_lib_folder, mat_lib_host, "cycles", "scripts", script_name + ext)
2233                         elif library != "bundled" and os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "scripts", script_name + ext)):
2234                             script_filepath = os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "scripts", script_name + ext)
2235                         elif library == "bundled" and os.path.exists(os.path.join(mat_lib_folder, "bundled", "cycles", "scripts", script_name + ext)):
2236                             script_filepath = os.path.join(mat_lib_folder, "bundled", "cycles", "scripts", script_name + ext)
2237                         elif working_mode == "online":
2238                             connection = http.client.HTTPConnection(mat_lib_host)
2239                             connection.request("GET", mat_lib_location + "cycles/scripts/" + script_name + ext)
2240                             response = connection.getresponse().read()
2241                             
2242                             #Cache OSL script
2243                             if library == "composite":
2244                                 script_filepath = os.path.join(mat_lib_folder, mat_lib_host, "cycles", "scripts", script_name + ext)
2245                                 script_file = open(script_filepath, mode="w+b")
2246                                 script_file.write(response)
2247                                 script_file.close()
2248                             else:
2249                                 script_filepath = os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "scripts", script_name + ext)
2250                                 script_file = open(script_filepath, mode="w+b")
2251                                 script_file.write(response)
2252                                 script_file.close()
2253                         else:
2254                             node_message = ['ERROR', "The OSL script, \"%s\", is not cached; cannot download in offline mode." % (script_name + ext)]
2255                             script_filepath = ""
2256                         
2257                         if script_filepath:
2258                             print(script_filepath)
2259                             script_file = open(script_filepath, mode="r+b")
2260                             script_data = script_file.read()
2261                             script_file.close()
2262                             saved_script = open(self.filepath[:-len(self.filename)] + node_data['script'].value, mode="w+b")
2263                             saved_script.write(script_data)
2264                             saved_script.close()
2265                     
2266                     updated_xml = original_xml.replace(node_data['script'].value, script_location)
2267                     material_file_contents = material_file_contents.replace(original_xml, updated_xml)
2268         
2269         bcm_file = open(self.filepath, mode="w", encoding="UTF-8")
2270         bcm_file.write(material_file_contents)
2271         bcm_file.close()
2272         
2273         self.report({'INFO'}, "Material saved.")
2274         current_material_cached = True
2275         
2276         return {'FINISHED'}
2277
2278 def createLinks(links, mat):
2279     node_tree = mat.node_tree
2280     for dom_link in links:
2281         link = {
2282     "to": int(dom_link.attributes['to'].value),
2283     "input": int(dom_link.attributes['input'].value),
2284     "from": int(dom_link.attributes['from'].value),
2285     "output": int(dom_link.attributes['output'].value)}
2286         node_tree.links.new(
2287     node_tree.nodes[link["to"]].inputs[link["input"]],
2288     node_tree.nodes[link["from"]].outputs[link["output"]])
2289
2290 def addNodes(nodes, mat):
2291     global node_message
2292     global osl_scripts
2293     
2294     for dom_node in nodes:
2295         node_type = dom_node.attributes['type'].value
2296         loc = dom_node.attributes['loc'].value
2297         node_location = [int(loc[:loc.index(",")]), int(loc[(loc.index(",") + 1):])]
2298         node_tree = mat.node_tree
2299         node_data = dom_node.attributes
2300         
2301         #Below here checks the type of the node and adds the correct type
2302         
2303         #INPUT TYPES
2304         #This is totally crafty, but some of these nodes actually
2305         # store their values as their output's default value!
2306         if node_type == "ATTRIBUTE":
2307             print ("ATTRIBUTE")
2308             node = node_tree.nodes.new(node_type)
2309             node.attribute_name = node_data['attribute'].value
2310         
2311         elif node_type == "CAMERA":
2312             print ("CAMERA")
2313             node = node_tree.nodes.new(node_type)
2314         
2315         elif node_type == "FRESNEL":
2316             print ("FRESNEL")
2317             node = node_tree.nodes.new(node_type)
2318             node.inputs['IOR'].default_value = float(node_data['ior'].value)
2319                 
2320         elif node_type == "LAYER_WEIGHT":
2321             print ("LAYER_WEIGHT")
2322             node = node_tree.nodes.new(node_type)
2323             node.inputs['Blend'].default_value = float(node_data['blend'].value)
2324                 
2325         elif node_type == "LIGHT_PATH":
2326             print ("LIGHT_PATH")
2327             node = node_tree.nodes.new(node_type)
2328         
2329         elif node_type == "NEW_GEOMETRY":
2330             print ("NEW_GEOMETRY")
2331             node = node_tree.nodes.new(node_type)
2332         
2333         elif node_type == "OBJECT_INFO":
2334             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) < 2.64:
2335                 node_message = ['ERROR', """The material file contains the node \"%s\".
2336 This node is not available in the Blender version you are currently using.
2337 You may need a newer version of Blender for this material to work properly.""" % node_type]
2338                 return
2339             print ("OBJECT_INFO")
2340             node = node_tree.nodes.new(node_type)
2341         
2342         elif node_type == "PARTICLE_INFO":
2343             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) < 2.64:
2344                 node_message = ['ERROR', """The material file contains the node \"%s\".
2345 This node is not available in the Blender version you are currently using.
2346 You may need a newer version of Blender for this material to work properly.""" % node_type]
2347                 return
2348             print ("PARTICLE_INFO")
2349             node = node_tree.nodes.new(node_type)
2350         
2351         elif node_type == "RGB":
2352             print ("RGB")
2353             node = node_tree.nodes.new(node_type)
2354             node.outputs['Color'].default_value = color(node_data['color'].value)
2355         
2356         elif node_type == "TANGENT":
2357             print ("TANGENT")
2358             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) < 2.65:
2359                 node_message = ['ERROR', """The material file contains the node \"%s\".
2360 This node is not available in the Blender version you are currently using.
2361 You may need a newer version of Blender for this material to work properly.""" % node_type]
2362                 return
2363             node = node_tree.nodes.new(node_type)
2364             node.direction_type = node_data['direction'].value
2365             node.axis = node_data['axis'].value
2366         
2367         elif node_type == "TEX_COORD":
2368             print ("TEX_COORD")
2369             node = node_tree.nodes.new(node_type)
2370             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) > 2.64 and "dupli" in node_data:
2371                 node.from_dupli = boolean(node_data['dupli'].value)
2372         
2373         elif node_type == "VALUE":
2374             print ("VALUE")
2375             node = node_tree.nodes.new(node_type)
2376             node.outputs['Value'].default_value = float(node_data['value'].value)
2377             
2378             #OUTPUT TYPES
2379         elif node_type == "OUTPUT_LAMP":
2380             print ("OUTPUT_LAMP")
2381             node = node_tree.nodes.new(node_type)
2382         
2383         elif node_type == "OUTPUT_MATERIAL":
2384             print ("OUTPUT_MATERIAL")
2385             node = node_tree.nodes.new(node_type)
2386         
2387         elif node_type == "OUTPUT_WORLD":
2388             print ("OUTPUT_WORLD")
2389             node = node_tree.nodes.new(node_type)
2390         
2391             #SHADER TYPES
2392         elif node_type == "ADD_SHADER":
2393             print ("ADD_SHADER")
2394             node = node_tree.nodes.new(node_type)
2395             
2396         elif node_type == "AMBIENT_OCCLUSION":
2397             print ("AMBIENT_OCCLUSION")
2398             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) < 2.65:
2399                 node_message = ['ERROR', """The material file contains the node \"%s\".
2400 This node is not available in the Blender version you are currently using.
2401 You may need a newer version of Blender for this material to work properly.""" % node_type]
2402                 return
2403             node = node_tree.nodes.new(node_type)
2404             node.inputs['Color'].default_value = color(node_data['color'].value)
2405         
2406         elif node_type == "BACKGROUND":
2407             print ("BACKGROUND")
2408             node = node_tree.nodes.new(node_type)
2409             node.inputs['Color'].default_value = color(node_data['color'].value)
2410             node.inputs['Strength'].default_value = float(node_data['strength'].value)
2411             
2412         elif node_type == "BSDF_ANISOTROPIC":
2413             print ("BSDF_ANISOTROPIC")
2414             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) < 2.65:
2415                 node_message = ['ERROR', """The material file contains the node \"%s\".
2416 This node is not available in the Blender version you are currently using.
2417 You may need a newer version of Blender for this material to work properly.""" % node_type]
2418                 return
2419             node = node_tree.nodes.new(node_type)
2420             node.inputs['Color'].default_value = color(node_data['color'].value)
2421             node.inputs['Roughness'].default_value = float(node_data['roughness'].value)
2422             node.inputs['Anisotropy'].default_value = float(node_data['anisotropy'].value)
2423             node.inputs['Rotation'].default_value = float(node_data['rotation'].value)
2424             
2425         elif node_type == "BSDF_DIFFUSE":
2426             print ("BSDF_DIFFUSE")
2427             node = node_tree.nodes.new(node_type)
2428             node.inputs['Color'].default_value = color(node_data['color'].value)
2429             node.inputs['Roughness'].default_value = float(node_data['roughness'].value)
2430         
2431         elif node_type == "BSDF_GLASS":
2432             print ("BSDF_GLASS")
2433             node = node_tree.nodes.new(node_type)
2434             node.distribution = node_data['distribution'].value
2435             node.inputs['Color'].default_value = color(node_data['color'].value)
2436             node.inputs['Roughness'].default_value = float(node_data['roughness'].value)
2437             node.inputs['IOR'].default_value = float(node_data['ior'].value)
2438             
2439         elif node_type == "BSDF_GLOSSY":
2440             print ("BSDF_GLOSSY")
2441             node = node_tree.nodes.new(node_type)
2442             node.distribution = node_data['distribution'].value
2443             node.inputs['Color'].default_value = color(node_data['color'].value)
2444             node.inputs['Roughness'].default_value = float(node_data['roughness'].value)
2445         
2446         elif node_type == "BSDF_REFRACTION":
2447             print ("BSDF_REFRACTION")
2448             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) < 2.65:
2449                 node_message = ['ERROR', """The material file contains the node \"%s\".
2450 This node is not available in the Blender version you are currently using.
2451 You may need a newer version of Blender for this material to work properly.""" % node_type]
2452                 return
2453             node = node_tree.nodes.new(node_type)
2454             node.distribution = node_data['distribution'].value
2455             node.inputs['Color'].default_value = color(node_data['color'].value)
2456             node.inputs['Roughness'].default_value = float(node_data['roughness'].value)
2457             node.inputs['IOR'].default_value = float(node_data['ior'].value)
2458         
2459         elif node_type == "BSDF_TRANSLUCENT":
2460             print ("BSDF_TRANSLUCENT")
2461             node = node_tree.nodes.new(node_type)
2462             node.inputs['Color'].default_value = color(node_data['color'].value)
2463         
2464         elif node_type == "BSDF_TRANSPARENT":
2465             print ("BSDF_TRANSPARENT")
2466             node = node_tree.nodes.new(node_type)
2467             node.inputs['Color'].default_value = color(node_data['color'].value)
2468         
2469         elif node_type == "BSDF_VELVET":
2470             print ("BSDF_VELVET")
2471             node = node_tree.nodes.new(node_type)
2472             node.inputs['Color'].default_value = color(node_data['color'].value)
2473             node.inputs['Sigma'].default_value = float(node_data['sigma'].value)
2474         
2475         elif node_type == "EMISSION":
2476             print ("EMISSION")
2477             node = node_tree.nodes.new(node_type)
2478             node.inputs['Color'].default_value = color(node_data['color'].value)
2479             node.inputs['Strength'].default_value = float(node_data['strength'].value)
2480         
2481         elif node_type == "HOLDOUT":
2482             print ("HOLDOUT")
2483             node = node_tree.nodes.new(node_type)
2484         
2485         elif node_type == "MIX_SHADER":
2486             print ("MIX_SHADER")
2487             node = node_tree.nodes.new(node_type)
2488             node.inputs['Fac'].default_value = float(node_data['fac'].value)
2489         
2490             #TEXTURE TYPES
2491         elif node_type == "TEX_BRICK":
2492             print ("TEX_BRICK")
2493             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) < 2.64:
2494                 node_message = ['ERROR', """The material file contains the node \"%s\".
2495 This node is not available in the Blender version you are currently using.
2496 You may need a newer version of Blender for this material to work properly.""" % node_type]
2497                 return
2498             node = node_tree.nodes.new(node_type)
2499             node.offset = float(node_data['offset'].value)
2500             node.offset_frequency = float(node_data['offset_freq'].value)
2501             node.squash = float(node_data['squash'].value)
2502             node.squash_frequency = float(node_data['squash_freq'].value)
2503             node.inputs['Color1'].default_value = color(node_data['color1'].value)
2504             node.inputs['Color2'].default_value = color(node_data['color2'].value)
2505             node.inputs['Mortar'].default_value = color(node_data['mortar'].value)
2506             node.inputs['Scale'].default_value = float(node_data['scale'].value)
2507             node.inputs['Mortar Size'].default_value = float(node_data['mortar_size'].value)
2508             node.inputs['Bias'].default_value = float(node_data['bias'].value)
2509             node.inputs['Brick Width'].default_value = float(node_data['width'].value)
2510             node.inputs['Row Height'].default_value = float(node_data['height'].value)
2511             
2512         elif node_type == "TEX_CHECKER":
2513             print ("TEX_CHECKER")
2514             node = node_tree.nodes.new(node_type)
2515             node.inputs['Color1'].default_value = color(node_data['color1'].value)
2516             node.inputs['Color2'].default_value = color(node_data['color2'].value)
2517             node.inputs['Scale'].default_value = float(node_data['scale'].value)
2518             
2519         elif node_type == "TEX_ENVIRONMENT":
2520             print ("TEX_ENVIRONMENT")
2521             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) < 2.64:
2522                 node_message = ['ERROR', """The material file contains the node \"%s\".
2523 This node is not available in the Blender version you are currently using.
2524 You may need a newer version of Blender for this material to work properly.""" % node_type]
2525                 return
2526             node = node_tree.nodes.new(node_type)
2527             node.color_space = node_data['color_space'].value
2528             node.projection = node_data['projection'].value
2529             if 'image' in node_data:
2530                 if "file://" in node_data['image'].value:
2531                     image_filepath = node_data['image'].value[7:]
2532                     image_name = node_data['image'].value.split(os.sep)[-1]
2533                     image_datablock = bpy.data.images.new(name=image_name, width=4, height=4)
2534                     image_datablock.source = node_data['source'].value
2535                     image_datablock.filepath = image_filepath
2536                     node.image = image_datablock
2537                     if node_data['source'].value == 'MOVIE' or node_data['source'].value == 'SEQUENCE':
2538                         node.image_user.frame_duration = int(node_data['frame_duration'].value)
2539                         node.image_user.frame_start = int(node_data['frame_start'].value)
2540                         node.image_user.frame_offset = int(node_data['frame_offset'].value)
2541                         node.image_user.use_cyclic = boolean(node_data['cyclic'].value)
2542                         node.image_user.use_auto_refresh = boolean(node_data['auto_refresh'].value)
2543                 elif "http://" in node_data['image'].value and bpy.context.scene.mat_lib_images_only_trusted == False:
2544                     ext = "." + node_data['image'].value.split(".")[-1]
2545                     image_name = node_data['image'].value.split("/")[-1][:-4]
2546                     image_host = node_data['image'].value[7:].split("/")[0]
2547                     image_location = node_data['image'].value[(7 + len(image_host)):]
2548                     
2549                     if ext.lower() != ".jpg" and ext.lower() != ".png":
2550                         node_message = ['ERROR', "The image file referenced by this image texture node is not .jpg or .png; not downloading."]
2551                         return
2552                     
2553                     connection = http.client.HTTPConnection(image_host)
2554                     connection.request("GET", image_location)
2555                     response = connection.getresponse().read()
2556                     #Save image texture
2557                     image_filepath = os.path.join(mat_lib_folder, "my-materials", image_name + ext)
2558                     image_file = open(image_filepath, mode="w+b")
2559                     image_file.write(response)
2560                     image_file.close()
2561                     image_datablock = bpy.data.images.new(name=(image_name + ext), width=4, height=4)
2562                     image_datablock.source = node_data['source'].value
2563                     image_datablock.filepath = image_filepath
2564                     node.image = image_datablock
2565                     if node_data['source'].value == 'MOVIE' or node_data['source'].value == 'SEQUENCE':
2566                         node.image_user.frame_duration = int(node_data['frame_duration'].value)
2567                         node.image_user.frame_start = int(node_data['frame_start'].value)
2568                         node.image_user.frame_offset = int(node_data['frame_offset'].value)
2569                         node.image_user.use_cyclic = boolean(node_data['cyclic'].value)
2570                         node.image_user.use_auto_refresh = boolean(node_data['auto_refresh'].value)
2571                 else:
2572                     ext = "." + node_data['image'].value.split(".")[-1]
2573                     image_name = node_data['image'].value[:-4]
2574                     
2575                     if ext.lower() != ".jpg" and ext.lower() != ".png":
2576                         node_message = ['ERROR', "The image file referenced by this image texture node is not .jpg or .png; not downloading."]
2577                         return
2578                         
2579                     if library == "composite" and os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures", image_name + ext)):
2580                         image_filepath = os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures", image_name + ext)
2581                     elif library != "bundled" and os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "textures", image_name + ext)):
2582                         image_filepath = os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "textures", image_name + ext)
2583                     elif library == "bundled" and os.path.exists(os.path.join(mat_lib_folder, "bundled", "cycles", "textures", image_name + ext)):
2584                         image_filepath = os.path.join(mat_lib_folder, "bundled", "cycles", "textures", image_name + ext)
2585                     elif working_mode == "online":
2586                         connection = http.client.HTTPConnection(mat_lib_host)
2587                         connection.request("GET", mat_lib_location + "cycles/textures/" + image_name + ext)
2588                         response = connection.getresponse().read()
2589                         
2590                         #Cache image texture
2591                         if library == "composite":
2592                             image_filepath = os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures", image_name + ext)
2593                             image_file = open(image_filepath, mode="w+b")
2594                             image_file.write(response)
2595                             image_file.close()
2596                         else:
2597                             image_filepath = os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "textures", image_name + ext)
2598                             image_file = open(image_filepath, mode="w+b")
2599                             image_file.write(response)
2600                             image_file.close()
2601                     else:
2602                         node_message = ['ERROR', "The image texture, \"%s\", is not cached; cannot download in offline mode." % (image_name + ext)]
2603                         image_filepath = ""
2604                     if image_filepath != "":
2605                         image_datablock = bpy.data.images.new(name=(image_name + ext), width=4, height=4)
2606                         image_datablock.source = node_data['source'].value
2607                         image_datablock.filepath = image_filepath
2608                         node.image = image_datablock
2609                         if node_data['source'].value == 'MOVIE' or node_data['source'].value == 'SEQUENCE':
2610                             node.image_user.frame_duration = int(node_data['frame_duration'].value)
2611                             node.image_user.frame_start = int(node_data['frame_start'].value)
2612                             node.image_user.frame_offset = int(node_data['frame_offset'].value)
2613                             node.image_user.use_cyclic = boolean(node_data['cyclic'].value)
2614                             node.image_user.use_auto_refresh = boolean(node_data['auto_refresh'].value)
2615             
2616         elif node_type == "TEX_GRADIENT":
2617             print ("TEX_GRADIENT")
2618             node = node_tree.nodes.new(node_type)
2619             node.gradient_type = node_data['gradient'].value
2620         
2621         elif node_type == "TEX_IMAGE":
2622             print ("TEX_IMAGE")
2623             node = node_tree.nodes.new(node_type)
2624             node.color_space = node_data['color_space'].value
2625             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) > 2.63 and "projection" in node_data:
2626                 node.projection = node_data['projection'].value
2627             if 'image' in node_data:
2628                 if "file://" in node_data['image'].value:
2629                     image_filepath = node_data['image'].value[7:]
2630                     image_name = node_data['image'].value.split(os.sep)[-1]
2631                     image_datablock = bpy.data.images.new(name=image_name, width=4, height=4)
2632                     image_datablock.source = node_data['source'].value
2633                     image_datablock.filepath = image_filepath
2634                     node.image = image_datablock
2635                     if node_data['source'].value == 'MOVIE' or node_data['source'].value == 'SEQUENCE':
2636                         node.image_user.frame_duration = int(node_data['frame_duration'].value)
2637                         node.image_user.frame_start = int(node_data['frame_start'].value)
2638                         node.image_user.frame_offset = int(node_data['frame_offset'].value)
2639                         node.image_user.use_cyclic = boolean(node_data['cyclic'].value)
2640                         node.image_user.use_auto_refresh = boolean(node_data['auto_refresh'].value)
2641                 elif "http://" in node_data['image'].value and bpy.context.scene.mat_lib_images_only_trusted == False:
2642                     ext = "." + node_data['image'].value.split(".")[-1]
2643                     image_name = node_data['image'].value.split("/")[-1][:-4]
2644                     image_host = node_data['image'].value[7:].split("/")[0]
2645                     image_location = node_data['image'].value[(7 + len(image_host)):]
2646                     
2647                     if ext.lower() != ".jpg" and ext.lower() != ".png":
2648                         node_message = ['ERROR', "The image file referenced by this image texture node is not .jpg or .png; not downloading."]
2649                         return
2650                     
2651                     connection = http.client.HTTPConnection(image_host)
2652                     connection.request("GET", image_location)
2653                     response = connection.getresponse().read()
2654                     #Save image texture
2655                     image_filepath = os.path.join(mat_lib_folder, "my-materials", image_name + ext)
2656                     image_file = open(image_filepath, mode="w+b")
2657                     image_file.write(response)
2658                     image_file.close()
2659                     image_datablock = bpy.data.images.new(name=(image_name + ext), width=4, height=4)
2660                     image_datablock.source = node_data['source'].value
2661                     image_datablock.filepath = image_filepath
2662                     node.image = image_datablock
2663                     if node_data['source'].value == 'MOVIE' or node_data['source'].value == 'SEQUENCE':
2664                         node.image_user.frame_duration = int(node_data['frame_duration'].value)
2665                         node.image_user.frame_start = int(node_data['frame_start'].value)
2666                         node.image_user.frame_offset = int(node_data['frame_offset'].value)
2667                         node.image_user.use_cyclic = boolean(node_data['cyclic'].value)
2668                         node.image_user.use_auto_refresh = boolean(node_data['auto_refresh'].value)
2669                 else:
2670                     ext = "." + node_data['image'].value.split(".")[-1]
2671                     image_name = node_data['image'].value[:-4]
2672                     
2673                     if ext.lower() != ".jpg" and ext.lower() != ".png":
2674                         node_message = ['ERROR', "The image file referenced by this image texture node is not .jpg or .png; not downloading."]
2675                         return
2676                         
2677                     if library == "composite" and os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures", image_name + ext)):
2678                         image_filepath = os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures", image_name + ext)
2679                     elif library != "bundled" and os.path.exists(os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "textures", image_name + ext)):
2680                         image_filepath = os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "textures", image_name + ext)
2681                     elif library == "bundled" and os.path.exists(os.path.join(mat_lib_folder, "bundled", "cycles", "textures", image_name + ext)):
2682                         image_filepath = os.path.join(mat_lib_folder, "bundled", "cycles", "textures", image_name + ext)
2683                     elif working_mode == "online":
2684                         connection = http.client.HTTPConnection(mat_lib_host)
2685                         connection.request("GET", mat_lib_location + "cycles/textures/" + image_name + ext)
2686                         response = connection.getresponse().read()
2687                         
2688                         #Cache image texture
2689                         if library == "composite":
2690                             image_filepath = os.path.join(mat_lib_folder, mat_lib_host, "cycles", "textures", image_name + ext)
2691                             image_file = open(image_filepath, mode="w+b")
2692                             image_file.write(response)
2693                             image_file.close()
2694                         else:
2695                             image_filepath = os.path.join(mat_lib_folder, mat_lib_host, library, "cycles", "textures", image_name + ext)
2696                             image_file = open(image_filepath, mode="w+b")
2697                             image_file.write(response)
2698                             image_file.close()
2699                     else:
2700                         node_message = ['ERROR', "The image texture, \"%s\", is not cached; cannot download in offline mode." % (image_name + ext)]
2701                         image_filepath = ""
2702                     if image_filepath != "":
2703                         image_datablock = bpy.data.images.new(name=(image_name + ext), width=4, height=4)
2704                         image_datablock.source = node_data['source'].value
2705                         image_datablock.filepath = image_filepath
2706                         node.image = image_datablock
2707                         if node_data['source'].value == 'MOVIE' or node_data['source'].value == 'SEQUENCE':
2708                             node.image_user.frame_duration = int(node_data['frame_duration'].value)
2709                             node.image_user.frame_start = int(node_data['frame_start'].value)
2710                             node.image_user.frame_offset = int(node_data['frame_offset'].value)
2711                             node.image_user.use_cyclic = boolean(node_data['cyclic'].value)
2712                             node.image_user.use_auto_refresh = boolean(node_data['auto_refresh'].value)
2713                 
2714         elif node_type == "TEX_MAGIC":
2715             print ("TEX_MAGIC")
2716             node = node_tree.nodes.new(node_type)
2717             node.turbulence_depth = int(node_data['depth'].value)
2718             node.inputs['Scale'].default_value = float(node_data['scale'].value)
2719             node.inputs['Distortion'].default_value = float(node_data['distortion'].value)
2720         
2721         elif node_type == "TEX_MUSGRAVE":
2722             print ("TEX_MUSGRAVE")
2723             node = node_tree.nodes.new(node_type)
2724             node.musgrave_type = node_data['musgrave'].value
2725             node.inputs['Scale'].default_value = float(node_data['scale'].value)
2726             node.inputs['Detail'].default_value = float(node_data['detail'].value)
2727             node.inputs['Dimension'].default_value = float(node_data['dimension'].value)
2728             node.inputs['Lacunarity'].default_value = float(node_data['lacunarity'].value)
2729             node.inputs['Offset'].default_value = float(node_data['offset'].value)
2730             node.inputs['Gain'].default_value = float(node_data['gain'].value)
2731         
2732         elif node_type == "TEX_NOISE":
2733             print ("TEX_NOISE")
2734             node = node_tree.nodes.new(node_type)
2735             node.inputs['Scale'].default_value = float(node_data['scale'].value)
2736             node.inputs['Detail'].default_value = float(node_data['detail'].value)
2737             node.inputs['Distortion'].default_value = float(node_data['distortion'].value)
2738                         
2739         elif node_type == "TEX_SKY":
2740             print ("TEX_SKY")
2741             node = node_tree.nodes.new(node_type)
2742             node.sun_direction = vector(node_data['sun_direction'].value)
2743             node.turbidity = float(node_data['turbidity'].value)
2744         
2745         elif node_type == "TEX_VORONOI":
2746             print ("TEX_VORONOI")
2747             node = node_tree.nodes.new(node_type)
2748             node.coloring = node_data['coloring'].value
2749             node.inputs['Scale'].default_value = float(node_data['scale'].value)
2750         
2751         elif node_type == "TEX_WAVE":
2752             print ("TEX_WAVE")
2753             node = node_tree.nodes.new(node_type)
2754             node.wave_type = node_data['wave'].value
2755             node.inputs['Scale'].default_value = float(node_data['scale'].value)
2756             node.inputs['Distortion'].default_value = float(node_data['distortion'].value)
2757             node.inputs['Detail'].default_value = float(node_data['detail'].value)
2758             node.inputs['Detail Scale'].default_value = float(node_data['detail_scale'].value)
2759         
2760             #COLOR TYPES
2761         elif node_type == "BRIGHTCONTRAST":
2762             print ("BRIGHTCONTRAST")
2763             node = node_tree.nodes.new(node_type)
2764             node.inputs['Color'].default_value = color(node_data['color'].value)
2765             node.inputs['Bright'].default_value = float(node_data['bright'].value)
2766             node.inputs['Contrast'].default_value = float(node_data['contrast'].value)
2767         
2768         elif node_type == "GAMMA":
2769             print ("GAMMA")
2770             node = node_tree.nodes.new(node_type)
2771             node.inputs['Color'].default_value = color(node_data['color'].value)
2772             node.inputs['Gamma'].default_value = float(node_data['gamma'].value)
2773         
2774         elif node_type == "HUE_SAT":
2775             print ("HUE_SAT")
2776             node = node_tree.nodes.new(node_type)
2777             node.inputs['Hue'].default_value = float(node_data['hue'].value)
2778             node.inputs['Saturation'].default_value = float(node_data['saturation'].value)
2779             node.inputs['Value'].default_value = float(node_data['value'].value)
2780             node.inputs['Fac'].default_value = float(node_data['fac'].value)
2781             node.inputs['Color'].default_value = color(node_data['color'].value)
2782             
2783         elif node_type == "INVERT":
2784             print ("INVERT")
2785             node = node_tree.nodes.new(node_type)
2786             node.inputs['Fac'].default_value = float(node_data['fac'].value)
2787             node.inputs['Color'].default_value = color(node_data['color'].value)
2788         
2789         elif node_type == "LIGHT_FALLOFF":
2790             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) < 2.64:
2791                 node_message = ['ERROR', """The material file contains the node \"%s\".
2792 This node is not available in the Blender version you are currently using.
2793 You may need a newer version of Blender for this material to work properly.""" % node_type]
2794                 return
2795             print ("LIGHT_FALLOFF")
2796             node = node_tree.nodes.new(node_type)
2797             node.inputs['Strength'].default_value = float(node_data['strength'].value)
2798             node.inputs['Smooth'].default_value = float(node_data['smooth'].value)
2799         
2800         elif node_type == "MIX_RGB":
2801             print ("MIX_RGB")
2802             node = node_tree.nodes.new(node_type)
2803             node.blend_type = node_data['blend_type'].value
2804             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) > 2.63 and "clamp" in node_data:
2805                 node.use_clamp = boolean(node_data['clamp'].value)
2806             node.inputs['Fac'].default_value = float(node_data['fac'].value)
2807             node.inputs['Color1'].default_value = color(node_data['color1'].value)
2808             node.inputs['Color2'].default_value = color(node_data['color2'].value)
2809         
2810             #VECTOR TYPES
2811         elif node_type == "BUMP":
2812             print ("BUMP")
2813             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) < 2.65:
2814                 node_message = ['ERROR', """The material file contains the node \"%s\".
2815 This node is not available in the Blender version you are currently using.
2816 You may need a newer version of Blender for this material to work properly.""" % node_type]
2817                 return
2818             node = node_tree.nodes.new(node_type)
2819             node.inputs["Strength"].default_value = float(node_data['strength'].value)
2820             
2821         elif node_type == "MAPPING":
2822             print ("MAPPING")
2823             node = node_tree.nodes.new(node_type)
2824             node.translation = vector(node_data['translation'].value)
2825             node.rotation = vector(node_data['rotation'].value)
2826             node.scale = vector(node_data['scale'].value)
2827             if boolean(node_data['use_min'].value):
2828                 node.use_min = True
2829                 node.min = vector(node_data['min'].value)
2830             if boolean(node_data['use_max'].value):
2831                 node.use_max = True
2832                 node.max = vector(node_data['max'].value)
2833             node.inputs['Vector'].default_value = vector(node_data['vector'].value)
2834         
2835         elif node_type == "NORMAL":
2836             print ("NORMAL")
2837             node = node_tree.nodes.new(node_type)
2838             node.outputs['Normal'].default_value = vector(node_data['vector_output'].value)
2839             node.inputs['Normal'].default_value = vector(node_data['vector_input'].value)
2840             
2841         elif node_type == "NORMAL_MAP":
2842             print ("NORMAL_MAP")
2843             if bpy.app.version[0] + (bpy.app.version[1] / 100.0) < 2.65:
2844                 node_message = ['ERROR', """The material file contains the node \"%s\".
2845 This node is not available in the Blender version you are currently using.
2846 You may need a newer version of Blender for this material to work properly.""" % node_type]
2847                 return
2848             node = node_tree.nodes.new(node_type)
2849             node.space = node_data['space'].value