LuxBlend_0.1.py
author Jean-Philippe Grimaldi <jeanphi@via.ecp.fr>
Wed Jul 07 21:55:06 2010 +0200 (22 months ago)
changeset 574 017fc3edcc51
parent 573 d6736528de3d
child 576 b55171a1bf1d
permissions -rw-r--r--
Update versions.

Jeanphi
     1 #!BPY
     2 # -*- coding: utf-8 -*-
     3 # coding=utf-8
     4 """Registration info for Blender menus:
     5 Name: 'LuxBlend v0.7 Exporter'
     6 Blender: 248
     7 Group: 'Render'
     8 Tooltip: 'Export/Render to LuxRender v0.7 scene format (.lxs)'
     9 """
    10 
    11 __author__ = "radiance, zuegs, ideasman42, luxblender, dougal2, SATtva"
    12 __version__ = "0.7"
    13 __url__ = [
    14     "http://www.luxrender.net/",
    15     "http://www.luxrender.net/forum/viewforum.php?f=11",
    16     "http://www.luxrender.net/wiki/index.php/Tutorial_1:_Your_first_scene_%26_render"
    17 ]
    18 __bpydoc__ = """\
    19 LuxRender is an open-source rendering system for physically correct, unbiased image synthesis.
    20 This is the Luxrender Blender Export Script.
    21 
    22 Useful links:
    23 - For updates: http://www.luxrender.net/forum/viewforum.php?f=11
    24 - For Blender Tutorial: http://www.luxrender.net/wiki/index.php/Tutorial_1:_Your_first_scene_%26_render
    25 
    26 Usage: 
    27 - Run the script from the render menu.
    28 - Set the default location of the Luxrender.exe.
    29 
    30 Please check the lux tutorials & forums for more information.
    31 """
    32 
    33 #
    34 # ***** BEGIN GPL LICENSE BLOCK *****
    35 #
    36 # --------------------------------------------------------------------------
    37 # LuxBlend v0.7 exporter
    38 # --------------------------------------------------------------------------
    39 #
    40 # Authors:
    41 # radiance, zuegs, ideasman42, luxblender, dougal2, SATtva
    42 #
    43 # This program is free software; you can redistribute it and/or
    44 # modify it under the terms of the GNU General Public License
    45 # as published by the Free Software Foundation; either version 2
    46 # of the License, or (at your option) any later version.
    47 #
    48 # This program is distributed in the hope that it will be useful,
    49 # but WITHOUT ANY WARRANTY; without even the implied warranty of
    50 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    51 # GNU General Public License for more details.
    52 #
    53 # You should have received a copy of the GNU General Public License
    54 # along with this program; if not, write to the Free Software Foundation,
    55 # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    56 #
    57 # ***** END GPL LICENCE BLOCK *****
    58 # --------------------------------------------------------------------------
    59 
    60 
    61 
    62 
    63 ######################################################
    64 # Importing modules
    65 ######################################################
    66 
    67 import math
    68 import time
    69 import os
    70 import sys as osys
    71 import types
    72 import subprocess
    73 import Blender
    74 from Blender import Mesh, Scene, Object, Material, Modifier, Texture, Window, sys, Draw, BGL, Mathutils, Lamp, Image, Particle, Curve
    75 
    76 # critical export function profiling
    77 if False:
    78     import hotshot, hotshot.stats 
    79     def profileit(printlines=1):
    80         def _my(func):
    81             def _func(*args, **kargs):
    82                 prof = hotshot.Profile("profiling.data")
    83                 res = prof.runcall(func, *args, **kargs)
    84                 prof.close()
    85                 stats = hotshot.stats.load("profiling.data")
    86                 stats.strip_dirs()
    87                 stats.sort_stats('time', 'calls')
    88                 print ">>>---- Begin profiling print for %s" % func.__name__
    89                 stats.print_stats(printlines)
    90                 print ">>>---- End profiling print"
    91                 return res
    92             return _func
    93         return _my
    94 else:
    95     def profileit(arg=None):
    96         def _my(func):
    97             return func
    98         return _my
    99 
   100 
   101 ######################################################
   102 # Functions
   103 ######################################################
   104 
   105 # New name based on old with a different extension
   106 def newFName(ext):
   107     return Blender.Get('filename')[: -len(Blender.Get('filename').split('.', -1)[-1]) ] + ext
   108 
   109 
   110 # some helpers
   111 def luxstr(str):
   112     return str.replace("\\", "\\\\")
   113 
   114 
   115 ### relpath ##########################
   116 def relpath(base, target):
   117     if target[0:2] == "\\\\" or target[0:2] == "//":
   118         return target[2:len(target)]
   119     if not os.path.isabs(base):
   120         base = os.path.abspath(base)
   121     if not os.path.isabs(target):
   122         target = os.path.abspath(target)
   123     if os.sep == "\\":
   124         base = os.path.normcase(base)
   125         target = os.path.normcase(target)
   126     if base == os.sep:
   127         return '.' + target
   128     baselist = base.split(os.sep)
   129     if baselist[-1] == "":
   130         baselist = baselist[:-1]
   131     targetlist = target.split(os.sep)
   132     i = 0
   133     top = min([len(baselist), len(targetlist)])
   134     while i < top and baselist[i] == targetlist[i]:
   135         i+=1
   136     if i == 0:
   137         return os.sep.join(targetlist)
   138     if i == len(baselist):
   139         return os.sep.join(targetlist[i:])
   140     else:
   141         return ('..' + os.sep) * (len(baselist) - i) + os.sep.join(targetlist[i:])
   142 
   143 ### luxFilePath #####################
   144 lxs_filename = ""
   145 previewing = False
   146 def luxFilePath(filename):
   147     global lxs_filename, previewing
   148     scn = Scene.GetCurrent()
   149     pm = luxProp(scn, "pathmode", "absolute").get()
   150     if (pm=="absolute") or previewing: # absolute paths (the old / default mode)
   151         return filename
   152     elif pm=="relative": # relative paths
   153         base = os.path.dirname(lxs_filename)
   154         return relpath(base, filename)
   155     elif pm=="flat": # flat mode - only filename
   156         return os.path.basename(filename)
   157 
   158 
   159 
   160 ###### RGC ##########################
   161 def rg(col):
   162     scn = Scene.GetCurrent()
   163     if luxProp(scn, "RGC", "true").get()=="true":
   164         gamma = luxProp(scn, "film.gamma", 2.2).get()
   165     else:
   166         gamma = 1.0
   167     ncol = col**gamma
   168     if luxProp(scn, "colorclamp", "false").get()=="true":
   169         ncol = ncol * 0.9
   170         if ncol > 0.9:
   171             ncol = 0.9
   172         if ncol < 0.0:
   173             ncol = 0.0
   174     return ncol
   175 
   176 def texturegamma():
   177     scn = Scene.GetCurrent()
   178     if luxProp(scn, "RGC", "true").get()=="true":
   179         return luxProp(scn, "film.gamma", 2.2).get()
   180     else:
   181         return 1.0
   182 
   183 def exportMaterial(mat):
   184     str = "# Material '%s'\n" %mat.name
   185     return str+luxMaterial(mat)+"\n"
   186 
   187 
   188 def exportMaterialGeomTag(mat):
   189     return "%s\n"%(luxProp(mat, "link", "").get())
   190 
   191 
   192 # generate and attach a permanent UID to the scene if there isn't any
   193 def luxGenUID(scn):
   194     global luxUID
   195     guid = luxProp(scn, 'UID', '')
   196     g = guid.get()
   197     if (not Blender.Get('filename') and not luxUID) or not g:
   198         print 'Lux scene UID is missing. Generating a new one...'
   199         try:
   200             import hashlib
   201             h = hashlib.sha1
   202         except ImportError:
   203             try:
   204                 import sha
   205                 h = sha.new
   206             except ImportError:
   207                 h = hash
   208         try:
   209             r = os.urandom(20)
   210         except NotImplementedError:
   211             import random
   212             r = str(random.getrandbits(160))
   213         g = h(str(sys.time())+'|'+r)
   214         try: g = g.hexdigest()
   215         except: g = hex(g)[2:]
   216         print 'Generated UID:', g, "\n"
   217         guid.set(g)
   218     return g
   219 
   220 
   221 def bitmask(n, max=20):
   222     bits = []
   223     for i in range(max-1, -1, -1):
   224         v = pow(2, i)
   225         if n < v:
   226             continue
   227         else:
   228             n = n - v
   229             bits.insert(0, i+1)
   230     return bits
   231 
   232 
   233 ################################################################
   234 
   235 
   236 dummyMat = 2394723948 # random identifier for dummy material
   237 clayMat = None
   238 
   239 #-------------------------------------------------
   240 # getMaterials(obj)
   241 # helper function to get the material list of an object in respect of obj.colbits
   242 #-------------------------------------------------
   243 def getMaterials(obj, compress=False):
   244     if not obj.type in ['Mesh', 'Curve', 'Surf', 'Text', 'MBall']:
   245     	return []
   246     
   247     global clayMat
   248     mats = [None]*16
   249     colbits = obj.colbits
   250     objMats = obj.getMaterials(1)
   251     data = obj.getData(mesh=1)
   252     try:
   253         dataMats = data.materials
   254     except:
   255         try:
   256             dataMats = data.getMaterials(1)
   257         except:
   258             try:
   259                 dataMats = Curve.Get(obj.getData().getName()).getMaterials()
   260             except:
   261                 dataMats = []
   262                 colbits = 0xffff
   263     m = max(len(objMats), len(dataMats))
   264     if m>0:
   265         objMats.extend([None]*16)
   266         dataMats.extend([None]*16)
   267         for i in range(m):
   268             if (colbits & (1<<i) > 0):
   269                 mats[i] = objMats[i]
   270             else:
   271                 mats[i] = dataMats[i]
   272         if compress:
   273             mats = [m for m in mats if m]
   274     
   275     slots = [m for m in mats if m]
   276     if m==0 or not slots:
   277         print("Warning: object %s has no material assigned" % (obj.getName()))
   278         mats = []
   279     # clay option
   280     if luxProp(Scene.GetCurrent(), "clay", "false").get()=="true":
   281         if clayMat==None:
   282             clayMat = Material.New("lux_clayMat")
   283             resetMatTex(clayMat)
   284             # resetting clay material to diffuse 0.6
   285             luxProp(clayMat, 'type', '').set('matte')
   286             luxProp(clayMat, ':Kd', '').set(' '.join([str(rg(0.6))]*3))
   287         for i in range(len(mats)):
   288             if mats[i]:
   289                 mattype = luxProp(mats[i], "type", "").get()
   290                 if (mattype not in ["portal","light","boundvolume"]): mats[i] = clayMat
   291         if not mats and clayMat is not None: mats.append(clayMat)
   292     return mats
   293 
   294 
   295 #-------------------------------------------------
   296 # getModifiers(obj)
   297 # returns modifiers stack and modifiers settings of an object
   298 # (modifier rendering parameter is honored)
   299 #-------------------------------------------------
   300 def getModifiers(obj):
   301     stack = []
   302     s = []
   303     for mod in obj.modifiers:
   304         if not mod[Modifier.Settings.RENDER]: continue
   305         for k in Modifier.Settings.keys():
   306             try:
   307                 v = mod[getattr(Modifier.Settings, k)]
   308                 s.append(k+'='+str(v))
   309             except KeyError:
   310                 pass
   311         stack.append([mod.type, s])
   312     return str(stack) if len(stack) else ''
   313 
   314 
   315 ######################################################
   316 # luxExport class
   317 ######################################################
   318 
   319 class luxExport:
   320     #-------------------------------------------------
   321     # __init__
   322     # initializes the exporter object
   323     #-------------------------------------------------
   324     def __init__(self, scene, master_progress):
   325         self.scene = scene
   326         self.camera = scene.objects.camera
   327         self.objects = []
   328         self.portals = []
   329         self.volumes = []
   330         self.namedVolumes = []
   331         self.hair = {'obj':{}, 'motion':{}}
   332         self.meshes = {}
   333         self.instances = {}  # only for instances with quirks: redefined materials and modifiers
   334         self.groups = {}
   335         self.materials = []
   336         self.lights = []
   337         self.duplis = set()
   338         self.mpb = master_progress
   339 
   340     #-------------------------------------------------
   341     # analyseObject(self, obj, matrix, name)
   342     # called by analyseScene to build the lists before export
   343     #-------------------------------------------------
   344     def analyseObject(self, obj, matrix, name, isOriginal=True, isDupli=False):
   345         light = False
   346         export_emitter = False
   347         export_emitter_mats = False
   348         if (obj.users > 0):
   349             obj_type = obj.getType()
   350 
   351             psystems = obj.getParticleSystems()
   352             for psys in psystems:
   353                 if ( (psys.type == Particle.TYPE['EMITTER'] or psys.type == Particle.TYPE['REACTOR']) and psys.drawAs == Particle.DRAWAS['OBJECT']):
   354                     if psys.renderEmitter: export_emitter = True
   355                     dup_obj = psys.duplicateObject
   356                     self.duplis.add(dup_obj)
   357                     
   358                     obj_matrix = dup_obj.getMatrix()
   359                     obj_translation_vec = obj_matrix.translationPart()
   360                     obj_rotation_scale_mat = obj_matrix.rotationPart()  # This gets a 3D submatrix with the rotation AND scale parts.
   361                     
   362                     locs = psys.getLoc()
   363                     scales = psys.getSize()
   364                     rots = psys.getRot()
   365                     if(len(locs) != len(scales) or len(locs) != len(rots)):
   366                         print("ERROR: Please bake particle systems before rendering")
   367                         Draw.PupMenu("ERROR: Please bake particle systems before rendering%t|OK%x1")
   368                         break
   369                     
   370                     for i in range(len(locs)) :
   371                         part_rotation_quat = Mathutils.Quaternion(rots[i])
   372                         part_rotation_mat = part_rotation_quat.toMatrix()
   373                         rotation_scale_mat =  obj_rotation_scale_mat * part_rotation_mat * scales[i]
   374                                     
   375                         # If dup_obj is translated, the particles are translated by the same amount but
   376                         # the direction is rotated by the particle rotation. If dup_obj is rotated, that rotation
   377                         # does not affect the translation. I know it's a bit odd, but that's the way Blender does it
   378                         # and so that's why the order of the matrix multiplications is like this.
   379                         translation_vec = Mathutils.Vector(locs[i]) + part_rotation_quat*obj_translation_vec
   380                         translation_mat = Mathutils.TranslationMatrix(translation_vec)
   381                                     
   382                         rotation_scale_mat.resize4x4()
   383                         
   384                         # Translation must be last because of the way the rotations and translations are encoded in 4D matrices.
   385                         #combined_matrix = scale_matrix*rotation_mat*translation_mat
   386                         combined_matrix = rotation_scale_mat*translation_mat
   387                         #print "combined_matrix = ", combined_matrix
   388                         self.analyseObject(dup_obj, combined_matrix, "%s.%s"%(obj.getName(), dup_obj.getName()), False, True)
   389                         #if self.analyseObject(dup_obj, combined_matrix, "%s.%s"%(obj.getName(), dup_obj.getName()), True, True): light = True
   390                 elif psys.type == Particle.TYPE['HAIR'] and psys.drawAs == Particle.DRAWAS['PATH']:
   391                     if psys.renderEmitter: export_emitter = True
   392                     if not obj in self.hair['obj']: self.hair['obj'][obj] = []
   393                     try:
   394                         if not psys.getName() in self.hair['obj'][obj]: self.hair['obj'][obj].append(psys.getName())
   395                     except AttributeError:
   396                         print 'ERROR: Installed version of Blender does not properly supports hair particles'
   397                         print '       export. Please use this version of LuxBlend with Blender 2.49b only.'
   398                         if osys.platform == 'win32':
   399                             print '       Important note for users of Blender 2.49b on Windows systems: if you'
   400                             print '       received this message, then you\'re using an inappropriate build of'
   401                             print '       Blender program. You can find the correct version build in blender.org'
   402                             print '       download section in a *zip archive* (not in an installer!).'
   403                         print
   404                         Draw.PupMenu('ERROR: Blender version does not properly supports hair export (see console for details)%t|OK%x1')
   405                         break
   406                     if not psys.renderEmitter:
   407                         export_emitter_mats = True
   408                 elif psys.drawAs == Particle.DRAWAS['GROUP']:
   409                     grpObjs =  obj.DupObjects
   410                     grpObjName = obj.name
   411                     for i in grpObjs:
   412                         o = i[0]
   413                         m = i[1]
   414                         # Prefix the name of all particle objects with "luxGroupParticle".
   415                         self.analyseObject(o, m, "%s.%s"%("luxGroupParticle", grpObjName), False, True)
   416                 else:
   417                     print "Unknown particle type for particle system [" + obj.name + "]."
   418             
   419             if (obj.enableDupFrames and isOriginal):
   420                 for o, m in obj.DupObjects:
   421                     light = self.analyseObject(o, m, "%s.%s"%(name, o.getName()), False)
   422             if (obj.enableDupGroup or obj.enableDupVerts or obj.enableDupFaces):
   423                 self.duplis.add(obj)
   424                 for o, m in obj.DupObjects:
   425                     if not o.restrictRender and not isDupli:
   426                         if obj.enableDupGroup:
   427                             objGroups = []
   428                             for g in Blender.Group.Get():
   429                                 if o in g.objects: objGroups.append(g)
   430                             if not objGroups or not True in [ l in o.layers for l in self.groups[g] for g in objGroups ]:
   431                                 continue
   432                         light = self.analyseObject(o, m, "%s.%s"%(name, o.getName()), True, True)
   433             elif ((isDupli or (not obj.getParent() in self.duplis)) and ((obj_type == "Mesh") or (obj_type == "Surf") or (obj_type == "Curve") or (obj_type == "Text"))):
   434                 if (len(psystems) == 0) or export_emitter or export_emitter_mats:
   435                     mats = getMaterials(obj)
   436                     if (len(mats)>0) and (mats[0]!=None) and ((mats[0].name=="PORTAL") or (luxProp(mats[0], "type", "").get()=="portal")):
   437                         self.portals.append([obj, matrix])
   438                     elif (len(mats)>0) and (luxProp(mats[0], "type", "").get()=="boundvolume"):
   439                         self.volumes.append([obj, matrix])
   440                     else:
   441                         for mat in mats:
   442                             if (mat!=None) and (mat not in self.materials):
   443                                 self.materials.append(mat)
   444                                 # collect used named volumes ids
   445                                 for volume_prop in ['Exterior', 'Interior']:
   446                                     if luxProp(mat, '%s_vol_used'%(volume_prop), 'false').get() == 'true':
   447                                         volumeId = luxProp(mat, '%s_vol_id' % (volume_prop), 0).get()
   448                                         if volumeId not in self.namedVolumes:
   449                                             self.namedVolumes.append(volumeId)
   450                             if (mat!=None) and ((luxProp(mat, "type", "").get()=="light") or (luxProp(mat, "emission", "false").get()=="true")) \
   451                              and luxProp(Scene.GetCurrent(), "lightgroup.disable."+luxProp(mat, "light.lightgroup", "default").get(), "false").get() != "true":
   452                                 light = True
   453                         if len(psystems) == 0 or export_emitter:
   454                             mesh_name = obj.getData(name_only=True)
   455                             try:
   456                                 self.meshes[mesh_name] += [obj]
   457                             except KeyError:
   458                                 self.meshes[mesh_name] = [obj]
   459                             self.objects.append([obj, matrix])
   460             elif (obj_type == "Lamp"):
   461                 ltype = obj.getData(mesh=1).getType() # data
   462                 if (ltype == Lamp.Types["Lamp"]) or (ltype == Lamp.Types["Spot"]) or (ltype == Lamp.Types["Area"]):
   463                     if luxProp(Scene.GetCurrent(), "lightgroup.disable."+luxProp(obj, "light.lightgroup", "default").get(), "false").get() != "true":
   464                         self.lights.append([obj, matrix])
   465                         light = True
   466         return light
   467 
   468     #-------------------------------------------------
   469     # analyseScene(self)
   470     # this function builds the lists of object, lights, meshes and materials before export
   471     #-------------------------------------------------
   472     def analyseScene(self):
   473         light = False
   474         for g in Blender.Group.Get():
   475             # caching groups layers
   476             self.groups[g] = bitmask(g.layers)
   477         for obj in self.scene.objects:
   478             if ((obj.Layers & self.scene.Layers) > 0) and not obj.restrictRender:
   479                 if self.analyseObject(obj, obj.getMatrix(), obj.getName()): light = True
   480         return light
   481 
   482     #-------------------------------------------------
   483     # exportInstanceObjName(self, mesh_name, matId, shapeId)
   484     # format instanced material-separated mesh name
   485     #-------------------------------------------------
   486     def exportInstanceObjName(self, mesh_name, matId=None, shapeId=None):
   487         s = mesh_name
   488         if matId is None and shapeId is None:
   489             return s
   490         s += ':luxInstancedObj'
   491         if matId is not None: s += ':matId%s' % matId
   492         if shapeId is not None: s += ':shapeId%s' % shapeId
   493         return s
   494 
   495     #-------------------------------------------------
   496     # exportMaterialLink(self, file, mat)
   497     # exports material link. LuxRender "Material" 
   498     #-------------------------------------------------
   499     def exportMaterialLink(self, file, mat):
   500         if mat == dummyMat:
   501             file.write("\tMaterial \"matte\" # dummy material\n")
   502         else:
   503             file.write("\t%s"%exportMaterialGeomTag(mat)) # use original methode
   504 
   505     #-------------------------------------------------
   506     # exportMaterial(self, file, mat)
   507     # exports material. LuxRender "Texture" 
   508     #-------------------------------------------------
   509     def exportMaterial(self, file, mat):
   510         #print("material %s"%(mat.getName()))
   511         file.write("\t%s"%exportMaterial(mat)) # use original methode        
   512     
   513     #-------------------------------------------------
   514     # exportMaterials(self, file)
   515     # exports materials to the file
   516     #-------------------------------------------------
   517     def exportMaterials(self, file):
   518         #pb = exportProgressBar(len(self.materials), self.mpb)
   519         for mat in self.materials:
   520             #pb.counter('Exporting Materials')
   521             self.exportMaterial(file, mat)
   522 
   523     #-------------------------------------------------
   524     # exportNamedVolumes(self, file)
   525     # exports named volumes to the file
   526     #-------------------------------------------------
   527     def exportNamedVolumes(self, file):
   528         #pb = exportProgressBar(len(self.namedVolumes), self.mpb)
   529         output = ''
   530         volumes = listNamedVolumes()
   531         for linked, new in importedVolumeIdsTranslation.items():
   532             if linked in self.namedVolumes:
   533                 self.namedVolumes.remove(linked)
   534                 self.namedVolumes.append(new)
   535         for volume in volumes.values():
   536             if volume in self.namedVolumes:
   537                 #pb.counter('Exporting Mediums Definitions')
   538                 data = getNamedVolume(volume)
   539                 output = "\t# Volume '%s'\n" % data['name']
   540                 tex = luxNamedVolumeTexture(volume)
   541                 output += "%s\nMakeNamedVolume \"%s\" %s" % (tex[0], data['name'], tex[1])
   542                 output += "\n\n"
   543                 file.write(output)
   544 
   545     #-------------------------------------------------
   546     # exportHairSystems(self, file)
   547     # collects hair particles and exports hair systems
   548     # primitives to the file
   549     #-------------------------------------------------
   550     def exportHairSystems(self, file):
   551         #pb = exportProgressBar(len(self.hair), self.mpb)
   552         clay_export = (luxProp(self.scene, 'clay', 'false').get() != 'true')
   553         ob_moblur = (luxProp(self.camera.data, 'objectmblur', 'true').get() == 'true' and luxProp(self.camera.data, 'usemblur', 'false').get() == 'true')
   554         frame = Blender.Get('curframe')
   555         for obj, obj_psystems in self.hair['obj'].items():
   556             #pb.counter('Exporting Hair Particles')
   557             for psys in obj.getParticleSystems():
   558                 psysname = psys.getName()
   559                 if not psysname in obj_psystems: continue
   560                 
   561                 if clay_export:
   562                     mat = psys.getMat() or dummyMat
   563                 else:
   564                     mat = getMaterials(obj, True)[0]
   565                 
   566                 size = luxProp(mat, 'hair_thickness', 0.5).get() * luxScaleUnits('hair_thickness', 'mm', mat)
   567                 legname = '%s:%s:luxHairPrimitive:leg' % (obj.name, psysname)
   568                 jointname = '%s:%s:luxHairPrimitive:joint' % (obj.name, psysname)
   569                 primitives = {
   570                   legname: "\tShape \"cylinder\" \"float radius\" %f \"float zmin\" 0.0 \"float zmax\" 1.0\n" % (0.5*size),
   571                   jointname: "\tShape \"sphere\" \"float radius\" %f\n" % (0.5*size)
   572                 }
   573                 # exporting primitive objects
   574                 for name, shape in primitives.items():
   575                     file.write("ObjectBegin \"%s\"\n" % name)
   576                     self.exportMaterialLink(file, mat)
   577                     file.write(shape)
   578                     file.write("ObjectEnd # %s\n\n" % name)
   579                 # collecting segment objects (instanced)
   580                 self.luxCollectHairObjs(psys, jointname, legname, size)
   581                 if ob_moblur:
   582                     # to make motion blur work we must also get transform matrices from the following frame
   583                     Blender.Set('curframe', frame+1)
   584                     self.luxCollectHairObjs(psys, jointname, legname, size, True)
   585                     Blender.Set('curframe', frame)
   586                 # removing psys from the list to avoid multiple exports
   587                 self.hair['obj'][obj].remove(psysname)
   588 
   589     # collect hair strand segment objects/matrices pairs
   590     def luxCollectHairObjs(self, psys, jointname, legname, size, motion=False):
   591         # matrix check helper function
   592         def matrixHasNaN(m):
   593             for i in range(len(m)):
   594                 for v in m[i]:
   595                     if type(v) is not float and matrixHasNaN(v): return True
   596                     elif str(v) == 'nan': return True
   597             return False
   598         # it seams to be a bug in Blender Python API here -- if an object
   599         # has more than one particle system, then beginning from the
   600         # second system the call to Particles.getLoc() results in an empty
   601         # list for the first time
   602         segmentsLoc = psys.getLoc()
   603         segmentsLoc = psys.getLoc()  # sic
   604         for i, strand in enumerate(segmentsLoc):
   605             for j in range(0, len(strand)*2-1):
   606             	j_over_2 = j/2
   607                 if j%2 == 0:
   608                     name = jointname
   609                     matrix = Mathutils.Matrix([1.0, 0.0, 0.0, 0.0], [0.0, 1.0, 0.0, 0.0], [0.0, 0.0, 1.0, 0.0], [strand[j_over_2][0], strand[j_over_2][1], strand[j_over_2][2], 1.0])
   610                 else:
   611                     name = legname
   612                     m = self.getHairSegmentTransform(strand[j_over_2], strand[j_over_2+1])
   613                     matrix = Mathutils.Matrix(m[0], m[1], m[2], m[3])
   614                 # check to cull out point-sized strands
   615                 if j == 1 and not motion and matrixHasNaN(matrix[:3]) is True:
   616                     self.objects.pop()
   617                     break
   618                 obj = self.luxHair('%s_strand%s_segment%s' % (name,i,j), name)
   619                 if not motion:
   620                     self.objects.append([obj, matrix])
   621                     try:
   622                         self.instances[name]['obj_mods'][''] = ['luxHair']
   623                     except KeyError:
   624                         self.instances[name] = {'obj_mods': {'': ['luxHair']}}
   625                 else:
   626                     self.hair['motion'][obj] = matrix
   627 
   628     # minimalistic Blender-like object for holding strand obj properties
   629     class luxHair:
   630         def __init__(self, objName, parentName):
   631             self.objName = objName
   632             self.parentName = parentName
   633         def __cmp__(self, other):
   634             return cmp(self.__repr__(), other.__repr__())
   635         def __hash__(self):
   636             return hash(self.__repr__())
   637         def __repr__(self):
   638             return '[Object "%s"]' % self.objName
   639         def __str__(self):
   640             return self.__repr__()
   641         def getData(self, **args):
   642             return self.parentName
   643         def getName(self):
   644             return self.objName
   645 
   646     # hair export helper function (by e_jackson)
   647     def getHairSegmentTransform(self, p1, p2):
   648         """
   649         This function selects an orthogonal basis V_1 = (p2-p1), V_2, V_3 such that V_2 and V_3
   650         have unit length and calculates a transformation matrix from standard orthnormal basis
   651         to the selected one.
   652         Arguments:
   653             p1, p2 == coordinate triples of beginning and end points of a vector
   654         Returns:
   655             string which represents transformation matrix in a format compatible with Luxrender SDL
   656         """
   657         # standard orthonormal basis
   658         Standard_basis = [Mathutils.Vector(1.0, 0.0, 0.0), Mathutils.Vector(0.0, 1.0, 0.0), Mathutils.Vector(0.0, 0.0, 1.0)]
   659         
   660         V = [(), (), ()]
   661         V[2] = Mathutils.Vector(p2) - Mathutils.Vector(p1)
   662         # we choose an ort which corresponds to the smallest absolute value of coordinate in V[0]
   663         W = Standard_basis[0]
   664         Length = abs(V[2].x)
   665         for Node in zip([1, 2], [abs(V[2].y), abs(V[2].z)]) :
   666             if Node[1] < Length :
   667                 Length = Node[1]
   668                 W = Standard_basis[Node[0]]
   669         V[1] = V[2].cross(W)
   670         V[1].normalize()
   671         V[0] = V[1].cross(V[2])
   672         V[0].normalize()
   673         # transition matrix from standard basis to V
   674         M = Mathutils.Matrix(V[0], V[1], V[2])
   675         Result = []
   676         for Count in range(3) :
   677             Result.append([M[Count][0], M[Count][1], M[Count][2], 0.0])
   678         Result.append([p1[0], p1[1], p1[2], 1.0])
   679         return Result
   680 
   681     #-------------------------------------------------
   682     # getMeshType(self, vertcount, mat, instancedMats)
   683     # returns type of mesh as string to use depending on thresholds
   684     #-------------------------------------------------
   685     def getMeshType(self, vertcount, mat, instancedMats=None):
   686         scn = Scene.GetCurrent()
   687         if mat != dummyMat and not instancedMats:
   688             usesubdiv = luxProp(mat, "subdiv", "false")
   689             usedisp = luxProp(mat, "dispmap", "false")
   690             sharpbound = luxProp(mat, "sharpbound", "false")
   691             nsmooth = luxProp(mat, "nsmooth", "true")
   692             sdoffset = luxProp(mat, "sdoffset", 0.0)
   693             dstr = ""
   694             if usesubdiv.get() == "true":
   695                 nlevels = luxProp(mat, "sublevels", 1)
   696                 dstr += "\"loopsubdiv\" \"integer nlevels\" [%i] \"bool dmnormalsmooth\" [\"%s\"] \"bool dmsharpboundary\" [\"%s\"]"% (nlevels.get(), nsmooth.get(), sharpbound.get())
   697             
   698             if usedisp.get() == "true":
   699                 dstr += " \"string displacementmap\" [\"%s::dispmap.scale\"] \"float dmscale\" [-1.0] \"float dmoffset\" [%f]"%(mat.getName(), sdoffset.get()) # scale is scaled in texture
   700 
   701             if dstr != "": return dstr
   702 
   703         return "\"trianglemesh\""
   704 
   705     #-------------------------------------------------
   706     # exportMesh(self, file, mesh, mats, name, portal, instancedMats, instancedShapes)
   707     # exports mesh to the file without any optimization
   708     #-------------------------------------------------
   709     def exportMesh(self, file, mesh, mats, name, portal=False, instancedMats=None, instancedShapes=None):
   710         #print("    exporting mesh")
   711         if mats == []:
   712             mats = [dummyMat]
   713         usedmats = [f.mat for f in mesh.faces]
   714         i = 0
   715         for matIndex in range(len(mats)):
   716             if not matIndex in usedmats:
   717                 continue
   718             if not(portal):
   719                 mat = mats[matIndex]
   720                 if not mat:
   721                    mat = dummyMat
   722                 if instancedMats:
   723                     file.write("ObjectBegin \"%s\"\n" % self.exportInstanceObjName(instancedMats, i, instancedShapes))
   724                 self.exportMaterialLink(file, mat)
   725             mesh_str = self.getMeshType(len(mesh.verts), mats[matIndex], instancedMats)
   726             if not(portal):
   727                 file.write("\tShape %s \"integer indices\" [\n"% mesh_str)
   728             else:
   729                 self.exportMaterialLink(file, mats[matIndex])
   730                 file.write("\tPortalShape %s \"integer indices\" [\n"% mesh_str)
   731             index = 0
   732             ffaces = [f for f in mesh.faces if f.mat == matIndex]
   733             for face in ffaces:
   734                 file.write("%d %d %d\n"%(index, index+1, index+2))
   735                 if (len(face)==4):
   736                     file.write("%d %d %d\n"%(index, index+2, index+3))
   737                 index += len(face.verts)
   738             file.write("\t] \"point P\" [\n")
   739             for face in ffaces:
   740                 for vertex in face:
   741                     file.write("%f %f %f\n"% tuple(vertex.co))
   742             file.write("\t] \"normal N\" [\n")
   743             for face in ffaces:
   744                 normal = face.no
   745                 for vertex in face:
   746                     if (face.smooth):
   747                         normal = vertex.no
   748                     file.write("%f %f %f\n"% tuple(normal))
   749             if (mesh.faceUV):
   750                 file.write("\t] \"float uv\" [\n")
   751                 # Check if there is a render specific UV layer and make it active for export.
   752                 activeUVLayer_orig = mesh.activeUVLayer
   753                 renderUVLayer = mesh.renderUVLayer
   754                 if renderUVLayer != activeUVLayer_orig:
   755                     mesh.activeUVLayer = renderUVLayer
   756                 for face in ffaces:
   757                     for uv in face.uv:
   758                         file.write("%f %f\n"% tuple(uv))
   759                 # If we changed the active UV layer: reset it to the original.
   760                 if renderUVLayer != activeUVLayer_orig:
   761                     mesh.activeUVLayer = activeUVLayer_orig 
   762             file.write("\t]\n")
   763             if instancedMats:
   764                 file.write("ObjectEnd # %s\n\n" % self.exportInstanceObjName(instancedMats, i, instancedShapes))
   765             i += 1
   766 
   767     #-------------------------------------------------
   768     # exportMeshOpt(self, file, mesh, mats, name, portal, optNormals, instancedMats, instancedShapes)
   769     # exports mesh to the file with optimization.
   770     # portal: export without normals and UVs
   771     # optNormals: speed and filesize optimization, flat faces get exported without normals
   772     #-------------------------------------------------
   773     def exportMeshOpt(self, file, mesh, mats, name, portal=False, optNormals=True, instancedMats=None, instancedShapes=None):
   774         #print("    exporting optimized mesh")
   775         shapeList, smoothFltr, shapeText = [0], [[0,1]], [""]
   776         if portal:
   777             normalFltr, uvFltr, shapeText = [0], [0], ["portal"] # portal, no normals, no UVs
   778         else:
   779             uvFltr, normalFltr, shapeText = [1], [1], ["mixed with normals"] # normals and UVs
   780             if optNormals: # one pass for flat faces without normals and another pass for smoothed faces with normals, all with UVs
   781                 shapeList, smoothFltr, normalFltr, uvFltr, shapeText = [0,1], [[0],[1]], [0,1], [1,1], ["flat w/o normals", "smoothed with normals"]
   782         if mats == []:
   783             mats = [dummyMat]
   784         usedmats = [f.mat for f in mesh.faces]
   785         i = 0
   786         # Check if there is a render specific UV layer and make it active for export.
   787         activeUVLayer_orig = mesh.activeUVLayer
   788         renderUVLayer = mesh.renderUVLayer
   789         if renderUVLayer != activeUVLayer_orig:
   790             mesh.activeUVLayer = renderUVLayer
   791         for matIndex in range(len(mats)):
   792             if not matIndex in usedmats:
   793                 continue
   794             if not(portal):
   795                 mat = mats[matIndex]
   796                 if not mat:
   797                    mat = dummyMat
   798                 if instancedMats:
   799                     file.write("ObjectBegin \"%s\"\n" % self.exportInstanceObjName(instancedMats, i, instancedShapes))
   800                 self.exportMaterialLink(file, mat)
   801             for shape in shapeList:
   802                 blenderExportVertexMap = []
   803                 exportVerts = []
   804                 exportFaces = []
   805                 ffaces = [f for f in mesh.faces if (f.mat == matIndex) and (f.smooth in smoothFltr[shape])]
   806                 for face in ffaces:
   807                     exportVIndices = []
   808                     index = 0
   809                     for vertex in face:
   810 #                            v = [vertex.co[0], vertex.co[1], vertex.co[2]]
   811                         v = [vertex.co]
   812                         if normalFltr[shape]:
   813                             if (face.smooth):
   814 #                                    v.extend(vertex.no)
   815                                 v.append(vertex.no)
   816                             else:
   817 #                                    v.extend(face.no)
   818                                 v.append(face.no)
   819                         if (uvFltr[shape]) and (mesh.faceUV):
   820 #                                v.extend(face.uv[index])
   821                             v.append(face.uv[index])
   822                         blenderVIndex = vertex.index
   823                         newExportVIndex = -1
   824                         length = len(v)
   825                         if (blenderVIndex < len(blenderExportVertexMap)):
   826                             for exportVIndex in blenderExportVertexMap[blenderVIndex]:
   827                                 v2 = exportVerts[exportVIndex]
   828                                 if (length==len(v2)) and (v == v2):
   829                                     newExportVIndex = exportVIndex
   830                                     break
   831                         if (newExportVIndex < 0):
   832                             newExportVIndex = len(exportVerts)
   833                             exportVerts.append(v)
   834                             while blenderVIndex >= len(blenderExportVertexMap):
   835                                 blenderExportVertexMap.append([])
   836                             blenderExportVertexMap[blenderVIndex].append(newExportVIndex)
   837                         exportVIndices.append(newExportVIndex)
   838                         index += 1
   839                     exportFaces.append(exportVIndices)
   840                 if (len(exportVerts)>0):
   841                     mesh_str = self.getMeshType(len(exportVerts), mats[matIndex], instancedMats)
   842                     if portal:
   843                         file.write("\tPortalShape %s \"integer indices\" [\n"% mesh_str)
   844                     else:
   845                         file.write("\tShape %s \"integer indices\" [\n"% mesh_str)
   846                     for face in exportFaces:
   847                         file.write("%d %d %d\n"%(face[0], face[1], face[2]))
   848                         if (len(face)==4):
   849                             file.write("%d %d %d\n"%(face[0], face[2], face[3]))
   850                     file.write("\t] \"point P\" [\n")
   851 #                        for vertex in exportVerts:
   852 #                            file.write("%f %f %f\n"%(vertex[0], vertex[1], vertex[2]))
   853                     file.write("".join(["%f %f %f\n"%tuple(vertex[0]) for vertex in exportVerts]))
   854                     if normalFltr[shape]:
   855                         file.write("\t] \"normal N\" [\n")
   856 #                            for vertex in exportVerts:
   857 #                                file.write("%f %f %f\n"%(vertex[3], vertex[4], vertex[5]))
   858                         file.write("".join(["%f %f %f\n"%tuple(vertex[1]) for vertex in exportVerts])) 
   859                         if (uvFltr[shape]) and (mesh.faceUV):
   860                             file.write("\t] \"float uv\" [\n")
   861 #                                for vertex in exportVerts:
   862 #                                    file.write("%f %f\n"%(vertex[6], vertex[7]))
   863                             file.write("".join(["%f %f\n"%tuple(vertex[2]) for vertex in exportVerts])) 
   864                     else:            
   865                         if (uvFltr[shape]) and (mesh.faceUV):
   866                             file.write("\t] \"float uv\" [\n")
   867 #                                for vertex in exportVerts:
   868 #                                    file.write("%f %f\n"%(vertex[3], vertex[4]))
   869                             file.write("".join(["%f %f\n"%tuple(vertex[1]) for vertex in exportVerts])) 
   870                     file.write("\t]\n")
   871                     if instancedMats:
   872                         file.write("ObjectEnd # %s\n\n" % self.exportInstanceObjName(instancedMats, i, instancedShapes))
   873                     #print("  shape(%s): %d vertices, %d faces"%(shapeText[shape], len(exportVerts), len(exportFaces)))
   874             i += 1
   875         # If we changed the active UV layer: reset it to the original.
   876         if renderUVLayer != activeUVLayer_orig:
   877             mesh.activeUVLayer = activeUVLayer_orig 
   878 
   879     
   880     #-------------------------------------------------
   881     # exportMeshes(self, file)
   882     # exports meshes that uses instancing (meshes that are used by at least "instancing_threshold" objects)
   883     #-------------------------------------------------
   884     def exportMeshes(self, file):
   885         scn = Scene.GetCurrent()
   886         instancing_threshold = luxProp(scn, "instancing_threshold", 2).get()
   887         mesh_optimizing = luxProp(scn, "mesh_optimizing", "true")
   888         mesh = Mesh.New('')
   889         #pb = exportProgressBar(len(self.meshes), self.mpb)
   890         for (mesh_name, objs) in self.meshes.items():
   891             #pb.counter('Exporting Meshes')
   892             self.instances[mesh_name] = {'obj_mats':{}, 'obj_vols':{}, 'obj_mods':{}}
   893             allow_instancing = True
   894             mats = getMaterials(objs[0])
   895             for mat in mats: # don't instance if one of the materials is emissive
   896                 if (mat!=None) and (luxProp(mat, "type", "").get()=="light"):
   897                     allow_instancing = False
   898             for obj in objs:
   899                 obj_mats = getMaterials(obj)
   900                 obj_name = obj.getName()
   901                 if obj_mats != mats:
   902                     obj_mats_used = getMaterials(obj, True)
   903                     # if an instance overrides mesh's materials, copy them
   904                     self.instances[mesh_name]['obj_mats'][obj_name] = obj_mats_used
   905                     # lets not forget volume definitions in overridden materials
   906                     self.instances[mesh_name]['obj_vols'][obj_name] = {}
   907                     for obj_mat in obj_mats_used:
   908                         self.instances[mesh_name]['obj_vols'][obj_name][obj_mat.name] = {}
   909                         for volume_prop in ['Exterior', 'Interior']:
   910                             if luxProp(obj_mat, '%s_vol_used'%(volume_prop), 'false').get() == 'true':
   911                                 volId = luxProp(obj_mat, '%s_vol_id' % (volume_prop), 0).get()
   912                             else:
   913                                 volId = ''
   914                             self.instances[mesh_name]['obj_vols'][obj_name][obj_mat.name][volume_prop] = volId
   915                     if not 'mesh_mats' in self.instances[mesh_name]:
   916                         self.instances[mesh_name]['mesh_mats'] = getMaterials(objs[0])
   917                         self.instances[mesh_name]['mesh_mats_used'] = getMaterials(objs[0], True)
   918                 obj_mods = getModifiers(obj)
   919                 # collect modifier configurations to export all possible shapes later
   920                 try:
   921                     self.instances[mesh_name]['obj_mods'][obj_mods].append(obj_name)
   922                 except KeyError:
   923                     self.instances[mesh_name]['obj_mods'][obj_mods] = [obj_name]
   924             if allow_instancing and (len(objs) >= instancing_threshold):
   925                 del self.meshes[mesh_name]
   926                 j = 0 if len(self.instances[mesh_name]['obj_mods']) > 1 else None
   927                 for shape in self.instances[mesh_name]['obj_mods'].values():
   928                     mesh.getFromObject(shape[0], 0, 1)
   929                     #print("blender-mesh: %s (%d vertices, %d faces)"%(mesh_name, len(mesh.verts), len(mesh.faces)))
   930                     if not self.instances[mesh_name].has_key('mesh_mats_used'):
   931                         file.write("ObjectBegin \"%s\"\n" % self.exportInstanceObjName(mesh_name, shapeId=j))
   932                         if (mesh_optimizing.get() == "true"):
   933                             self.exportMeshOpt(file, mesh, mats, mesh_name, instancedShapes=j)
   934                         else:
   935                             self.exportMesh(file, mesh, mats, mesh_name, instancedShapes=j)
   936                         file.write("ObjectEnd # %s\n\n" % self.exportInstanceObjName(mesh_name, shapeId=j))
   937                     else:
   938                         if (mesh_optimizing.get() == "true"):
   939                             self.exportMeshOpt(file, mesh, mats, mesh_name, instancedMats=mesh_name, instancedShapes=j)
   940                         else:
   941                             self.exportMesh(file, mesh, mats, mesh_name, instancedMats=mesh_name, instancedShapes=j)
   942                     if j is not None: j += 1
   943         mesh.verts = None
   944 
   945     #-------------------------------------------------
   946     # exportObjects(self, file)
   947     # exports objects to the file
   948     #-------------------------------------------------
   949     def exportObjects(self, file):
   950         # write transformation matrix without losing float precision
   951         def preciseMatrix(m):
   952             l = []
   953             for i in range(len(m)):
   954                 for v in m[i]:
   955                     if type(v) is not float: l.append(preciseMatrix(v))
   956                     elif abs(v) in [0, 1.0]: l.append('%0.1f' % v)
   957                     else: l.append('%0.18f' % v)
   958             return ' '.join(l)
   959         scn = Scene.GetCurrent()
   960         #cam = scn.getCurrentCamera().data
   961         cam = scn.objects.camera.data
   962         objectmblur = luxProp(cam, "objectmblur", "true").get()
   963         usemblur = luxProp(cam, "usemblur", "false").get()
   964         mesh_optimizing = luxProp(scn, "mesh_optimizing", "true").get()
   965         mesh = Mesh.New('')
   966         #pb = exportProgressBar(len(self.objects), self.mpb)
   967         for [obj, matrix] in self.objects:
   968             #pb.counter('Exporting Objects')
   969             #print("object: %s"%(obj.getName()))
   970             mesh_name = obj.getData(name_only=True)
   971             obj_name = obj.getName()
   972 
   973             motion = None
   974             if objectmblur == "true" and usemblur == "true":
   975                 # motion blur
   976                 try:
   977                     motion = self.hair['motion'][obj]
   978                 except KeyError:
   979                     frame = Blender.Get('curframe')
   980                     Blender.Set('curframe', frame+1)
   981                     m1 = matrix.copy() 
   982                     Blender.Set('curframe', frame)
   983                     if m1 != matrix:
   984                         #print("  motion blur")
   985                         motion = m1
   986     
   987             if motion: # motion-blur only works with instances, so ensure mesh is exported as instance first
   988                 if mesh_name in self.meshes:
   989                     del self.meshes[mesh_name]
   990                     j = 0 if len(self.instances[mesh_name]['obj_mods']) > 1 else None
   991                     for shape in self.instances[mesh_name]['obj_mods'].values():
   992                         mesh.getFromObject(Object.Get(shape[0]), 0, 1)
   993                         mats = getMaterials(obj)
   994                         #print("  blender-mesh: %s (%d vertices, %d faces)"%(mesh_name, len(mesh.verts), len(mesh.faces)))
   995                         if not self.instances[mesh_name].has_key('mesh_mats_used'):
   996                             file.write("ObjectBegin \"%s\"\n" % self.exportInstanceObjName(mesh_name, shapeId=j))
   997                             if mesh_optimizing == "true":
   998                                 self.exportMeshOpt(file, mesh, mats, mesh_name, instancedShapes=j)
   999                             else:
  1000                                 self.exportMesh(file, mesh, mats, mesh_name, instancedShapes=j)
  1001                             file.write("ObjectEnd # %s\n\n" % self.exportInstanceObjName(mesh_name, shapeId=j))
  1002                         else:
  1003                             if mesh_optimizing == "true":
  1004                                 self.exportMeshOpt(file, mesh, mats, mesh_name, instancedMats=mesh_name, instancedShapes=j)
  1005                             else:
  1006                                 self.exportMesh(file, mesh, mats, mesh_name, instancedMats=mesh_name, instancedShapes=j)
  1007                         if j is not None: j += 1
  1008 
  1009             j = 0 if len(self.instances[mesh_name]['obj_mods']) > 1 else None
  1010             for shape in self.instances[mesh_name]['obj_mods'].values():
  1011                 if shape != ['luxHair'] and not obj_name in shape:
  1012                     j += 1
  1013                     continue
  1014                 i = 0 if self.instances[mesh_name].has_key('mesh_mats_used') else None
  1015                 for mat in self.instances[mesh_name]['mesh_mats_used'] if self.instances[mesh_name].has_key('mesh_mats_used') else [None]:
  1016                     file.write("AttributeBegin # %s\n" % self.exportInstanceObjName(obj_name, i, j))
  1017                     file.write("\tTransform [%s]\n" % preciseMatrix(matrix))
  1018                     if motion:
  1019                         file.write("\tTransformBegin\n")
  1020                         file.write("\t\tIdentity\n")
  1021                         file.write("\t\tTransform [%s]\n" % preciseMatrix(motion))
  1022                         file.write("\t\tCoordinateSystem \"%s\"\n" % (self.exportInstanceObjName(obj_name, i, j)+'_motion'))
  1023                         file.write("\tTransformEnd\n")
  1024                     if mesh_name in self.meshes:
  1025                         mesh.getFromObject(obj, 0, 1)
  1026                         mats = getMaterials(obj)
  1027                         #print("  blender-mesh: %s (%d vertices, %d faces)"%(mesh_name, len(mesh.verts), len(mesh.faces)))
  1028                         if mesh_optimizing == "true":
  1029                             self.exportMeshOpt(file, mesh, mats, mesh_name)
  1030                         else:
  1031                             self.exportMesh(file, mesh, mats, mesh_name)
  1032                     else:
  1033                         #print("  instance %s"%(mesh_name))
  1034                         if mat is not None and obj_name in self.instances[mesh_name]['obj_mats']:  #and self.instances[mesh_name]['mesh_mats'] != getMaterials(obj):
  1035                             file.write("\tNamedMaterial \"%s\"\n" % self.instances[mesh_name]['obj_mats'][obj_name][i].name)
  1036                             for volume_prop in ['Exterior', 'Interior']:
  1037                                 vol = self.instances[mesh_name]['obj_vols'][obj_name][self.instances[mesh_name]['obj_mats'][obj_name][i].name][volume_prop]
  1038                                 file.write("\t%s \"%s\"\n" % (volume_prop, '' if type(vol) is str else getNamedVolume(vol)['name']))
  1039                         if motion:
  1040                             file.write("\tMotionInstance \"%s\" 0.0 1.0 \"%s\"\n" % (self.exportInstanceObjName(mesh_name, i, j), self.exportInstanceObjName(obj_name, i, j)+'_motion'))
  1041                         else:
  1042                             file.write("\tObjectInstance \"%s\"\n" % self.exportInstanceObjName(mesh_name, i, j))
  1043                     file.write("AttributeEnd\n\n")
  1044                     if i is not None: i += 1
  1045                 if j is not None: j += 1
  1046         mesh.verts = None
  1047 
  1048     #-------------------------------------------------
  1049     # exportPortals(self, file)
  1050     # exports portals objects to the file
  1051     #-------------------------------------------------
  1052     def exportPortals(self, file):
  1053         scn = Scene.GetCurrent()
  1054         mesh_optimizing = luxProp(scn, "mesh_optimizing", "true")
  1055         mesh = Mesh.New('')
  1056         for [obj, matrix] in self.portals:
  1057             #print("portal: %s"%(obj.getName()))
  1058             file.write("\tTransform [%s %s %s %s  %s %s %s %s  %s %s %s %s  %s %s %s %s]\n"\
  1059                 %(matrix[0][0], matrix[0][1], matrix[0][2], matrix[0][3],\
  1060                   matrix[1][0], matrix[1][1], matrix[1][2], matrix[1][3],\
  1061                   matrix[2][0], matrix[2][1], matrix[2][2], matrix[2][3],\
  1062                     matrix[3][0], matrix[3][1], matrix[3][2], matrix[3][3]))
  1063             mesh_name = obj.getData(name_only=True)
  1064             mesh.getFromObject(obj, 0, 1)
  1065             mats = getMaterials(obj) # mats = obj.getData().getMaterials()
  1066             if (mesh_optimizing.get() == "true"):
  1067                 self.exportMeshOpt(file, mesh, mats, mesh_name, True)
  1068             else:
  1069                 self.exportMesh(file, mesh, mats, mesh_name, True)
  1070         mesh.verts = None
  1071 
  1072     #-------------------------------------------------
  1073     # exportLights(self, file)
  1074     # exports lights to the file
  1075     #-------------------------------------------------
  1076     def exportLights(self, file):
  1077         for [obj, matrix] in self.lights:
  1078             ltype = obj.getData(mesh=1).getType() # data
  1079             if (ltype == Lamp.Types["Lamp"]) or (ltype == Lamp.Types["Spot"]) or (ltype == Lamp.Types["Area"]):
  1080                 lightgroup = luxProp(obj, "light.lightgroup", "default")
  1081                 if luxProp(Scene.GetCurrent(), "lightgroup.disable."+lightgroup.get(), "false").get() == "true":
  1082                     continue
  1083                 #print("light: %s"%(obj.getName()))
  1084                 if ltype == Lamp.Types["Area"]:
  1085                     (str, link) = luxLight("", "", obj, None, 0)
  1086                     file.write(str)
  1087                 if ltype == Lamp.Types["Area"]: file.write("AttributeBegin # %s\n"%obj.getName())
  1088                 else: file.write("TransformBegin # %s\n"%obj.getName())
  1089                 file.write("\tTransform [%s %s %s %s  %s %s %s %s  %s %s %s %s  %s %s %s %s]\n"\
  1090                     %(matrix[0][0], matrix[0][1], matrix[0][2], matrix[0][3],\
  1091                       matrix[1][0], matrix[1][1], matrix[1][2], matrix[1][3],\
  1092                       matrix[2][0], matrix[2][1], matrix[2][2], matrix[2][3],\
  1093                         matrix[3][0], matrix[3][1], matrix[3][2], matrix[3][3]))
  1094                 col = obj.getData(mesh=1).col # data
  1095                 energy = obj.getData(mesh=1).energy # data
  1096                 if ltype == Lamp.Types["Lamp"]:
  1097                     if luxProp(Scene.GetCurrent(), "nolg", "false").get()!="true":
  1098                         file.write("LightGroup \"%s\"\n"%lightgroup.get())
  1099                     (str, link) = luxLamp("", "", obj, None, 0)
  1100                     file.write(str+"LightSource \"point\""+link+"\n")
  1101                 if ltype == Lamp.Types["Spot"]:
  1102                     (str, link) = luxSpot("", "", obj, None, 0)
  1103                     file.write(str)
  1104                     proj = luxProp(obj, "light.usetexproj", "false")
  1105                     if luxProp(Scene.GetCurrent(), "nolg", "false").get()!="true":
  1106                         file.write("LightGroup \"%s\"\n"%lightgroup.get())
  1107                     if(proj.get() == "true"):
  1108                         file.write("Rotate 180 0 1 0\n")
  1109                         file.write("LightSource \"projection\" \"float fov\" [%f]"%(obj.getData(mesh=1).spotSize))
  1110                     else:
  1111                         file.write("LightSource \"spot\" \"point from\" [0 0 0] \"point to\" [0 0 -1] \"float coneangle\" [%f] \"float conedeltaangle\" [%f]"\
  1112                             %(obj.getData(mesh=1).spotSize*0.5, obj.getData(mesh=1).spotSize*0.5*obj.getData(mesh=1).spotBlend)) # data
  1113                     file.write(link+"\n")
  1114                 if ltype == Lamp.Types["Area"]:
  1115                     if luxProp(Scene.GetCurrent(), "nolg", "false").get()!="true":
  1116                         file.write("LightGroup \"%s\"\n"%lightgroup.get())
  1117                     file.write("\tAreaLightSource \"area\"")
  1118                     file.write(link)
  1119 #                    file.write(luxLight("", "", obj, None, 0))
  1120                     file.write("\n")
  1121                     areax = obj.getData(mesh=1).getAreaSizeX()
  1122                     # lamps "getAreaShape()" not implemented yet - so we can't detect shape! Using square as default
  1123                     # todo: ideasman42
  1124                     if (True): areay = areax
  1125                     else: areay = obj.getData(mesh=1).getAreaSizeY()
  1126                     file.write('\tShape "trianglemesh" "integer indices" [0 1 2 0 2 3] "point P" [-%(x)f %(y)f 0.0 %(x)f %(y)f 0.0 %(x)f -%(y)f 0.0 -%(x)f -%(y)f 0.0]\n'%{"x":areax/2, "y":areay/2})
  1127                 if ltype == Lamp.Types["Area"]: file.write("AttributeEnd # %s\n"%obj.getName())
  1128                 else: file.write("TransformEnd # %s\n"%obj.getName())
  1129                 file.write("\n")
  1130 
  1131 
  1132     #-------------------------------------------------
  1133     # exportVolumes(self, file)
  1134     # exports volumes to the file
  1135     #-------------------------------------------------
  1136     def exportVolumes(self, file):
  1137         #pb = exportProgressBar(len(self.volumes), self.mpb)
  1138         for [obj, matrix] in self.volumes:
  1139             #pb.counter('Exporting Volumes')
  1140             #print("volume: %s"%(obj.getName()))
  1141             file.write("# Volume: %s\n"%(obj.getName()))
  1142 
  1143             # trickery to obtain objectspace boundingbox AABB
  1144             mat = obj.matrixWorld.copy().invert()
  1145             bb = [vec * mat for vec in obj.getBoundBox()]
  1146             minx = miny = minz = 100000000000000.0
  1147             maxx = maxy = maxz = -100000000000000.0
  1148             for vec in bb:
  1149                 if (vec[0] < minx): minx = vec[0]
  1150                 if (vec[1] < miny): miny = vec[1]
  1151                 if (vec[2] < minz): minz = vec[2]
  1152                 if (vec[0] > maxx): maxx = vec[0]
  1153                 if (vec[1] > maxy): maxy = vec[1]
  1154                 if (vec[2] > maxz): maxz = vec[2]
  1155 
  1156             file.write("Transform [%s %s %s %s  %s %s %s %s  %s %s %s %s  %s %s %s %s]\n"\
  1157                 %(matrix[0][0], matrix[0][1], matrix[0][2], matrix[0][3],\
  1158                   matrix[1][0], matrix[1][1], matrix[1][2], matrix[1][3],\
  1159                   matrix[2][0], matrix[2][1], matrix[2][2], matrix[2][3],\
  1160                     matrix[3][0], matrix[3][1], matrix[3][2], matrix[3][3]))
  1161 
  1162             str_opt = (" \"point p0\" [%f %f %f] \"point p1\" [%f %f %f]"%(minx, miny, minz, maxx, maxy, maxz))
  1163             mats = getMaterials(obj)
  1164             if (len(mats)>0) and (mats[0]!=None) and (luxProp(mats[0], "type", "").get()=="boundvolume"):
  1165                 mat = mats[0]
  1166                 (str, link) = luxMaterialBlock("", "", "", mat, None, 0, str_opt)
  1167                 file.write("%s"%link)
  1168                 file.write("\n\n")
  1169 
  1170 
  1171 # Note - radiance - this is a work in progress
  1172 def luxFlashBlock(camObj):
  1173     str = ""
  1174     str += "CoordSysTransform \"camera\"\n"
  1175 
  1176     str += "Texture \"camflashtex\" \"color\" \"blackbody\" \"float temperature\" [5500.0]"
  1177     str += "AreaLightSource \"area\" \"texture L\" [\"camflashtex\"] \"float power\" [100.000000] \"float efficacy\" [17.000000] \"float gain\" [1.000000]\n"
  1178 
  1179     up = 10.0
  1180 
  1181     str += "Shape \"trianglemesh\" \"integer indices\" [ 0 1 2 0 2 3 ] \"point P\" [ 0.014 0.012 0.0   0.006 0.012 0.0   0.006 0.008 0.0   0.014 0.008 0.0 ]\n"
  1182 
  1183     return str
  1184 
  1185 class exportProgressBar(object):
  1186     totalSteps    = None
  1187     realSteps   = None
  1188     currentStep    = None
  1189     subMode     = False
  1190     counterStep = None
  1191     
  1192     def __init__(self, ts, other=None):
  1193         self.realSteps = ts
  1194         self.counterStep = 0
  1195         if other is None:
  1196             # Master progress indicator
  1197             self.totalSteps  = self.realSteps
  1198             self.currentStep = 0
  1199         else:
  1200             # sub-progress of another progress indicator
  1201             self.totalSteps  = self.realSteps * other.totalSteps
  1202             self.currentStep = self.realSteps * (other.currentStep-1)
  1203             self.subMode = True
  1204             
  1205     def __repr__(self):
  1206         return '<progress total:%i real:%i current:%i count:%i frac:%f>' % (self.totalSteps, self.realSteps, self.currentStep, self.counterStep, self.get_frac())
  1207             
  1208     def get_frac(self):
  1209         return float(self.currentStep) / float(self.totalSteps)
  1210     
  1211     def next(self, msg):
  1212         amt = self.get_frac()
  1213         Window.DrawProgressBar(amt, msg)
  1214         #print('%s %s'%(self,msg))
  1215         Blender.Redraw()
  1216         self.currentStep += 1
  1217         
  1218     def counter(self, prefix):
  1219         
  1220         amt = self.get_frac()
  1221         if self.subMode:
  1222             msg = '%s (%i/%i)' % (prefix, self.counterStep, self.realSteps)
  1223         else:
  1224             msg = '%s (%i/%i)' % (prefix, self.counterStep, self.totalSteps)
  1225         Window.DrawProgressBar(amt, msg)
  1226         #print('%s %s'%(self,msg))
  1227         Blender.Redraw()
  1228         self.currentStep += 1
  1229         self.counterStep += 1
  1230         
  1231     def finished(self):
  1232         if not self.subMode:
  1233             Window.DrawProgressBar(1.0, 'Finished')
  1234             Blender.Redraw()
  1235 
  1236 ######################################################
  1237 # EXPORT
  1238 ######################################################
  1239 
  1240 
  1241 
  1242 def save_lux(filename, unindexedname, anim_progress=None):
  1243     global meshlist, matnames, lxs_filename, geom_filename, geom_pfilename, mat_filename, mat_pfilename, vol_filename, vol_pfilename, LuxIsGUI
  1244     
  1245     if LuxIsGUI:
  1246         pb = exportProgressBar(12, anim_progress)
  1247     else:
  1248         pb = None
  1249     
  1250     global render_status_text
  1251     global render_status
  1252     render_status_text = 'Exporting...'
  1253     render_status = True
  1254 
  1255     print("LuxRender Export started...\n")
  1256     time1 = Blender.sys.time()
  1257     scn = Scene.GetCurrent()
  1258 
  1259     filepath = os.path.dirname(filename)
  1260     filebase = os.path.splitext(os.path.basename(filename))[0]
  1261 
  1262     lxs_filename = filename
  1263 
  1264     geom_filename = os.path.join(filepath, filebase + "-geom.lxo")
  1265     geom_pfilename = filebase + "-geom.lxo"
  1266 
  1267     mat_filename = os.path.join(filepath, filebase + "-mat.lxm")
  1268     mat_pfilename = filebase + "-mat.lxm"
  1269     
  1270     vol_filename = os.path.join(filepath, filebase + "-vol.lxv")
  1271     vol_pfilename = filebase + "-vol.lxv"
  1272 
  1273     ### Zuegs: initialization for export class
  1274     export = luxExport(Blender.Scene.GetCurrent(), pb)
  1275 
  1276     # check if a light is present
  1277     envtype = luxProp(scn, "env.type", "infinite").get()
  1278     skycomponent = luxProp(scn, "env.sunsky.skycomponent", "true").get()
  1279     suncomponent = luxProp(scn, "env.sunsky.suncomponent", "true").get()
  1280     if envtype == "sunsky":
  1281         sun = None
  1282         for obj in scn.objects:
  1283             if (obj.getType() == "Lamp") and ((obj.Layers & scn.Layers) > 0):
  1284                 if obj.getData(mesh=1).getType() == 1: # sun object # data
  1285                     sun = obj
  1286     if not(export.analyseScene()) and not(envtype == "infinite") and not(envtype == "infinitesample") \
  1287      and not (envtype == "sunsky" and sun != None and suncomponent == "true") \
  1288      and not (envtype == "sunsky" and skycomponent == "true"):
  1289         print("ERROR: No light source found")
  1290         Draw.PupMenu("ERROR: No light source found%t|OK%x1")
  1291         render_status_text = ''
  1292         render_status = False
  1293         Blender.Window.QRedrawAll()
  1294         del export
  1295         return False
  1296     
  1297     # check render region dimensions > 0
  1298     if scn.getRenderingContext().borderRender:
  1299         border = scn.getRenderingContext().border
  1300         if (border[0]==border[2]) or (border[1]==border[3]):
  1301             Draw.PupMenu("ERROR: Empty render region, use SHIFT-B to set render region in Blender%t|OK%x1")
  1302             render_status_text = ''
  1303             render_status = False
  1304             Blender.Window.QRedrawAll()
  1305             del export
  1306             return False
  1307     
  1308     if LuxIsGUI: pb.next('Setting up Scene file')
  1309     
  1310     class output_proxy():
  1311         load_result = False
  1312         combine_all_output = False
  1313         f = None
  1314         def close(self):
  1315             if self.f is not None: self.f.close()
  1316         def write(self, str):
  1317             if self.f is not None:
  1318                 self.f.write(str)
  1319                 self.f.flush()
  1320             
  1321     class file_output(output_proxy):
  1322         def __init__(self,filename):
  1323             self.f = open(filename, "w")
  1324             
  1325     from threading import Thread
  1326     class pipe_output(output_proxy, Thread):
  1327         combine_all_output = True
  1328         
  1329         def __init__(self, xr,yr, haltspp, halttime, filename):
  1330             Thread.__init__(self)
  1331             
  1332             self.filename = filename
  1333             self.haltspp = haltspp
  1334             self.halttime = halttime
  1335             self.xr = xr
  1336             self.yr = yr
  1337             
  1338             if self.haltspp > 0 or self.halttime > 0:
  1339                 bintype = "luxconsole"
  1340                 self.load_result = True
  1341             else:
  1342                 bintype = "luxrender"
  1343                
  1344             print("pipe: using %s" % bintype)
  1345                 
  1346             self.p = get_lux_pipe(scn, 1, bintype)
  1347             self.f = self.p.stdin
  1348         def close(self):
  1349             global render_status_text
  1350             global render_status
  1351             render_status = True
  1352             render_status_text = "Rendering ..."
  1353             Blender.Window.QRedrawAll()
  1354             self.start()
  1355         
  1356         def run(self):
  1357             if self.load_result: self.data = self.p.communicate()[0]
  1358             self.f.close()
  1359             if self.load_result: # self.load_image()
  1360                 self.load_data()
  1361             print("LuxRender process finished")
  1362             self.update_status()
  1363             
  1364         def load_image(self):
  1365             i = Blender.Image.Load(self.filename)
  1366             i.makeCurrent()
  1367             i.reload()
  1368            
  1369         def load_data(self):
  1370             print("processing %i image bytes" % len(self.data))
  1371             i = Blender.Image.New('luxrender', self.xr, self.yr, 32)
  1372             raw_image = []
  1373             for j in self.data:
  1374                 raw_image.append(ord(j))
  1375             del self.data
  1376             bi = 0
  1377             for y in range(self.yr-1, -1, -1):
  1378                 for x in range(0, self.xr):
  1379                     i.setPixelI(x,y, raw_image[bi:bi+3]+[0])
  1380                     bi+=3
  1381             i.makeCurrent()
  1382             
  1383         def update_status(self):
  1384             global render_status_text
  1385             global render_status
  1386             render_status = False
  1387             render_status_text = "Rendering complete"
  1388             if self.haltspp > 0 or self.halttime > 0: render_status_text += ", check Image Editor window"
  1389             Blender.Window.QRedrawAll()
  1390             
  1391     use_pipe_output = luxProp(scn, "pipe", "false").get() == "true" and luxProp(scn, "run", "true").get() == "true"
  1392     
  1393     file = output_proxy()
  1394     
  1395     if luxProp(scn, "lxs", "true").get()=="true" or use_pipe_output:
  1396         ##### Determine/open files
  1397         if use_pipe_output:
  1398             print("using pipe output")
  1399             print("Exporting scene to pipe")
  1400             xr,yr = get_render_resolution(scn)
  1401             file = pipe_output(xr, yr,
  1402                 luxProp(scn, "haltspp", 0).get(),
  1403                 luxProp(scn, "halttime", 0).get(),
  1404                 os.path.join(filepath, filebase + ".png")
  1405             )
  1406         else:
  1407             print("using file output")
  1408             print("Exporting scene to '" + filename + "'...\n")
  1409             file = file_output(filename)
  1410 
  1411         ##### Write Header ######
  1412         file.write("# LuxRender v%s Scene File\n"%__version__)
  1413         file.write("# Exported by LuxBlend Blender Exporter\n")
  1414         file.write("\n")
  1415     
  1416         ##### Write camera ######
  1417         #camObj = scn.getCurrentCamera()
  1418         camObj = scn.objects.camera
  1419 
  1420         if LuxIsGUI: pb.next('Exporting Camera')
  1421         if camObj:
  1422             print("processing Camera...")
  1423             cam = camObj.data
  1424             cammblur = luxProp(cam, "cammblur", "true")
  1425             usemblur = luxProp(cam, "usemblur", "false")
  1426 
  1427             matrix = camObj.getMatrix()
  1428 
  1429             motion = None
  1430             if(cammblur.get() == "true" and usemblur.get() == "true"):
  1431                 # motion blur
  1432                 frame = Blender.Get('curframe')
  1433                 Blender.Set('curframe', frame+1)
  1434                 m1 = 1.0*matrix # multiply by 1.0 to get a copy of original matrix (will be frame-independant) 
  1435                 Blender.Set('curframe', frame)
  1436                 if m1 != matrix:
  1437                     # Motion detected, write endtransform
  1438                     print("  motion blur")
  1439                     motion = m1
  1440                     pos = m1[3]
  1441                     forwards = -m1[2]
  1442                     target = pos + forwards
  1443                     up = m1[1]
  1444                     file.write("TransformBegin\n")
  1445                     file.write("   LookAt %f %f %f \n       %f %f %f \n       %f %f %f\n" % ( pos[0], pos[1], pos[2], target[0], target[1], target[2], up[0], up[1], up[2] ))
  1446                     file.write("   CoordinateSystem \"CameraEndTransform\"\n")
  1447                     file.write("TransformEnd\n\n")
  1448 
  1449             # Write original lookat transform
  1450             pos = matrix[3]
  1451             forwards = -matrix[2]
  1452             target = pos + forwards
  1453             up = matrix[1]
  1454             file.write("LookAt %f %f %f \n       %f %f %f \n       %f %f %f\n\n" % ( pos[0], pos[1], pos[2], target[0], target[1], target[2], up[0], up[1], up[2] ))
  1455             file.write(luxCamera(camObj.data, scn.getRenderingContext()))
  1456             if motion:
  1457                 file.write("\n   \"string endtransform\" [\"CameraEndTransform\"]")
  1458             file.write("\n")
  1459         file.write("\n")
  1460     
  1461         if LuxIsGUI: pb.next('Exporting Film Settings')
  1462         ##### Write film ######
  1463         file.write(luxFilm(scn))
  1464         file.write("\n")
  1465 
  1466         if LuxIsGUI: pb.next('Exporting Pixel Filter')
  1467         ##### Write Pixel Filter ######
  1468         file.write(luxPixelFilter(scn))
  1469         file.write("\n")
  1470     
  1471         if LuxIsGUI: pb.next('Exporting Sampler')
  1472         ##### Write Sampler ######
  1473         file.write(luxSampler(scn))
  1474         file.write("\n")
  1475     
  1476         if LuxIsGUI: pb.next('Exporting Surface Integrator')
  1477         ##### Write Surface Integrator ######
  1478         file.write(luxSurfaceIntegrator(scn))
  1479         file.write("\n")
  1480         
  1481         if LuxIsGUI: pb.next('Exporting Volume Integrator')
  1482         ##### Write Volume Integrator ######
  1483         file.write(luxVolumeIntegrator(scn))
  1484         file.write("\n")
  1485         
  1486         if LuxIsGUI: pb.next('Exporting Accelerator')
  1487         ##### Write Acceleration ######
  1488         file.write(luxAccelerator(scn))
  1489         file.write("\n")    
  1490     
  1491         ########## BEGIN World
  1492         file.write("\n")
  1493         file.write("WorldBegin\n")
  1494         file.write("\n")
  1495 
  1496         ########## World scale
  1497         #scale = luxProp(scn, "global.scale", 1.0).get()
  1498         #if scale != 1.0:
  1499         #    # TODO: not working yet !!!
  1500         #    # TODO: propabily scale needs to be applyed on camera coords too 
  1501         #    file.write("Transform [%s 0.0 0.0 0.0  0.0 %s 0.0 0.0  0.0 0.0 %s 0.0  0.0 0.0 0 1.0]\n"%(scale, scale, scale))
  1502         #    file.write("\n")
  1503         
  1504         if LuxIsGUI: pb.next('Exporting Environment')
  1505         ##### Write World Background, Sunsky or Env map ######
  1506         env = luxEnvironment(scn)
  1507         if env != "":
  1508             # switch env type from infinite to infinitesample if there are no portals in the scene
  1509             if luxProp(scn, "env.type", "infinite").get() == "infinite" and not export.portals:
  1510                 luxProp(scn, "env.type", "infinite").set("infinitesample")
  1511                 env = luxEnvironment(scn)
  1512             
  1513             file.write("AttributeBegin\n")
  1514             file.write(env)
  1515             export.exportPortals(file)
  1516             file.write("AttributeEnd\n")
  1517             file.write("\n")    
  1518 
  1519     # Note - radiance - this is a work in progress
  1520 #        flash = luxFlashBlock(camObj)
  1521 #        if flash != "":
  1522 #            file.write("# Camera flash lamp\n")
  1523 #            file.write("AttributeBegin\n")
  1524 #            #file.write("CoordSysTransform \"camera\"\n")
  1525 #            file.write(flash)
  1526 #            file.write("AttributeEnd\n\n")
  1527 
  1528         #### Write material & geometry file includes in scene file
  1529         if not file.combine_all_output: file.write("Include \"%s\"\n\n" %(mat_pfilename))
  1530         if not file.combine_all_output: file.write("Include \"%s\"\n\n" %(geom_pfilename))
  1531         if not file.combine_all_output: file.write("Include \"%s\"\n\n" %(vol_pfilename))
  1532         
  1533     if luxProp(scn, "lxm", "true").get()=="true" or use_pipe_output:
  1534         if LuxIsGUI: pb.next('Exporting Materials')
  1535         ##### Write Material file #####
  1536         if not file.combine_all_output: print("Exporting materials to '" + mat_filename + "'...\n")
  1537         mat_file = open(mat_filename, 'w') if not file.combine_all_output else file
  1538         mat_file.write("")
  1539         export.exportMaterials(mat_file)
  1540         mat_file.write("")
  1541         export.exportNamedVolumes(mat_file)
  1542         mat_file.write("")
  1543         if not file.combine_all_output: mat_file.close()
  1544     
  1545     if luxProp(scn, "lxo", "true").get()=="true" or use_pipe_output:
  1546         if LuxIsGUI: pb.next('Exporting Geometry')
  1547         ##### Write Geometry file #####
  1548         if not file.combine_all_output: print("Exporting geometry to '" + geom_filename + "'...\n")
  1549         geom_file = open(geom_filename, 'w') if not file.combine_all_output else file
  1550         meshlist = []
  1551         geom_file.write("")
  1552         export.exportLights(geom_file)
  1553         export.exportMeshes(geom_file)
  1554         export.exportHairSystems(geom_file)
  1555         export.exportObjects(geom_file)
  1556         geom_file.write("")
  1557         if not file.combine_all_output: geom_file.close()
  1558 
  1559     if luxProp(scn, "lxv", "true").get()=="true" or use_pipe_output:
  1560         if LuxIsGUI: pb.next('Exporting Volumes')
  1561         ##### Write Volume file #####
  1562         if not file.combine_all_output: print("Exporting volumes to '" + vol_filename + "'...\n")
  1563         vol_file = open(vol_filename, 'w') if not file.combine_all_output else file
  1564         meshlist = []
  1565         vol_file.write("")
  1566         export.exportVolumes(vol_file)
  1567         vol_file.write("")
  1568         if not file.combine_all_output: vol_file.close()
  1569 
  1570     render_status_text = ''
  1571     render_status = False
  1572     Blender.Window.QRedrawAll()
  1573 
  1574     if luxProp(scn, "lxs", "true").get()=="true" or use_pipe_output:
  1575         #### Write End Tag
  1576         file.write("WorldEnd\n\n")
  1577         file.close()
  1578 
  1579     if LuxIsGUI: pb.finished()
  1580     print("Finished.\n")
  1581     del export
  1582     
  1583     time2 = Blender.sys.time()
  1584     print("Processing time: %f\n" %(time2-time1))
  1585 
  1586     if use_pipe_output:
  1587         #if luxProp(scn, "haltspp", 0).get() > 0:
  1588             # Wait for piped luxconsole render thread to end
  1589         file.join()
  1590 
  1591         # Don't launch it again as a piped scene is started implicitly
  1592         return False
  1593 
  1594     return True
  1595 
  1596 ########################################################################
  1597 ####  Construct server string argument
  1598 ########################################################################
  1599 
  1600 def networkstring(scn):
  1601     servers_string = ""
  1602     if (luxProp(scn,"network","false").get() == "true"):
  1603         if (luxProp(scn,"network_use_file","false").get() == "true"):
  1604             where_to_look = luxProp(scn,"network_file_path","false").get()
  1605             print "Reading rendering slaves list from the file:", where_to_look
  1606             try:
  1607                 f = open(where_to_look)
  1608                 for l in f:
  1609                     s = l.strip()
  1610                     if s and s[0] != '#':
  1611                         print "   adding slave:", s
  1612                         servers_string += " -u " + s
  1613                 f.close()
  1614             except:
  1615                 print "There was an error encountered while reading a file", where_to_look
  1616         elif luxProp(scn,"network_servers","").get():
  1617                  for server in luxProp(scn,"network_servers","").get().split(","):
  1618                     servers_string=servers_string+" -u "+ server
  1619     return servers_string
  1620 
  1621 
  1622 #########################################################################
  1623 ###     LAUNCH LuxRender AND RENDER CURRENT SCENE
  1624 #########################################################################
  1625 
  1626 def get_lux_exec(scn, type="luxrender"):
  1627     
  1628     #get blenders 'bpydata' directory
  1629     datadir=Blender.Get("datadir")
  1630     
  1631     ic = luxProp(scn, "lux", "").get()
  1632     ic = Blender.sys.dirname(ic) + os.sep + type
  1633     
  1634     if osys.platform == "win32": ic = ic + ".exe"
  1635     
  1636     if type=="luxrender" and osys.platform == "darwin": ic = ic + ".app/Contents/MacOS/luxrender"
  1637     
  1638     return ic
  1639     
  1640 def get_lux_args(filename, extra_args=[], anim=False):
  1641     ostype = osys.platform
  1642     scn = Scene.GetCurrent()
  1643     
  1644     ic = get_lux_exec(scn, type=(anim and 'luxconsole' or 'luxrender'))
  1645     
  1646     servers_string = networkstring(scn)
  1647     update_int=luxProp(scn,"network_interval",180).get()
  1648     
  1649     checkluxpath = luxProp(scn, "checkluxpath", True).get()
  1650     if checkluxpath:
  1651         if sys.exists(ic) != 1:
  1652             Draw.PupMenu("Error: LuxRender not found. Please set path on System page.%t|OK")
  1653             return
  1654     autothreads = luxProp(scn, "autothreads", "true").get()
  1655     threads = luxProp(scn, "threads", 1).get()
  1656     luxnice = luxProp(scn, "luxnice", 0).get()
  1657     noopengl = luxProp(scn, "noopengl", "false").get()
  1658     
  1659     if noopengl == "true" and not anim:
  1660         extra_args.append("--noopengl")
  1661     
  1662     lux_args = "\"%s\" " % ic
  1663     
  1664     extra_args.append('%s'%servers_string)
  1665     extra_args.append("-i %d " % update_int)
  1666     
  1667     if autothreads != "true":
  1668         extra_args.append("--threads=%d " % threads)
  1669     
  1670     lux_args2 = ' '.join(extra_args)
  1671     
  1672     if filename == '-':
  1673         lux_args2 = " - " + lux_args2
  1674     else:
  1675         filename = "\"%s\"" % filename
  1676         lux_args2 = lux_args2 + filename
  1677         
  1678     lux_args += lux_args2
  1679     
  1680     if ostype == "win32":
  1681         prio = ""
  1682         if luxnice > 15: prio = "/low"
  1683         elif luxnice > 5: prio = "/belownormal"
  1684         elif luxnice > -5: prio = "/normal"
  1685         elif luxnice > -15: prio = "/abovenormal"
  1686         else: prio = "/high"
  1687         
  1688         if not anim:
  1689             cmd = "start /b %s \"\" %s" % (prio, lux_args)
  1690         else:
  1691             # if animation/luxconsole, start minimised and wait for completion
  1692             cmd = "start /WAIT /MIN %s \"\" %s" % (prio, lux_args)
  1693         
  1694 #    if ostype == "linux2" or ostype == "darwin":
  1695     else:
  1696         if not anim:
  1697             cmd = "(nice -n %d %s)&"%(luxnice, lux_args)
  1698         else:
  1699             cmd = "(nice -n %d %s)"%(luxnice, lux_args)
  1700     
  1701     return cmd, lux_args2
  1702 
  1703 def get_lux_pipe(scn, buf = 1024, type="luxconsole"):
  1704     bin = "\"%s\"" % get_lux_exec(scn, type)
  1705     
  1706     print("piping to LuxRender binary: " + bin)
  1707     
  1708     PIPE = subprocess.PIPE
  1709     
  1710     cmd, raw_args = get_lux_args('-',
  1711         extra_args=['-b'] if type=="luxconsole" else []
  1712     )
  1713     
  1714     # dirty hack to filter "noopengl" option from luxconsole args
  1715     raw_args = raw_args.replace('--noopengl', '')
  1716 
  1717     return subprocess.Popen(bin + raw_args, shell=True, bufsize=buf, stdin=PIPE, stdout=PIPE, stderr=PIPE)
  1718 
  1719 def launchLux(filename):
  1720     cmd, raw_args = get_lux_args(filename, extra_args=[])
  1721     print("Running LuxRender:\n"+cmd)
  1722     os.system(cmd)
  1723 
  1724 def launchLuxWait(filename, anim=False):
  1725     ostype = osys.platform
  1726     cmd, raw_args = get_lux_args(filename, extra_args=[], anim=anim)
  1727     
  1728     if ostype == "win32":
  1729         os.system(cmd)
  1730     
  1731 #    if ostype == "linux2" or ostype == "darwin":
  1732     else:
  1733         subprocess.call(cmd,shell=True)
  1734 
  1735 #### SAVE ANIMATION ####
  1736 
  1737 
  1738 #def save_anim(filename):
  1739 #    global LuxIsGUI
  1740 #    scn = Scene.GetCurrent()
  1741 #    to = luxProp(scn, 'export.threaded', 'true').get()
  1742 #    run = luxProp(scn, "run", "true").get()
  1743 #    deflt = luxProp(scn, "default", "true").get()
  1744 #    if to == 'true' and run == 'true' and deflt == 'false':
  1745 #        import threading
  1746 #        anim_thread = threading.Thread(target=save_anim_real, args=(filename,True))
  1747 #        anim_thread.start()
  1748 #    else:
  1749 #        save_anim_real(filename)
  1750 
  1751 def save_anim(filename, as_thread=False):
  1752     if as_thread: print('SAR thread started')
  1753     global MatSaved, LuxIsGUI
  1754     
  1755     MatSaved = 0
  1756     startF = Blender.Get('staframe')
  1757     endF = Blender.Get('endframe')
  1758     scn = Scene.GetCurrent()
  1759 
  1760     Run = luxProp(scn, "run", "true").get()
  1761 
  1762     if Run == "true":
  1763         haltspp = luxProp(scn, "haltspp", 0).get()
  1764         halttime = luxProp(scn, "halttime", 0).get()
  1765         if haltspp == 0 and halttime == 0:
  1766             Draw.PupMenu("ERROR: You must set a limit for spp (Output->halt) or for time (Output->time) when doing animation and the 'run' flag is switched on")
  1767             if LuxIsGUI:
  1768                 Draw.Redraw()
  1769             return
  1770 
  1771     print("\n\nRendering animation (frame %i to %i)\n\n"%(startF, endF))
  1772 
  1773     v_frame = Blender.Get('curframe')
  1774     
  1775     pb = None
  1776     if LuxIsGUI: pb = exportProgressBar(endF-startF +1)
  1777 
  1778     for i in range (startF, endF+1):
  1779         if LuxIsGUI: pb.next('Exporting frame %d\n'%i)
  1780         # Seems to get stuck unless we redraw the UI
  1781 #        if LuxIsGUI:
  1782 #            Window.QRedrawAll()
  1783         Blender.Set('curframe', i)
  1784         print("Rendering frame %i"%(i))
  1785         Blender.Redraw()
  1786         frameindex = ("-%05d" % (i)) + ".lxs"
  1787         indexedname = sys.makename(filename, frameindex)
  1788         unindexedname = filename
  1789         luxProp(scn, "filename", Blender.Get("filename")).set(sys.makename(filename, "-%05d" %  (Blender.Get('curframe'))))
  1790 
  1791         if Run == "true":
  1792             if save_lux(filename, unindexedname, pb):
  1793                 launchLuxWait(filename, anim=True)
  1794         else:
  1795             save_lux(indexedname, unindexedname, pb)
  1796 
  1797         MatSaved = 1
  1798         # Seems to get stuck unless we redraw the UI
  1799 #        if LuxIsGUI:
  1800 #            Window.QRedrawAll()
  1801 
  1802     if LuxIsGUI: pb.finished()
  1803             
  1804     Blender.Set('curframe', v_frame)
  1805 
  1806     print("\n\nFinished Rendering animation\n")
  1807     if as_thread: print('SAR thread finished')
  1808 
  1809 #### SAVE STILL (hackish...) ####
  1810 
  1811 #import threading
  1812 #def save_still(filename):
  1813 #    global LuxIsGUI
  1814 #    scn = Scene.GetCurrent()
  1815 #    to = luxProp(scn, 'export.threaded', 'true').get()
  1816 #    if to == 'true' and luxProp(scn, "run", "true").get() == "true":
  1817 #        import threading
  1818 #        still_thread = threading.Thread(target=save_still_real, args=(filename,))
  1819 #        still_thread.start()
  1820 #    else:
  1821 #        save_still_real(filename)
  1822 
  1823 def save_still(filename):
  1824     global MatSaved, runRenderAfterExport
  1825     scn = Scene.GetCurrent()
  1826     luxProp(scn, "filename", Blender.Get("filename")).set(sys.makename(filename, ""))
  1827     MatSaved = 0
  1828     unindexedname = filename
  1829     # Seems to get stuck unless we redraw the UI
  1830 #    if LuxIsGUI:
  1831 #        Window.QRedrawAll()
  1832     if save_lux(filename, unindexedname):
  1833         if runRenderAfterExport and luxProp(scn, "pipe", "false").get() == "false": #(run == None and luxProp(scn, "run", "true").get() == "true") or run:
  1834             launchLux(filename)
  1835             
  1836     # Seems to get stuck unless we redraw the UI
  1837 #    if LuxIsGUI:
  1838 #        Window.QRedrawAll()
  1839 
  1840 
  1841 ######################################################
  1842 # Icons
  1843 ######################################################
  1844 
  1845 def base64value(char):
  1846     if 64 < ord(char) < 91: return ord(char)-65
  1847     if 96 < ord(char) < 123: return ord(char)-97+26
  1848     if 47 < ord(char) < 58: return ord(char)-48+52
  1849     if char == '+': return 62
  1850     return 63
  1851 
  1852 def decodeIconStr(s):
  1853     buf = BGL.Buffer(BGL.GL_BYTE, [16,16,4])
  1854     offset = 0
  1855     for y in range(16):
  1856         for x in range(16):
  1857             for c in range(4):
  1858                 buf[y][x][c] = int(base64value(s[offset])*4.048)
  1859                 offset += 1
  1860     return buf
  1861 
  1862 def decodeLogoStr(s):
  1863     buf = BGL.Buffer(BGL.GL_BYTE, [18,118,4])
  1864     offset = 0
  1865     for y in range(18):
  1866         for x in range(118):
  1867             for c in range(4):
  1868                 buf[y][x][c] = int(base64value(s[offset])*4.048)
  1869                 offset += 1
  1870     return buf
  1871 
  1872 def decodeArrowStr(s):
  1873     buf = BGL.Buffer(BGL.GL_BYTE, [22,22,4])
  1874     offset = 0
  1875     for y in range(22):
  1876         for x in range(22):
  1877             for c in range(4):
  1878                 buf[y][x][c] = int(base64value(s[offset])*4.048)
  1879                 offset += 1
  1880     return buf
  1881 
  1882 def decodeBarStr(s):
  1883     buf = BGL.Buffer(BGL.GL_BYTE, [17,138,4])
  1884     offset = 0
  1885     for y in range(17):
  1886         for x in range(138):
  1887             for c in range(4):
  1888                 buf[y][x][c] = int(base64value(s[offset])*4.048)
  1889                 offset += 1
  1890     return buf
  1891 
  1892 arrow_down = decodeArrowStr("///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///Q///G///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///3///e///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///V///////7///D///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///1///////////e///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///a///////////////7///C///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///B///5///////////////////c///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///f///////////////////////7///C///A///A///A///A///A///A///A///A///A///A///A///A///A///C///6///////////////////////////c///A///A///A///A///A///A///A///A///A///A///A///A///A///i///////////////////////////////6///C///A///A///A///A///A///A///A///A///A///A///A///G///9///////////////////////////////////e///A///A///A///A///A///A///A///A///A///A///I///n///////////////////////////////////////6///N///A///A///A///A///A///A///A///A///A///L///b///e///e///e///e///e///e///e///e///e///g///O///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A")
  1893 
  1894 arrow_right = decodeArrowStr("///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///L///I///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///b///n///G///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///e///////9///i///C///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///e///////////////6///f///B///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///e///////////////////////5///a///A///A///A///A///A///A///A///A///A///A///A///A///A///A///e///////////////////////////////1///V///A///A///A///A///A///A///A///A///A///A///A///A///e///////////////////////////////////////3///Q///A///A///A///A///A///A///A///A///A///A///e///////////////////////////////////7///e///G///A///A///A///A///A///A///A///A///A///A///e///////////////////////////7///e///D///A///A///A///A///A///A///A///A///A///A///A///A///e///////////////////7///c///C///A///A///A///A///A///A///A///A///A///A///A///A///A///A///e///////////6///c///C///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///g///6///e///C///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///O///N///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A")
  1895 
  1896 icon_luxblend = decodeLogoStr("///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A/gAA/gAA/gAA/gAA/gAA/gAA/gAa/gA5/gAZ/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A/gAA/gAA/gAA/gAA/gAA/gAA/gAj/gA//gAh/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A/gAA/gAA/gAA/gAA/gAA/gAA/gAC/gAO/gAC/gAB/gAS/gAQ/gAA/gAA/gAA/gAA/gAA///A///A///A/gAA/gAZ/gAu/gA7/gA//gA//gA//gA//gA//gA//gA//gAd/gAA/gAZ/gAu/gA//gA//gA//gA//gA//gA//gA3/gAm/gAI/gAE/gAz/gA//gA//gAZ/gAA/gAA/gAA/gAZ/gA//gA//gAm/gAR/gA//gA//gA//gA//gA//gA//gA//gA//gA//gA//gAz/gAd/gAE/gAA/gA//gA//gAd/gAA/gAI/gAm/gA3/gA//gA//gA//gAR/gAA/gAA/gAA/gAA/gAA/gAu/gA//gAu/gAA/gAA/gAA/gAA/gAA/gAA/gAd/gA//gA//gAA/gAE/gAd/gAz/gA//gA//gA//gA//gA//gA7/gAq/gAV/gAA///A///A///A///A///A///A/gAA/gAA/gAA/gAI/gAK/gAA/gAA/gAA/gAA/gAn/gA//gA//gAc/gAA/gAA/gAA/gAA///A///A///A/gAi/gA//gA//gA//gA//gA//gA//gA//gA//gA//gA//gAd/gAZ/gA//gA//gA//gA//gA//gA//gA//gA//gA//gA//gA7/gAE/gAE/gAz/gA//gA//gAR/gAA/gAZ/gA//gA//gAm/gAA/gAR/gA//gA//gA//gA//gA//gA//gA//gA//gA//gA//gA//gA//gAz/gAA/gA//gA//gAd/gAI/gA7/gA//gA//gA//gA//gA//gAR/gAA/gAA/gAA/gAA/gAA/gAu/gA//gAu/gAA/gAA/gAA/gAA/gAA/gAA/gAd/gA//gA//gAA/gAu/gA//gA//gA//gA//gA//gA//gA//gA//gA//gA//gAi///A///A///A///A///A///A/gAA/gAA/gAA/gAv/gA4/gAA/gAA/gAA/gAD/gA9/gA//gA//gAz/gAA/gAA/gAA/gAA///A///A///A/gA//gA//gAq/gAI/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAu/gA//gA3/gAI/gAA/gAA/gAA/gAA/gAA/gAd/gA//gA//gAR/gAA/gAM/gA7/gA//gA7/gAZ/gA//gA//gAz/gAA/gAA/gAR/gA//gA//gAR/gAA/gAA/gAA/gAA/gAA/gAA/gAE/gAd/gA//gA//gAM/gA//gA//gAd/gAd/gA//gA//gAd/gAI/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAu/gA//gAu/gAA/gAA/gAA/gAA/gAA/gAA/gAd/gA//gA//gAA/gA//gA//gAq/gAA/gAA/gAA/gAA/gAA/gAE/gAq/gA//gA////A///A///A///A///A///A/gAA/gAA/gAA/gAN/gAQ/gAA/gAA/gAA/gAA/gAs/gA//gA//gA+/gAs/gAp/gAZ/gAA///A///A///A/gA//gA//gAd/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAu/gA//gAu/gAA/gAA/gAA/gAA/gAA/gAA/gAR/gA//gA//gAR/gAA/gAA/gAM/gA7/gA//gA//gA//gAz/gAE/gAA/gAA/gAR/gA//gA//gAR/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAR/gA//gA//gAR/gA//gA//gAd/gAd/gA//gA//gAu/gAu/gAu/gAu/gAu/gAu/gAu/gAu/gAu/gAM/gAu/gA//gAu/gAA/gAA/gAA/gAA/gAA/gAA/gAd/gA//gA//gAA/gA//gA//gAd/gAA/gAA/gAA/gAA/gAA/gAA/gAd/gA//gA////A///A///A///A///A///A/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAI/gAA/gAE/gAZ/gAw/gA//gA//gA//gA//gAh///A///A///A/gA//gA//gAd/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAu/gA//gAu/gAA/gAA/gAA/gAA/gAA/gAA/gAR/gA//gA//gAR/gAA/gAA/gAA/gAR/gA//gA//gA//gAI/gAA/gAA/gAA/gAR/gA//gA//gAm/gAd/gAd/gAd/gAd/gAd/gAd/gAd/gA3/gA//gA3/gAA/gA//gA//gAd/gAd/gA//gA//gA//gA//gA//gA//gA//gA//gA//gA//gA//gAR/gAu/gA//gAu/gAA/gAA/gAA/gAA/gAA/gAA/gAd/gA//gA//gAA/gA//gA//gAd/gAA/gAA/gAA/gAA/gAA/gAA/gAd/gA//gA////A///A///A///A///A///A/gAl/gAL/gAA/gAA/gAA/gAA/gAf/gA+/gAd/gAA/gAA/gAT/gA//gA//gA//gA//gA6///A///A///A/gA//gA//gAd/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAu/gA//gAu/gAA/gAA/gAA/gAA/gAA/gAA/gAR/gA//gA//gAR/gAA/gAA/gAE/gAz/gA//gA//gA//gAz/gAE/gAA/gAA/gAR/gA//gA//gA//gA//gA//gA//gA//gA//gA//gA//gA//gA//gAd/gAA/gA//gA//gAd/gAd/gA//gA//gAR/gAR/gAR/gAR/gAR/gAR/gAd/gA//gA//gAR/gAu/gA//gAu/gAA/gAA/gAA/gAA/gAA/gAA/gAd/gA//gA//gAA/gA//gA//gAd/gAA/gAA/gAA/gAA/gAA/gAA/gAd/gA//gA////A///A///A///A///A///A/gAl/gAK/gAA/gAA/gAA/gAA/gAf/gA+/gAd/gAA/gAA/gAT/gA//gA//gA//gA//gA6///A///A///A/gA//gA//gAd/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAu/gA//gAu/gAA/gAA/gAA/gAA/gAA/gAA/gAR/gA//gA//gAR/gAA/gAA/gAz/gA//gA7/gAd/gA//gA//gAm/gAA/gAA/gAR/gA//gA//gAm/gAd/gAd/gAd/gAd/gAd/gAd/gAd/gAu/gA//gA//gAI/gA//gA//gAd/gAd/gA//gA//gAd/gAR/gAR/gAR/gAR/gAR/gAq/gA//gA//gAR/gAu/gA//gA7/gAZ/gAR/gAR/gAR/gAR/gAV/gAz/gA//gA//gAA/gA3/gA//gA7/gAi/gAd/gAd/gAd/gAd/gAd/gAu/gA//gA////A///A///A///A///A///A/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAI/gAA/gAE/gAa/gAw/gA//gA//gA//gA//gAg///A///A///A/gA//gA//gAd/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAu/gA//gAu/gAA/gAA/gAA/gAA/gAA/gAA/gAR/gA//gA//gAR/gAA/gAm/gA//gA7/gAM/gAA/gAZ/gA//gA//gAm/gAA/gAR/gA//gA//gAR/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAR/gA//gA//gAR/gA//gA//gAd/gAE/gAz/gA//gA//gA//gA//gA//gA//gA//gA//gA//gAz/gAA/gAV/gA//gA//gA//gA//gA//gA//gA//gA//gA//gA//gAi/gAA/gAV/gA7/gA//gA//gA//gA//gA//gA//gA//gA//gA//gA////A///A///A///A///A///A/gAA/gAA/gAA/gAO/gAR/gAA/gAA/gAA/gAA/gAt/gA//gA//gA+/gAs/gAq/gAZ/gAA///A///A///A/gA//gA//gAd/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAi/gAu/gAi/gAA/gAA/gAA/gAA/gAA/gAA/gAM/gAu/gAu/gAM/gAm/gA//gA7/gAM/gAA/gAA/gAA/gAm/gA//gA//gAd/gAR/gA//gA//gAd/gAR/gAR/gAR/gAR/gAR/gAR/gAR/gAq/gA//gA//gAM/gA//gA//gAd/gAA/gAA/gAZ/gAm/gAu/gAu/gAu/gAu/gAu/gAq/gAZ/gAA/gAA/gAA/gAI/gAd/gAu/gAu/gAu/gAu/gAu/gAu/gAi/gAV/gAA/gAA/gAA/gAE/gAV/gAd/gAd/gAd/gAd/gAd/gAd/gAu/gA//gA////A///A///A///A///A///A/gAA/gAA/gAA/gAv/gA4/gAA/gAA/gAA/gAD/gA9/gA//gA//gAz/gAA/gAA/gAA/gAA///A///A///A/gA//gA//gAd/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAR/gA//gA//gA//gA//gA//gA//gA//gA//gA//gA//gA//gA//gAm/gAA/gA//gA//gAd/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAd/gA//gA////A///A///A///A///A///A/gAA/gAA/gAA/gAI/gAK/gAA/gAA/gAA/gAA/gAn/gA//gA//gAc/gAA/gAA/gAA/gAA///A///A///A/gA//gA//gAd/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAM/gAu/gAu/gAu/gAu/gAu/gAu/gAu/gAu/gAu/gAu/gAm/gAR/gAA/gAA/gA//gA//gAd/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAd/gA//gA////A///A///A///A///A///A/gAA/gAA/gAA/gAA/gAA/gAA/gAC/gAO/gAC/gAB/gAS/gAP/gAA/gAA/gAA/gAA/gAA///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A/gAA/gAA/gAA/gAA/gAA/gAA/gAj/gA//gAh/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A/gAA/gAA/gAA/gAA/gAA/gAA/gAa/gA5/gAY/gAA/gAA/gAA/gAA/gAA/gAA/gAA/gAA///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A")
  1897 
  1898 
  1899 icon_blender = decodeIconStr("///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A27wA27wA27wA27wA27wAFFFGIIIsNNN5IIIsFFFG27wA27wA27wA27wA27wA///A27wA27wA27wA27wA27wAFFFmnnn9sss/kkk9FFFm27wA27wA27wA27wA27wA///A27wA27wA27wA27wA27wAEEEvwww/AAA/sss/EEEv27wA27wA27wA27wA27wA///A27wA27wA27wA27wA27wAFFFxzzz/xxx/vvv/FFFx27wA27wA27wA27wA27wA///A27wAGGGRLLLtKKK7KKK9JJJ/111/ppp/xxx/III/JJJ9JJJ7LLLtGGGR27wA///AGGGQPPP8xxx/444/vvv/555/333/999/zzz/xxx/jjj/nnn/nnn/OOO8GGGQ///ALLL2222/zzz/lll/+++/888/666/444/222/000/yyy/aaa/nnn/vvv/LLL2///AMMMxqqq/+++/ttt/////AAA/888/666/444/AAA/000/iii/zzz/nnn/MMMx///AGGGKLLLqKKK7ZZZ/yyy/yyy/yyy/888/vvv/ttt/rrr/VVV/JJJ7LLLqGGGK///A27wA27wA27wAJJJ1999+////sss5UUU8qqq5777/333+III127wA27wA27wA///A27wA27wA27wAHHHJMMMzUUU7GGGpHHHIGGGpSSS7MMMzHHHJ27wA27wA27wA///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A")
  1900 icon_col = decodeIconStr("///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A27wA27wA27wA27wAVIAPXKB5VIAS27wA27wA27wA27wA///A///A///A///A///A27wA27wA27wAVIAPXKB8shU/XLC9VIAS27wA27wA27wA///A///A///A///A///A27wA27wAVIAPXKB8ymU/7xd/0qb/XLC9VIAS27wA27wA///A///A///A///A///A27wAVIAPXKA8xkO/7uW/7wa/7xd/0qb/XLC9VIAS27wA///A///A///A///A///AVIAPXKA8xiJ/6rO/6sS/7uW/7wZ/7xd/0qa/XLC9VIAS///A///A///A///A///AXKA1ypd/+6z/6rO/6rO/6sS/7uW/7vZ/7xd/shT/XKB5///A///A///A///A///AVJAMYMC873w/+6z/6rO/6rO/6sS/7uV/ymT/XKB8VIAP///A///A///A///A///A27wAVJAMYMC873w/+6z/6rO/6rO/xkN/XKB8VIAP27wA///A///A///A///A///A27wA27wAVJAMYMC873w/+6z/xiJ/XKA8VIAP27wA27wA///A///A///A///A///A27wA27wA27wAVJAMYMC8xpc/XKA8VIAP27wA27wA27wA///A///A///A///A///A27wA27wA27wA27wAVJAMXKA1VIAP27wA27wA27wA27wA///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A")
  1901 icon_float = decodeIconStr("///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A27wA27wA27wA27wAMMMSOOO5MMMP27wA27wA27wA27wA///A///A///A///A///A27wA27wA27wAMMMSPPP9nnn/PPP8MMMP27wA27wA27wA///A///A///A///A///A27wA27wAMMMSPPP9ttt/333/vvv/PPP8MMMP27wA27wA///A///A///A///A///A27wAMMMSOOO9ppp/zzz/111/333/vvv/PPP8MMMP27wA///A///A///A///A///AMMMSOOO9lll/uuu/www/zzz/111/333/vvv/PPP8MMMP///A///A///A///A///AOOO5sss/666/sss/uuu/www/zzz/111/333/kkk/PPP1///A///A///A///A///AMMMPQQQ8444/666/ttt/uuu/www/zzz/ppp/OOO8MMMM///A///A///A///A///A27wAMMMPQQQ8444/666/ttt/uuu/mmm/OOO8MMMM27wA///A///A///A///A///A27wA27wAMMMPQQQ8444/555/jjj/OOO8MMMM27wA27wA///A///A///A///A///A27wA27wA27wAMMMPQQQ8ppp/OOO8MMMM27wA27wA27wA///A///A///A///A///A27wA27wA27wA27wAMMMPOOO1MMMM27wA27wA27wA27wA///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A")
  1902 icon_map2d = decodeIconStr("///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A27wA27wA27wA27wA27wAMMMUMMMzMMMzMMMU27wA27wA27wA27wA27wA///A///A27wA27wA27wANNNPMMMyYVQ/wnV/bbb/RRR/MMMyNNNP27wA27wA27wA///A///A27wAMMMLMMMtWUQ/vnZ/7vY/6rP/aaa/eee/ZZZ/PPP/MMMtMMML27wA///A///AMMMfTSQ/tnc/7yg/7uV/6qN/6qM/YYY/ZZZ/ddd/fff/YYY/OOO/MMMf///A///AMMM/71o/7wb/6sQ/rgK/dVG/6qM/YYY/ZZZ/bbb/ccc/fff/ggg/MMM////A///AMMM/92q/AAA/6rP/dVH/AAA/6qM/YYY/ZZZ/bbb/ccc/eee/iii/MMM////A///AMMM/93r/dWI/6rP/dVH/AAA/6qM/XXX/ZZZ/bbb/ccc/eee/iii/MMM////A///AMMM/94t/6sR/6rQ/6rO/6qN/6qM/XXX/ZZZ/bbb/ccc/eee/jjj/MMM////A///AMMM/94u/dWI/dVI/6rP/6rN/6qM/XXX/ZZZ/bbb/ccc/eee/kkk/MMM////A///AMMM/+5v/AAA/AAA/6rP/7vX/94t/xxx/ggg/bbb/ccc/eee/lll/MMM////A///AMMM/+5x/6sR/7xd/+6y/////////////////111/mmm/eee/mmm/MMM////A///AMMM/+72//96/////////////////////////////////666/vvv/MMM////A///AMMMiTTS/wuq/986/////////////////////////555/ppp/SSS/MMMi///A///A27wAMMMHMMMdMMM0aZX/0yu/+97/888/uuu/XXX/MMM0MMMdMMMH27wA///A///A27wA27wA27wA27wANNNLMMMhMMM3MMM3MMMhNNNL27wA27wA27wA27wA///A")
  1903 icon_map2dparam = decodeIconStr("///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A27wAQQQB27wA27wA27wA27wA27wA27wA27wA27wA27wA27wA27wA27wA///A///A27wAUUUwMMM9EEE3AAAvAAAlAAAbAAAI27wA27wA27wA27wA27wA27wA///A///A27wAeeeOVVV9OOO/MMM/CCC/AAA+AAA9AAAg27wA27wA27wA27wA27wA///A///A27wA27wAfffKWWW9ggg/mmm/TTT/AAA/AAA9AAAS27wA27wA27wA27wA///A///A27wA27wA27wAeeeXVVV9hhh/lll/TTT/BBB/BBB6AAAN27wA27wA27wA///A///A27wAAAAK27wA27wAdddgTTT8NNN/NNN/JJJ/VVV9EEE8AAAoAAAG27wA///A///A27wAAAAXAAAA27wA27wAeeeaVVV2QQQ/nnn+222/mmm/PPP9JGF8KGCX///A///A27wAAAAkAAAA27wA27wA27wA27wAVVVXYYY8+++/333/gec+ZPL+XOJq///A///A27wAAAAxAAAB27wA27wA27wA27wA27wAXXXiiii83219ofY8eUO/aQL2///A///A27wAAAA9AAAC27wA27wA27wA27wA27wAgggAWWVwmgc84yt/oeW/gWP1///A///ACCC6AAA/AAA/CCC627wA27wA27wA27wA27wAKFFDKGDzxsm52wq/peW2///A///AAAA/////////AAA/AAABAAAAAAAAAAAA27wA27wALFCFMHE31wr61uo5///A///AAAA/////////AAA/AAA+AAAzAAAmAAAZAAAM27wA27wAKFDJPLH6umez///A///ACCC6AAA/AAA/CCC627wA27wA27wA27wA27wA27wA27wA27wAKFCOOJFf///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A")
  1904 icon_map3dparam = decodeIconStr("27wA27wA27wA27wA27wA27wA3nIC6pMJ6pMJ3nIC27wA27wA27wA27wA27wA27wA27wA27wA27wA27wA27wA3nIC6qMj6qM/6qM/6qMj3nIC27wA27wA27wA27wA27wA27wA27wA27wA27wA27wA6pMJ6qM/////////6qM/6pMJ27wA27wA27wA27wA27wA27wA27wA27wA27wANNNOSQMz5qM/////////5qM/SQMzNNNO27wA27wA27wA27wA27wA27wAMMMIMMMrXXX/www/5wg/6qM/5qM/vnX/bbb/PPP/MMMrMMMI27wA27wA27wA27wAMMM1xxx/777/222/yyy/zxu/caY/bbb/ggg/iii/YYY/MMM127wA27wA27wA27wAMMM/+++/zzz/yyy/yyy/yyy/ZZZ/bbb/ddd/fff/kkk/MMM/27wA27wA27wA27wAMMM/////yyy/yyy/yyy/yyy/ZZZ/bbb/ddd/eee/lll/MMM/27wA27wA27wA27wAMMM/////yyy/yyy/yyy/yyy/ZZZ/bbb/ddd/eee/nnn/MMM/27wA27wA27wA3nICRPM//97/yyy/yyy/yyy/yyy/ZZZ/bbb/ddd/eee/rpm/RPM/3nIC27wA3nIC6qMj5qM/6qM/2ue/zzz/444/999/666/rrr/fff/tkU/5qM/5qM/6qMj3nIC6pMJ6qM/////////6qM/+96/////////////////985/6qM/////////6qM/6pMJ6pMJ6qM/////////6qM/+86/////////////////974/6qM/////////6qM/6pMJ3nIC6qMj6qM/6qM/pfM2PPP+mmm/555/000/hhh/PPP+pfM26qM/6qM/6qMj3nIC27wA3nIC6pMJ6pMJ3nICMMMEMMMaMMMwMMMwMMMaMMME3nIC6pMJ6pMJ3nIC27wA///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A")
  1905 icon_mat = decodeIconStr("///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A27wA27wA27wAVJAMXKBnXLB1WJA9XLB1XKBnVJAM27wA27wA27wA///A///A///A27wAVAAAWJBgYMD9ukW/1sc/5we/0qY/sgQ/XLB9WJBgVAAA27wA///A///A///A27wAWJBghXM96zk/8yf/7wa/7vY/7vZ/YUN/TQM/aPF9WJBg27wA///A///A///AVIALZNE970o/7wb/QNG/QNG/7vX/7vX/JHD/DDD/bXP/XKB9VIAL///A///A///AXKBpype/8zj/7vX/QNG/QNG/7vX/7vX/sjR/IGD/keS/rfQ/XKBp///A///A///AXLB36zp/7xc/7vX/7vX/7vX/7vX/7vX/7vX/7vX/7vZ/0qZ/XLB3///A///A///AVJA+95x/2rX/fYM/zoU/7vX/7vX/7vX/7vX/7vX/7vY/6wf/VJA+///A///A///AXKB361s/VTO/AAA/NKF/7vX/7vX/meP/IGD/JHD/tkU/1rc/XKB3///A///A///AXKBq0tj/cba/AAA/HGD/7vX/7vX/IGD/AAA/AAA/VTQ/ujW/XKBq///A///A///AVIAMaPG920w/RPN/meP/7vX/7vX/gaM/BAA/HHH/njd/YMD9VIAM///A///A///A27wAWKBilbS995y/91n/8xd/7vZ/7xc/4wh/4yn/iXM9WKBi27wA///A///A///A27wAQQABWKBiaOF9zsj/61s/95x/5zp/xpe/ZNE9WKBiQQAB27wA///A///A///A27wA27wA27wAVIAMXKBqXKB3VJA+XKB3XKBqVIAM27wA27wA27wA///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A")
  1906 icon_matmix = decodeIconStr("27wA27wA27wA27wA27wA27wA27wAMIFdUMG7WNF+WNF+SLG5LHFS27wA27wA///A27wA27wA27wA27wA27wASLGAOJGziYN/xmV/wmT/pgQ/jaN/YPH/NJGm27wA///A27wA27wA27wA27wA27wAMIFjlbR/9ye/6sQ/zlJ/sgJ/ofM/ngT/YPH/MIGT///A27wA27wA27wA27wA27wAXQJ/6xk/9xZ/6sQ/5qM/zlK/sfI/ofM/jaN/SLG5///A27wA27wA27wA27wAHHHGgXQ//4r/8xc/7vX/7tR/5pM/zlK/sgJ/pgQ/WMF+///A27wA27wA27wA27wAJOVVYbf/58//y27/wz3/7vY/7tS/4pM/ykJ/vmU/WMF////A27wAAAAALIGkTMG+NQU/Qcu/Sfz/Sfz/Wi1/wz4/7vZ/7sR/6sR/wmW/UMH8///ASLGAOJG1iYN/xmV/kns/Rfz/99+/++//Rfz/z27/8ye/9yc/9zg/iYN/LIFh///AMHFilbR/9ye/6sQ/jns/Rfz/////////Rfz/57///4q/6xk/lbR/NJG227wA///AYQJ96xk/9xZ/6sQ/orw/Tgz/Rfz/Rez/Qdw/Xaf/gXP/XPI/MIGoAAAA27wA///AgXQ//4r/8xc/7vX/7tR/nrw/jms/dhn/ein/WNG/GGGRAAAA27wA27wA27wA///AhYR//7x/91m/8xe/7vY/7tS/4pM/ykJ/vmU/WMF/GGGH27wA27wA27wA27wA///AbTM995x//+6/80k/8ye/7vZ/7sR/6sR/wmW/TMG+27wA27wA27wA27wA27wA///APIEitld///7//+6/91n/8ye/9yc/9zg/iYN/LIGk27wA27wA27wA27wA27wA///ARKGCRKFyskc/94v//7w//4q/6xk/lbR/OJG0AAAA27wA27wA27wA27wA27wA///A27wAQJECPIEibTL9hYQ/gXP/YQJ9MHEi27wA27wA27wA27wA27wA27wA27wA///A")
  1907 icon_tex = decodeIconStr("///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///AOOO6MMM/MMM/MMM/MMM/MMM/MMM/MMM/MMM/MMM/MMM/MMM/OOO6///A///A///AMMM/444/555/555/555/555/666/666/777/777/888/888/MMM////A///A///AMMM/555/mmm/TTT/aaa/xxx/111/222/222/QQQ/ZZZ/777/MMM////A///A///AMMM/333/DDD/AAA/AAA/YYY/zzz/111/xxx/AAA/AAA/nnn/MMM////A///A///AMMM/222/DDD/AAA/AAA/bbb/yyy/zzz/111/RRR/AAA/iii/MMM////A///A///AMMM/666/jjj/TTT/ddd/vvv/xxx/yyy/zzz/000/rrr/555/MMM////A///A///AMMM/666/rrr/sss/uuu/vvv/www/xxx/yyy/zzz/000/666/MMM////A///A///AMMM/666/qqq/iii/qqq/uuu/vvv/ppp/nnn/yyy/zzz/555/MMM////A///A///AMMM/777/jjj/AAA/RRR/sss/bbb/AAA/AAA/SSS/yyy/555/MMM////A///A///AMMM/888/mmm/LLL/ccc/rrr/QQQ/AAA/AAA/AAA/www/555/MMM////A///A///AMMM/888/nnn/ooo/ppp/qqq/jjj/HHH/DDD/XXX/www/555/MMM////A///A///AMMM/666/888/888/777/666/666/555/555/555/444/333/MMM////A///A///ANNN4NNN+NNN+NNN+NNN+NNN+NNN+NNN+NNN+NNN+NNN+NNN+OOO4///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A")
  1908 icon_texcol = decodeIconStr("///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///AWKA4VJA+VJA+VJA+VJA+VJA+VJA+VJA+VJA+VJA+VJA+VJA+WKA4///A///A///AVIA/82p/93r/93r/93s/93s/93s/93t/94u/94u/94w/95w/VIA////A///A///AVIA/93s/xoV/ZVM/icR/6wf/8zi/80k/80l/USN/daU/94v/VIA////A///A///AVIA/72r/FDC/AAA/AAA/eZP/8yf/8zh/3vg/AAA/AAA/olf/VIA////A///A///AVIA/50p/DCB/AAA/AAA/faO/8xd/8yf/8zh/SPK/AAA/jga/VIA////A///A///AVIA/94t/rhO/WRI/haN/5uY/7wb/8xd/8yf/7yg/tma/72r/VIA////A///A///AVIA/94u/6sQ/6tT/7uV/7uX/7vY/7wa/7xc/8ye/8yg/93s/VIA////A///A///AVIA/94u/6rO/ylO/5sS/7uU/7uW/1qV/yoW/7xc/8ye/93s/VIA////A///A///AVIA/+5w/zlL/DDD/bVK/6tS/mdN/AAA/AAA/YTK/7xc/93r/VIA////A///A///AVIA/+5x/3oL/NKD/mcK/6sQ/WQG/AAA/AAA/BAA/5uZ/93r/VIA////A///A///AVIA/+6z/6qM/6qM/6qM/6rO/ujM/IGC/BBA/aUJ/7vX/93r/VIA////A///A///AVIA/+5w/+6y/+5w/+4v/94t/93s/82r/93r/93r/93r/92p/VIA////A///A///AWJA6VJA/WJB/WJB/WJB/WJB/WJB/WJB/WJB/WJB/WJB/VJA/WJA6///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A")
  1909 icon_texmix = decodeIconStr("///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///APPP7ccc/ddd/ccc/bbb/bbb/ddd/eee/RRR9///A///A///A///A///A///A///AYYY+yyy/fff/qqq/000/111/jjj/sss/eee////A///A///A///A///A///A///Aaaa9XXX/AAA/III/rrr/xxx/LLL/GGG/VVV////A///A///A///A///A///A///AZZZ9hhh/JJJ/XXX/rrr/uuu/kkk/eee/YYY////A///A///A///A///A///A///AVYd/sv0/imq/nqu/rrr/ttt/vvv/000/bbb////A///APPP7ccc/ddd/ccc/Ycg/Qcu/Sfz/Sfz/Wi1/fin/RRR/bbb/yyy/bbb////A///AYYY+yyy/fff/qqq/x05/Rfz/99+/++//Rfz/PSX/AAA/AAA/uuu/bbb////A///Aaaa9XXX/AAA/III/orw/Rfz/////////Rfz/lpu/XXX/eee/000/ccc////A///AZZZ9hhh/JJJ/XXX/osw/Tgz/Rfz/Rez/Qdw/Wae/bbb9aaa9YYY9PPP7///A///AYYY9vvv/lll/ppp/rrr/pty/sw1/w06/Ych////A///A///A///A///A///A///AZZZ9sss/SSS/iii/hhh/RRR/bbb/yyy/bbb////A///A///A///A///A///A///AZZZ9rrr/JJJ/eee/SSS/AAA/AAA/uuu/bbb////A///A///A///A///A///A///AZZZ+111/ttt/uuu/ooo/XXX/eee/000/ccc////A///A///A///A///A///A///AOOO4aaa9aaa9ZZZ9ZZZ9bbb9aaa9YYY9PPP7///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A")
  1910 icon_texmixcol = decodeIconStr("///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///AaOE7mcS/ndT/mcS/lbS/kbS/ndU/neV/bQH9///A///A///A///A///A///A///AiYP+92o/niY/0tg//4p//6s/pme/wun/neV////A///A///A///A///A///A///AkZP9aYT/AAA/LJF/5vd//2j/OMH/GHH/eVN////A///A///A///A///A///A///AjYP9qlZ/OKE/haO/7wb/+zf/voZ/jgY/hYP////A///A///A///A///A///A///AXae/z27/qty/ux2/9wZ/+yc//1f//5o/lbS////A///AaOE7mcS/ndT/mcS/adh/Qcu/Sfz/Sfz/Wi1/lot/ZUK/leQ//3l/kbR////A///AiYP+92o/niY/0tg/25+/Rfz/99+/++//Rfz/TXc/AAA/BAA/9zg/lbS////A///AkZP9aYT/AAA/LJF/tx2/Rfz/////////Rfz/quz/bZU/lhX//6o/lcS////A///AjYP9qlZ/OKE/haO/uy3/Tgz/Rfz/Rez/Qdw/Ybg/laQ9laQ9iYP9aOE7///A///AhXP9/1f/6sQ/8vW/9wZ/wz4/y28/26//aej////A///A///A///A///A///A///AhXQ98xd/eWH/znR/xmR/ZUK/leQ//3l/kbR////A///A///A///A///A///A///AhYR97xb/TMA/xkL/dVG/AAA/BAA/9zg/lbS////A///A///A///A///A///A///AiYR+/7q//zb//0d/1tb/bZU/lhX//6o/lcS////A///A///A///A///A///A///AZNE4iZS9iZS9iYR9jZQ9laQ9laQ9iYP9aOE7///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A")
  1911 icon_texparam = decodeIconStr("///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A27wAOOO5GGG/BBB9AAA5AAAwAAAnAAAO27wA27wA27wA27wA27wA27wA///A875F27wAYYYZPPP/KKK/III/BBB/AAA/AAA/AAAxAAAB27wA27wA27wA27wA///AoooO875K27wAaaaTRRR/eee/lll/SSS/AAA/AAA/AAAk27wA27wA27wA27wA///AeeeX222V876J27wAbbbkSSS/iii/mmm/TTT/AAA/AAA/CCCW27wA27wA27wA///AXXXfxxxftttW887I27wAcccwSSS/OOO/PPP/III/RRR/CCC/CCC3CCCL27wA///ATTTmtttsQQQvbbbd887H27wAdddrVVV/PPP/hhh/222/lll/NNN/HFE/KFCo///APPPssss3HHH6NNNwZZZd988G27wA27wAXXXlXXX/999/333/jhg/ZPK/WOJ5///AMMMvsss/jjj1XXXxrrrf333R998F27xA27wAYYYvggg/554/meX/eUO/ZQL////AJJJyvvv/jjj/oooztttoyyyc444Q999E27xAfffAYXW7jeZ/4yt/pfX/gWP////AHHH0zzz/iii/jjj+oooytttnlllggggX+99D27xALFAFKGD9wql/2wr/peW////AFFF3333/HHH/QQQ/jjj9mmmyDDD8KKKxTTTe555D26xAIFDKMHE+0vq/1uo////ADDD6666/HHH/QQQ/jjj/kkk8DDD+BBB+JJJyrrrR+++C26xAKFDROKG/wog+///ABBB9555/777/333/000/www/rrr9bbb6fffv000Y555M///B26xAKFCYOKF0///ABBB5BBB9DDD6EEE4GGG1IIIzKKKxNNNtPPPmSSSfUUUXUUUODDDE26xA27wA///A")
  1912 
  1913 icon_emission = decodeIconStr("///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A27wA27wA27wAAAAgAAA/AAAg27wA27wA27wA///A///A///A///A///A///A///A27wAAAAFAAAxAAA/AAA/AAA/AAAxAAAF27wA///A///A///A///A///A///A///A27wAAAAZooo5////444/nnn/KKK2AAAZ27wA///A///A///A///A///A///A///A27wAAAALSSS/ggg/bbb/AAA/AAA/AAAL27wA///A///A///A///A///A///A///A27wAAAAYrrr/////777/nnn/KKJ+AAAZ27wA///A///A///A///A///A///A///A27wAPNBRTRI+kiX8ebQ+ebN8NLA+PNCP27wA///A///A///A///A///A///A///AQQABVRB1qlQ483g2qlR+81Z2pkO6VRB0QQAB///A///A///A///A///A///A///ATQBlieP685t361ezjcD+5ySx61c0dYG6TQBl///A///A///A///A///A///A///AVRA453x650gwhbB93vRthbB+4yXvwrX0VRA4///A///A///A///A///A///A///AVRA+++8941ow2xbs0tRp0tRp1vUr2yiyVRA+///A///A///A///A///A///A///AUQA48868/++999772yiszuYo2yhsvsdxVRA5///A///A///A///A///A///A///ATPBlqof6//////++64yy64yy7611cYK4TPBl///A///A///A///A///A///A///AKKABUQA1qnf59989//++6525ifT4UQA1KKAB///A///A///A///A///A///A///A27wAKKABSPBkUQA4VQA+UQA4SPBkKKAB27wA///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A")
  1914 
  1915 icon_spectex = decodeIconStr("///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///AAAATGGGzAAAiAAAA27wA27wA27wA27wA27wA27wA27wAAAADAAAjGGGxAAAT///AFFFy555/SBx/MA5+ASx9AhZ9ArC9AwA9WvA9xnA9/WA97AA/xBB/555/FFFz///AAAAUccc/ka1/MA6/ASx/AhZ/ArC/AwA/WvA/xnA//WA/9AA/1ff/SSS+AAAZ///A27wAMMM6ph2/MA6/Xi0/AhZ/ArC/AwA/WvA/xnA//WA/1bb/jjj/AAAY27wA///A27wABBBnpmv/ni6/lr1/AhZ/ArC/AwA/WvA/xnA//WA/6vv/SSS/AAAE27wA///A27wAAAAEGGG1PPP/SUY/Zsn/ArC/AwA/hyS/xnA//WA/5uu/DDDw27wA27wA///A27wA27wAAAABAAAEIII3oyw/ArC/WvW/syn/31u/3vr/nll/AAAa27wA27wA///A27wA27wA///A///AAAAnlus/BrE/v4v/TTT/kkk/444/PPP+AAAE27wA27wA///A27wA27wA27wA27wAAAAZnnn/444/555/GGG3AAAdEEExAAAM27wA27wA27wA///A27wA27wA27wA27wAAAAKaaa/555/zzz/AAAn27wA27wA27wA27wA27wA27wA///A27wA27wA27wA27wAAAAALLL8555/iii/AAAX27wA27wA27wA27wA27wA27wA///A27wA27wA27wA27wA27wAAAAPKKK6AAArAAAB27wA27wA27wA27wA27wA27wA///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A")
  1916 
  1917 icon_c_filter = decodeIconStr("///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///AAAASGGG1BBBsAAAW27wA27wA27wA27wA27wA27wA27wAAAAWBBBsGGGyAAAU///AHHHx555/333/ddd/AAAl27wA27wA27wA27wA27wAAAAlddd/333/555/FFFz///AAAAUMMM8eee/555/ccc/AAAT27wA27wA27wAAAATccc/555/eee/MMM8AAAV///A27wAAAAAAAAbfff/222/GGG1AAAA27wAAAAAGGG1222/fff/AAAbAAAA27wA///A27wA27wAAAAAFFFz222/hhh/AAAW27wAAAAWhhh/222/FFFzAAAA27wA27wA///A27wA27wA27wAAAAQccc/333/EEEz27wAEEEz333/ccc/AAAQ27wA27wA27wA///A27wA27wA27wA27wAGGG1444/aaa/AAAdaaa/444/GGG127wA27wA27wA27wA///A27wA27wA27wA27wAAAAakkk/000/UUU/000/kkk/AAAa27wA27wA27wA27wA///A27wA27wA27wA27wAAAACGGG1xxx/555/xxx/GGG1AAAC27wA27wA27wA27wA///A27wA27wA27wA27wA27wAAAAFAAAoJJJ1AAAoAAAF27wA27wA27wA27wA27wA///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A")
  1918 
  1919 icon_c_camera = decodeIconStr("///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A27wA27wA27wA27wAAAAAAAABAAABAAABAAABAAAA27wA27wA27wA27wA///A///ANNN6MMM/MMM/JJJ/MMM/LLL/LLL/LLL/LLL/MMM/MMM/MMM/MMM/OOO6///A///AMMM/vvv/ttt/ccc/mmm/jjj/ggg/hhh/jjj/ooo/sss/www/iii/MMM////A///AMMM/uuu/eee/RRR/XXX/ZZZ/mmm/xxx/ppp/ggg/jjj/ppp/eee/MMM////A///AMMM/ttt/aaa/OOO/WWW/rrr/aaa/TTT/jjj/zzz/hhh/lll/ccc/MMM////A///AMMM/sss/XXX/LLL/ggg/QQQ/HHH/KKK/QQQ/hhh/rrr/ggg/bbb/MMM////A///AMMM/rrr/VVV/JJJ/ooo/QQQ/TTT/III/JJJ/RRR/yyy/ddd/ZZZ/MMM////A///AMMM/sss/UUU/JJJ/eee/eee/www/RRR/EEE/VVV/ooo/ccc/ZZZ/MMM////A///AMMM/uuu/VVV/KKK/RRR/kkk/fff/QQQ/OOO/ooo/bbb/eee/ZZZ/MMM////A///AMMM/xxx/WWW/LLL/NNN/SSS/eee/ooo/hhh/YYY/YYY/ggg/ZZZ/MMM////A///AMMM/zzz/vvv/aaa/fff/VVV/OOO/PPP/RRR/bbb/mmm/sss/fff/NNN9///A///ANNN6MLJ/MJE/IHG/OOO+ggg/bbb/ccc/eee/jjj/NNN+MMM/NNN9MMMP///A///A27wAMHAl9jA/NIApMMMmWWW/888/////888/bbb/NNNmAAAA27wA27wA///A///A27wALGAPMHAoMHASMMMGSSS/777/////888/WWW/NNNF27wA27wA27wA///A///A27wA27wA27wA27wA27wARRRiPPP+QQQ/RRR+VVVi27wA27wA27wA27wA///A")
  1920 
  1921 icon_c_environment = decodeIconStr("///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///AGMV1HNV7HNV7HNV7HNV7HNV7HNV7HNV7HNV7HNV7HNV7GMV1///A///A///A///AHNV7y0u/z0u/y0t/xzs/wyr/wyq/vxp/uxo/twn/svm/HNV7///A///A///A///AIOW8341/tvm/qtj/qtj/qtj/qtj/qtj/qtj/qtj/two/HNV7///A///A///A///AINV8sts/cdc/qrp/uxp/qtj/qtj/qtj/qtj/qtj/tvo/HNU7///A///A///A///AGMV7svy/Ubh/VZb/ZZZ/xyt/ruk/qtj/qtj/rul/bcb/GLU7///A///A///A///AGMV7twz/Uci/Uci/Tbg/TUU/ssq/y0u/vxr/TVT/fko/GMV7///A///A///A///AGMV7vy0/Vdj/Zgl/Xfk/Uci/RWZ/TVV/PSU/Tag/hnr/GMV7///A///A///A///AGMV7wz1/gmq/023/txz/Xfk/Uci/Uci/Uci/Uci/jos/GMV7///A///A///A///AGMV7y02/jos/////023/Zgl/Uci/Uci/Uci/Uci/kpt/GMV7///A///A///A///AGMV7z23/ahm/jos/gmq/Vdj/Uci/Uci/Uci/Uci/mru/GMV7///A///A///A///AGMV7x02/023/y02/wz1/vy0/uxz/swy/rux/ptw/lqu/GMV7///A///A///A///AGMV1GMV7GMV7GMV7GMV7GMV7GMV7GMV7GMV7GMV7GMV7GMV1///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A")
  1922 
  1923 icon_c_sampler = decodeIconStr("///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A27wA27wA27wAMMMXSSS3MMMg27wA27wA27wA27wAMMMdTTT2MMMc27wA27wA27wA27wA27wAMMMSggg/////XXX+MMMB27wA27wA27wAUUU8////jjj/MMMT27wA27wA27wAMMMIYYY8+++/xxx/NNNuMMMCMMMCMMMCMMMCNNNqwww/////ZZZ9MMMJ27wAMMMASSS0666/+++/fff/bbb/bbb/eee/eee/bbb/bbb/ddd/999/777/SSS327wAMMMGjjj/////////////////////////////////////////////////lll/MMMI27wARRRz555/999/ccc/YYY/YYY+aaa+bbb+YYY+YYY+bbb/999/666/RRR2MMMB27wAMMMHWWW7999/yyy/NNNu27wA27wA27wA27wANNNtxxx/+++/YYY8MMMI27wAVVqARfzGOTZZeee/////WWW+QctHRfzLRfzLRfzGUUU8////hhh/OSYaRfzGVVqARfzGRezcRfz5PXj8STV5NPSiRezcRfz5Rfz5RezcNQThRST6PYk7Rfz5RezcRfzGRfzLRfz5////////Rfz5RfzMRfz5////////Rfz5RfzMRfz5////////Rfz5RfzLRfzLRfz5////////Rfz5RfzMRfz5////////Rfz5RfzMRfz5////////Rfz5RfzLRfzGRezcRfz5Rfz5RezcRfzKRezcRfz5Rfz5RezcRfzKRezcRfz5Rfz5RezcRfzGVVqARfzGRfzLRfzLRfzGVVqARfzGRfzLRfzLRfzGVVqARfzGRfzLRfzLRfzGVVqA///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A")
  1924 
  1925 icon_c_integrator = decodeIconStr("///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A27wA27wAAAAAEJPYHMT0MRY+GLS0EJPYAAAA27wA27wA27wA27wA27wA27wA27wA27wAAAVAEJPoVai/lr0/elv/Xeo/LRZ/EIPnDHOEAAVA27wA27wA27wA27wA27wA27wAEIPcZel/rw5/cir/NTb/SYi/PWh/MSb/QVd/KPW6EJPfAJSB27wA27wA27wAAAAAHMT4ty7/hmv/FKQyDGMXEJP7bhq/nt2/pv4/sy6/diq/EJQuIIQC27wA27wAFIQGRWd/u08/TYf/CFKQDGLfUai/flv/Zfp/SZj/bgp/rx6/fks/EJQuAJSB27wAAJSBINT7uz8/glt/EIOqGKQ4Xeo/SYi/KQY/SZk/IOW/Ydl/ty7/diq/EJPg27wA27wAEJPhflt/u08/Yel/JOW/QXi/HNV/TZi/Yfp/GLS6EJPuejr/tz8/HMT6AMMB27wAGGMCFLRxiow/u08/ciq/SZk/Yeo/gnw/Vaj/DINhDGJQQVd/u08/SXe/FIQG27wA27wAFJODFKRyhmu/u08/sy7/pv4/cir/FJQ7CGLTEIPudir/uz8/JOU6AMMB27wA27wA27wAFJODEJPjKPW8UZh/RXg/RYj/QXg/MSa/Zfo/pv4/diq/EJPg27wA27wA27wA27wA27wA27wAFKQDEIPLEJPtPVe/ahr/flv/jpz/diq/FJQtAJSB27wA27wA27wA27wA27wA27wA27wA27wAAMMBEJPeGLS5OUb/INU5EJPeAMMB27wA27wA27wA27wA27wA27wA27wA27wA27wA27wA27wAAAAAEIQEAAVA27wA27wA27wA27wA///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A")
  1926 
  1927 icon_c_volumeintegrator = decodeIconStr("///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A27wA27wA27wA27wAMMMAMMMWNNN8NNN9MMMWMMMA27wA27wA27wA27wA///A27wA27wAAAAAEJPYIMS3MRY/KOU/gik/ggg/TTT/MMMzMMMR27wA27wA27wA27wA27wAAAVAFJPtVai/lr0/elv/Xeo/LRZ/NQU/ggh/ddd/RRR/MMMvMMMN27wA27wA27wAHKOvZel/rw5/cir/NTb/SYi/PWh/MSb/QVd/LQW/TWZ/aaa/PPP/MMMh27wAAAAAIMS/ty7/hmv/OSX/gik/HMS/bhq/nt2/pv4/sy6/diq/MQV/hhh/MMM/27wAFIQGRWd/u08/TYf/lmn/bdf/Uai/flv/Zfp/SZj/bgp/rx6/fks/NRV/MMN/27wAAJSBINT/uz8/glt/TWa/LPU/Xeo/SYi/KQY/SZk/IOW/Ydl/ty7/diq/IKO/27wA27wAIKO/flt/u08/Yel/JOW/QXi/HNV/TZi/Yfp/INT/LOT/ejr/tz8/IMT/AMMB27wAMMM/SWb/iow/u08/ciq/SZk/Yeo/gnw/Vaj/PRU/XXY/QVd/u08/SXe/FIQG27wAMMM/899/PTY/hmu/u08/sy7/pv4/cir/HLR/UVX/KOT/dir/uz8/JOU/AMMB27wAMMM/////vww/WZd/MRX/UZh/RXg/RYj/QXg/MSa/Zfo/pv4/diq/IKO/27wA27wAMMM/////999/////999/123/VZd/PVe/ahr/flv/jpz/diq/RUa/MMN/27wA27wAMMMxRRR/rrr/888/////////+++/jmp/MRX/OUb/NRX/WYb/PQQ/MMMx27wA27wA27wANNNEMMMaMMMxWWW/www/+++/999/uuu/UUV/MMMxMMMaNNNE27wA27wA///A27wA27wA27wA27wANNNIMMMfMMM1MMM1MMMfNNNI27wA27wA27wA27wA///A")
  1928 
  1929 icon_help = decodeIconStr("///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A27wA27wA27wAAAAOGGGtFFF4HHH6GGG3GGGqAAAK27wA27wA27wA///A///A///A27wAAAABEEEnNNN7vvv/666/888/888/vvv/III7EEEgAAAA27wA///A///A///A27wAEEEmfff+333/333/333/lll/999/999/999/WWW8EEEd27wA///A///A///AAAAPSSS7333/zzz/111/xxx/III/+++/777/999/999/JJJ6AAAJ///A///A///AFFFtxxx/yyy/xxx/zzz/444/999/777/666/777/999/ppp/FFFh///A///A///AEEE4555/uuu/vvv/xxx/ttt/MMM/yyy/666/666/777/111/GGGy///A///A///AJJJ7666/sss/ttt/vvv/yyy/ttt/HHH/yyy/666/555/777/FFF5///A///A///ADDD3777/sss/qqq/lll/vvv/yyy/sss/EEE/777/444/xxx/GGGv///A///A///ADDDq000/xxx/iii/FFF/kkk/lll/hhh/HHH/555/333/kkk/DDDe///A///A///AAAAJNNN8999/rrr/iii/DDD/DDD/GGG/000/000/000/GGG6AAAE///A///A///A27wACCCcccc9999/yyy/ttt/sss/www/000/000/QQQ8CCCT27wA///A///A///A27wA27wACCCXMMM7www/444/777/000/ooo+III5BBBR27wA27wA///A///A///A27wA27wA27wAAAAFBBBbEEEsEEE1FFFqBBBZAAAD27wA27wA27wA///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A///A")
  1930 
  1931 
  1932 bar_spectrum = decodeBarStr("AAA/AAA/AAB/AAC/AAD/BAF/CAH/DAK/EAN/GAQ/HAU/JAX/LAb/MAf/OAj/PAm/QAq/RAt/SAv/SAx/SAz/SA1/SA3/SA4/SA5/RA6/PA6/OA6/MA6/IA5/CA4/AA3/AD2/AJ1/AN0/AQy/ATw/AVu/AYr/AZo/Abl/Adj/Aeg/Agd/Ahb/AiY/AjW/AlT/AmR/AnO/AoL/AqI/AqE/ArA/AsA/AtA/AuA/AvA/AvA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/CwA/OvA/UvA/ZuA/euA/htA/lsA/orA/rqA/tpA/woA/ymA/0lA/2jA/4hA/5fA/7eA/8cA/9aA/+YA//VA//TA//QA//NA//KA//FA/+AA/+AA/8AA/7AA/6AA/5AA/3AA/2AA/0AA/yAA/wAA/uAA/sAA/qAA/oAA/lAA/jAA/hAA/fAA/dAA/bAA/ZAA/YAA/WAA/UAA/SAA/RAA/QAA/OAA/NAA/MAA/LAA/KAA/JAA/IAA/HAA/GAA/FAA/FAA/EAA/DAA/DAA/CAA/CAA/CAA/AAA/AAA/AAB/AAC/AAD/BAF/CAH/DAK/EAN/GAQ/HAU/JAX/LAb/MAf/OAj/PAm/QAq/RAt/SAv/SAx/SAz/SA1/SA3/SA4/SA5/RA6/PA6/OA6/MA6/IA5/CA4/AA3/AD2/AJ1/AN0/AQy/ATw/AVu/AYr/AZo/Abl/Adj/Aeg/Agd/Ahb/AiY/AjW/AlT/AmR/AnO/AoL/AqI/AqE/ArA/AsA/AtA/AuA/AvA/AvA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/CwA/OvA/UvA/ZuA/euA/htA/lsA/orA/rqA/tpA/woA/ymA/0lA/2jA/4hA/5fA/7eA/8cA/9aA/+YA//VA//TA//QA//NA//KA//FA/+AA/+AA/8AA/7AA/6AA/5AA/3AA/2AA/0AA/yAA/wAA/uAA/sAA/qAA/oAA/lAA/jAA/hAA/fAA/dAA/bAA/ZAA/YAA/WAA/UAA/SAA/RAA/QAA/OAA/NAA/MAA/LAA/KAA/JAA/IAA/HAA/GAA/FAA/FAA/EAA/DAA/DAA/CAA/CAA/CAA/AAA/AAA/AAB/AAC/AAD/BAF/CAH/DAK/EAN/GAQ/HAU/JAX/LAb/MAf/OAj/PAm/QAq/RAt/SAv/SAx/SAz/SA1/SA3/SA4/SA5/RA6/PA6/OA6/MA6/IA5/CA4/AA3/AD2/AJ1/AN0/AQy/ATw/AVu/AYr/AZo/Abl/Adj/Aeg/Agd/Ahb/AiY/AjW/AlT/AmR/AnO/AoL/AqI/AqE/ArA/AsA/AtA/AuA/AvA/AvA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/CwA/OvA/UvA/ZuA/euA/htA/lsA/orA/rqA/tpA/woA/ymA/0lA/2jA/4hA/5fA/7eA/8cA/9aA/+YA//VA//TA//QA//NA//KA//FA/+AA/+AA/8AA/7AA/6AA/5AA/3AA/2AA/0AA/yAA/wAA/uAA/sAA/qAA/oAA/lAA/jAA/hAA/fAA/dAA/bAA/ZAA/YAA/WAA/UAA/SAA/RAA/QAA/OAA/NAA/MAA/LAA/KAA/JAA/IAA/HAA/GAA/FAA/FAA/EAA/DAA/DAA/CAA/CAA/CAA/AAA/AAA/AAB/AAC/AAD/BAF/CAH/DAK/EAN/GAQ/HAU/JAX/LAb/MAf/OAj/PAm/QAq/RAt/SAv/SAx/SAz/SA1/SA3/SA4/SA5/RA6/PA6/OA6/MA6/IA5/CA4/AA3/AD2/AJ1/AN0/AQy/ATw/AVu/AYr/AZo/Abl/Adj/Aeg/Agd/Ahb/AiY/AjW/AlT/AmR/AnO/AoL/AqI/AqE/ArA/AsA/AtA/AuA/AvA/AvA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/CwA/OvA/UvA/ZuA/euA/htA/lsA/orA/rqA/tpA/woA/ymA/0lA/2jA/4hA/5fA/7eA/8cA/9aA/+YA//VA//TA//QA//NA//KA//FA/+AA/+AA/8AA/7AA/6AA/5AA/3AA/2AA/0AA/yAA/wAA/uAA/sAA/qAA/oAA/lAA/jAA/hAA/fAA/dAA/bAA/ZAA/YAA/WAA/UAA/SAA/RAA/QAA/OAA/NAA/MAA/LAA/KAA/JAA/IAA/HAA/GAA/FAA/FAA/EAA/DAA/DAA/CAA/CAA/CAA/AAA/AAA/AAB/AAC/AAD/BAF/CAH/DAK/EAN/GAQ/HAU/JAX/LAb/MAf/OAj/PAm/QAq/RAt/SAv/SAx/SAz/SA1/SA3/SA4/SA5/RA6/PA6/OA6/MA6/IA5/CA4/AA3/AD2/AJ1/AN0/AQy/ATw/AVu/AYr/AZo/Abl/Adj/Aeg/Agd/Ahb/AiY/AjW/AlT/AmR/AnO/AoL/AqI/AqE/ArA/AsA/AtA/AuA/AvA/AvA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/CwA/OvA/UvA/ZuA/euA/htA/lsA/orA/rqA/tpA/woA/ymA/0lA/2jA/4hA/5fA/7eA/8cA/9aA/+YA//VA//TA//QA//NA//KA//FA/+AA/+AA/8AA/7AA/6AA/5AA/3AA/2AA/0AA/yAA/wAA/uAA/sAA/qAA/oAA/lAA/jAA/hAA/fAA/dAA/bAA/ZAA/YAA/WAA/UAA/SAA/RAA/QAA/OAA/NAA/MAA/LAA/KAA/JAA/IAA/HAA/GAA/FAA/FAA/EAA/DAA/DAA/CAA/CAA/CAA/AAA/AAA/AAB/AAC/AAD/BAF/CAH/DAK/EAN/GAQ/HAU/JAX/LAb/MAf/OAj/PAm/QAq/RAt/SAv/SAx/SAz/SA1/SA3/SA4/SA5/RA6/PA6/OA6/MA6/IA5/CA4/AA3/AD2/AJ1/AN0/AQy/ATw/AVu/AYr/AZo/Abl/Adj/Aeg/Agd/Ahb/AiY/AjW/AlT/AmR/AnO/AoL/AqI/AqE/ArA/AsA/AtA/AuA/AvA/AvA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/CwA/OvA/UvA/ZuA/euA/htA/lsA/orA/rqA/tpA/woA/ymA/0lA/2jA/4hA/5fA/7eA/8cA/9aA/+YA//VA//TA//QA//NA//KA//FA/+AA/+AA/8AA/7AA/6AA/5AA/3AA/2AA/0AA/yAA/wAA/uAA/sAA/qAA/oAA/lAA/jAA/hAA/fAA/dAA/bAA/ZAA/YAA/WAA/UAA/SAA/RAA/QAA/OAA/NAA/MAA/LAA/KAA/JAA/IAA/HAA/GAA/FAA/FAA/EAA/DAA/DAA/CAA/CAA/CAA/AAA/AAA/AAB/AAC/AAD/BAF/CAH/DAK/EAN/GAQ/HAU/JAX/LAb/MAf/OAj/PAm/QAq/RAt/SAv/SAx/SAz/SA1/SA3/SA4/SA5/RA6/PA6/OA6/MA6/IA5/CA4/AA3/AD2/AJ1/AN0/AQy/ATw/AVu/AYr/AZo/Abl/Adj/Aeg/Agd/Ahb/AiY/AjW/AlT/AmR/AnO/AoL/AqI/AqE/ArA/AsA/AtA/AuA/AvA/AvA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/CwA/OvA/UvA/ZuA/euA/htA/lsA/orA/rqA/tpA/woA/ymA/0lA/2jA/4hA/5fA/7eA/8cA/9aA/+YA//VA//TA//QA//NA//KA//FA/+AA/+AA/8AA/7AA/6AA/5AA/3AA/2AA/0AA/yAA/wAA/uAA/sAA/qAA/oAA/lAA/jAA/hAA/fAA/dAA/bAA/ZAA/YAA/WAA/UAA/SAA/RAA/QAA/OAA/NAA/MAA/LAA/KAA/JAA/IAA/HAA/GAA/FAA/FAA/EAA/DAA/DAA/CAA/CAA/CAA/AAA/AAA/AAB/AAC/AAD/BAF/CAH/DAK/EAN/GAQ/HAU/JAX/LAb/MAf/OAj/PAm/QAq/RAt/SAv/SAx/SAz/SA1/SA3/SA4/SA5/RA6/PA6/OA6/MA6/IA5/CA4/AA3/AD2/AJ1/AN0/AQy/ATw/AVu/AYr/AZo/Abl/Adj/Aeg/Agd/Ahb/AiY/AjW/AlT/AmR/AnO/AoL/AqI/AqE/ArA/AsA/AtA/AuA/AvA/AvA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/CwA/OvA/UvA/ZuA/euA/htA/lsA/orA/rqA/tpA/woA/ymA/0lA/2jA/4hA/5fA/7eA/8cA/9aA/+YA//VA//TA//QA//NA//KA//FA/+AA/+AA/8AA/7AA/6AA/5AA/3AA/2AA/0AA/yAA/wAA/uAA/sAA/qAA/oAA/lAA/jAA/hAA/fAA/dAA/bAA/ZAA/YAA/WAA/UAA/SAA/RAA/QAA/OAA/NAA/MAA/LAA/KAA/JAA/IAA/HAA/GAA/FAA/FAA/EAA/DAA/DAA/CAA/CAA/CAA/AAA/AAA/AAB/AAC/AAD/BAF/CAH/DAK/EAN/GAQ/HAU/JAX/LAb/MAf/OAj/PAm/QAq/RAt/SAv/SAx/SAz/SA1/SA3/SA4/SA5/RA6/PA6/OA6/MA6/IA5/CA4/AA3/AD2/AJ1/AN0/AQy/ATw/AVu/AYr/AZo/Abl/Adj/Aeg/Agd/Ahb/AiY/AjW/AlT/AmR/AnO/AoL/AqI/AqE/ArA/AsA/AtA/AuA/AvA/AvA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/CwA/OvA/UvA/ZuA/euA/htA/lsA/orA/rqA/tpA/woA/ymA/0lA/2jA/4hA/5fA/7eA/8cA/9aA/+YA//VA//TA//QA//NA//KA//FA/+AA/+AA/8AA/7AA/6AA/5AA/3AA/2AA/0AA/yAA/wAA/uAA/sAA/qAA/oAA/lAA/jAA/hAA/fAA/dAA/bAA/ZAA/YAA/WAA/UAA/SAA/RAA/QAA/OAA/NAA/MAA/LAA/KAA/JAA/IAA/HAA/GAA/FAA/FAA/EAA/DAA/DAA/CAA/CAA/CAA/AAA4AAA/AAB/AAC/AAD/BAF/CAH/DAK/EAN/GAQ/HAU/JAX/LAb/MAf/OAj/PAm/QAq/RAt/SAv/SAx/SAz/SA1/SA3/SA4/SA5/RA6/PA6/OA6/MA6/IA5/CA4/AA3/AD2/AJ1/AN0/AQy/ATw/AVu/AYr/AZo/Abl/Adj/Aeg/Agd/Ahb/AiY/AjW/AlT/AmR/AnO/AoL/AqI/AqE/ArA/AsA/AtA/AuA/AvA/AvA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/CwA/OvA/UvA/ZuA/euA/htA/lsA/orA/rqA/tpA/woA/ymA/0lA/2jA/4hA/5fA/7eA/8cA/9aA/+YA//VA//TA//QA//NA//KA//FA/+AA/+AA/8AA/7AA/6AA/5AA/3AA/2AA/0AA/yAA/wAA/uAA/sAA/qAA/oAA/lAA/jAA/hAA/fAA/dAA/bAA/ZAA/YAA/WAA/UAA/SAA/RAA/QAA/OAA/NAA/MAA/LAA/KAA/JAA/IAA/HAA/GAA/FAA/FAA/EAA/DAA/DAA/CAA/CAA/CAA4AAAsAAA/AAB/AAC/AAD/BAF/CAH/DAK/EAN/GAQ/HAU/JAX/LAb/MAf/OAj/PAm/QAq/RAt/SAv/SAx/SAz/SA1/SA3/SA4/SA5/RA6/PA6/OA6/MA6/IA5/CA4/AA3/AD2/AJ1/AN0/AQy/ATw/AVu/AYr/AZo/Abl/Adj/Aeg/Agd/Ahb/AiY/AjW/AlT/AmR/AnO/AoL/AqI/AqE/ArA/AsA/AtA/AuA/AvA/AvA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/CwA/OvA/UvA/ZuA/euA/htA/lsA/orA/rqA/tpA/woA/ymA/0lA/2jA/4hA/5fA/7eA/8cA/9aA/+YA//VA//TA//QA//NA//KA//FA/+AA/+AA/8AA/7AA/6AA/5AA/3AA/2AA/0AA/yAA/wAA/uAA/sAA/qAA/oAA/lAA/jAA/hAA/fAA/dAA/bAA/ZAA/YAA/WAA/UAA/SAA/RAA/QAA/OAA/NAA/MAA/LAA/KAA/JAA/IAA/HAA/GAA/FAA/FAA/EAA/DAA/DAA/CAA/CAA/CAAsAAAcAAA/AAB/AAC/AAD/BAF/CAH/DAK/EAN/GAQ/HAU/JAX/LAb/MAf/OAj/PAm/QAq/RAt/SAv/SAx/SAz/SA1/SA3/SA4/SA5/RA6/PA6/OA6/MA6/IA5/CA4/AA3/AD2/AJ1/AN0/AQy/ATw/AVu/AYr/AZo/Abl/Adj/Aeg/Agd/Ahb/AiY/AjW/AlT/AmR/AnO/AoL/AqI/AqE/ArA/AsA/AtA/AuA/AvA/AvA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/CwA/OvA/UvA/ZuA/euA/htA/lsA/orA/rqA/tpA/woA/ymA/0lA/2jA/4hA/5fA/7eA/8cA/9aA/+YA//VA//TA//QA//NA//KA//FA/+AA/+AA/8AA/7AA/6AA/5AA/3AA/2AA/0AA/yAA/wAA/uAA/sAA/qAA/oAA/lAA/jAA/hAA/fAA/dAA/bAA/ZAA/YAA/WAA/UAA/SAA/RAA/QAA/OAA/NAA/MAA/LAA/KAA/JAA/IAA/HAA/GAA/FAA/FAA/EAA/DAA/DAA/CAA/CAA/CAAcAAAKAAAzAAB/AAC/AAD/BAF/CAH/DAK/EAN/GAQ/HAU/JAX/LAb/MAf/OAj/PAm/QAq/RAt/SAv/SAx/SAz/SA1/SA3/SA4/SA5/RA6/PA6/OA6/MA6/IA5/CA4/AA3/AD2/AJ1/AN0/AQy/ATw/AVu/AYr/AZo/Abl/Adj/Aeg/Agd/Ahb/AiY/AjW/AlT/AmR/AnO/AoL/AqI/AqE/ArA/AsA/AtA/AuA/AvA/AvA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/CwA/OvA/UvA/ZuA/euA/htA/lsA/orA/rqA/tpA/woA/ymA/0lA/2jA/4hA/5fA/7eA/8cA/9aA/+YA//VA//TA//QA//NA//KA//FA/+AA/+AA/8AA/7AA/6AA/5AA/3AA/2AA/0AA/yAA/wAA/uAA/sAA/qAA/oAA/lAA/jAA/hAA/fAA/dAA/bAA/ZAA/YAA/WAA/UAA/SAA/RAA/QAA/OAA/NAA/MAA/LAA/KAA/JAA/IAA/HAA/GAA/FAA/FAA/EAA/DAA/DAA/CAA/CAAzCAAK///AAAAaAAB/AAC/AAD/BAF/CAH/DAK/EAN/GAQ/HAU/JAX/LAb/MAf/OAj/PAm/QAq/RAt/SAv/SAx/SAz/SA1/SA3/SA4/SA5/RA6/PA6/OA6/MA6/IA5/CA4/AA3/AD2/AJ1/AN0/AQy/ATw/AVu/AYr/AZo/Abl/Adj/Aeg/Agd/Ahb/AiY/AjW/AlT/AmR/AnO/AoL/AqI/AqE/ArA/AsA/AtA/AuA/AvA/AvA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/CwA/OvA/UvA/ZuA/euA/htA/lsA/orA/rqA/tpA/woA/ymA/0lA/2jA/4hA/5fA/7eA/8cA/9aA/+YA//VA//TA//QA//NA//KA//FA/+AA/+AA/8AA/7AA/6AA/5AA/3AA/2AA/0AA/yAA/wAA/uAA/sAA/qAA/oAA/lAA/jAA/hAA/fAA/dAA/bAA/ZAA/YAA/WAA/UAA/SAA/RAA/QAA/OAA/NAA/MAA/LAA/KAA/JAA/IAA/HAA/GAA/FAA/FAA/EAA/DAA/DAA/CAA/CAAa///A///A///AAABfAAC/AAD/BAF/CAH/DAK/EAN/GAQ/HAU/JAX/LAb/MAf/OAj/PAm/QAq/RAt/SAv/SAx/SAz/SA1/SA3/SA4/SA5/RA6/PA6/OA6/MA6/IA5/CA4/AA3/AD2/AJ1/AN0/AQy/ATw/AVu/AYr/AZo/Abl/Adj/Aeg/Agd/Ahb/AiY/AjW/AlT/AmR/AnO/AoL/AqI/AqE/ArA/AsA/AtA/AuA/AvA/AvA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/CwA/OvA/UvA/ZuA/euA/htA/lsA/orA/rqA/tpA/woA/ymA/0lA/2jA/4hA/5fA/7eA/8cA/9aA/+YA//VA//TA//QA//NA//KA//FA/+AA/+AA/8AA/7AA/6AA/5AA/3AA/2AA/0AA/yAA/wAA/uAA/sAA/qAA/oAA/lAA/jAA/hAA/fAA/dAA/bAA/ZAA/YAA/WAA/UAA/SAA/RAA/QAA/OAA/NAA/MAA/LAA/KAA/JAA/IAA/HAA/GAA/FAA/FAA/EAA/DAA/DAA/CAAf///A///A///A///A///AAACaAADzBAF/CAH/DAK/EAN/GAQ/HAU/JAX/LAb/MAf/OAj/PAm/QAq/RAt/SAv/SAx/SAz/SA1/SA3/SA4/SA5/RA6/PA6/OA6/MA6/IA5/CA4/AA3/AD2/AJ1/AN0/AQy/ATw/AVu/AYr/AZo/Abl/Adj/Aeg/Agd/Ahb/AiY/AjW/AlT/AmR/AnO/AoL/AqI/AqE/ArA/AsA/AtA/AuA/AvA/AvA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/CwA/OvA/UvA/ZuA/euA/htA/lsA/orA/rqA/tpA/woA/ymA/0lA/2jA/4hA/5fA/7eA/8cA/9aA/+YA//VA//TA//QA//NA//KA//FA/+AA/+AA/8AA/7AA/6AA/5AA/3AA/2AA/0AA/yAA/wAA/uAA/sAA/qAA/oAA/lAA/jAA/hAA/fAA/dAA/bAA/ZAA/YAA/WAA/UAA/SAA/RAA/QAA/OAA/NAA/MAA/LAA/KAA/JAA/IAA/HAA/GAA/FAA/FAA/EAA/DAAzDAAa///A///A///A///A///A///A///AAADKBAFcCAHsDAK4EAN/GAQ/HAU/JAX/LAb/MAf/OAj/PAm/QAq/RAt/SAv/SAx/SAz/SA1/SA3/SA4/SA5/RA6/PA6/OA6/MA6/IA5/CA4/AA3/AD2/AJ1/AN0/AQy/ATw/AVu/AYr/AZo/Abl/Adj/Aeg/Agd/Ahb/AiY/AjW/AlT/AmR/AnO/AoL/AqI/AqE/ArA/AsA/AtA/AuA/AvA/AvA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/AwA/CwA/OvA/UvA/ZuA/euA/htA/lsA/orA/rqA/tpA/woA/ymA/0lA/2jA/4hA/5fA/7eA/8cA/9aA/+YA//VA//TA//QA//NA//KA//FA/+AA/+AA/8AA/7AA/6AA/5AA/3AA/2AA/0AA/yAA/wAA/uAA/sAA/qAA/oAA/lAA/jAA/hAA/fAA/dAA/bAA/ZAA/YAA/WAA/UAA/SAA/RAA/QAA/OAA/NAA/MAA/LAA/KAA/JAA/IAA/HAA/GAA/FAA4FAAsEAAcDAAK///A///A///A///A")
  1933 
  1934 bar_blackbody = decodeBarStr("+LA/+LA/+MA/+NA/+OA/+QA/+RA/+SA/+TA/+VA/+WA/+XA/+ZA/+aA//bA//cA//eA//fA//gA//hA//iA//kA//lA//mA//nA//oA//oB//pD//pE//qF//qG//qH//rI//rJ//sK//sM//sN//tO//tP//uQ//uR//vS//vT//vU//wW//wX//xY//xZ//xa//yb//yc//zd//ze//zg//0h//0i//1j//1k//1l//2m//2n//3p//3q//4r//4s//4t//5u//5v//6w//6y//6y//70//71//82//83//84//95//96//+7//+9///9/////////////////+///++//++//++//++//9+//9+//99//99//89//89//89//88//88//78//78//78//78//67//67//67//67//57//57//56//56//56//46//46//46//45//35//35//35//35//24//24//24//24//24//13//13//13//13//03//03//03//02//z2//z2//z2//z2//02//+LA/+LA/+MA/+NA/+OA/+QA/+RA/+SA/+TA/+VA/+WA/+XA/+ZA/QQQ//bA//cA//eA//fA//gA//hA//iA//kA//lA//mA//nA//oA//oB//pD//pE//qF//qG//qH//rI//rJ/QQQ//sM//sN//tO//tP//uQ//uR//vS//vT//vU//wW//wX//xY//xZ//xa//yb//yc//zd//ze//zg//0h//0i//1j//1k//1l//2m//2n//3p//3q//4r//4s//4t//5u//5v//6w//6y//6y//70//71//82//83//84//95//96//+7//+9///9/////////////////QQQ/++//++//++//++//9+//9+//99//99//89//89//89//88//88//78//78//78//78//67//67//67//67//57//57//56//56//56//46//46//46//45//35//35//35//35//24//24//24//24//24//13//13//13//13//03//03//03//02//z2//z2//z2//z2//02//+LA/+LA/+MA/+NA/+OA/+QA/+RA/+SA/+TA/+VA/+WA/+XA/+ZA/QQQ//bA//cA//eA//fA//gA//hA//iA//kA//lA//mA//nA//oA//oB//pD//pE//qF//qG//qH//rI//rJ/QQQ//sM//sN//tO//tP//uQ//uR//vS//vT//vU//wW//wX//xY//xZ//xa//yb//yc//zd//ze//zg//0h//0i//1j//1k//1l//2m//2n//3p//3q//4r//4s//4t//5u//5v//6w//6y//6y//70//71//82//83//84//95//96//+7//+9///9/////////////////QQQ/++//++//++//++//9+//9+//99//99//89//89//89//88//88//78//78//78//78//67//67//67//67//57//57//56//56//56//46//46//46//45//35//35//35//35//24//24//24//24//24//13//13//13//13//03//03//03//02//z2//z2//z2//z2//02//+LA/+LA/+MA/+NA/+OA/+QA/+RA/+SA/+TA/+VA/+WA/+XA/+ZA/QQQ//bA//cA//eA//fA//gA//hA//iA//kA//lA//mA//nA//oA//oB//pD//pE//qF//qG//qH//rI//rJ/QQQ//sM//sN//tO//tP//uQ//uR//vS//vT//vU//wW//wX//xY//xZ//xa//yb//yc//zd//ze//zg//0h//0i//1j//1k//1l//2m//2n//3p//3q//4r//4s//4t//5u//5v//6w//6y//6y//70//71//82//83//84//95//96//+7//+9///9/////////////////QQQ/++//++//++//++//9+//9+//99//99//89//89//89//88//88//78//78//78//78//67//67//67//67//57//57//56//56//56//46//46//46//45//35//35//35//35//24//24//24//24//24//13//13//13//13//03//03//03//02//z2//z2//z2//z2//02//+LA/+LA/+MA/+NA/+OA/+QA/+RA/+SA/+TA/+VA/+WA/+XA/+ZA/QQQ//bA//cA//eA//fA//gA//hA//iA//kA//lA//mA//nA//oA//oB//pD//pE//qF//qG//qH//rI//rJ/QQQ//sM//sN//tO//tP//uQ//uR//vS//vT//vU//wW//wX//xY//xZ//xa//yb//yc//zd//ze//zg//0h//0i//1j//1k//1l//2m//2n//3p//3q//4r//4s//4t//5u//5v//6w//6y//6y//70//71//82//83//84//95//96//+7//+9///9/////////////////QQQ/++//++//++//++//9+//9+//99//99//89//89//89//88//88//78//78//78//78//67//67//67//67//57//57//56//56//56//46//46//46//45//35//35//35//35//24//24//24//24//24//13//13//13//13//03//03//03//02//z2//z2//z2//z2//02//+LA/+LA/+MA/+NA/+OA/+QA/+RA/+SA/+TA/+VA/+WA/+XA/+ZA/+aA//bA//cA//eA//fA//gA//hA//iA//kA//lA//mA//nA//oA//oB//pD//pE//qF//qG//qH//rI//rJ//sK//sM//sN//tO//tP//uQ//uR//vS//vT//vU//wW//wX//xY//xZ//xa//yb//yc//zd//ze//zg//0h//0i//1j//1k//1l//2m//2n//3p//3q//4r//4s//4t//5u//5v//6w//6y//6y//70//71//82//83//84//95//96//+7//+9///9/////////////////+///++//++//++//++//9+//9+//99//99//89//89//89//88//88//78//78//78//78//67//67//67//67//57//57//56//56//56//46//46//46//45//35//35//35//35//24//24//24//24//24//13//13//13//13//03//03//03//02//z2//z2//z2//z2//02//+LA/+LA/+MA/+NA/+OA/+QA/+RA/+SA/+TA/+VA/+WA/+XA/+ZA/QQQ/QQQ/QQQ//eA//fA//gA//hA//iA//kA//lA//mA//nA//oA//oB//pD//pE//qF//qG//qH//rI//rJ/QQQ//sM//sN//tO//tP//uQ//uR//vS//vT//vU//wW//wX//xY//xZ//xa//yb//yc//zd//ze//zg//0h//0i//1j//1k//1l//2m//2n//3p//3q//4r//4s//4t//5u//5v//6w//6y//6y//70//71//82//83//84//95//96//+7//+9///9/////////////////QQQ/QQQ/QQQ/++//++//9+//9+//99//99//89//89//89//88//88//78//78//78//78//67//67//67//67//57//57//56//56//56//46//46//46//45//35//35//35//35//24//24//24//24//24//13//13//13//13//03//03//03//02//z2//z2//z2//z2//02//+LA/+LA/+MA/+NA/+OA/+QA/+RA/+SA/+TA/+VA/+WA/+XA/+ZA/QQQ//bA//cA//eA//fA//gA//hA//iA//kA//lA//mA//nA//oA//oB//pD//pE//qF//qG//qH//rI//rJ/QQQ//sM//sN//tO//tP//uQ//uR//vS//vT//vU//wW//wX//xY//xZ//xa//yb//yc//zd//ze//zg//0h//0i//1j//1k//1l//2m//2n//3p//3q//4r//4s//4t//5u//5v//6w//6y//6y//70//71//82//83//84//95//96//+7//+9///9/////////////////QQQ/++//QQQ/++//++//9+//9+//99//99//89//89//89//88//88//78//78//78//78//67//67//67//67//57//57//56//56//56//46//46//46//45//35//35//35//35//24//24//24//24//24//13//13//13//13//03//03//03//02//z2//z2//z2//z2//02//+LA/+LA/+MA/+NA/+OA/+QA/+RA/+SA/+TA/+VA/+WA/+XA/+ZA/QQQ//bA//cA//eA//fA//gA//hA//iA//kA//lA//mA//nA//oA//oB//pD//pE//qF//qG//qH//rI//rJ/QQQ//sM//sN//tO//tP//uQ//uR//vS//vT//vU//wW//wX//xY//xZ//xa//yb//yc//zd//ze//zg//0h//0i//1j//1k//1l//2m//2n//3p//3q//4r//4s//4t//5u//5v//6w//6y//6y//70//71//82//83//84//95//96//+7//+9///9/////////////////QQQ/++//QQQ/++//++//9+//9+//99//99//89//89//89//88//88//78//78//78//78//67//67//67//67//57//57//56//56//56//46//46//46//45//35//35//35//35//24//24//24//24//24//13//13//13//13//03//03//03//02//z2//z2//z2//z2//02//+LA4+LA/+MA/+NA/+OA/+QA/+RA/+SA/+TA/+VA/+WA/+XA/+ZA/+aA/QQQ/QQQ//eA//fA//gA//hA//iA//kA//lA//mA//nA//oA//oB//pD//pE//qF//qG//qH//rI//rJ/QQQ//sM//sN//tO//tP//uQ//uR//vS//vT//vU//wW//wX//xY//xZ//xa//yb//yc//zd//ze//zg//0h//0i//1j//1k//1l//2m//2n//3p//3q//4r//4s//4t//5u//5v//6w//6y//6y//70//71//82//83//84//95//96//+7//+9///9/////////////////QQQ/QQQ/++//++//++//9+//9+//99//99//89//89//89//88//88//78//78//78//78//67//67//67//67//57//57//56//56//56//46//46//46//45//35//35//35//35//24//24//24//24//24//13//13//13//13//03//03//03//02//z2//z2//z2//z2//02//+LAs+LA/+MA/+NA/+OA/+QA/+RA/+SA/+TA/+VA/+WA/+XA/+ZA/+aA//bA//cA//eA//fA//gA//hA//iA//kA//lA//mA//nA//oA//oB//pD//pE//qF//qG//qH//rI//rJ//sK//sM//sN//tO//tP//uQ//uR//vS//vT//vU//wW//wX//xY//xZ//xa//yb//yc//zd//ze//zg//0h//0i//1j//1k//1l//2m//2n//3p//3q//4r//4s//4t//5u//5v//6w//6y//6y//70//71//82//83//84//95//96//+7//+9///9/////////////////+///++//++//++//++//9+//9+//99//99//89//89//89//88//88//78//78//78//78//67//67//67//67//57//57//56//56//56//46//46//46//45//35//35//35//35//24//24//24//24//24//13//13//13//13//03//03//03//02//z2//z2//z2//z2//02//+LAc+LA/+MA/+NA/+OA/+QA/+RA/+SA/+TA/+VA/+WA/+XA/+ZA/+aA//bA//cA//eA//fA//gA//hA//iA//kA//lA//mA//nA//oA//oB//pD//pE//qF//qG//qH//rI//rJ//sK//sM//sN//tO//tP//uQ//uR//vS//vT//vU//wW//wX//xY//xZ//xa//yb//yc//zd//ze//zg//0h//0i//1j//1k//1l//2m//2n//3p//3q//4r//4s//4t//5u//5v//6w//6y//6y//70//71//82//83//84//95//96//+7//+9///9/////////////////+///++//++//++//++//9+//9+//99//99//89//89//89//88//88//78//78//78//78//67//67//67//67//57//57//56//56//56//46//46//46//45//35//35//35//35//24//24//24//24//24//13//13//13//13//03//03//03//02//z2//z2//z2//z2//02//+LAK+LAz+MA/+NA/+OA/+QA/+RA/+SA/+TA/+VA/+WA/+XA/+ZA/+aA//bA//cA//eA//fA//gA//hA//iA//kA//lA//mA//nA//oA//oB//pD//pE//qF//qG//qH//rI//rJ//sK//sM//sN//tO//tP//uQ//uR//vS//vT//vU//wW//wX//xY//xZ//xa//yb//yc//zd//ze//zg//0h//0i//1j//1k//1l//2m//2n//3p//3q//4r//4s//4t//5u//5v//6w//6y//6y//70//71//82//83//84//95//96//+7//+9///9/////////////////+///++//++//++//++//9+//9+//99//99//89//89//89//88//88//78//78//78//78//67//67//67//67//57//57//56//56//56//46//46//46//45//35//35//35//35//24//24//24//24//24//13//13//13//13//03//03//03//02//z2//z2//z2//z2//02//+LAA+LAa+MA/+NA/+OA/+QA/+RA/+SA/+TA/+VA/+WA/+XA/+ZA/+aA//bA//cA//eA//fA//gA//hA//iA//kA//lA//mA//nA//oA//oB//pD//pE//qF//qG//qH//rI//rJ//sK//sM//sN//tO//tP//uQ//uR//vS//vT//vU//wW//wX//xY//xZ//xa//yb//yc//zd//ze//zg//0h//0i//1j//1k//1l//2m//2n//3p//3q//4r//4s//4t//5u//5v//6w//6y//6y//70//71//82//83//84//95//96//+7//+9///9/////////////////+///++//++//++//++//9+//9+//99//99//89//89//89//88//88//78//78//78//78//67//67//67//67//57//57//56//56//56//46//46//46//45//35//35//35//35//24//24//24//24//24//13//13//13//13//03//03//03//02//z2//z2//z2//z2//02//+LAA+LAA+MAf+NA/+OA/+QA/+RA/+SA/+TA/+VA/+WA/+XA/+ZA/+aA//bA//cA//eA//fA//gA//hA//iA//kA//lA//mA//nA//oA//oB//pD//pE//qF//qG//qH//rI//rJ//sK//sM//sN//tO//tP//uQ//uR//vS//vT//vU//wW//wX//xY//xZ//xa//yb//yc//zd//ze//zg//0h//0i//1j//1k//1l//2m//2n//3p//3q//4r//4s//4t//5u//5v//6w//6y//6y//70//71//82//83//84//95//96//+7//+9///9/////////////////+///++//++//++//++//9+//9+//99//99//89//89//89//88//88//78//78//78//78//67//67//67//67//57//57//56//56//56//46//46//46//45//35//35//35//35//24//24//24//24//24//13//13//13//13//03//03//03//02//z2//z2//z2//z2//02//+LAA+LAA+MAA+NAa+OAz+QA/+RA/+SA/+TA/+VA/+WA/+XA/+ZA/+aA//bA//cA//eA//fA//gA//hA//iA//kA//lA//mA//nA//oA//oB//pD//pE//qF//qG//qH//rI//rJ//sK//sM//sN//tO//tP//uQ//uR//vS//vT//vU//wW//wX//xY//xZ//xa//yb//yc//zd//ze//zg//0h//0i//1j//1k//1l//2m//2n//3p//3q//4r//4s//4t//5u//5v//6w//6y//6y//70//71//82//83//84//95//96//+7//+9///9/////////////////+///++//++//++//++//9+//9+//99//99//89//89//89//88//88//78//78//78//78//67//67//67//67//57//57//56//56//56//46//46//46//45//35//35//35//35//24//24//24//24//24//13//13//13//13//03//03//03//02//z2//z2//z2//z2//02//+LAA+LAA+MAA+NAA+OAK+QAc+RAs+SA4+TA/+VA/+WA/+XA/+ZA/+aA//bA//cA//eA//fA//gA//hA//iA//kA//lA//mA//nA//oA//oB//pD//pE//qF//qG//qH//rI//rJ//sK//sM//sN//tO//tP//uQ//uR//vS//vT//vU//wW//wX//xY//xZ//xa//yb//yc//zd//ze//zg//0h//0i//1j//1k//1l//2m//2n//3p//3q//4r//4s//4t//5u//5v//6w//6y//6y//70//71//82//83//84//95//96//+7//+9///9/////////////////+///++//++//++//++//9+//9+//99//99//89//89//89//88//88//78//78//78//78//67//67//67//67//57//57//56//56//56//46//46//46//45//35//35//35//35//24//24//24//24//24//13//13//13//13//03//03//03//02//z2//z2//z2//z2//02//")
  1935 
  1936 bar_equalenergy = decodeBarStr("AAA/AAA/AAA/AAA/BBB/BBB/BBB/BBB/CCC/CCC/CDC/DDD/DDD/DDD/EEE/EEE/EFF/FFF/FFF/GGG/GGG/HGG/HHH/HHH/III/III/JJJ/JJJ/KJK/KKK/KKK/LLL/LLL/MMM/MMM/NNN/NNN/OOO/OOO/PPP/PPP/QQQ/QQQ/RRR/RRR/SSS/TSS/TTT/UUU/UUU/VVV/VVV/WWW/WWW/XXX/XXY/YYY/ZZZ/ZZZ/aaa/aaa/bbb/cbb/ccc/ddd/ddd/eee/eee/fff/ffg/ggg/hhh/hhh/iii/iii/jjj/kkk/kkk/lll/lll/mmm/mnm/nnn/ono/ooo/ppp/ppp/qqq/qqq/rrr/rrr/sss/sss/ttt/utu/uuu/vvv/vvv/www/www/xxx/xxx/yyy/yyy/zzz/zzz/000/000/111/111/122/222/222/333/333/444/444/545/555/555/666/666/667/777/777/788/888/888/999/999/999/9++/+++/+++/+++/////////////AAA/AAA/AAA/AAA/BBB/BBB/BBB/BBB/CCC/CCC/CCC/DDD/DDD/DDD/EEE/EEE/EEF/FFF/FFF/GGG/GGG/GGH/HHH/HHH/III/III/JJJ/JJJ/KJJ/KKK/KKK/LLL/LLL/MMM/MMM/NNN/NNN/OOO/OOO/PPP/PPP/QQQ/QQQ/RRR/RSR/SSS/TTT/TTT/UUU/UUU/VVV/VVV/WWW/WWW/XXX/YXX/YYY/ZZZ/ZZZ/aaa/aaa/bbb/cbb/ccc/ddd/ddd/eee/eee/fff/ffg/ggg/hhh/hhh/iii/iii/jjj/kkj/kkk/lll/lll/mmm/mmn/nnn/ono/ooo/ppp/ppp/qqq/qqq/rrr/rrr/sss/stt/ttt/tuu/uuu/vvv/vvv/www/www/xxx/xxx/yyy/yyy/zzz/zzz/000/000/111/111/111/222/222/333/333/444/444/554/555/555/666/666/766/777/777/777/888/888/999/999/999/9++/+++/+++/+++/////////////AAA/AAA/AAA/AAA/BBB/BBB/BBB/BCC/CCC/CCC/DCC/DDD/DDD/DDD/EEE/EEE/FEF/FFF/FFF/GGG/GGG/GGG/HHH/HHH/III/III/JJJ/JJJ/KJJ/KKK/KLK/LLL/LLL/MMM/MMM/NNN/NNN/OOO/OOO/PPP/PPP/QQQ/RQQ/RRR/SRR/SSS/STT/TTT/UTU/UUU/VVV/VVV/WWW/WWW/XXX/XXX/YYY/ZZZ/ZZZ/aaa/aaa/bbb/cbb/ccc/ddd/ddd/eee/eee/fff/ggg/ggg/hhh/hhh/iii/iii/jjj/jjk/kkk/lll/lll/mmm/mmm/nnn/ooo/ooo/ppp/ppp/qqq/qqq/rrr/rrr/sss/tst/ttt/utt/uuu/vvv/vvv/www/www/xxx/xxx/yyy/yyy/zzz/zzz/000/000/111/111/122/222/222/333/333/444/444/555/555/555/666/666/667/777/777/888/888/888/999/999/999/++9/+++/+++/+++/////////////AAA/AAA/AAA/AAA/BBB/BBB/BBB/BBB/CCC/CCC/CCC/DDD/DDD/DDD/EEE/EEE/FEE/FFF/FFF/GGG/GGG/HGG/HHH/HHH/III/III/JJJ/JJJ/JKJ/KKK/KKK/LLL/LLL/MMM/MMM/NNN/NNN/OOO/OOO/PPP/PPP/QQQ/QRQ/RRR/RRS/SSS/TST/TTT/UUU/UUU/VVV/VVV/WWW/WWW/XXX/XYY/YYY/ZZZ/ZZZ/aaa/aaa/bbb/bcc/ccc/ddd/ddd/eee/eee/fff/gfg/ggg/hhh/hhh/iii/iii/jjj/jjk/kkk/lll/lll/mmm/mmm/nnn/onn/ooo/ppp/ppp/qqq/qqq/rrr/rrr/sss/tst/ttt/uuu/uuu/vvv/vvv/www/www/xxx/xxx/yyy/yyy/zzz/zzz/000/000/111/111/211/222/222/333/333/444/444/455/555/555/666/666/776/777/777/887/888/888/899/999/999/999/+++/+++/+++/////////////AAA/AAA/AAA/AAA/BBB/BBB/BBB/CBC/CCC/CCC/CCC/DDD/DDD/DDD/EEE/EEE/EEF/FFF/FFF/GGG/GGG/HHG/HHH/HHH/III/III/JJJ/JJJ/KKJ/KKK/KKK/LLL/LLL/MMM/MMM/NNN/NNN/OOO/OOO/PPP/PPP/QQQ/QQR/RRR/RRR/SSS/STT/TTT/UUU/UUU/VVV/VVV/WWW/WWW/XXX/XXX/YYY/ZZZ/ZZZ/aaa/aaa/bbb/bcb/ccc/ddd/ddd/eee/eee/fff/gff/ggg/hhh/hhh/iii/iii/jjj/jkk/kkk/lll/lll/mmm/mmm/nnn/noo/ooo/ppp/ppp/qqq/qqq/rrr/rrr/sss/sss/ttt/uuu/uuu/vvv/vvv/www/www/xxx/xxx/yyy/yyy/zzz/zzz/000/000/111/111/211/222/322/333/333/444/444/554/555/555/666/666/766/777/777/878/888/888/999/999/999/+9+/+++/+++/+++/////////////AAA/AAA/AAA/AAA/BBB/BBB/BBB/BBB/CCC/CCC/CCC/DDD/DDD/DDD/EEE/EEE/EEF/FFF/FFF/GGG/GGG/GGH/HHH/HHH/III/III/JJJ/JJJ/KJJ/KKK/KKK/LLL/LLL/MMM/MMM/NNN/NNN/OOO/OOO/PPP/PPP/QQQ/QQQ/RRR/RSR/SSS/TTT/TTT/UUU/UUU/VVV/VVV/WWW/WWW/XXX/YXX/YYY/ZZZ/ZZZ/aaa/aaa/bbb/cbb/ccc/ddd/ddd/eee/eee/fff/ffg/ggg/hhh/hhh/iii/iii/jjj/kkj/kkk/lll/lll/mmm/mmn/nnn/ono/ooo/ppp/ppp/qqq/qqq/rrr/rrr/sss/stt/ttt/tuu/uuu/vvv/vvv/www/www/xxx/xxx/yyy/yyy/zzz/zzz/000/000/111/111/111/222/222/333/333/444/444/554/555/555/666/666/766/777/777/777/888/888/999/999/999/9++/+++/+++/+++/////////////AAA/AAA/AAA/AAA/BBB/BBB/BBB/BCC/CCC/CCC/DCC/DDD/DDD/DDD/EEE/EEE/FEF/FFF/FFF/GGG/GGG/GGG/HHH/HHH/III/III/JJJ/JJJ/KJJ/KKK/KLK/LLL/LLL/MMM/MMM/NNN/NNN/OOO/OOO/PPP/PPP/QQQ/RQQ/RRR/SRR/SSS/STT/TTT/UTU/UUU/VVV/VVV/WWW/WWW/XXX/XXX/YYY/ZZZ/ZZZ/aaa/aaa/bbb/cbb/ccc/ddd/ddd/eee/eee/fff/ggg/ggg/hhh/hhh/iii/iii/jjj/jjk/kkk/lll/lll/mmm/mmm/nnn/ooo/ooo/ppp/ppp/qqq/qqq/rrr/rrr/sss/tst/ttt/utt/uuu/vvv/vvv/www/www/xxx/xxx/yyy/yyy/zzz/zzz/000/000/111/111/122/222/222/333/333/444/444/555/555/555/666/666/667/777/777/888/888/888/999/999/999/++9/+++/+++/+++/////////////AAA/AAA/AAA/AAA/BBB/BBB/BBB/BBB/CCC/CCC/CCC/DDD/DDD/DDD/EEE/EEE/FEE/FFF/FFF/GGG/GGG/HGG/HHH/HHH/III/III/JJJ/JJJ/JKJ/KKK/KKK/LLL/LLL/MMM/MMM/NNN/NNN/OOO/OOO/PPP/PPP/QQQ/QRQ/RRR/RRS/SSS/TST/TTT/UUU/UUU/VVV/VVV/WWW/WWW/XXX/XYY/YYY/ZZZ/ZZZ/aaa/aaa/bbb/bcc/ccc/ddd/ddd/eee/eee/fff/gfg/ggg/hhh/hhh/iii/iii/jjj/jjk/kkk/lll/lll/mmm/mmm/nnn/onn/ooo/ppp/ppp/qqq/qqq/rrr/rrr/sss/tst/ttt/uuu/uuu/vvv/vvv/www/www/xxx/xxx/yyy/yyy/zzz/zzz/000/000/111/111/211/222/222/333/333/444/444/455/555/555/666/666/776/777/777/887/888/888/899/999/999/999/+++/+++/+++/////////////AAA/AAA/AAA/AAA/BBB/BBB/BBB/CBC/CCC/CCC/CCC/DDD/DDD/DDD/EEE/EEE/EEF/FFF/FFF/GGG/GGG/HHG/HHH/HHH/III/III/JJJ/JJJ/KKJ/KKK/KKK/LLL/LLL/MMM/MMM/NNN/NNN/OOO/OOO/PPP/PPP/QQQ/QQR/RRR/RRR/SSS/STT/TTT/UUU/UUU/VVV/VVV/WWW/WWW/XXX/XXX/YYY/ZZZ/ZZZ/aaa/aaa/bbb/bcb/ccc/ddd/ddd/eee/eee/fff/gff/ggg/hhh/hhh/iii/iii/jjj/jkk/kkk/lll/lll/mmm/mmm/nnn/noo/ooo/ppp/ppp/qqq/qqq/rrr/rrr/sss/sss/ttt/uuu/uuu/vvv/vvv/www/www/xxx/xxx/yyy/yyy/zzz/zzz/000/000/111/111/211/222/322/333/333/444/444/554/555/555/666/666/766/777/777/878/888/888/999/999/999/+9+/+++/+++/+++/////////////GBA+AAA/AAA/AAA/BBB/BBB/BBB/BBB/CCC/CCC/CCC/DDD/DDD/DDD/EEE/EEE/EEF/FFF/FFF/GGG/GGG/GGH/HHH/HHH/III/III/JJJ/JJJ/KJJ/KKK/KKK/LLL/LLL/MMM/MMM/NNN/NNN/OOO/OOO/PPP/PPP/QQQ/QQQ/RRR/RSR/SSS/TTT/TTT/UUU/UUU/VVV/VVV/WWW/WWW/XXX/YXX/YYY/ZZZ/ZZZ/aaa/aaa/bbb/cbb/ccc/ddd/ddd/eee/eee/fff/ffg/ggg/hhh/hhh/iii/iii/jjj/kkj/kkk/lll/lll/mmm/mmn/nnn/ono/ooo/ppp/ppp/qqq/qqq/rrr/rrr/sss/stt/ttt/tuu/uuu/vvv/vvv/www/www/xxx/xxx/yyy/yyy/zzz/zzz/000/000/111/111/111/222/222/333/333/444/444/554/555/555/666/666/766/777/777/777/888/888/999/999/999/9++/+++/+++/+++/////////++/+OCA5AAA/AAA/AAA/BBB/BBB/BBB/BCC/CCC/CCC/DCC/DDD/DDD/DDD/EEE/EEE/FEF/FFF/FFF/GGG/GGG/GGG/HHH/HHH/III/III/JJJ/JJJ/KJJ/KKK/KLK/LLL/LLL/MMM/MMM/NNN/NNN/OOO/OOO/PPP/PPP/QQQ/RQQ/RRR/SRR/SSS/STT/TTT/UTU/UUU/VVV/VVV/WWW/WWW/XXX/XXX/YYY/ZZZ/ZZZ/aaa/aaa/bbb/cbb/ccc/ddd/ddd/eee/eee/fff/ggg/ggg/hhh/hhh/iii/iii/jjj/jjk/kkk/lll/lll/mmm/mmm/nnn/ooo/ooo/ppp/ppp/qqq/qqq/rrr/rrr/sss/tst/ttt/utt/uuu/vvv/vvv/www/www/xxx/xxx/yyy/yyy/zzz/zzz/000/000/111/111/122/222/222/333/333/444/444/555/555/555/666/666/667/777/777/888/888/888/999/999/999/++9/+++/+++/+++/////////89/5WEAsAAA/AAA/AAA/BBB/BBB/BBB/BBB/CCC/CCC/CCC/DDD/DDD/DDD/EEE/EEE/FEE/FFF/FFF/GGG/GGG/HGG/HHH/HHH/III/III/JJJ/JJJ/JKJ/KKK/KKK/LLL/LLL/MMM/MMM/NNN/NNN/OOO/OOO/PPP/PPP/QQQ/QRQ/RRR/RRS/SSS/TST/TTT/UUU/UUU/VVV/VVV/WWW/WWW/XXX/XYY/YYY/ZZZ/ZZZ/aaa/aaa/bbb/bcc/ccc/ddd/ddd/eee/eee/fff/gfg/ggg/hhh/hhh/iii/iii/jjj/jjk/kkk/lll/lll/mmm/mmm/nnn/onn/ooo/ppp/ppp/qqq/qqq/rrr/rrr/sss/tst/ttt/uuu/uuu/vvv/vvv/www/www/xxx/xxx/yyy/yyy/zzz/zzz/000/000/111/111/211/222/222/333/333/444/444/455/555/555/666/666/776/777/777/887/888/888/899/999/999/999/+++/+++/+++/////////78/scFASKCA9AAA/AAA/BBB/BBB/BBB/CBC/CCC/CCC/CCC/DDD/DDD/DDD/EEE/EEE/EEF/FFF/FFF/GGG/GGG/HHG/HHH/HHH/III/III/JJJ/JJJ/KKJ/KKK/KKK/LLL/LLL/MMM/MMM/NNN/NNN/OOO/OOO/PPP/PPP/QQQ/QQR/RRR/RRR/SSS/STT/TTT/UUU/UUU/VVV/VVV/WWW/WWW/XXX/XXX/YYY/ZZZ/ZZZ/aaa/aaa/bbb/bcb/ccc/ddd/ddd/eee/eee/fff/gff/ggg/hhh/hhh/iii/iii/jjj/jkk/kkk/lll/lll/mmm/mmm/nnn/noo/ooo/ppp/ppp/qqq/qqq/rrr/rrr/sss/sss/ttt/uuu/uuu/vvv/vvv/www/www/xxx/xxx/yyy/yyy/zzz/zzz/000/000/111/111/211/222/322/333/333/444/444/554/555/555/666/666/766/777/777/878/888/888/999/999/999/+9+/+++/+++/+++/////99/967/S///AXEApBAA/AAA/BBB/BBB/BBB/BBB/CCC/CCC/CCC/DDD/DDD/DDD/EEE/EEE/EEF/FFF/FFF/GGG/GGG/GGH/HHH/HHH/III/III/JJJ/JJJ/KJJ/KKK/KKK/LLL/LLL/MMM/MMM/NNN/NNN/OOO/OOO/PPP/PPP/QQQ/QQQ/RRR/RSR/SSS/TTT/TTT/UUU/UUU/VVV/VVV/WWW/WWW/XXX/YXX/YYY/ZZZ/ZZZ/aaa/aaa/bbb/cbb/ccc/ddd/ddd/eee/eee/fff/ffg/ggg/hhh/hhh/iii/iii/jjj/kkj/kkk/lll/lll/mmm/mmn/nnn/ono/ooo/ppp/ppp/qqq/qqq/rrr/rrr/sss/stt/ttt/tuu/uuu/vvv/vvv/www/www/xxx/xxx/yyy/yyy/zzz/zzz/000/000/111/111/111/222/222/333/333/444/444/554/555/555/666/666/766/777/777/777/888/888/999/999/999/9++/+++/+++/+++/////67/p///A///A///AVEAvBAA/BBB/BBB/BBB/BCC/CCC/CCC/DCC/DDD/DDD/DDD/EEE/EEE/FEF/FFF/FFF/GGG/GGG/GGG/HHH/HHH/III/III/JJJ/JJJ/KJJ/KKK/KLK/LLL/LLL/MMM/MMM/NNN/NNN/OOO/OOO/PPP/PPP/QQQ/RQQ/RRR/SRR/SSS/STT/TTT/UTU/UUU/VVV/VVV/WWW/WWW/XXX/XXX/YYY/ZZZ/ZZZ/aaa/aaa/bbb/cbb/ccc/ddd/ddd/eee/eee/fff/ggg/ggg/hhh/hhh/iii/iii/jjj/jjk/kkk/lll/lll/mmm/mmm/nnn/ooo/ooo/ppp/ppp/qqq/qqq/rrr/rrr/sss/tst/ttt/utt/uuu/vvv/vvv/www/www/xxx/xxx/yyy/yyy/zzz/zzz/000/000/111/111/122/222/222/333/333/444/444/555/555/555/666/666/667/777/777/888/888/888/999/999/999/++9/+++/+++/+++/78/v///A///A///A///A///AXFApKDA9BBB/BBB/BBB/CCC/CCC/CCC/DDD/DDD/DDD/EEE/EEE/FEE/FFF/FFF/GGG/GGG/HGG/HHH/HHH/III/III/JJJ/JJJ/JKJ/KKK/KKK/LLL/LLL/MMM/MMM/NNN/NNN/OOO/OOO/PPP/PPP/QQQ/QRQ/RRR/RRS/SSS/TST/TTT/UUU/UUU/VVV/VVV/WWW/WWW/XXX/XYY/YYY/ZZZ/ZZZ/aaa/aaa/bbb/bcc/ccc/ddd/ddd/eee/eee/fff/gfg/ggg/hhh/hhh/iii/iii/jjj/jjk/kkk/lll/lll/mmm/mmm/nnn/onn/ooo/ppp/ppp/qqq/qqq/rrr/rrr/sss/tst/ttt/uuu/uuu/vvv/vvv/www/www/xxx/xxx/yyy/yyy/zzz/zzz/000/000/111/111/211/222/222/333/333/444/444/455/555/555/666/666/776/777/777/887/888/888/899/999/999/999/+++/89+967/p///A///A///A///A///A///A///AdHASXGAsQFB5IDB+CCC/CCC/CCC/DDD/DDD/DDD/EEE/EEE/EEF/FFF/FFF/GGG/GGG/HHG/HHH/HHH/III/III/JJJ/JJJ/KKJ/KKK/KKK/LLL/LLL/MMM/MMM/NNN/NNN/OOO/OOO/PPP/PPP/QQQ/QQR/RRR/RRR/SSS/STT/TTT/UUU/UUU/VVV/VVV/WWW/WWW/XXX/XXX/YYY/ZZZ/ZZZ/aaa/aaa/bbb/bcb/ccc/ddd/ddd/eee/eee/fff/gff/ggg/hhh/hhh/iii/iii/jjj/jkk/kkk/lll/lll/mmm/mmm/nnn/noo/ooo/ppp/ppp/qqq/qqq/rrr/rrr/sss/sss/ttt/uuu/uuu/vvv/vvv/www/www/xxx/xxx/yyy/yyy/zzz/zzz/000/000/111/111/211/222/322/333/333/444/444/554/555/555/666/666/766/777/777/878/888/888/999/999/88++78+567+s56/S///A///A///A///A") 
  1937 
  1938 def drawIcon(icon, x, y):
  1939     BGL.glEnable(BGL.GL_BLEND)
  1940     BGL.glBlendFunc(BGL.GL_SRC_ALPHA, BGL.GL_ONE_MINUS_SRC_ALPHA) 
  1941     BGL.glRasterPos2f(int(x)+0.5, int(y)+0.5)
  1942     BGL.glDrawPixels(16, 16, BGL.GL_RGBA, BGL.GL_UNSIGNED_BYTE, icon)
  1943     BGL.glDisable(BGL.GL_BLEND)
  1944 
  1945 def drawArrow(icon, x, y):
  1946     BGL.glEnable(BGL.GL_BLEND)
  1947     BGL.glBlendFunc(BGL.GL_SRC_ALPHA, BGL.GL_ONE_MINUS_SRC_ALPHA) 
  1948     BGL.glRasterPos2f(int(x)+0.5, int(y)+0.5)
  1949     BGL.glDrawPixels(22, 22, BGL.GL_RGBA, BGL.GL_UNSIGNED_BYTE, icon)
  1950     BGL.glDisable(BGL.GL_BLEND)
  1951 
  1952 def drawLogo(icon, x, y):
  1953     BGL.glEnable(BGL.GL_BLEND)
  1954     BGL.glBlendFunc(BGL.GL_SRC_ALPHA, BGL.GL_ONE_MINUS_SRC_ALPHA) 
  1955     BGL.glRasterPos2f(int(x)+0.5, int(y)+0.5)
  1956     BGL.glDrawPixels(118, 18, BGL.GL_RGBA, BGL.GL_UNSIGNED_BYTE, icon)
  1957     BGL.glDisable(BGL.GL_BLEND)
  1958 
  1959 def drawBar(icon, x, y):
  1960     BGL.glEnable(BGL.GL_BLEND)
  1961     BGL.glBlendFunc(BGL.GL_SRC_ALPHA, BGL.GL_ONE_MINUS_SRC_ALPHA) 
  1962     BGL.glRasterPos2f(int(x)+0.5, int(y)+0.5)
  1963     BGL.glDrawPixels(138, 17, BGL.GL_RGBA, BGL.GL_UNSIGNED_BYTE, icon)
  1964     BGL.glDisable(BGL.GL_BLEND)
  1965 
  1966 
  1967 
  1968 #-------------------------------------------------
  1969 # luxImage()
  1970 # helper class to handle images and icons for the GUI
  1971 #-------------------------------------------------
  1972 
  1973 class luxImage:
  1974     def resize(self, width, height):
  1975         self.width = width
  1976         self.height = height
  1977         self.buf = BGL.Buffer(BGL.GL_BYTE, [width,height,4]) # GL buffer
  1978     def __init__(self, width=0, height=0):
  1979         self.resize(width, height)
  1980     def draw(self, x, y):
  1981         BGL.glEnable(BGL.GL_BLEND)
  1982         BGL.glBlendFunc(BGL.GL_SRC_ALPHA, BGL.GL_ONE_MINUS_SRC_ALPHA) 
  1983         BGL.glRasterPos2f(int(x)+0.5, int(y)+0.5)
  1984         BGL.glDrawPixels(self.width, self.height, BGL.GL_RGBA, BGL.GL_UNSIGNED_BYTE, self.buf)
  1985         BGL.glDisable(BGL.GL_BLEND)        
  1986     def decodeStr(self, width, height, s):
  1987         self.resize(width, height)
  1988         offset = 0
  1989         for y in range(self.height):
  1990             for x in range(self.width):
  1991                 for c in range(4):
  1992                     self.buf[y][x][c] = int(base64value(s[offset])*4.048)
  1993                     offset += 1
  1994 
  1995     def decodeLuxConsole(self, width, height, data):
  1996         self.resize(width, height)
  1997         offset = 0
  1998         for y in range(self.height-1,-1,-1):
  1999             for x in range(self.width):
  2000                 for c in range(3):
  2001                     self.buf[y][x][c] = ord(data[offset])
  2002                     offset += 1
  2003                 self.buf[y][x][3] = 255
  2004 
  2005 
  2006 previewCache = {}  # dictionary that will hold all preview images
  2007 
  2008 
  2009 ######################################################
  2010 # New GUI by Zuegs
  2011 ######################################################
  2012 
  2013 from types import *
  2014 
  2015 evtLuxGui = 99
  2016 evtSavePreset = 98
  2017 evtDeletePreset = 97
  2018 evtSaveMaterial = 96
  2019 evtLoadMaterial = 95
  2020 evtDeleteMaterial = 94
  2021 evtConvertMaterial = 92
  2022 evtSaveMaterial2 = 91
  2023 evtLoadMaterial2 = 90
  2024 
  2025 
  2026 # default settings
  2027 defaultsExclude = ['preset','filename','page','link','UID']
  2028 try:
  2029     luxdefaults = Blender.Registry.GetKey('luxblend', True)
  2030     if not(type(luxdefaults) is DictType):
  2031         luxdefaults = {}
  2032 except:
  2033     luxdefaults = {}
  2034 newluxdefaults = luxdefaults.copy()
  2035 
  2036 
  2037 def saveluxdefaults():
  2038     try: del newluxdefaults['page']
  2039     except: pass
  2040     try: Blender.Registry.SetKey('luxblend', newluxdefaults, True)
  2041     except: pass
  2042 
  2043 
  2044 
  2045 
  2046 
  2047 # *** PRESETS **************************************
  2048 presetsExclude = ['preset','lux','datadir','threads','filename','page','RGC','film.gamma','colorclamp','link']
  2049 def getPresets(key):
  2050     presets = Blender.Registry.GetKey(key, True)
  2051     if not(type(presets) is DictType):
  2052         presets = {}
  2053     return presets
  2054 def getScenePresets():
  2055     presets = getPresets('luxblend_presets').copy()
  2056 
  2057     # radiance's hardcoded render presets:
  2058 
  2059     presets['0 Preview - Global Illumination'] = {
  2060     'film.displayinterval': 4,
  2061     'haltspp': 0,
  2062     'halttime': 0,
  2063     'useparamkeys': 'false',
  2064     'sampler.showadvanced': 'false',
  2065     'sintegrator.showadvanced': 'false',
  2066     'pixelfilter.showadvanced': 'false',
  2067 
  2068     'sampler.type': 'lowdiscrepancy',
  2069     'sampler.lowdisc.pixelsamples': 1,
  2070     'sampler.lowdisc.pixelsampler': 'lowdiscrepancy',
  2071 
  2072     'sintegrator.type': 'distributedpath',
  2073     'sintegrator.distributedpath.directsampleall': 'true',
  2074     'sintegrator.distributedpath.directsamples': 1,
  2075     'sintegrator.distributedpath.directdiffuse': 'true',
  2076     'sintegrator.distributedpath.directglossy': 'true',
  2077     'sintegrator.distributedpath.indirectsampleall': 'false',
  2078     'sintegrator.distributedpath.indirectsamples': 1,
  2079     'sintegrator.distributedpath.indirectdiffuse': 'true',
  2080     'sintegrator.distributedpath.indirectglossy': 'true',
  2081     'sintegrator.distributedpath.diffusereflectdepth': 1,
  2082     'sintegrator.distributedpath.diffusereflectsamples': 4,
  2083     'sintegrator.distributedpath.diffuserefractdepth': 4,
  2084     'sintegrator.distributedpath.diffuserefractsamples': 1,
  2085     'sintegrator.distributedpath.glossyreflectdepth': 1,
  2086     'sintegrator.distributedpath.glossyreflectsamples': 2,
  2087     'sintegrator.distributedpath.glossyrefractdepth': 4,
  2088     'sintegrator.distributedpath.glossyrefractsamples': 1,
  2089     'sintegrator.distributedpath.specularreflectdepth': 2,
  2090     'sintegrator.distributedpath.specularrefractdepth': 4,
  2091     'sintegrator.distributedpath.causticsonglossy': 'true',
  2092     'sintegrator.distributedpath.causticsondiffuse': 'false',
  2093     'sintegrator.distributedpath.strategy': 'auto',
  2094 
  2095     'pixelfilter.type': 'mitchell',
  2096     'pixelfilter.mitchell.sharp': 0.250, 
  2097     'pixelfilter.mitchell.xwidth': 2.0, 
  2098     'pixelfilter.mitchell.ywidth': 2.0, 
  2099     'pixelfilter.mitchell.optmode': "slider" }
  2100 
  2101     presets['0b Preview - Direct Lighting'] = {
  2102     'film.displayinterval': 4,
  2103     'haltspp': 0,
  2104     'halttime': 0,
  2105     'useparamkeys': 'false',
  2106     'sampler.showadvanced': 'false',
  2107     'sintegrator.showadvanced': 'false',
  2108     'pixelfilter.showadvanced': 'false',
  2109 
  2110     'sampler.type': 'lowdiscrepancy',
  2111     'sampler.lowdisc.pixelsamples': 1,
  2112     'sampler.lowdisc.pixelsampler': 'lowdiscrepancy',
  2113 
  2114     'sintegrator.type': 'directlighting',
  2115     'sintegrator.dlighting.maxdepth': 5,
  2116 
  2117     'pixelfilter.type': 'mitchell',
  2118     'pixelfilter.mitchell.sharp': 0.3333,
  2119     'pixelfilter.mitchell.xwidth': 1.5,
  2120     'pixelfilter.mitchell.ywidth': 1.5,
  2121     'pixelfilter.mitchell.supersample': "true",
  2122     'pixelfilter.mitchell.optmode': "slider" }
  2123 
  2124     presets['1 Final - MLT/Bidir Path Tracing (interior) (recommended)'] =  {
  2125     'film.displayinterval': 8,
  2126     'haltspp': 0,
  2127     'halttime': 0,
  2128     'useparamkeys': 'false',
  2129     'sampler.showadvanced': 'false',
  2130     'sintegrator.showadvanced': 'false',
  2131     'pixelfilter.showadvanced': 'false',
  2132 
  2133     'sampler.type': 'metropolis',
  2134     'sampler.metro.strength': 0.6,
  2135     'sampler.metro.lmprob': 0.4,
  2136     'sampler.metro.maxrejects': 512,
  2137     #'sampler.metro.initsamples': 262144,
  2138     'sampler.metro.usevariance': "false",
  2139 
  2140     'sintegrator.type': 'bidirectional',
  2141     'sintegrator.bidir.bounces': 16,
  2142     'sintegrator.bidir.eyedepth': 16,
  2143     'sintegrator.bidir.lightdepth': 16,
  2144 
  2145     'pixelfilter.type': 'mitchell',
  2146     'pixelfilter.mitchell.sharp': 0.3333,
  2147     'pixelfilter.mitchell.xwidth': 1.5,
  2148     'pixelfilter.mitchell.ywidth': 1.5,
  2149     'pixelfilter.mitchell.supersample': "true",
  2150     'pixelfilter.mitchell.optmode': "slider" }
  2151 
  2152     presets['2 Final - MLT/Path Tracing (exterior)'] =  {
  2153     'film.displayinterval': 8,
  2154     'haltspp': 0,
  2155     'halttime': 0,
  2156     'useparamkeys': 'false',
  2157     'sampler.showadvanced': 'false',
  2158     'sintegrator.showadvanced': 'false',
  2159     'pixelfilter.showadvanced': 'false',
  2160 
  2161     'sampler.type': 'metropolis',
  2162     'sampler.metro.strength': 0.6,
  2163     'sampler.metro.lmprob': 0.4,
  2164     'sampler.metro.maxrejects': 512,
  2165     #'sampler.metro.initsamples': 262144,
  2166     'sampler.metro.usevariance': "false",
  2167 
  2168     'sintegrator.type': 'path',
  2169     'sintegrator.bidir.bounces': 10,
  2170     'sintegrator.bidir.maxdepth': 10,
  2171 
  2172     'pixelfilter.type': 'mitchell',
  2173     'pixelfilter.mitchell.sharp': 0.3333,
  2174     'pixelfilter.mitchell.xwidth': 1.5,
  2175     'pixelfilter.mitchell.ywidth': 1.5,
  2176     'pixelfilter.mitchell.supersample': "true",
  2177     'pixelfilter.mitchell.optmode': "slider" }
  2178     
  2179     presets['4 '] = { }
  2180 
  2181     presets['5 Progressive - Bidir Path Tracing (interior)'] =  {
  2182     'film.displayinterval': 8,
  2183     'haltspp': 0,
  2184     'halttime': 0,
  2185     'useparamkeys': 'false',
  2186     'sampler.showadvanced': 'false',
  2187     'sintegrator.showadvanced': 'false',
  2188     'pixelfilter.showadvanced': 'false',
  2189 
  2190     'sampler.type': 'lowdiscrepancy',
  2191     'sampler.lowdisc.pixelsamples': 1,
  2192     'sampler.lowdisc.pixelsampler': 'lowdiscrepancy',
  2193 
  2194     'sintegrator.type': 'bidirectional',
  2195     'sintegrator.bidir.bounces': 16,
  2196     'sintegrator.bidir.eyedepth': 16,
  2197     'sintegrator.bidir.lightdepth': 16,
  2198 
  2199     'pixelfilter.type': 'mitchell',
  2200     'pixelfilter.mitchell.sharp': 0.3333,
  2201     'pixelfilter.mitchell.xwidth': 1.5,
  2202     'pixelfilter.mitchell.ywidth': 1.5,
  2203     'pixelfilter.mitchell.supersample': "true",
  2204     'pixelfilter.mitchell.optmode': "slider" }
  2205 
  2206     presets['6 Progressive - Path Tracing (exterior)'] =  {
  2207     'film.displayinterval': 8,
  2208     'haltspp': 0,
  2209     'halttime': 0,
  2210     'useparamkeys': 'false',
  2211     'sampler.showadvanced': 'false',
  2212     'sintegrator.showadvanced': 'false',
  2213     'pixelfilter.showadvanced': 'false',
  2214 
  2215     'sampler.type': 'lowdiscrepancy',
  2216     'sampler.lowdisc.pixelsamples': 1,
  2217     'sampler.lowdisc.pixelsampler': 'lowdiscrepancy',
  2218 
  2219     'sintegrator.type': 'path',
  2220     'sintegrator.bidir.bounces': 10,
  2221     'sintegrator.bidir.maxdepth': 10,
  2222 
  2223     'pixelfilter.type': 'mitchell',
  2224     'pixelfilter.mitchell.sharp': 0.3333,
  2225     'pixelfilter.mitchell.xwidth': 1.5,
  2226     'pixelfilter.mitchell.ywidth': 1.5,
  2227     'pixelfilter.mitchell.supersample': "true",
  2228     'pixelfilter.mitchell.optmode': "slider" }
  2229 
  2230     presets['7 '] = { }
  2231 
  2232     presets['8 Bucket - Bidir Path Tracing (interior)'] =  {
  2233     'film.displayinterval': 8,
  2234     'haltspp': 0,
  2235     'halttime': 0,
  2236     'useparamkeys': 'false',
  2237     'sampler.showadvanced': 'false',
  2238     'sintegrator.showadvanced': 'false',
  2239     'pixelfilter.showadvanced': 'false',
  2240 
  2241     'sampler.type': 'lowdiscrepancy',
  2242     'sampler.lowdisc.pixelsamples': 64,
  2243     'sampler.lowdisc.pixelsampler': 'hilbert',
  2244 
  2245     'sintegrator.type': 'bidirectional',
  2246     'sintegrator.bidir.bounces': 8,
  2247     'sintegrator.bidir.eyedepth': 8,
  2248     'sintegrator.bidir.lightdepth': 10,
  2249 
  2250     'pixelfilter.type': 'mitchell',
  2251     'pixelfilter.mitchell.sharp': 0.3333,
  2252     'pixelfilter.mitchell.xwidth': 1.5,
  2253     'pixelfilter.mitchell.ywidth': 1.5,
  2254     'pixelfilter.mitchell.supersample': "true",
  2255     'pixelfilter.mitchell.optmode': "slider" }
  2256 
  2257     presets['9 Bucket - Path Tracing (exterior)'] =  {
  2258     'film.displayinterval': 8,
  2259     'haltspp': 0,
  2260     'halttime': 0,
  2261     'useparamkeys': 'false',
  2262     'sampler.showadvanced': 'false',
  2263     'sintegrator.showadvanced': 'false',
  2264     'pixelfilter.showadvanced': 'false',
  2265 
  2266     'sampler.type': 'lowdiscrepancy',
  2267     'sampler.lowdisc.pixelsamples': 64,
  2268     'sampler.lowdisc.pixelsampler': 'hilbert',
  2269 
  2270     'sintegrator.type': 'path',
  2271     'sintegrator.bidir.bounces': 8,
  2272     'sintegrator.bidir.maxdepth': 8,
  2273 
  2274     'pixelfilter.type': 'mitchell',
  2275     'pixelfilter.mitchell.sharp': 0.3333,
  2276     'pixelfilter.mitchell.xwidth': 1.5,
  2277     'pixelfilter.mitchell.ywidth': 1.5,
  2278     'pixelfilter.mitchell.supersample': "true",
  2279     'pixelfilter.mitchell.optmode': "slider" }
  2280 
  2281     presets['A '] = { }
  2282 
  2283     presets['B Anim - Distributed/GI low Q'] =  {
  2284     'film.displayinterval': 8,
  2285     'haltspp': 1,
  2286     'halttime': 0,
  2287     'useparamkeys': 'false',
  2288     'sampler.showadvanced': 'false',
  2289     'sintegrator.showadvanced': 'false',
  2290     'pixelfilter.showadvanced': 'false',
  2291 
  2292     'sampler.type': 'lowdiscrepancy',
  2293     'sampler.lowdisc.pixelsamples': 16,
  2294     'sampler.lowdisc.pixelsampler': 'hilbert',
  2295 
  2296     'sintegrator.type': 'distributedpath',
  2297     'sintegrator.distributedpath.causticsonglossy': 'true',
  2298     'sintegrator.distributedpath.diffuserefractdepth': 5,
  2299     'sintegrator.distributedpath.indirectglossy': 'true',
  2300     'sintegrator.distributedpath.directsamples': 1,
  2301     'sintegrator.distributedpath.diffuserefractsamples': 1,
  2302     'sintegrator.distributedpath.glossyreflectdepth': 2,
  2303     'sintegrator.distributedpath.causticsondiffuse': 'false',
  2304     'sintegrator.distributedpath.directsampleall': 'true',
  2305     'sintegrator.distributedpath.indirectdiffuse': 'true',
  2306     'sintegrator.distributedpath.specularreflectdepth': 3,
  2307     'sintegrator.distributedpath.diffusereflectsamples': 1,
  2308     'sintegrator.distributedpath.glossyreflectsamples': 1,
  2309     'sintegrator.distributedpath.glossyrefractdepth': 5,
  2310     'sintegrator.distributedpath.diffusereflectdepth': '2',
  2311     'sintegrator.distributedpath.indirectsamples': 1,
  2312     'sintegrator.distributedpath.indirectsampleall': 'false',
  2313     'sintegrator.distributedpath.glossyrefractsamples': 1,
  2314     'sintegrator.distributedpath.directdiffuse': 'true',
  2315     'sintegrator.distributedpath.directglossy': 'true',
  2316     'sintegrator.distributedpath.strategy': 'auto',
  2317     'sintegrator.distributedpath.specularrefractdepth': 5,
  2318 
  2319     'pixelfilter.type': 'mitchell',
  2320     'pixelfilter.mitchell.sharp': 0.3333,
  2321     'pixelfilter.mitchell.xwidth': 1.5,
  2322     'pixelfilter.mitchell.ywidth': 1.5,
  2323     'pixelfilter.mitchell.supersample': "true",
  2324     'pixelfilter.mitchell.optmode': "slider" }
  2325 
  2326     presets['C Anim - Distributed/GI medium Q'] =  {
  2327     'film.displayinterval': 8,
  2328     'haltspp': 1,
  2329     'halttime': 0,
  2330     'useparamkeys': 'false',
  2331     'sampler.showadvanced': 'false',
  2332     'sintegrator.showadvanced': 'false',
  2333     'pixelfilter.showadvanced': 'false',
  2334 
  2335     'sampler.type': 'lowdiscrepancy',
  2336     'sampler.lowdisc.pixelsamples': 64,
  2337     'sampler.lowdisc.pixelsampler': 'hilbert',
  2338 
  2339     'sintegrator.type': 'distributedpath',
  2340     'sintegrator.distributedpath.causticsonglossy': 'true',
  2341     'sintegrator.distributedpath.diffuserefractdepth': 5,
  2342     'sintegrator.distributedpath.indirectglossy': 'true',
  2343     'sintegrator.distributedpath.directsamples': 1,
  2344     'sintegrator.distributedpath.diffuserefractsamples': 1,
  2345     'sintegrator.distributedpath.glossyreflectdepth': 2,
  2346     'sintegrator.distributedpath.causticsondiffuse': 'false',
  2347     'sintegrator.distributedpath.directsampleall': 'true',
  2348     'sintegrator.distributedpath.indirectdiffuse': 'true',
  2349     'sintegrator.distributedpath.specularreflectdepth': 3,
  2350     'sintegrator.distributedpath.diffusereflectsamples': 1,
  2351     'sintegrator.distributedpath.glossyreflectsamples': 1,
  2352     'sintegrator.distributedpath.glossyrefractdepth': 5,
  2353     'sintegrator.distributedpath.diffusereflectdepth': '2',
  2354     'sintegrator.distributedpath.indirectsamples': 1,
  2355     'sintegrator.distributedpath.indirectsampleall': 'false',
  2356     'sintegrator.distributedpath.glossyrefractsamples': 1,
  2357     'sintegrator.distributedpath.directdiffuse': 'true',
  2358     'sintegrator.distributedpath.directglossy': 'true',
  2359     'sintegrator.distributedpath.strategy': 'auto',
  2360     'sintegrator.distributedpath.specularrefractdepth': 5,
  2361 
  2362     'pixelfilter.type': 'mitchell',
  2363     'pixelfilter.mitchell.sharp': 0.3333,
  2364     'pixelfilter.mitchell.xwidth': 1.5,
  2365     'pixelfilter.mitchell.ywidth': 1.5,
  2366     'pixelfilter.mitchell.supersample': "true",
  2367     'pixelfilter.mitchell.optmode': "slider" }
  2368     
  2369     presets['D Anim - Distributed/GI high Q'] =  {
  2370     'film.displayinterval': 8,
  2371     'haltspp': 1,
  2372     'halttime': 0,
  2373     'useparamkeys': 'false',
  2374     'sampler.showadvanced': 'false',
  2375     'sintegrator.showadvanced': 'false',
  2376     'pixelfilter.showadvanced': 'false',
  2377 
  2378     'sampler.type': 'lowdiscrepancy',
  2379     'sampler.lowdisc.pixelsamples': 256,
  2380     'sampler.lowdisc.pixelsampler': 'hilbert',
  2381 
  2382     'sintegrator.type': 'distributedpath',
  2383     'sintegrator.distributedpath.causticsonglossy': 'true',
  2384     'sintegrator.distributedpath.diffuserefractdepth': 5,
  2385     'sintegrator.distributedpath.indirectglossy': 'true',
  2386     'sintegrator.distributedpath.directsamples': 1,
  2387     'sintegrator.distributedpath.diffuserefractsamples': 1,
  2388     'sintegrator.distributedpath.glossyreflectdepth': 2,
  2389     'sintegrator.distributedpath.causticsondiffuse': 'false',
  2390     'sintegrator.distributedpath.directsampleall': 'true',
  2391     'sintegrator.distributedpath.indirectdiffuse': 'true',
  2392     'sintegrator.distributedpath.specularreflectdepth': 3,
  2393     'sintegrator.distributedpath.diffusereflectsamples': 1,
  2394     'sintegrator.distributedpath.glossyreflectsamples': 1,
  2395     'sintegrator.distributedpath.glossyrefractdepth': 5,
  2396     'sintegrator.distributedpath.diffusereflectdepth': '2',
  2397     'sintegrator.distributedpath.indirectsamples': 1,
  2398     'sintegrator.distributedpath.indirectsampleall': 'false',
  2399     'sintegrator.distributedpath.glossyrefractsamples': 1,
  2400     'sintegrator.distributedpath.directdiffuse': 'true',
  2401     'sintegrator.distributedpath.directglossy': 'true',
  2402     'sintegrator.distributedpath.strategy': 'auto',
  2403     'sintegrator.distributedpath.specularrefractdepth': 5,
  2404 
  2405     'pixelfilter.type': 'mitchell',
  2406     'pixelfilter.mitchell.sharp': 0.3333,
  2407     'pixelfilter.mitchell.xwidth': 1.5,
  2408     'pixelfilter.mitchell.ywidth': 1.5,
  2409     'pixelfilter.mitchell.supersample': "true",
  2410     'pixelfilter.mitchell.optmode': "slider" }
  2411 
  2412     presets['E Anim - Distributed/GI very high Q'] =  {
  2413     'film.displayinterval': 8,
  2414     'haltspp': 1,
  2415     'halttime': 0,
  2416     'useparamkeys': 'false',
  2417     'sampler.showadvanced': 'false',
  2418     'sintegrator.showadvanced': 'false',
  2419     'pixelfilter.showadvanced': 'false',
  2420 
  2421     'sampler.type': 'lowdiscrepancy',
  2422     'sampler.lowdisc.pixelsamples': 512,
  2423     'sampler.lowdisc.pixelsampler': 'hilbert',
  2424 
  2425     'sintegrator.type': 'distributedpath',
  2426     'sintegrator.distributedpath.causticsonglossy': 'true',
  2427     'sintegrator.distributedpath.diffuserefractdepth': 5,
  2428     'sintegrator.distributedpath.indirectglossy': 'true',
  2429     'sintegrator.distributedpath.directsamples': 1,
  2430     'sintegrator.distributedpath.diffuserefractsamples': 1,
  2431     'sintegrator.distributedpath.glossyreflectdepth': 2,
  2432     'sintegrator.distributedpath.causticsondiffuse': 'false',
  2433     'sintegrator.distributedpath.directsampleall': 'true',
  2434     'sintegrator.distributedpath.indirectdiffuse': 'true',
  2435     'sintegrator.distributedpath.specularreflectdepth': 3,
  2436     'sintegrator.distributedpath.diffusereflectsamples': 1,
  2437     'sintegrator.distributedpath.glossyreflectsamples': 1,
  2438     'sintegrator.distributedpath.glossyrefractdepth': 5,
  2439     'sintegrator.distributedpath.diffusereflectdepth': '2',
  2440     'sintegrator.distributedpath.indirectsamples': 1,
  2441     'sintegrator.distributedpath.indirectsampleall': 'false',
  2442     'sintegrator.distributedpath.glossyrefractsamples': 1,
  2443     'sintegrator.distributedpath.directdiffuse': 'true',
  2444     'sintegrator.distributedpath.directglossy': 'true',
  2445     'sintegrator.distributedpath.strategy': 'auto',
  2446     'sintegrator.distributedpath.specularrefractdepth': 5,
  2447 
  2448     'pixelfilter.type': 'mitchell',
  2449     'pixelfilter.mitchell.sharp': 0.3333,
  2450     'pixelfilter.mitchell.xwidth': 1.5,
  2451     'pixelfilter.mitchell.ywidth': 1.5,
  2452     'pixelfilter.mitchell.supersample': "true",
  2453     'pixelfilter.mitchell.optmode': "slider" }
  2454 
  2455     return presets
  2456 
  2457 def getMaterialPresets():
  2458     return getPresets('luxblend_materials')
  2459 
  2460 def savePreset(key, name, d):
  2461     try:
  2462         presets = getPresets(key)
  2463         if d:
  2464             presets[name] = d.copy()
  2465         else:
  2466             del presets[name]
  2467         Blender.Registry.SetKey(key, presets, True)
  2468     except: pass    
  2469 def saveScenePreset(name, d):
  2470     try:
  2471         for n in presetsExclude:
  2472             try: del d[n]
  2473             except: pass
  2474         savePreset('luxblend_presets', name, d)
  2475     except: pass
  2476 def saveMaterialPreset(name, d):
  2477     try:
  2478         for n in presetsExclude:
  2479             try: del d[n]
  2480             except: pass
  2481         savePreset('luxblend_materials', name, d)
  2482     except: pass
  2483 
  2484 
  2485 # **************************************************
  2486 
  2487 
  2488 
  2489 
  2490 
  2491 usedproperties = {} # global variable to collect used properties for storing presets
  2492 usedpropertiesfilterobj = None # assign a object to only collect the properties that are assigned to this object
  2493 
  2494 # class to access properties (for lux settings)
  2495 class luxProp:
  2496     def __init__(self, obj, name, default):
  2497         self.obj = obj
  2498         self.name = name
  2499 #        if len(name)>31: print("Warning: property-name \"%s\" has more than 31 chars."%(name))
  2500         self.hashmode = len(name)>31   # activate hash mode for keynames longer 31 chars (limited by blenders ID-prop)
  2501         self.hashname = "__hash:%x"%(name.__hash__())
  2502         self.default = default
  2503     def parseassignment(self, s, name):
  2504         l = s.split(" = ")
  2505         if l[0] != name: print("Warning: property-name \"%s\" has hash-collide with \"%s\"."%(name, l[0]))
  2506         return l[1]
  2507     def createassignment(self, name, value):
  2508         return "%s = %s"%(name, value)
  2509     def get(self):
  2510         global usedproperties, usedpropertiesfilterobj, luxdefaults
  2511         if self.obj:
  2512             try:
  2513                 value = self.obj.properties['luxblend'][self.name]
  2514                 if not(usedpropertiesfilterobj) or (usedpropertiesfilterobj == self.obj):
  2515                     usedproperties[self.name] = value
  2516                 return value
  2517             except KeyError:
  2518                 try:
  2519                     value = self.parseassignment(self.obj.properties['luxblend'][self.hashname], self.name)
  2520                     if not(usedpropertiesfilterobj) or (usedpropertiesfilterobj == self.obj):
  2521                         usedproperties[self.name] = value
  2522                     return value
  2523                 except KeyError:
  2524                     if self.obj.__class__.__name__ == "Scene": # luxdefaults only for global setting
  2525                         try:
  2526                             value = luxdefaults[self.name]
  2527                             if not(usedpropertiesfilterobj) or (usedpropertiesfilterobj == self.obj):
  2528                                 usedproperties[self.name] = value
  2529                             return value
  2530                         except KeyError:
  2531                             if not(usedpropertiesfilterobj) or (usedpropertiesfilterobj == self.obj):
  2532                                 usedproperties[self.name] = self.default
  2533                             return self.default
  2534                     if not(usedpropertiesfilterobj) or (usedpropertiesfilterobj == self.obj):
  2535                         usedproperties[self.name] = self.default
  2536                     return self.default
  2537         return None
  2538     def getobj(self):
  2539         if self.obj:
  2540             return self.obj
  2541         else:
  2542             return None
  2543     def getname(self):
  2544         if self.name:
  2545             return self.name
  2546         else:
  2547             return None
  2548     def set(self, value):
  2549         global newluxdefaults
  2550         if self.obj:
  2551             if self.hashmode: n, v = self.hashname, self.createassignment(self.name, value)
  2552             else: n, v = self.name, value
  2553             if value is not None:
  2554                 try: self.obj.properties['luxblend'][n] = v
  2555                 except (KeyError, TypeError):
  2556                     self.obj.properties['luxblend'] = {}
  2557                     self.obj.properties['luxblend'][n] = v
  2558             else:
  2559                 try: del self.obj.properties['luxblend'][n]
  2560                 except:    pass
  2561             if self.obj.__class__.__name__ == "Scene": # luxdefaults only for global setting
  2562                 # value has changed, so this are user settings, remove preset reference
  2563                 if not(self.name in defaultsExclude):
  2564                     newluxdefaults[self.name] = value
  2565                     try: self.obj.properties['luxblend']['preset']=""
  2566                     except: pass
  2567     def delete(self):
  2568         if self.obj:
  2569             try: del self.obj.properties['luxblend'][self.name]
  2570             except:    pass
  2571             try: del self.obj.properties['luxblend'][self.hashname]
  2572             except:    pass
  2573     def getFloat(self):
  2574         v = self.get()
  2575         if type(v) == types.FloatType: return float(v)
  2576         try:
  2577             if type(v) == types.StringType: return float(v.split(" ")[0])
  2578         except: pass
  2579         v = self.default
  2580         if type(v) == types.FloatType: return float(v)
  2581         try:
  2582             if type(v) == types.StringType: return float(v.split(" ")[0])
  2583         except: pass
  2584         return 0.0
  2585     def getInt(self):
  2586         try: return int(self.get())
  2587         except: return int(self.default)
  2588     def getRGB(self):
  2589         return self.getVector()
  2590     def getVector(self):
  2591         v = self.get()
  2592         if type(v) in [types.FloatType, types.IntType]: return (float(v), float(v), float(v))
  2593         l = None
  2594         try:
  2595             if type(v) == types.StringType: l = self.get().split(" ")
  2596         except: pass
  2597         try:
  2598             if (l==None) or (len(l) != 3): l = self.default.split(" ")
  2599             return (float(l[0]), float(l[1]), float(l[2]))
  2600         except AttributeError:
  2601             return (float(l[0]), float(l[0]), float(l[0]))
  2602         
  2603     def getVectorStr(self):
  2604         return "%f %f %f"%self.getVector()
  2605     def isFloat(self):
  2606         return type(self.get()) == types.FloatType
  2607     def getRGC(self):
  2608         col = self.getRGB()
  2609         return "%f %f %f"%(rg(col[0]), rg(col[1]),rg(col[2]))
  2610     def setRGB(self, value):
  2611         self.set("%f %f %f"%(value[0], value[1], value[2]))
  2612     def setVector(self, value):
  2613         self.set("%f %f %f"%(value[0], value[1], value[2]))
  2614 
  2615 
  2616 # class to access blender attributes (for lux settings)
  2617 class luxAttr:
  2618     def __init__(self, obj, name):
  2619         self.obj = obj
  2620         self.name = name
  2621     def get(self):
  2622         if self.obj:
  2623             return getattr(self.obj, self.name)
  2624         else:
  2625             return None
  2626     def getFloat(self):
  2627         return float(self.get())
  2628     def getInt(self):
  2629         return int(self.get())
  2630     def getobj(self):
  2631         if self.obj:
  2632             return self.obj
  2633         else:
  2634             return None
  2635     def getname(self):
  2636         if self.name:
  2637             return self.name
  2638         else:
  2639             return None
  2640     def set(self, value):
  2641         if self.obj:
  2642             setattr(self.obj, self.name, value)
  2643             Window.QRedrawAll()
  2644 
  2645 
  2646 # class for dynamic gui
  2647 class luxGui:
  2648     def __init__(self, y=200):
  2649         self.x = 110 # left start position after captions
  2650         self.xmax = 110+2*(140+4)
  2651         self.y = y
  2652         self.w = 140 # default element width in pixels
  2653         self.h = 18  # default element height in pixels
  2654         self.hmax = 0
  2655         self.xgap = 4
  2656         self.ygap = 4
  2657         self.resethmax = False
  2658     def getRect(self, wu, hu):
  2659         w = int(self.w * wu + self.xgap * (wu-1))
  2660         h = int(self.h * hu + self.ygap * (hu-1))
  2661         if self.x + w > self.xmax: self.newline()
  2662         if self.resethmax: self.hmax = 0; self.resethmax = False
  2663         rect = [int(self.x), int(self.y-h), int(w), int(h)]
  2664         self.x += int(w + self.xgap)
  2665         if h+self.ygap > self.hmax: self.hmax = int(h+self.ygap)
  2666         return rect
  2667     def newline(self, title="", distance=0, level=0, icon=None, color=None):
  2668         self.x = 110
  2669         if not(self.resethmax): self.y -= int(self.hmax + distance)
  2670         if color!=None:    BGL.glColor3f(color[0],color[1],color[2]); BGL.glRectf(0,self.y-self.hmax,self.xmax,self.y+distance); BGL.glColor3f(0.9, 0.9, 0.9)
  2671         if icon!=None: drawIcon(icon, 2+level*10, self.y-16)
  2672         self.resethmax = True
  2673         if title!="":
  2674             self.getRect(0, 1)
  2675             BGL.glColor3f(0.9,0.9,0.9); BGL.glRasterPos2i(20+level*10,self.y-self.h+5); Draw.Text(title)
  2676     
  2677 def luxHelp(name, lux, caption, hint, gui, width=1.0):
  2678     if gui:
  2679         r = gui.getRect(width, 1)
  2680         Draw.Toggle(caption, evtLuxGui, r[0], r[1], r[2], r[3], lux.get()=="true", hint, lambda e,v: lux.set(["false","true"][bool(v)]))
  2681         drawIcon(icon_help, r[0], r[1])
  2682 
  2683     return "\n   \"bool %s\" [\"%s\"]"%(name, lux.get())
  2684 
  2685 # lux parameter types
  2686 def luxOption(name, lux, options, caption, hint, gui, width=1.0):
  2687     if gui:
  2688         menustr = caption+": %t"
  2689         for i, v in enumerate(options): menustr = "%s %%x%d|%s"%(v, i, menustr)
  2690         try:
  2691             i = options.index(lux.get())
  2692         except ValueError:
  2693             try:
  2694                 lux.set(lux.default) # not found, so try default value
  2695                 i = options.index(lux.get())
  2696             except ValueError:
  2697                 print("value %s not found in options list"%(lux.get()))
  2698                 i = 0
  2699         r = gui.getRect(width, 1)
  2700         Draw.Menu(menustr, evtLuxGui, r[0], r[1], r[2], r[3], i, hint, lambda e,v: lux.set(options[v]))
  2701     return "\n   \"string %s\" [\"%s\"]"%(name, lux.get())
  2702 
  2703 def luxOptionRect(name, lux, options, caption, hint, gui, x, y, xx, yy):
  2704     if gui:
  2705         menustr = caption+": %t"
  2706         for i, v in enumerate(options): menustr = "%s %%x%d|%s"%(v, i, menustr)
  2707         try:
  2708             i = options.index(lux.get())
  2709         except ValueError:
  2710             try:
  2711                 lux.set(lux.default) # not found, so try default value
  2712                 i = options.index(lux.get())
  2713             except ValueError:
  2714                 print ("value %s not found in options list"%(lux.get()))
  2715                 i = 0
  2716         Draw.Menu(menustr, evtLuxGui, x, y, xx, yy, i, hint, lambda e,v: lux.set(options[v]))
  2717     return "\n   \"string %s\" [\"%s\"]"%(name, lux.get())
  2718 
  2719 def luxIdentifier(name, lux, options, caption, hint, gui, icon=None, width=1.0):
  2720     if gui: gui.newline(caption+":", 8, 0, icon, [0.75,0.5,0.25])
  2721     luxOption(name, lux, options, caption, hint, gui, width)
  2722     return "\n%s \"%s\""%(name, lux.get())
  2723 
  2724 def luxFloat(name, lux, min, max, caption, hint, gui, width=1.0, useslider=0):
  2725     if gui:
  2726         if (luxProp(Scene.GetCurrent(), "useparamkeys", "false").get()=="true"):
  2727             r = gui.getRect(width-0.12, 1)
  2728         else:
  2729             r = gui.getRect(width, 1)
  2730 
  2731         # Value
  2732         if(useslider==1):
  2733             Draw.Slider(caption+": ", evtLuxGui, r[0], r[1], r[2], r[3], lux.getFloat(), min, max, 0, hint, lambda e,v: lux.set(v))
  2734         else:
  2735             Draw.Number(caption+": ", evtLuxGui, r[0], r[1], r[2], r[3], lux.getFloat(), min, max, hint, lambda e,v: lux.set(v))
  2736         if (luxProp(Scene.GetCurrent(), "useparamkeys", "false").get()=="true"):
  2737             # IPO Curve
  2738             obj = lux.getobj()
  2739             keyname = lux.getname()
  2740     
  2741             useipo = luxProp(obj, keyname+".IPOuse", "false")
  2742             i = gui.getRect(0.12, 1)
  2743             Draw.Toggle("I", evtLuxGui, i[0], i[1], i[2], i[3], useipo.get()=="true", "Use IPO Curve", lambda e,v: useipo.set(["false","true"][bool(v)]))
  2744             
  2745             if useipo.get() == "true":
  2746                 if gui: gui.newline(caption+"IPO:", 8, 0, None, [0.5,0.45,0.35])
  2747                 curve = luxProp(obj, keyname+".IPOCurveName", "") 
  2748                 if curve.get() == "":
  2749                     c = gui.getRect(2.0, 1)
  2750                 else:
  2751                     c = gui.getRect(1.1, 1)
  2752                 
  2753                 Draw.String("Ipo:", evtLuxGui, c[0], c[1], c[2], c[3], curve.get(), 250, "Set IPO Name", lambda e,v: curve.set(v))
  2754                 
  2755                 usemapping = luxProp(obj, keyname+".IPOmap", "false")
  2756                 icu_value = 0
  2757     
  2758                 # Apply IPO to value
  2759                 if curve.get() != "":
  2760                     try:
  2761                         ipoob = Blender.Ipo.Get(curve.get())
  2762                     except: 
  2763                         curve.set("")
  2764                     pass
  2765                     if curve.get() != "":
  2766                         names = list([x[0] for x in ipoob.curveConsts.items()])
  2767                         ipotype = luxProp(obj, keyname+".IPOCurveType", "OB_LOCZ")
  2768                         luxOption("ipocurve", ipotype, names, "IPO Curve", "Set IPO Curve", gui, 0.6)
  2769     
  2770                         icu = ipoob[eval("Blender.Ipo.%s" % (ipotype.get()))]
  2771                         icu_value = icu[Blender.Get('curframe')]
  2772                         if usemapping.get() == "false": # if true is set during mapping below
  2773                             lux.set(icu_value)    
  2774     
  2775                         # Mapping options
  2776                         m = gui.getRect(0.3, 1)
  2777                         Draw.Toggle("Map", evtLuxGui, m[0], m[1], m[2], m[3], usemapping.get()=="true", "Edit Curve mapping", lambda e,v: usemapping.set(["false","true"][bool(v)]))
  2778                         if usemapping.get() == "true":
  2779                             if gui: gui.newline(caption+"IPO:", 8, 0, None, [0.5,0.45,0.35])
  2780                             fmin = luxProp(obj, keyname+".IPOCurvefmin", 0.0)
  2781                             luxFloatNoIPO("ipofmin", fmin, -100, 100, "fmin", "Map minimum value from Curve", gui, 0.5)
  2782                             fmax = luxProp(obj, keyname+".IPOCurvefmax", 1.0)
  2783                             luxFloatNoIPO("ipofmax", fmax, -100, 100, "fmax", "Map maximum value from Curve", gui, 0.5)
  2784                             tmin = luxProp(obj, keyname+".IPOCurvetmin", min)
  2785                             luxFloatNoIPO("ipotmin", tmin, min, max, "tmin", "Map miminum value to", gui, 0.5)
  2786                             tmax = luxProp(obj, keyname+".IPOCurvetmax", max)
  2787                             luxFloatNoIPO("ipotmax", tmax, min, max, "tmax", "Map maximum value to", gui, 0.5)
  2788     
  2789                             sval = (icu_value - fmin.getFloat()) / (fmax.getFloat() - fmin.getFloat())
  2790                             lux.set(tmin.getFloat() + (sval * (tmax.getFloat() - tmin.getFloat())))
  2791 
  2792                             # invert
  2793                             #v = gui.getRect(0.5, 1)
  2794                             #Draw.Toggle("Invert", evtLuxGui, v[0], v[1], v[2], v[3], useipo.get()=="true", "Invert Curve values", lambda e,v: useipo.set(["false","true"][bool(v)]))
  2795     else:
  2796         if (luxProp(Scene.GetCurrent(), "useparamkeys", "false").get()=="true"):
  2797             obj = lux.getobj()
  2798             keyname = lux.getname()
  2799             useipo = luxProp(obj, keyname+".IPOuse", "false")
  2800             if useipo.get() == "true":
  2801                 curve = luxProp(obj, keyname+".IPOCurveName", "") 
  2802                 try:
  2803                     ipoob = Blender.Ipo.Get(curve.get())
  2804                 except: 
  2805                     curve.set("")
  2806                 pass
  2807                 usemapping = luxProp(obj, keyname+".IPOmap", "false")
  2808                 icu_value = 0
  2809                 if curve.get() != "":
  2810                     names = list([x[0] for x in ipoob.curveConsts.items()])
  2811                     ipotype = luxProp(obj, keyname+".IPOCurveType", "OB_LOCZ")
  2812     
  2813                     icu = ipoob[eval("Blender.Ipo.%s" % (ipotype.get()))]
  2814                     icu_value = icu[Blender.Get('curframe')]
  2815                     if usemapping.get() == "false": # if true is set during mapping below
  2816                         lux.set(icu_value)    
  2817     
  2818                 if usemapping.get() == "true":
  2819                     if gui: gui.newline(caption+"IPO:", 8, 0, None, [0.5,0.45,0.35])
  2820                     fmin = luxProp(obj, keyname+".IPOCurvefmin", 0.0)
  2821                     fmax = luxProp(obj, keyname+".IPOCurvefmax", 1.0)
  2822                     tmin = luxProp(obj, keyname+".IPOCurvetmin", min)
  2823                     tmax = luxProp(obj, keyname+".IPOCurvetmax", max)
  2824                     sval = (icu_value - fmin.getFloat()) / (fmax.getFloat() - fmin.getFloat())
  2825                     lux.set(tmin.getFloat() + (sval * (tmax.getFloat() - tmin.getFloat())))
  2826 
  2827     return "\n   \"float %s\" [%f]"%(name, lux.getFloat())
  2828 
  2829 def luxFloatNoIPO(name, lux, min, max, caption, hint, gui, width=1.0, useslider=0):
  2830     if gui:
  2831         r = gui.getRect(width, 1)
  2832         if(useslider==1):
  2833             Draw.Slider(caption+": ", evtLuxGui, r[0], r[1], r[2], r[3], lux.getFloat(), min, max, 0, hint, lambda e,v: lux.set(v))
  2834         else:
  2835             Draw.Number(caption+": ", evtLuxGui, r[0], r[1], r[2], r[3], lux.getFloat(), min, max, hint, lambda e,v: lux.set(v))
  2836     return "\n   \"float %s\" [%f]"%(name, lux.getFloat())
  2837 
  2838 
  2839 
  2840 def luxInt(name, lux, min, max, caption, hint, gui, width=1.0):
  2841     if gui:
  2842         r = gui.getRect(width, 1)
  2843         Draw.Number(caption+": ", evtLuxGui, r[0], r[1], r[2], r[3], lux.getInt(), min, max, hint, lambda e,v: lux.set(v))
  2844     return "\n   \"integer %s\" [%d]"%(name, lux.getInt())
  2845 
  2846 def luxBool(name, lux, caption, hint, gui, width=1.0):
  2847     if gui:
  2848         r = gui.getRect(width, 1)
  2849         Draw.Toggle(caption, evtLuxGui, r[0], r[1], r[2], r[3], lux.get()=="true", hint, lambda e,v: lux.set(["false","true"][bool(v)]))
  2850     return "\n   \"bool %s\" [\"%s\"]"%(name, lux.get())
  2851 
  2852 def luxLabel(caption, gui):
  2853     if gui:
  2854         r = gui.getRect(2,1); BGL.glRasterPos2i(r[0],r[1]+5)
  2855         Draw.Text(caption)
  2856 
  2857 def luxCollapse(name, lux, caption, hint, gui, width=1.0):
  2858     if gui:
  2859         r = gui.getRect(width, 1)
  2860         if lux.get() == "true":
  2861             drawArrow(arrow_down, r[0]-22, r[1]-2)
  2862         else:
  2863             drawArrow(arrow_right, r[0]-22, r[1]-2)
  2864         Draw.Toggle(caption, evtLuxGui, r[0], r[1], r[2], r[3], lux.get()=="true", hint, lambda e,v: lux.set(["false","true"][bool(v)]))
  2865     return "\n   \"bool %s\" [\"%s\"]"%(name, lux.get())
  2866 
  2867 def luxString(name, lux, caption, hint, gui, width=1.0):
  2868     if gui:
  2869         r = gui.getRect(width, 1)
  2870         Draw.String(caption+": ", evtLuxGui, r[0], r[1], r[2], r[3], lux.get(), 250, hint, lambda e,v: lux.set(v))
  2871     if lux.get()==lux.default: return ""
  2872     else: return "\n   \"string %s\" [\"%s\"]"%(name, luxstr(lux.get()))
  2873 
  2874 def luxFile(name, lux, caption, hint, gui, width=1.0):
  2875     if gui:
  2876         r = gui.getRect(width, 1)
  2877         Draw.String(caption+": ", evtLuxGui, r[0], r[1], r[2]-r[3]-2, r[3], lux.get(), 250, hint, lambda e,v: lux.set(v))
  2878         Draw.Button("...", 0, r[0]+r[2]-r[3], r[1], r[3], r[3], "click to open file selector", lambda e,v:Window.FileSelector(lambda s:lux.set(s), "Select %s"%(caption), lux.get()))
  2879     return "\n   \"string %s\" [\"%s\"]"%(name, luxstr(luxFilePath(lux.get())))
  2880 
  2881 def luxPath(name, lux, caption, hint, gui, width=1.0):
  2882     if gui:
  2883         r = gui.getRect(width, 1)
  2884         Draw.String(caption+": ", evtLuxGui, r[0], r[1], r[2]-r[3]-2, r[3], lux.get(), 250, hint, lambda e,v: lux.set(Blender.sys.dirname(v)+os.sep))
  2885         Draw.Button("...", 0, r[0]+r[2]-r[3], r[1], r[3], r[3], "click to open file selector", lambda e,v:Window.FileSelector(lambda s:lux.set(s), "Select %s"%(caption), lux.get()))
  2886     return "\n   \"string %s\" [\"%s\"]"%(name, luxstr(lux.get()))
  2887 
  2888 def luxRGB(name, lux, max, caption, hint, gui, width=2.0):
  2889     if gui:
  2890         r = gui.getRect(width, 1)
  2891         scale = 1.0
  2892         rgb = lux.getRGB()
  2893         if max > 1.0:
  2894             for i in range(3):
  2895                 if rgb[i] > scale: scale = rgb[i]
  2896             rgb = (rgb[0]/scale, rgb[1]/scale, rgb[2]/scale)
  2897         Draw.ColorPicker(evtLuxGui, r[0], r[1], r[3], r[3], rgb, "click to select color", lambda e,v: lux.setRGB((v[0]*scale,v[1]*scale,v[2]*scale)))
  2898         w = int((r[2]-r[3])/3); m = max
  2899         if max > 1.0:
  2900             w = int((r[2]-r[3])/4); m = 1.0
  2901         drawR, drawG, drawB, drawS = Draw.Create(rgb[0]), Draw.Create(rgb[1]), Draw.Create(rgb[2]), Draw.Create(scale)
  2902         drawR = Draw.Number("R:", evtLuxGui, r[0]+r[3], r[1], w, r[3], drawR.val, 0.0, m, "red", lambda e,v: lux.setRGB((v*scale,drawG.val*scale,drawB.val*scale)))
  2903         drawG = Draw.Number("G:", evtLuxGui, r[0]+r[3]+w, r[1], w, r[3], drawG.val, 0.0, m, "green", lambda e,v: lux.setRGB((drawR.val*scale,v*scale,drawB.val*scale)))
  2904         drawB = Draw.Number("B:", evtLuxGui, r[0]+r[3]+2*w, r[1], w, r[3], drawB.val, 0.0, m, "blue", lambda e,v: lux.setRGB((drawR.val*scale,drawG.val*scale,v*scale)))
  2905         if max > 1.0:
  2906             Draw.Number("s:", evtLuxGui, r[0]+r[3]+3*w, r[1], w, r[3], drawS.val, 0.0, max, "color scale", lambda e,v: lux.setRGB((drawR.val*v,drawG.val*v,drawB.val*v)))
  2907     if max <= 1.0:
  2908         return "\n   \"color %s\" [%s]"%(name, lux.getRGC())
  2909     return "\n   \"color %s\" [%s]"%(name, lux.get())
  2910 
  2911 def luxVector(name, lux, min, max, caption, hint, gui, width=2.0):
  2912     if gui:
  2913         r = gui.getRect(width, 1)
  2914         vec = lux.getVector()
  2915         w = int(r[2]/3)
  2916         drawX, drawY, drawZ = Draw.Create(vec[0]), Draw.Create(vec[1]), Draw.Create(vec[2])
  2917         drawX = Draw.Number("x:", evtLuxGui, r[0], r[1], w, r[3], drawX.val, min, max, "", lambda e,v: lux.setVector((v,drawY.val,drawZ.val)))
  2918         drawY = Draw.Number("y:", evtLuxGui, r[0]+w, r[1], w, r[3], drawY.val, min, max, "", lambda e,v: lux.setVector((drawX.val,v,drawZ.val)))
  2919         drawZ = Draw.Number("z:", evtLuxGui, r[0]+2*w, r[1], w, r[3], drawZ.val, min, max, "", lambda e,v: lux.setVector((drawX.val,drawY.val,v)))
  2920     return "\n   \"vector %s\" [%s]"%(name, lux.get())
  2921 
  2922 def luxVectorUniform(name, lux, min, max, caption, hint, gui, width=2.0):
  2923     def setUniform(lux, value):
  2924         if value: lux.set(lux.getFloat())
  2925         else: lux.setVector(lux.getVector())
  2926     if gui:
  2927         r = gui.getRect(width, 1)
  2928         vec = lux.getVector()
  2929         Draw.Toggle("U", evtLuxGui, r[0], r[1], gui.h, gui.h, lux.isFloat(), "uniform", lambda e,v: setUniform(lux, v))
  2930         if lux.isFloat():
  2931             Draw.Number("v:", evtLuxGui, r[0]+gui.h, r[1], r[2]-gui.h, r[3], lux.getFloat(), min, max, "", lambda e,v: lux.set(v))
  2932         else:
  2933             w = int((r[2]-gui.h)/3)
  2934             drawX, drawY, drawZ = Draw.Create(vec[0]), Draw.Create(vec[1]), Draw.Create(vec[2])
  2935             drawX = Draw.Number("x:", evtLuxGui, r[0]+gui.h, r[1], w, r[3], drawX.val, min, max, "", lambda e,v: lux.setVector((v,drawY.val,drawZ.val)))
  2936             drawY = Draw.Number("y:", evtLuxGui, r[0]+w+gui.h, r[1], w, r[3], drawY.val, min, max, "", lambda e,v: lux.setVector((drawX.val,v,drawZ.val)))
  2937             drawZ = Draw.Number("z:", evtLuxGui, r[0]+2*w+gui.h, r[1], w, r[3], drawZ.val, min, max, "", lambda e,v: lux.setVector((drawX.val,drawY.val,v)))
  2938     return "\n   \"vector %s\" [%s]"%(name, lux.getVectorStr())
  2939 
  2940 # property translator for lux<->blender camera types
  2941 class luxCameraLinker:
  2942     def __init__(self, cam, luxCams, blendCams):
  2943         self.id = luxProp(cam, 'camera.type.id', 0)
  2944         self.prop = luxProp(cam, 'camera.type', 'perspective')
  2945         self.cam = cam
  2946         self.default = cam.type
  2947         self.luxCams = luxCams
  2948         self.blendCams = blendCams
  2949     def get(self):
  2950         if self.blendCams[self.id.get()] is None:
  2951             n = self.luxCams[self.id.get()]
  2952         else:
  2953             n = self.luxCams[self.blendCams.index(self.cam.type)]
  2954         self.prop.set(n)
  2955         return n
  2956     def set(self, value):
  2957         self.id.set(self.luxCams.index(value))
  2958         try:
  2959             self.cam.type = self.blendCams[self.luxCams.index(value)]
  2960         except ValueError:
  2961             pass
  2962         self.prop.set(value)
  2963         Window.QRedrawAll()
  2964 
  2965 # lux individual identifiers
  2966 def luxCamera(cam, context, gui=None):
  2967     global icon_c_camera
  2968     str = ""
  2969     if cam:
  2970         camtype = luxProp(cam, "camera.type", "perspective")
  2971         # camera types for lux<->blender property linking. make sure the number of elements are equal
  2972         camlist = ['perspective', 'orthographic', 'environment']
  2973         camvals = ['persp', 'ortho', None]
  2974         # Radiance - remarked 'realistic' for v0.6 release
  2975         #str = luxIdentifier("Camera", camtype, ["perspective","orthographic","environment","realistic"], "CAMERA", "select camera type", gui, icon_c_camera)
  2976         str = luxIdentifier("Camera", luxCameraLinker(cam, camlist, camvals), camlist, "CAMERA", "select camera type", gui, icon_c_camera)
  2977         scale = 1.0
  2978         
  2979         if camtype.get() == "perspective":
  2980             if gui: gui.newline("  View:")
  2981             str += luxFloat("fov", luxAttr(cam, "angle"), 8.0, 170.0, "fov", "camera field-of-view angle", gui)
  2982             fl = luxAttr(cam, "lens")
  2983             if gui:
  2984                 luxFloat("lens", fl, 1.0, 250.0, "focallength", "camera focal length", gui)
  2985             
  2986         if camtype.get() == "orthographic" :
  2987             str += luxFloat("scale", luxAttr(cam, "scale"), 0.01, 1000.0, "scale", "orthographic camera scale", gui)
  2988             scale = cam.scale / 2
  2989             
  2990         if camtype.get() == "realistic":
  2991             if gui: gui.newline("  View:")
  2992             fov = luxAttr(cam, "angle")
  2993             str += luxFloat("fov", fov, 8.0, 170.0, "fov", "camera field-of-view angle", gui)
  2994             if gui: luxFloat("lens", luxAttr(cam, "lens"), 1.0, 250.0, "focallength", "camera focal length", gui)
  2995             
  2996             if gui: gui.newline()
  2997             str += luxFile("specfile", luxProp(cam, "camera.realistic.specfile", ""), "spec-file", "", gui, 1.0)
  2998 #            if gui: gui.newline()
  2999 # auto calc        str += luxFloat("filmdistance", luxProp(cam, "camera.realistic.filmdistance", 70.0), 0.1, 1000.0, "film-dist", "film-distance [mm]", gui)
  3000             filmdiag = luxProp(cam, "camera.realistic.filmdiag", 35.0)
  3001             str += luxFloat("filmdiag", filmdiag, 0.1, 1000.0, "film-diag", "[mm]", gui)
  3002             if gui: gui.newline()
  3003             fstop = luxProp(cam, "camera.realistic.fstop", 1.0)
  3004             luxFloat("aperture_diameter", fstop, 0.1, 100.0, "f-stop", "", gui)
  3005             dofdist = luxAttr(cam, "dofDist")
  3006             luxFloat("focaldistance", dofdist, 0.0, 10000.0, "distance", "Distance from the camera at which objects will be in focus. Has no effect if Lens Radius is 0", gui)
  3007             if gui:
  3008                 Draw.Button("S", evtLuxGui, gui.x, gui.y-gui.h, gui.h, gui.h, "focus selected object", lambda e,v:setFocus("S"))
  3009                 Draw.Button("C", evtLuxGui, gui.x+gui.h, gui.y-gui.h, gui.h, gui.h, "focus cursor", lambda e,v:setFocus("C"))
  3010             focal = filmdiag.get()*0.001 / math.tan(fov.get() * math.pi / 360.0) / 2.0
  3011             print("calculated focal length: %f mm"%(focal * 1000.0))
  3012             aperture_diameter = focal / fstop.get()
  3013             print("calculated aperture diameter: %f mm"%(aperture_diameter * 1000.0))
  3014             str += "\n   \"float aperture_diameter\" [%f]"%(aperture_diameter*1000.0)
  3015             filmdistance = dofdist.get() * focal / (dofdist.get() - focal)
  3016             print("calculated film distance: %f mm"%(filmdistance * 1000.0))
  3017             str += "\n   \"float filmdistance\" [%f]"%(filmdistance*1000.0)
  3018 
  3019         # Clipping
  3020         useclip = luxProp(cam, "useclip", "false")
  3021         luxCollapse("useclip", useclip, "Near & Far Clipping", "Enable Camera near and far clipping options", gui, 2.0)
  3022         if(useclip.get() == "true"):
  3023             if gui: gui.newline("  Clipping:")
  3024             str += luxFloat("hither", luxAttr(cam, "clipStart"), 0.0, 100.0, "start", "near clip distance", gui)
  3025             str += luxFloat("yon", luxAttr(cam, "clipEnd"), 1.0, 10000.0, "end", "far clip distance", gui)
  3026 
  3027         # Depth of Field
  3028         usedof = luxProp(cam, "usedof", "false")
  3029         
  3030         if camtype.get() in ["perspective", "orthographic"]:
  3031             luxCollapse("usedof", usedof, "Depth of Field & Bokeh", "Enable Depth of Field & Aperture options", gui, 2.0)
  3032             
  3033             
  3034             if usedof.get() == "true":
  3035                 
  3036                 if gui: gui.newline("  DOF:")
  3037                 
  3038                 lr = luxProp(cam, "camera.lensradius", 0.01)
  3039                 fs = luxProp(cam, "camera.fstop", 2.8)
  3040                 
  3041                 if camtype.get() == "perspective":
  3042                     
  3043                     usefstop = luxProp(cam, "usefstop", "true")
  3044                     luxBool("usefstop", usefstop, "Use f/stop", "Use f/stop to define DOF effect", gui, 1.0)
  3045                     
  3046                     LR_SCALE = 1000.0       # lr in metres -> mm
  3047                     FL_SCALE = 1.0          # fl in mm -> mm
  3048                     
  3049                     def lr_2_fs(fl, lr):
  3050                         lr += 0.00000001
  3051                         return fl / ( 2.0 * lr )
  3052                     
  3053                     def fs_2_lr(fl, fs):
  3054                         return fl / ( 2.0 * fs )
  3055                     
  3056                     if usefstop.get() == 'true':
  3057                         halfstop = luxProp(cam, 'camera.halfstop', 'false')
  3058                         luxBool('camera.halfstop', halfstop, 'half-stop', 'Use half-stop presets', gui, 0.5)
  3059                         fs.default = 2.8 if halfstop.get() == 'false' else 3.3
  3060                         luxOption('camera.fstoppresets', fs, luxFstopPresets('full' if halfstop.get() == 'false' else 'half'), 'f/stop', 'Choose the focal ratio number (lens aperture)', gui, 0.5)
  3061                         lr.set(fs_2_lr(fl.get() * FL_SCALE, fs.get()) / LR_SCALE)
  3062                         str += luxFloat("lensradius", lr, 0.0, 1.0, "", "", None)
  3063                     else:
  3064                         fs.set(lr_2_fs(fl.get() * FL_SCALE, lr.get() * LR_SCALE))
  3065                         str += luxFloat("lensradius", lr, 0.0, 1.0, "lens-radius", "Defines the lens radius. Values higher than 0 enable DOF and control its amount", gui)
  3066                 else:
  3067                     str += luxFloat("lensradius", lr, 0.0, 1.0, "lens-radius", "Defines the lens radius. Values higher than 0 enable DOF and control its amount", gui)
  3068                 
  3069                 focustype = luxProp(cam, "camera.focustype", "autofocus")
  3070                 luxOption("focustype", focustype, ["autofocus", "manual", "object"], "Focus Type", "Choose the focus behaviour", gui)
  3071                 
  3072     
  3073                 if focustype.get() == "autofocus":
  3074                     str += luxBool("autofocus",luxProp(cam, "camera.autofocus", "true"), "autofocus", "Enable automatic focus", gui)
  3075                 if focustype.get() == "object":
  3076                     objectfocus = luxProp(cam, "camera.objectfocus", "")
  3077                     luxString("objectfocus", objectfocus, "object", "Always focus camera on named object", gui, 1.0)
  3078                     dofdist = luxAttr(cam, "dofDist")
  3079                     str += luxFloat("focaldistance", dofdist, 0.0, 100.0, "distance", "Distance from the camera at which objects will be in focus. Has no effect if Lens Radius is 0", gui)
  3080                     if objectfocus.get() != "":
  3081                         try:
  3082                             setFocus(objectfocus.get())
  3083                         except:
  3084                             luxProp(cam, "camera.objectfocus", "").set("")
  3085                             Draw.PupMenu("WARNING: focus-object does not match existing object-name")
  3086                             if LuxIsGUI: Draw.Redraw()
  3087                                                                   
  3088                 if focustype.get() == "manual":
  3089                     dofdist = luxAttr(cam, "dofDist")
  3090                     str += luxFloat("focaldistance", dofdist, 0.0, 100.0, "distance", "Distance from the camera at which objects will be in focus. Has no effect if Lens Radius is 0", gui)
  3091                     if gui:
  3092                         Draw.Button("S", evtLuxGui, gui.x, gui.y-gui.h, gui.h, gui.h, "focus selected object", lambda e,v:setFocus("S"))
  3093                         Draw.Button("C", evtLuxGui, gui.x+gui.h, gui.y-gui.h, gui.h, gui.h, "focus cursor", lambda e,v:setFocus("C"))
  3094 
  3095         if camtype.get() == "perspective" and usedof.get() == "true":
  3096             str += luxInt("blades", luxProp(cam, "camera.blades", 6), 0, 16, "aperture blades", "Number of blade edges of the aperture, values 0 to 2 defaults to a circle", gui)
  3097             str += luxOption("distribution", luxProp(cam, "camera.distribution", "uniform"), ["uniform", "exponential", "inverse exponential", "gaussian", "inverse gaussian"], "distribution", "Choose the lens sampling distribution. Non-uniform distributions allow for ring effects.", gui)
  3098             str += luxInt("power", luxProp(cam, "camera.power", 1), 0, 512, "power", "Exponent for the expression in exponential distribution. Higher value gives a more pronounced ring effect.", gui)
  3099 
  3100         useaspect = luxProp(cam, "useaspectratio", "false")
  3101         aspectratio = luxProp(cam, "ratio", 1.3333)
  3102         if camtype.get() in ["perspective", "orthographic"]:
  3103             useshift = luxProp(cam, "camera.useshift", "false")
  3104             luxCollapse("useshift", useshift, "Architectural (Lens Shift) & Aspect Ratio", "Enable Lens Shift and Aspect Ratio options", gui, 2.0)
  3105             if(useshift.get() == "true"):
  3106                 if gui: gui.newline("  Shift:")
  3107                 luxFloat("X", luxAttr(cam, "shiftX"), -2.0, 2.0, "X", "horizontal lens shift", gui)
  3108                 luxFloat("Y", luxAttr(cam, "shiftY"), -2.0, 2.0, "Y", "vertical lens shift", gui)
  3109 
  3110                 if gui: gui.newline("  AspectRatio:")
  3111                 luxBool("useaspectratio", useaspect, "Custom", "Define a custom frame aspect ratio", gui)
  3112                 if useaspect.get() == "true":
  3113                     str += luxFloat("frameaspectratio", aspectratio, 0.0001, 3.0, "aspectratio", "Frame aspect ratio", gui)
  3114             if context:
  3115                 if useaspect.get() == "true":
  3116                     ratio = 1./aspectratio.get()
  3117                 else:
  3118                         ratio = float(context.sizeY)/float(context.sizeX)
  3119                 if ratio < 1.0:
  3120                     screenwindow = [(2*cam.shiftX-1)*scale, (2*cam.shiftX+1)*scale, (2*cam.shiftY-ratio)*scale, (2*cam.shiftY+ratio)*scale]
  3121                 else:
  3122                     screenwindow = [(2*cam.shiftX-1/ratio)*scale, (2*cam.shiftX+1/ratio)*scale, (2*cam.shiftY-1)*scale, (2*cam.shiftY+1)*scale]
  3123                 # render region option
  3124                 if context.borderRender:
  3125                     (x1,y1,x2,y2) = context.border
  3126                     screenwindow = [screenwindow[0]*(1-x1)+screenwindow[1]*x1, screenwindow[0]*(1-x2)+screenwindow[1]*x2,\
  3127                             screenwindow[2]*(1-y1)+screenwindow[3]*y1, screenwindow[2]*(1-y2)+screenwindow[3]*y2]
  3128                 str += "\n   \"float screenwindow\" [%f %f %f %f]"%(screenwindow[0], screenwindow[1], screenwindow[2], screenwindow[3])
  3129 
  3130         # Note - radiance - this is a work in progress
  3131         # Flash lamp option for perspective and ortho cams
  3132 #        if camtype.get() in ["perspective", "orthographic"]:
  3133 #            useflash = luxProp(cam, "useflash", "false")
  3134 #            luxBool("useflash", useflash, "Flash Lamp", "Enable Camera mounted flash lamp options", gui, 2.0)
  3135 
  3136         # Motion Blur Options (common to all cameras)
  3137         usemblur = luxProp(cam, "usemblur", "false")
  3138         luxCollapse("usemblur", usemblur, "Motion Blur", "Enable Motion Blur", gui, 2.0)
  3139         if(usemblur.get() == "true"):    
  3140             if gui: gui.newline("  Shutter:")
  3141             mblurpreset = luxProp(cam, "mblurpreset", "true")
  3142             luxBool("mblurpreset", mblurpreset, "Preset", "Enable use of Shutter Presets", gui, 0.5)
  3143             if(mblurpreset.get() == "true"):
  3144                 mblurpresetstype = luxProp(cam, "mblurpresetstype", 'photo')
  3145                 luxOption("mblurpresetstype", mblurpresetstype, ['photo', 'cinema'], "camera type", "Choose whether to use still photographic or cinematographic camera presets", gui, 0.5)
  3146                 
  3147                 shutterpreset = luxProp(cam, "camera.shutterspeedpreset", "1/60" if mblurpresetstype.get() == 'photo' else "180-degree")
  3148                 luxOption("shutterpreset", shutterpreset, luxShutterSpeedPresets(mblurpresetstype.get()), "shutter speed", "Choose the Shutter speed preset", gui, 0.5 if mblurpresetstype.get() == 'cinema' else 1.0)
  3149                 
  3150                 shutfps = luxProp(cam, "camera.shutfps", "25 FPS")
  3151                 if mblurpresetstype.get() == 'cinema':
  3152                     luxOption("shutfps", shutfps, luxFPSPresets(), "@", "Choose the number of frames per second as the time base", gui, 0.5)
  3153 
  3154                 str += "\n   \"float shutteropen\" [%f]\n   \"float shutterclose\" [%f] " % (0, luxFilmExposure(mblurpresetstype.get(), shutterpreset.get(), shutfps.get()))
  3155 
  3156             else:
  3157                 str += luxFloat("shutteropen", luxProp(cam, "camera.shutteropen", 0.0), 0.0, 100.0, "open", "time in seconds when shutter opens", gui, 0.75)
  3158                 str += luxFloat("shutterclose", luxProp(cam, "camera.shutterclose", 1.0), 0.0, 100.0, "close", "time in seconds when shutter closes", gui, 0.75)
  3159 
  3160             str += luxOption("shutterdistribution", luxProp(cam, "camera.shutterdistribution", "gaussian"), ["uniform", "gaussian"], "distribution", "Choose the shutter sampling distribution", gui, 2.0)
  3161             objectmblur = luxProp(cam, "objectmblur", "true")
  3162             luxBool("objectmblur", objectmblur, "Object", "Enable Motion Blur for scene object motions", gui, 1.0)
  3163             cammblur = luxProp(cam, "cammblur", "true")
  3164             luxBool("cammblur", cammblur, "Camera", "Enable Motion Blur for Camera motion", gui, 1.0)
  3165     return str
  3166 
  3167 
  3168 def luxFPSPresets():
  3169     return ['10 FPS', '12 FPS', '20 FPS', '24 FPS', '25 FPS', '29.976 FPS', '30 FPS', '50 FPS', '60 FPS', '100 FPS', '200 FPS', '500 FPS']
  3170 
  3171 def luxISOPresets():
  3172     return [20, 25, 32, 40, 50, 64, 80, 100, 125, 160, 200, 250, 320, 400, 500, 640, 800, 1000]
  3173 
  3174 def luxShutterSpeedPresets(type):
  3175     speeds = {
  3176       'photo': ['1', '1/2', '1/4', '1/8', '1/15', '1/30', '1/60', '1/125', '1/250', '1/500', '1/1000'],
  3177       'cinema': ['45-degree', '90-degree', '180-degree', '270-degree']
  3178     }
  3179     return speeds[type]
  3180 
  3181 def luxFstopPresets(type):
  3182     fstops = {
  3183       'full': [0.5, 0.7, 1, 1.4, 2, 2.8, 4, 5.6, 8, 11, 16, 22, 32, 45, 64, 90, 128],
  3184       'half': [1.2, 1.7, 2.4, 3.3, 4.8, 6.7, 9.5, 13, 19, 27, 38, 54, 77, 109]
  3185     }
  3186     return fstops[type]
  3187 
  3188 def luxFilmExposure(type, shutterStr, fpsStr):
  3189     if type == 'photo': fps = 1
  3190     else: fps = float(fpsStr[:fpsStr.find(' ')])  # assuming fps are in form 'n FPS'
  3191     
  3192     if shutterStr == '1': exp = 1.0
  3193     elif type == 'photo': exp = 1.0 / float(shutterStr[2:])  # assuming still camera shutterspeed is in form '1/n'
  3194     elif type == 'cinema': exp = (1.0/fps) * (1-float(shutterStr[:shutterStr.find('-')])/360)  # assuming motion camera shutterspeed is in form 'n-degree'
  3195     return exp
  3196 
  3197 def get_render_resolution(scn, gui = None):
  3198     context = scn.getRenderingContext()
  3199     scale = luxProp(scn, "film.scale", "100 %")
  3200     scale = int(scale.get()[:-1])
  3201     xr = luxAttr(context, "sizeX").get()*scale/100
  3202     yr = luxAttr(context, "sizeY").get()*scale/100
  3203     
  3204     return xr, yr
  3205 
  3206 def borderResize(scn, xr, yr):
  3207     # update border region dimensions
  3208     
  3209     context = scn.getRenderingContext()
  3210     t = (yr - luxProp(scn, "film.border.T", yr-yr*context.border[3]).get()) / float(yr)
  3211     l = luxProp(scn, "film.border.L", xr*context.border[0]).get() / float(xr)
  3212     b = (yr - luxProp(scn, "film.border.B", yr-yr*context.border[1]).get()) / float(yr)
  3213     r = luxProp(scn, "film.border.R", xr*context.border[2]).get() / float(xr)
  3214     luxProp(scn, "film.border.T", 0).delete()
  3215     luxProp(scn, "film.border.L", 0).delete()
  3216     luxProp(scn, "film.border.B", yr).delete()
  3217     luxProp(scn, "film.border.R", xr).delete()
  3218     context.setBorder(l, b, r, t)
  3219 
  3220 def borderAspectReset(scn, xr, yr):
  3221     # recalculate border region coordinates according to the image
  3222     # aspect ratio. resize the region relative to its center.
  3223     
  3224     context = scn.getRenderingContext()
  3225     # unpack border proportions
  3226     (l, b, r, t) = context.border
  3227     # recalculate border edges pixel positions relative to image's left/top
  3228     l = xr*l
  3229     b = yr-yr*b
  3230     r = xr*r
  3231     t = yr-yr*t
  3232     # border region width/height
  3233     w = r-l
  3234     h = b-t
  3235     # center of the border region (relative to the image)
  3236     hc = l+w/2
  3237     vc = t+h/2
  3238     # image aspect ratio
  3239     aspect = float(xr) / float(yr)
  3240     # calculate new dimensions for the border region. silly ugly code -__-
  3241     if xr >= yr:
  3242         nw = w>h and w or h
  3243         nh = (w>h and w or h) / aspect
  3244     else:
  3245         nw = w<h and w or h
  3246         nh = (w<h and w or h) / aspect
  3247     # set new border edges pixel positions from the region's
  3248     # center and move it if we got out off the image boundaries
  3249     nl = int(hc-nw/2)
  3250     nb = int(vc+nh/2)
  3251     nr = int(hc+nw/2)
  3252     nt = int(vc-nh/2)
  3253     if nb > yr:
  3254         nt = nt-(nb-yr)
  3255         nb = yr
  3256     elif nt < 0:
  3257         nb = nb+(-1*nt)
  3258         nt = 0
  3259     if nr > xr:
  3260         nl = nl-(nr-xr)
  3261         nr = xr
  3262     elif nl < 0:
  3263         nr = nr+(-1*nl)
  3264         nl = 0
  3265     # update properties
  3266     luxProp(scn, "film.border.T", nt).set(nt)
  3267     luxProp(scn, "film.border.L", nl).set(nl)
  3268     luxProp(scn, "film.border.B", nb).set(nb)
  3269     luxProp(scn, "film.border.R", nr).set(nr)
  3270     borderResize(scn, xr, yr)
  3271 
  3272 def luxFilm(scn, gui=None):
  3273     str = ""
  3274     if scn:
  3275         filmtype = luxProp(scn, "film.type", "fleximage")
  3276         str = luxIdentifier("Film", filmtype, ["fleximage"], "FILM", "select film type", gui)
  3277         if filmtype.get() == "fleximage":
  3278             context = scn.getRenderingContext()
  3279             if context:
  3280                 if gui: gui.newline("  Resolution:")
  3281                 
  3282                 xr,yr = get_render_resolution(scn, gui)
  3283                 
  3284                 luxInt("xresolution", luxAttr(context, "sizeX"), 0, 8192, "X", "width of the render", gui, 0.666)
  3285                 luxInt("yresolution", luxAttr(context, "sizeY"), 0, 8192, "Y", "height of the render", gui, 0.666)
  3286                 scale = luxProp(scn, "film.scale", "100 %")
  3287                 luxOption("", scale, ["400 %", "200 %", "100 %", "75 %", "50 %", "25 %"], "scale", "scale resolution", gui, 0.666)
  3288                 
  3289                 # render region option
  3290                 if context.borderRender:
  3291                     if gui: gui.newline("  Border:")
  3292                     borderzoom = luxProp(scn, "film.border.zoom", "false")
  3293                     luxBool("borderzoom", borderzoom, "Zoom", "Zoom in to the border region", gui, borderzoom.get() == "true" and 1.0 or 2.0)
  3294                     if borderzoom.get() == "true" and gui:
  3295                         r = gui.getRect(1, 1)
  3296                         Draw.Button("aspect ratio", evtLuxGui, r[0], r[1], r[2], r[3], "Reset border dimensions to the image aspect ratio (relative to the region center)", lambda e,v: borderAspectReset(scn,xr,yr))
  3297                     
  3298                     borderprecise = luxProp(scn, "film.border.precise", "false")
  3299                     luxCollapse("borderprecise", borderprecise, "Precise border dimensions", "Manually configure border region dimensions", gui, 2.0)
  3300                     if borderprecise.get() == "true" and gui:
  3301                         luxInt("borderleft", luxProp(scn, "film.border.L", xr*context.border[0]), 0, xr*context.border[2], "L", "Left edge of the border region", gui, 0.5)
  3302                         luxInt("borderright", luxProp(scn, "film.border.R", xr*context.border[2]), xr*context.border[0], xr, "R", "Right edge of the border region", gui, 0.5)
  3303                         luxInt("bordertop", luxProp(scn, "film.border.T", yr-yr*context.border[3]), 0, yr-yr*context.border[1], "T", "Top edge of the border region", gui, 0.5)
  3304                         luxInt("borderbottom", luxProp(scn, "film.border.B", yr-yr*context.border[1]), yr-yr*context.border[3], yr, "B", "Bottom edge of the border region", gui, 0.5)
  3305                         borderResize(scn,xr,yr)
  3306                     
  3307                     (x1,y1,x2,y2) = context.border
  3308                     if (x1==x2) or (y1==y2): print("WARNING: Empty render region, use SHIFT-B to set render region in Blender.")
  3309                     if borderzoom.get() != "true":
  3310                         w = xr*(x2-x1)
  3311                         h = yr*(y2-y1)
  3312                         str += "\n   \"integer xresolution\" [%d] \n   \"integer yresolution\" [%d]"%(w, h)
  3313                     else:
  3314                         w = xr*(x2-x1)
  3315                         h = yr*(y2-y1)
  3316                         aspect = float(xr>yr and xr or yr) / float(w>h and w or h)
  3317                         w = w*aspect
  3318                         h = h*aspect
  3319                         str += "\n   \"integer xresolution\" [%d] \n   \"integer yresolution\" [%d]"%(w, h)
  3320                 else:
  3321                     str += "\n   \"integer xresolution\" [%d] \n   \"integer yresolution\" [%d]"%(xr, yr)
  3322                     luxProp(scn, "film.border.T", 0).delete()
  3323                     luxProp(scn, "film.border.L", 0).delete()
  3324                     luxProp(scn, "film.border.B", yr).delete()
  3325                     luxProp(scn, "film.border.R", xr).delete()
  3326 
  3327             if gui: gui.newline("  Output:")
  3328             str += luxInt("displayinterval", luxProp(scn, "film.displayinterval", 12), 4, 3600, "display interval", "Set display interval (seconds)", gui)
  3329             str += luxInt("writeinterval", luxProp(scn, "film.writeinterval", 120), 12, 3600, "write interval", "Set display interval (seconds)", gui)
  3330 
  3331             if gui: gui.newline("  Halt:")
  3332             if luxProp(scn, 'useparamkeys', 'false').get() == 'false':
  3333                 str += luxInt("haltspp", luxProp(scn, "haltspp", 0), 0, 32768, "halt at spp", "Stop rendering after specified amount of samples per pixel (0 = never halt)", gui)
  3334                 str += luxInt("halttime", luxProp(scn, "halttime", 0), 0, 86400, "halt at time", "Stop rendering after specified number of seconds (0 = never halt)", gui)
  3335             else:
  3336                 haltspp = luxProp(scn, 'haltspp', 0)
  3337                 halttime = luxProp(scn, 'halttime', 0)
  3338                 luxFloat("haltspp", haltspp, 0, 32768.0, "halt at spp", "Stop rendering after specified amount of samples per pixel (0 = never halt)", gui)
  3339                 luxFloat("halttime", halttime, 0, 86400.0, "halt at time", "Stop rendering after specified number of seconds (0 = never halt)", gui)
  3340                 haltspp.set(math.ceil(float(haltspp.get())))
  3341                 halttime.set(math.ceil(float(halttime.get())))
  3342                 str += luxInt('haltspp', haltspp, 0, 32768, '', '', None)
  3343                 str += luxInt('halttime', halttime, 0, 86400, '', '', None)
  3344             
  3345             if gui: gui.newline("  Tonemap:")
  3346             tonemapkernel =    luxProp(scn, "film.tonemapkernel", "reinhard")
  3347             str += luxOption("tonemapkernel", tonemapkernel, ["reinhard", "linear", "contrast", "maxwhite"], "Tonemapping Kernel", "Select the tonemapping kernel to use", gui, 1.2)
  3348             str += luxBool("premultiplyalpha", luxProp(scn, "film.premultiplyalpha", "false"), "Premultiply Alpha", "Premultiply film alpha channel during normalization", gui, 0.8)
  3349             if tonemapkernel.get() == "reinhard":
  3350                 str += luxFloat("reinhard_prescale", luxProp(scn, "film.reinhard.prescale", 1.0), 0.0, 10.0, "preScale", "Image scale before tonemap operator", gui)
  3351                 str += luxFloat("reinhard_postscale", luxProp(scn, "film.reinhard.postscale", 1.2), 0.0, 10.0, "postScale", "Image scale after tonemap operator", gui)
  3352                 str += luxFloat("reinhard_burn", luxProp(scn, "film.reinhard.burn", 6.0), 0.1, 12.0, "burn", "12.0: no burn out, 0.1 lot of burn out", gui, 2.0)
  3353             elif tonemapkernel.get() == "linear":
  3354                 linearSensitivity = luxProp(scn, 'film.linear.sensitivity', 100.0)
  3355                 linearExposure = luxProp(scn, 'film.linear.exposure', 0.008)
  3356                 linearFstop = luxProp(scn, 'film.linear.fstop', 2.8)
  3357                 linearPreset = luxProp(scn, 'film.linear.presets', 'true')
  3358                 luxBool('linearpresets', linearPreset, 'Preset', 'Enable use of lens and film presets', gui, 0.5 if linearPreset.get() == 'true' else 1.0)
  3359                 if linearPreset.get() == 'true':
  3360                     linearPresetsType = luxProp(scn, 'film.linear.cameratype', 'photo')
  3361                     luxOption('linearpresetstype', linearPresetsType, ['photo', 'cinema'], 'camera type', 'Choose whether to use still photographic or cinematographic camera presets', gui, 0.5)
  3362                     linearExposurePreset = luxProp(scn, 'film.linear.exposurepreset', '1/125' if linearPresetsType.get() == 'photo' else '180-degree')
  3363                     luxOption('linearexposurepreset', linearExposurePreset, luxShutterSpeedPresets(linearPresetsType.get()), 'exposure', 'Exposure duration in seconds, or shutter speed', gui, 0.5 if linearPresetsType.get() == 'cinema' else 1.0)
  3364                     linearFPSPreset = luxProp(scn, 'film.linear.camerafps', '25 FPS')
  3365                     if linearPresetsType.get() == 'cinema':
  3366                         luxOption('linearfpspreset', linearFPSPreset, luxFPSPresets(), '@', 'Choose the number of frames per second as the time base', gui, 0.5)
  3367                     linearExposure.set(luxFilmExposure(linearPresetsType.get(), linearExposurePreset.get(), linearFPSPreset.get()))
  3368                     linearHalfStop = luxProp(scn, 'film.linear.halfstop', 'false')
  3369                     luxBool('linearhalfstop', linearHalfStop, 'half-stop', 'Use half-stop presets', gui, 0.5)
  3370                     linearFstop.default = 2.8 if linearHalfStop.get() == 'false' else 3.3
  3371                     luxOption('linearpresetsfstop', linearFstop, luxFstopPresets('full' if linearHalfStop.get() == 'false' else 'half'), 'f/stop', 'Choose the focal ratio number (lens aperture)', gui, 0.5)
  3372                     linearFstop.set(float(linearFstop.get()))
  3373                     luxOption('linearpresetsiso', linearSensitivity, luxISOPresets(), 'film ISO', 'Choose film sensitivity (ISO scale number)', gui)
  3374                     linearSensitivity.set(float(linearSensitivity.get()))
  3375                     linearGUI = None
  3376                 else:
  3377                     linearGUI = gui
  3378                 str += luxFloat("linear_sensitivity", linearSensitivity, 1.0, 1000.0, "sensitivity", "Film adaption/sensitivity", linearGUI)
  3379                 str += luxFloat("linear_exposure", linearExposure, 0.0001, 1.0, "exposure", "Exposure duration in seconds", linearGUI)
  3380                 str += luxFloat("linear_fstop", linearFstop, 0.1, 128.0, "f/stop", "Focal ratio number", linearGUI)
  3381                 str += luxFloat("linear_gamma", luxProp(scn, "film.gamma", 2.2), 0.0, 8.0, "gamma", "Film gamma correction", None)
  3382             elif tonemapkernel.get() == "contrast":
  3383                 str += luxFloat("contrast_ywa", luxProp(scn, "film.contrast.ywa", 0.1), 0.001, 10000.0, "Ywa", "Display/World Adaption Luminance", gui, 2.0)
  3384 
  3385         # Image File Outputs
  3386 
  3387         # LDR clamping method
  3388         if gui: gui.newline("  Clamping:")
  3389         ldrclampmethod = luxProp(scn, "film.ldr_clamp_method", "lum")
  3390         str += luxOption("ldr_clamp_method", ldrclampmethod, ["lum", "hue", "cut"], "LDR clamping", "Method to clamp high luminance values for LDR output", gui, 0.5)
  3391         if gui: gui.newline()
  3392 
  3393         # OpenEXR Output
  3394         if gui: gui.newline("  OpenEXR:")
  3395         saveexr = luxProp(scn, "film.write_exr", "false")
  3396         str += luxCollapse("write_exr", saveexr, "OpenEXR Output", "Enable OpenEXR output", gui, 2.0)
  3397 
  3398         if saveexr.get() == "true":
  3399             exrchannels = luxProp(scn, "film.write_exr_channels", "RGBA")
  3400             str += luxOption("write_exr_channels", exrchannels, ["Y", "YA", "RGB", "RGBA"], "Channels", "Select channels type to write", gui, 0.5)
  3401             exrres = luxProp(scn, "film.write_exr_halftype", "true")
  3402             str += luxBool("write_exr_halftype", exrres, "16bit Half", "Enable 16bit Half resolution output, otherwise 32bit float", gui, 0.5)
  3403             exrcompression = luxProp(scn, "film.write_exr_compression", "PIZ (lossless)")
  3404             str += luxOption("write_exr_compressiontype", exrcompression, ["RLE (lossless)", "PIZ (lossless)", "ZIP (lossless)", "Pxr24 (lossy)", "None"], "Compression", "Select OpenEXR Compression algorithm to use", gui, 1.0)
  3405 
  3406             exrimaging = luxProp(scn, "film.write_exr_imaging", "true")
  3407             str += luxBool("write_exr_applyimaging", exrimaging, "Apply Imaging/Tonemapping", "Apply Imaging and Tonemapping pipeline", gui, 1.2)
  3408         
  3409             if exrimaging.get()=="true":
  3410                 exrgamutclamp = luxProp(scn, "film.write_exr_gamutclamp", "true")
  3411                 str += luxBool("write_exr_gamutclamp", exrgamutclamp, "Gamut Clamp", "Clamp out of gamut (bright) pixel values", gui, 0.8)
  3412 
  3413             if gui: gui.newline()
  3414             # Zbuf output
  3415             exrZ = luxProp(scn, "film.write_exr_Z", "true")
  3416             str += luxBool("write_exr_ZBuf", exrZ, "ZBuf", "Enable Z Depth Buffer channel", gui, 0.8)
  3417             if exrZ.get() == "true":
  3418                 exrZNormalize = luxProp(scn, "film.write_exr_ZNorm", "None")
  3419                 str += luxOption("write_exr_zbuf_normalizationtype", exrZNormalize, ["Camera Start/End clip", "Min/Max", "None"], "ZBuf Normalization", "Select type of normalization to use for Zbuf Depth Map", gui, 1.2)
  3420 
  3421         # PNG Output
  3422         if gui: gui.newline("  PNG:")
  3423         savepng = luxProp(scn, "film.write_png", "true")
  3424         str += luxCollapse("write_png", savepng, "PNG Output", "Enable PNG (Portable Network Graphics) output", gui, 2.0)
  3425 
  3426         if savepng.get() == "true":
  3427             pngchannels = luxProp(scn, "film.write_png_channels", "RGB")
  3428             str += luxOption("write_png_channels", pngchannels, ["Y", "YA", "RGB", "RGBA"], "Channels", "Select channels type to write", gui, 0.5)
  3429             png16bit = luxProp(scn, "film.write_png_16bit", "false")
  3430             str += luxBool("write_png_16bit", png16bit, "16bit", "Enable 16bits per channel resolution PNG output", gui, 0.5)
  3431             pnggamutclamp = luxProp(scn, "film.write_png_gamutclamp", "true")
  3432             str += luxBool("write_png_gamutclamp", pnggamutclamp, "Gamut Clamp", "Clamp out of gamut (bright) pixel values", gui, 1.0)
  3433 
  3434         # Zbuf output
  3435         #pngZ = luxProp(scn, "film.write_png_ZBuf", "false")
  3436         #str += luxBool("write_png_ZBuf", pngZ, "ZBuf (Separate)", "Enable Z Depth Buffer channel", gui, 0.8)
  3437         #if pngZ.get() == "true":
  3438         #    pngZNormalize = luxProp(scn, "film.write_png_ZNorm", "Min/Max")
  3439         #    str += luxOption("write_png_zbuf_normalizationtype", pngZNormalize, ["Camera Start/End clip", "Min/Max", "None"], "ZBuf Normalization", "Select type of normalization to use for Zbuf Depth Map", gui, 1.2)
  3440 
  3441         # TGA Output
  3442         if gui: gui.newline("  TGA:")
  3443         savetga = luxProp(scn, "film.write_tga", "false")
  3444         str += luxCollapse("write_tga", savetga, "TGA Output", "Enable TGA output", gui, 2.0)
  3445 
  3446         if savetga.get() == "true":
  3447             tgachannels = luxProp(scn, "film.write_tga_channels", "RGB")
  3448             str += luxOption("write_tga_channels", tgachannels, ["Y", "RGB", "RGBA"], "Channels", "Select channels type to write", gui, 0.5)
  3449             tgagamutclamp = luxProp(scn, "film.write_tga_gamutclamp", "true")
  3450             str += luxBool("write_tga_gamutclamp", tgagamutclamp, "Gamut Clamp", "Clamp out of gamut (bright) pixel values", gui, 1.5)
  3451 
  3452         # Zbuf output
  3453         #tgaZ = luxProp(scn, "film.write_tga_ZBuf", "false")
  3454         #str += luxBool("write_tga_ZBuf", tgaZ, "ZBuf (Separate)", "Enable Z Depth Buffer channel", gui, 0.8)
  3455         #if tgaZ.get() == "true":
  3456         #    tgaZNormalize = luxProp(scn, "film.write_tga_ZNorm", "Min/Max")
  3457         #    str += luxOption("write_tga_zbuf_normalizationtype", tgaZNormalize, ["Camera Start/End clip", "Min/Max", "None"], "ZBuf Normalization", "Select type of normalization to use for Zbuf Depth Map", gui, 1.2)
  3458 
  3459 
  3460         # override output image dir in case of command line batch mode 
  3461         overrideop = luxProp(scn, "overrideoutputpath", "")
  3462         if overrideop.get() != "":
  3463             filebase = os.path.splitext(os.path.basename(Blender.Get('filename')))[0]
  3464             filename = overrideop.get() + "/" + filebase + "-%05d" %  (Blender.Get('curframe'))
  3465             str += "\n   \"string filename\" [\"%s\"]"%(filename)
  3466         else:
  3467             fn = luxProp(scn, "filename", "default-%05d" %  (Blender.Get('curframe')))
  3468             str += "\n   \"string filename\" [\"%s\"]" % luxstr(luxFilePath(fn.get()))
  3469     
  3470         if gui: gui.newline("  Resume:")
  3471         resumeflm = luxProp(scn, "film.write_resume_flm", "false")
  3472         str += luxBool("write_resume_flm", resumeflm, "Write/Use FLM", "Write a resume fleximage .flm file, or resume rendering if it already exists", gui)
  3473         restartflm = luxProp(scn, "film.restart_resume_flm", "true")
  3474         str += luxBool("restart_resume_flm", restartflm, "Restart/Erase", "Restart with a black flm, even it a previous flm exists", gui)
  3475         if gui: gui.newline("  Reject:")
  3476         str += luxInt("reject_warmup", luxProp(scn, "film.reject_warmup", 128), 0, 32768, "warmup_spp", "Specify amount of samples per pixel for high intensity rejection", gui)
  3477         debugmode = luxProp(scn, "film.debug", "false")
  3478         str += luxBool("debug", debugmode, "debug", "Turn on debug reporting and switch off reject", gui)
  3479     
  3480         # Colorspace
  3481         if gui: gui.newline("  Colorspace:")
  3482     
  3483         cspaceusepreset = luxProp(scn, "film.colorspaceusepreset", "true")
  3484         luxBool("colorspaceusepreset", cspaceusepreset, "Preset", "Select from a list of predefined presets", gui, 0.4)
  3485     
  3486         # Default values for 'sRGB - HDTV (ITU-R BT.709-5)'
  3487         cspacewhiteX = luxProp(scn, "film.cspacewhiteX", 0.314275)
  3488         cspacewhiteY = luxProp(scn, "film.cspacewhiteY", 0.329411)
  3489         cspaceredX = luxProp(scn, "film.cspaceredX", 0.63)
  3490         cspaceredY = luxProp(scn, "film.cspaceredY", 0.34)
  3491         cspacegreenX = luxProp(scn, "film.cspacegreenX", 0.31)
  3492         cspacegreenY = luxProp(scn, "film.cspacegreenY", 0.595)
  3493         cspaceblueX = luxProp(scn, "film.cspaceblueX", 0.155)
  3494         cspaceblueY = luxProp(scn, "film.cspaceblueY", 0.07)
  3495         gamma = luxProp(scn, "film.gamma", 2.2)
  3496     
  3497         if(cspaceusepreset.get() == "true"):
  3498             # preset controls
  3499             cspace = luxProp(scn, "film.colorspace", "sRGB - HDTV (ITU-R BT.709-5)")
  3500             cspaces = ["sRGB - HDTV (ITU-R BT.709-5)", "ROMM RGB", "Adobe RGB 98", "Apple RGB", "NTSC (FCC 1953, ITU-R BT.470-2 System M)", "NTSC (1979) (SMPTE C, SMPTE-RP 145)", "PAL/SECAM (EBU 3213, ITU-R BT.470-6)", "CIE (1931) E"]
  3501             luxOption("colorspace", cspace, cspaces, "Colorspace", "select output working colorspace", gui, 1.6)
  3502 
  3503             if cspace.get()=="sRGB - HDTV (ITU-R BT.709-5)":
  3504                 cspacewhiteX.set(0.314275); cspacewhiteY.set(0.329411) # sRGB
  3505                 cspaceredX.set( 0.63); cspaceredY.set(0.34)
  3506                 cspacegreenX.set(0.31); cspacegreenY.set(0.595)
  3507                 cspaceblueX.set(0.155); cspaceblueY.set( 0.07)    
  3508             elif cspace.get()=="ROMM RGB":
  3509                 cspacewhiteX.set(0.346); cspacewhiteY.set(0.359) # D50
  3510                 cspaceredX.set(0.7347); cspaceredY.set(0.2653)
  3511                 cspacegreenX.set(0.1596); cspacegreenY.set(0.8404)
  3512                 cspaceblueX.set(0.0366); cspaceblueY.set(0.0001)
  3513             elif cspace.get()=="Adobe RGB 98":
  3514                 cspacewhiteX.set(0.313); cspacewhiteY.set(0.329) # D65
  3515                 cspaceredX.set(0.64); cspaceredY.set(0.34)
  3516                 cspacegreenX.set(0.21); cspacegreenY.set(0.71)
  3517                 cspaceblueX.set(0.15); cspaceblueY.set(0.06)
  3518             elif cspace.get()=="Apple RGB":
  3519                 cspacewhiteX.set(0.313); cspacewhiteY.set(0.329) # D65
  3520                 cspaceredX.set(0.625); cspaceredY.set(0.34)
  3521                 cspacegreenX.set(0.28); cspacegreenY.set(0.595)
  3522                 cspaceblueX.set(0.155); cspaceblueY.set(0.07)
  3523             elif cspace.get()=="NTSC (FCC 1953, ITU-R BT.470-2 System M)":
  3524                 cspacewhiteX.set(0.310); cspacewhiteY.set(0.316) # C
  3525                 cspaceredX.set(0.67); cspaceredY.set(0.33)
  3526                 cspacegreenX.set(0.21); cspacegreenY.set(0.71)
  3527                 cspaceblueX.set(0.14); cspaceblueY.set(0.08)
  3528             elif cspace.get()=="NTSC (1979) (SMPTE C, SMPTE-RP 145)":
  3529                 cspacewhiteX.set(0.313); cspacewhiteY.set(0.329) # D65
  3530                 cspaceredX.set(0.63); cspaceredY.set(0.34)
  3531                 cspacegreenX.set(0.31); cspacegreenY.set(0.595)
  3532                 cspaceblueX.set(0.155); cspaceblueY.set(0.07)
  3533             elif cspace.get()=="PAL/SECAM (EBU 3213, ITU-R BT.470-6)":
  3534                 cspacewhiteX.set(0.313); cspacewhiteY.set(0.329) # D65
  3535                 cspaceredX.set(0.64); cspaceredY.set(0.33)
  3536                 cspacegreenX.set(0.29); cspacegreenY.set(0.60)
  3537                 cspaceblueX.set(0.15); cspaceblueY.set(0.06)
  3538             elif cspace.get()=="CIE (1931) E":
  3539                 cspacewhiteX.set(0.333); cspacewhiteY.set(0.333) # E
  3540                 cspaceredX.set(0.7347); cspaceredY.set(0.2653)
  3541                 cspacegreenX.set(0.2738); cspacegreenY.set(0.7174)
  3542                 cspaceblueX.set(0.1666); cspaceblueY.set(0.0089)
  3543     
  3544             whitepointusecspace = luxProp(scn, "film.whitepointusecolorspace", "true")
  3545             luxBool("whitepointusecolorspace", whitepointusecspace, "Colorspace Whitepoint", "Use default whitepoint for selected colorspace", gui, 1.0)
  3546             gammausecspace = luxProp(scn, "film.gammausecolorspace", "true")
  3547             luxBool("gammausecolorspace", gammausecspace, "Colorspace Gamma", "Use default output gamma for selected colorspace", gui, 1.0)
  3548     
  3549             if(whitepointusecspace.get() == "false"):
  3550                 if gui: gui.newline("  Whitepoint:")
  3551                 whitepointusepreset = luxProp(scn, "film.whitepointusepreset", "true")
  3552                 luxBool("whitepointusepreset", whitepointusepreset, "Preset", "Select from a list of predefined presets", gui, 0.4)
  3553     
  3554                 if(whitepointusepreset.get() == "true"):
  3555                     whitepointpresets = ["A - incandescent", "B - sunlight", "C - daylight", "D50 - daylight, 5003K", "D55 - daylight, 5503K", "D65 - daylight, 6504K", "D75 - daylight, 7504K", "E - equal energy", "F2 - standard fluorescent", "F7 - broadband fluorescent", "F11 - narrow threeband fluorescent", "9300"]
  3556                     whitepointpreset = luxProp(scn, "film.whitepointpreset", "D65 - daylight, 6504K")
  3557                     luxOption("whitepointpreset", whitepointpreset, whitepointpresets, "  PRESET", "select Whitepoint preset", gui, 1.6)
  3558     
  3559                     if whitepointpreset.get().startswith("E - "): cspacewhiteX.set(0.333); cspacewhiteY.set(0.333)
  3560                     elif whitepointpreset.get().startswith("D50 - "): cspacewhiteX.set(0.346); cspacewhiteY.set(0.359)
  3561                     elif whitepointpreset.get().startswith("D55 - "): cspacewhiteX.set(0.332); cspacewhiteY.set(0.347)
  3562                     elif whitepointpreset.get().startswith("D65 - "): cspacewhiteX.set(0.313); cspacewhiteY.set(0.329)
  3563                     elif whitepointpreset.get().startswith("D75 - "): cspacewhiteX.set(0.299); cspacewhiteY.set(0.315)
  3564                     elif whitepointpreset.get().startswith("A - "): cspacewhiteX.set(0.448); cspacewhiteY.set(0.407)
  3565                     elif whitepointpreset.get().startswith("B - "): cspacewhiteX.set(0.348); cspacewhiteY.set(0.352)
  3566                     elif whitepointpreset.get().startswith("C - "): cspacewhiteX.set(0.310); cspacewhiteY.set(0.316)
  3567                     elif whitepointpreset.get().startswith("9300"): cspacewhiteX.set(0.285); cspacewhiteY.set(0.293)
  3568                     elif whitepointpreset.get().startswith("F2 - "): cspacewhiteX.set(0.372); cspacewhiteY.set(0.375)
  3569                     elif whitepointpreset.get().startswith("F7 - "): cspacewhiteX.set(0.313); cspacewhiteY.set(0.329)
  3570                     elif whitepointpreset.get().startswith("F11 - "): cspacewhiteX.set(0.381); cspacewhiteY.set(0.377)
  3571                 else:
  3572                     luxFloat("white X", cspacewhiteX, 0.0, 1.0, "white X", "Whitepoint X weight", gui, 0.8)
  3573                     luxFloat("white Y", cspacewhiteY, 0.0, 1.0, "white Y", "Whitepoint Y weight", gui, 0.8)
  3574     
  3575             if(gammausecspace.get() == "false"):
  3576                 if gui: gui.newline("  Gamma:")
  3577                 luxFloat("gamma", gamma, 0.1, 6.0, "gamma", "Output and RGC Gamma", gui, 2.0)
  3578         else:
  3579             # manual controls
  3580             luxFloat("white X", cspacewhiteX, 0.0, 1.0, "white X", "Whitepoint X weight", gui, 0.8)
  3581             luxFloat("white Y", cspacewhiteY, 0.0, 1.0, "white Y", "Whitepoint Y weight", gui, 0.8)
  3582             luxFloat("red X", cspaceredX, 0.0, 1.0, "red X", "Red component X weight", gui, 1.0)
  3583             luxFloat("red Y", cspaceredY, 0.0, 1.0, "red Y", "Red component Y weight", gui, 1.0)
  3584             luxFloat("green X", cspacegreenX, 0.0, 1.0, "green X", "Green component X weight", gui, 1.0)
  3585             luxFloat("green Y", cspacegreenY, 0.0, 1.0, "green Y", "Green component Y weight", gui, 1.0)
  3586             luxFloat("blue X", cspaceblueX, 0.0, 1.0, "blue X", "Blue component X weight", gui, 1.0)
  3587             luxFloat("blue Y", cspaceblueY, 0.0, 1.0, "blue Y", "Blue component Y weight", gui, 1.0)
  3588             if gui: gui.newline("  Gamma:")
  3589             luxFloat("gamma", gamma, 0.1, 6.0, "gamma", "Output and RGC Gamma", gui, 2.0)
  3590             
  3591         str += "\n   \"float colorspace_white\" [%f %f]"%(cspacewhiteX.get(), cspacewhiteY.get())
  3592         str += "\n   \"float colorspace_red\" [%f %f]"%(cspaceredX.get(), cspaceredY.get())
  3593         str += "\n   \"float colorspace_green\" [%f %f]"%(cspacegreenX.get(), cspacegreenY.get())
  3594         str += "\n   \"float colorspace_blue\" [%f %f]"%(cspaceblueX.get(), cspaceblueY.get())
  3595         str += "\n   \"float gamma\" [%f]"%(gamma.get())
  3596 
  3597     return str
  3598 
  3599 
  3600 def luxPixelFilter(scn, gui=None):
  3601     global icon_c_filter
  3602     str = ""
  3603     if scn:
  3604         filtertype = luxProp(scn, "pixelfilter.type", "mitchell")
  3605         str = luxIdentifier("PixelFilter", filtertype, ["box", "gaussian", "mitchell", "sinc", "triangle"], "FILTER", "select pixel filter type", gui, icon_c_filter)
  3606 
  3607         # Advanced toggle
  3608         parammodeadvanced = luxProp(scn, "parammodeadvanced", "false")
  3609         showadvanced = luxProp(scn, "pixelfilter.showadvanced", parammodeadvanced.get())
  3610         luxBool("advanced", showadvanced, "Advanced", "Show advanced options", gui, 0.6)
  3611         # Help toggle
  3612         showhelp = luxProp(scn, "pixelfilter.showhelp", "false")
  3613         luxHelp("help", showhelp, "Help", "Show Help Information", gui, 0.4)
  3614 
  3615         if filtertype.get() == "box":
  3616             if showadvanced.get()=="true":
  3617                 # Advanced parameters
  3618                 if gui: gui.newline()
  3619                 str += luxFloat("xwidth", luxProp(scn, "pixelfilter.box.xwidth", 0.5), 0.0, 10.0, "x-width", "Width of the filter in the x direction", gui)
  3620                 str += luxFloat("ywidth", luxProp(scn, "pixelfilter.box.ywidth", 0.5), 0.0, 10.0, "y-width", "Width of the filter in the y direction", gui)
  3621         if filtertype.get() == "gaussian":
  3622             if showadvanced.get()=="true":
  3623                 # Advanced parameters
  3624                 if gui: gui.newline()
  3625                 str += luxFloat("xwidth", luxProp(scn, "pixelfilter.gaussian.xwidth", 2.0), 0.0, 10.0, "x-width", "Width of the filter in the x direction", gui)
  3626                 str += luxFloat("ywidth", luxProp(scn, "pixelfilter.gaussian.ywidth", 2.0), 0.0, 10.0, "y-width", "Width of the filter in the y direction", gui)
  3627                 if gui: gui.newline()
  3628                 str += luxFloat("alpha", luxProp(scn, "pixelfilter.gaussian.alpha", 2.0), 0.0, 10.0, "alpha", "Gaussian rate of falloff. Lower values give blurrier images", gui)
  3629         if filtertype.get() == "mitchell":
  3630             if showadvanced.get()=="false":
  3631                 # Default parameters
  3632                 if gui: gui.newline("", 8, 0, None, [0.4,0.4,0.4])
  3633                 slidval = luxProp(scn, "pixelfilter.mitchell.sharp", 0.3333)
  3634                 luxFloat("sharpness", slidval, 0.0, 1.0, "sharpness", "Specify amount between blurred (left) and sharp/ringed (right)", gui, 2.0, 1)
  3635                 sharp = slidval.getFloat()
  3636                 width = 1.5
  3637                 str += "\n   \"float xwidth\" [%f]" % (width)
  3638                 str += "\n   \"float ywidth\" [%f]" % (width)
  3639                 str += "\n   \"float B\" [%f]" % (sharp)
  3640                 str += "\n   \"float C\" [%f]" % (sharp)
  3641                 str += "\n   \"bool supersample\" [\"true\"]"
  3642 
  3643             if showadvanced.get()=="true":
  3644                 # Advanced parameters
  3645                 if gui: gui.newline()
  3646                 str += luxFloat("xwidth", luxProp(scn, "pixelfilter.mitchell.xwidth", 1.5), 0.0, 10.0, "x-width", "Width of the filter in the x direction", gui)
  3647                 str += luxFloat("ywidth", luxProp(scn, "pixelfilter.mitchell.ywidth", 1.5), 0.0, 10.0, "y-width", "Width of the filter in the y direction", gui)
  3648                 if gui: gui.newline()
  3649     
  3650                 optmode = luxProp(scn, "pixelfilter.mitchell.optmode", "slider")
  3651                 luxOption("optmode", optmode, ["slider", "preset", "manual"], "Mode", "Mode of configuration", gui, 0.5)
  3652     
  3653                 if(optmode.get() == "slider"):
  3654                     slidval = luxProp(scn, "pixelfilter.mitchell.sharp", 0.3333)
  3655                     luxFloat("sharpness", slidval, 0.0, 1.0, "sharpness", "Specify amount between blurred (left) and sharp/ringed (right)", gui, 1.5, 1)
  3656                     # rule: B + 2*c = 1.0
  3657                     # we must apply the rule only when supersampling is disabled
  3658                     if luxProp(scn, 'pixelfilter.mitchell.supersample', 'true').get() != 'true':
  3659                         C = slidval.getFloat() * 0.5
  3660                         B = 1.0 - slidval.getFloat()
  3661                     else:
  3662                         B = C = slidval.getFloat()
  3663                     str += "\n   \"float B\" [%f]"%(B)
  3664                     str += "\n   \"float C\" [%f]"%(C)
  3665                 elif(optmode.get() == "preset"):
  3666                     print("not implemented")
  3667                 else:
  3668                     str += luxFloat("B", luxProp(scn, "pixelfilter.mitchell.B", 0.3333), 0.0, 1.0, "B", "Specify the shape of the Mitchell filter. Often best result is when B + 2C = 1", gui, 0.75)
  3669                     str += luxFloat("C", luxProp(scn, "pixelfilter.mitchell.C", 0.3333), 0.0, 1.0, "C", "Specify the shape of the Mitchell filter. Often best result is when B + 2C = 1", gui, 0.75)
  3670                 str += luxBool("supersample", luxProp(scn, "pixelfilter.mitchell.supersample", "true"), "Supersample", "Use supersampling to avoid aliasing with sharp narrow filter", gui, 1.0)
  3671 
  3672         if filtertype.get() == "sinc":
  3673             if showadvanced.get()=="true":
  3674                 # Advanced parameters
  3675                 if gui: gui.newline()
  3676                 str += luxFloat("xwidth", luxProp(scn, "pixelfilter.sinc.xwidth", 4.0), 0.0, 10.0, "x-width", "Width of the filter in the x direction", gui)
  3677                 str += luxFloat("ywidth", luxProp(scn, "pixelfilter.sinc.ywidth", 4.0), 0.0, 10.0, "y-width", "Width of the filter in the y direction", gui)
  3678                 if gui: gui.newline()
  3679                 str += luxFloat("tau", luxProp(scn, "pixelfilter.sinc.tau", 3.0), 0.0, 10.0, "tau", "Permitted number of cycles of the sinc function before it is clamped to zero", gui)
  3680         if filtertype.get() == "triangle":
  3681             if showadvanced.get()=="true":
  3682                 # Advanced parameters
  3683                 if gui: gui.newline()
  3684                 str += luxFloat("xwidth", luxProp(scn, "pixelfilter.triangle.xwidth", 2.0), 0.0, 10.0, "x-width", "Width of the filter in the x direction", gui)
  3685                 str += luxFloat("ywidth", luxProp(scn, "pixelfilter.triangle.ywidth", 2.0), 0.0, 10.0, "y-width", "Width of the filter in the y direction", gui)
  3686     return str            
  3687 
  3688 def luxSampler(scn, gui=None):
  3689     global icon_c_sampler, icon_help
  3690     str = ""
  3691     if scn:
  3692         samplertype = luxProp(scn, "sampler.type", "metropolis")
  3693         str = luxIdentifier("Sampler", samplertype, ["metropolis", "erpt", "lowdiscrepancy", "random"], "SAMPLER", "select sampler type", gui, icon_c_sampler)
  3694 
  3695         # Advanced toggle
  3696         parammodeadvanced = luxProp(scn, "parammodeadvanced", "false")
  3697         showadvanced = luxProp(scn, "sampler.showadvanced", parammodeadvanced.get())
  3698         luxBool("advanced", showadvanced, "Advanced", "Show advanced options", gui, 0.6)
  3699         # Help toggle
  3700         showhelp = luxProp(scn, "sampler.showhelp", "false")
  3701         luxHelp("help", showhelp, "Help", "Show Help Information", gui, 0.4)
  3702 
  3703         if samplertype.get() == "metropolis":
  3704             if showadvanced.get()=="false":
  3705                 # Default parameters
  3706                 if gui: gui.newline("  Mutation:", 8, 0, None, [0.4,0.4,0.4])
  3707                 strength = luxProp(scn, "sampler.metro.strength", 0.6)
  3708                 luxFloat("strength", strength, 0.0, 1.0, "strength", "Mutation Strength (lmprob = 1.0-strength)", gui, 2.0, 1)
  3709                 v = 1.0 - strength.get()
  3710                 str += "\n   \"float largemutationprob\" [%f]"%v
  3711             if showadvanced.get()=="true":
  3712                 # Advanced parameters
  3713                 if gui: gui.newline("  Mutation:")
  3714                 str += luxFloat("largemutationprob", luxProp(scn, "sampler.metro.lmprob", 0.4), 0.0, 1.0, "LM.prob.", "Probability of generating a large sample (mutation)", gui)
  3715                 str += luxInt("maxconsecrejects", luxProp(scn, "sampler.metro.maxrejects", 512), 0, 32768, "max.rejects", "number of consecutive rejects before a new mutation is forced", gui)
  3716                 if gui: gui.newline("  Screen:")
  3717                 #str += luxInt("initsamples", luxProp(scn, "sampler.metro.initsamples", 262144), 1, 1000000, "initsamples", "", gui)
  3718                 str += luxBool("usevariance",luxProp(scn, "sampler.metro.usevariance", "false"), "usevariance", "Accept based on variance", gui, 1.0)
  3719 
  3720             if showhelp.get()=="true":
  3721                 if gui: gui.newline("  Description:", 8, 0, icon_help, [0.4,0.5,0.56])
  3722                 r = gui.getRect(2,1); BGL.glRasterPos2i(r[0],r[1]+5) 
  3723                 Draw.Text("A Metropolis-Hastings mutating sampler which implements MLT", 'small')    
  3724 
  3725         if samplertype.get() == "erpt":
  3726             #str += luxInt("initsamples", luxProp(scn, "sampler.erpt.initsamples", 100000), 1, 1000000, "initsamples", "", gui)
  3727             if gui: gui.newline("  Mutation:")
  3728             str += luxInt("chainlength", luxProp(scn, "sampler.erpt.chainlength", 512), 1, 32768, "chainlength", "The number of mutations from a given seed", gui)
  3729             if gui: gui.newline()
  3730             str += luxInt("stratawidth", luxProp(scn, "sampler.erpt.stratawidth", 256), 1, 32768, "stratawidth", "The number of x/y strata for stratified sampling of seeds", gui)
  3731 
  3732         if samplertype.get() == "lowdiscrepancy":
  3733             if gui: gui.newline("  PixelSampler:")
  3734             str += luxOption("pixelsampler", luxProp(scn, "sampler.lowdisc.pixelsampler", "lowdiscrepancy"), ["linear", "tile", "random", "vegas","lowdiscrepancy","hilbert"], "pixel-sampler", "select pixel-sampler", gui)
  3735             str += luxInt("pixelsamples", luxProp(scn, "sampler.lowdisc.pixelsamples", 4), 1, 2048, "samples", "Average number of samples taken per pixel. More samples create a higher quality image at the cost of render time", gui)
  3736 
  3737         if samplertype.get() == "random":
  3738             if gui: gui.newline("  PixelSampler:")
  3739             str += luxOption("pixelsampler", luxProp(scn, "sampler.random.pixelsampler", "vegas"), ["linear", "tile", "random", "vegas","lowdiscrepancy","hilbert"], "pixel-sampler", "select pixel-sampler", gui)
  3740             if gui: gui.newline()
  3741             str += luxInt("pixelsamples", luxProp(scn, "sampler.random.pixelsamples", 4), 1, 512, "pixelsamples", "Allows you to specify how many samples per pixel are computed", gui)
  3742     return str            
  3743 
  3744 def luxSurfaceIntegrator(scn, gui=None):
  3745     global icon_c_integrator
  3746     str = ""
  3747     if scn:
  3748         integratortype = luxProp(scn, "sintegrator.type", "bidirectional")
  3749         
  3750         str = luxIdentifier("SurfaceIntegrator", integratortype, ["directlighting", "path", "bidirectional", "exphotonmap", "distributedpath", "igi" ], "INTEGRATOR", "select surface integrator type", gui, icon_c_integrator)
  3751 
  3752         # Advanced toggle
  3753         parammodeadvanced = luxProp(scn, "parammodeadvanced", "false")
  3754         showadvanced = luxProp(scn, "sintegrator.showadvanced", parammodeadvanced.get())
  3755         luxBool("advanced", showadvanced, "Advanced", "Show advanced options", gui, 0.6)
  3756         # Help toggle
  3757         showhelp = luxProp(scn, "sintegrator.showhelp", "false")
  3758         luxHelp("help", showhelp, "Help", "Show Help Information", gui, 0.4)
  3759 
  3760         if integratortype.get() == "directlighting":
  3761             if showadvanced.get()=="false":
  3762                 # Default parameters
  3763                 if gui: gui.newline("  Depth:", 8, 0, None, [0.4,0.4,0.4])
  3764                 str += luxInt("maxdepth", luxProp(scn, "sintegrator.dlighting.maxdepth", 8), 0, 2048, "bounces", "The maximum recursion depth for ray casting", gui, 2.0)
  3765 
  3766             if showadvanced.get()=="true":
  3767                 # Advanced parameters
  3768                 if gui: gui.newline("  Depth:")
  3769                 str += luxInt("maxdepth", luxProp(scn, "sintegrator.dlighting.maxdepth", 8), 0, 2048, "max-depth", "The maximum recursion depth for ray casting", gui)
  3770                 str += luxOption("lightstrategy", luxProp(scn, "sintegrator.dlighting.lightstrategy", "auto"), ["one", "all", "auto", "importance", "powerimp", "allpowerimp", "logpowerimp"], "light strategy", "select directlighting strategy", gui)
  3771                 str += luxInt("shadowraycount", luxProp(scn, "sintegrator.dlighting.shadowraycount", 1), 1, 512, "shadow ray count", "Number of shadow rays traced per sample", gui, 2.0)
  3772 
  3773         if integratortype.get() == "path":
  3774             if showadvanced.get()=="false":
  3775                 # Default parameters
  3776                 if gui: gui.newline("  Depth:", 8, 0, None, [0.4,0.4,0.4])
  3777                 str += luxInt("maxdepth", luxProp(scn, "sintegrator.path.maxdepth", 10), 0, 2048, "bounces", "The maximum recursion depth for ray casting", gui, 1.0)
  3778                 ienv = luxProp(scn, "sintegrator.path.ienvironment", "true")
  3779                 str += luxBool("includeenvironment", ienv, "Include Environment", "Enable/Disable rendering of environment lightsources", gui)
  3780 
  3781             if showadvanced.get()=="true":
  3782                 # Advanced parameters
  3783                 if gui: gui.newline("  Depth:")
  3784                 str += luxInt("maxdepth", luxProp(scn, "sintegrator.path.maxdepth", 10), 0, 2048, "maxdepth", "The maximum recursion depth for ray casting", gui)
  3785                 str += luxOption("lightstrategy", luxProp(scn, "sintegrator.path.lightstrategy", "auto"), ["one", "all", "auto", "importance", "powerimp", "allpowerimp", "logpowerimp"], "light strategy", "select directlighting strategy", gui)
  3786                 if gui: gui.newline("  RR:")
  3787                 rrstrat = luxProp(scn, "sintegrator.path.rrstrategy", "efficiency")
  3788                 str += luxOption("rrstrategy", rrstrat, ["efficiency", "probability", "none"], "RR strategy", "select Russian Roulette path termination strategy", gui)
  3789                 if rrstrat.get() == "probability":
  3790                     str += luxFloat("rrcontinueprob", luxProp(scn, "sintegrator.path.rrcontinueprob", 0.65), 0.0, 1.0, "rrprob", "Russian roulette continue probability", gui)
  3791                 ienv = luxProp(scn, "sintegrator.path.ienvironment", "true")
  3792                 str += luxBool("includeenvironment", ienv, "Include Environment", "Enable/Disable rendering of environment lightsources", gui)
  3793                 str += luxInt("shadowraycount", luxProp(scn, "sintegrator.dlighting.shadowraycount", 1), 1, 512, "shadow ray count", "Number of shadow rays traced per sample", gui, 2.0)
  3794 
  3795         if integratortype.get() == "bidirectional":
  3796             if showadvanced.get()=="false":
  3797                 # Default parameters
  3798                 if gui: gui.newline("  Depth:", 8, 0, None, [0.4,0.4,0.4])
  3799                 bounces = luxProp(scn, "sintegrator.bidir.bounces", 16)
  3800                 luxInt("bounces", bounces, 5, 32, "bounces", "The maximum recursion depth for ray casting (in both directions)", gui, 2.0)
  3801                 str += "\n   \"integer eyedepth\" [%i]\n"%bounces.get()
  3802                 str += "   \"integer lightdepth\" [%i]"%bounces.get()
  3803 
  3804             if showadvanced.get()=="true":
  3805                 # Advanced parameters
  3806                 if gui: gui.newline("  Depth:")
  3807                 str += luxInt("eyedepth", luxProp(scn, "sintegrator.bidir.eyedepth", 16), 0, 2048, "eyedepth", "The maximum recursion depth for ray casting", gui)
  3808                 str += luxInt("lightdepth", luxProp(scn, "sintegrator.bidir.lightdepth", 16), 0, 2048, "lightdepth", "The maximum recursion depth for light ray casting", gui)
  3809                 str += luxOption("strategy", luxProp(scn, "sintegrator.bidir.strategy", "auto"), ["one", "all", "auto"], "strategy", "select directlighting strategy", gui)
  3810                 
  3811                 str += luxFloat('eyerrthreshold', luxProp(scn, "sintegrator.bidir.eyerrthreshold", 0), 0, 1, "eyerrthreshold", "The minimum probability for russian roulette eye subpath termination ", gui)
  3812                 str += luxFloat('lightrrthreshold', luxProp(scn, "sintegrator.bidir.lightrrthreshold", 0), 0, 1, "lightrrthreshold", "The minimum probability for russian roulette light subpath termination ", gui)
  3813 
  3814         if integratortype.get() == "exphotonmap":
  3815             if gui: gui.newline("  Render:")
  3816             str += luxOption("renderingmode", luxProp(scn, "sintegrator.photonmap.renderingmode", "directlighting"), ["directlighting", "path"], "renderingmode", "select rendering mode", gui)
  3817             str += luxOption("lightstrategy", luxProp(scn, "sintegrator.photonmap.lightstrategy", "auto"), ["one", "all", "auto", "importance", "powerimp", "allpowerimp", "logpowerimp"], "light strategy", "select directlighting strategy", gui)
  3818             str += luxInt("shadowraycount", luxProp(scn, "sintegrator.dlighting.shadowraycount", 1), 1, 512, "shadow ray count", "Number of shadow rays traced per sample", gui, 2.0)
  3819             str += luxInt("maxphotondepth", luxProp(scn, "sintegrator.photonmap.maxphotondepth", 10), 1, 1024, "maxphotondepth", "The maximum recursion depth of photon tracing", gui)
  3820             str += luxInt("maxdepth", luxProp(scn, "sintegrator.photonmap.maxdepth", 6), 1, 1024, "maxdepth", "The maximum recursion depth of specular reflection and refraction", gui)
  3821             str += luxFloat("maxphotondist", luxProp(scn, "sintegrator.photonmap.maxphotondist", 0.1), 0.0, 10.0, "maxphotondist", "The maximum distance between a point being shaded and a photon that can contribute to that point", gui)
  3822             str += luxInt("nphotonsused", luxProp(scn, "sintegrator.photonmap.nphotonsused", 50), 0, 1000000, "nphotonsused", "The number of photons to use in density estimation", gui)
  3823 
  3824             if gui: gui.newline("  Photons:")
  3825             str += luxInt("indirectphotons", luxProp(scn, "sintegrator.photonmap.idphotons", 200000), 0, 10000000, "indirect", "The number of photons to shoot for indirect lighting during preprocessing of the photon map", gui)
  3826             str += luxInt("directphotons", luxProp(scn, "sintegrator.photonmap.dphotons", 1000000), 0, 10000000, "direct", "The number of photons to shoot for direct lighting during preprocessing of the photon map", gui)
  3827             str += luxInt("causticphotons", luxProp(scn, "sintegrator.photonmap.cphotons", 20000), 0, 10000000, "caustic", "The number of photons to shoot for caustics during preprocessing of the photon map", gui)
  3828             if gui: gui.newline("  FinalGather:")
  3829             fg = luxProp(scn, "sintegrator.photonmap.fgather", "true")
  3830             str += luxBool("finalgather", fg, "finalgather", "Enable use of final gather during rendering", gui)
  3831             if fg.get() == "true":
  3832                 rrstrat = luxProp(scn, "sintegrator.photonmap.rrstrategy", "efficiency")
  3833                 str += luxOption("rrstrategy", rrstrat, ["efficiency", "probability", "none"], "RR strategy", "select Russian Roulette gather termination strategy", gui)
  3834                 str += luxInt("finalgathersamples", luxProp(scn, "sintegrator.photonmap.fgathers", 32), 1, 1024, "samples", "The number of finalgather samples to take per pixel during rendering", gui)
  3835                 str += luxFloat("gatherangle", luxProp(scn, "sintegrator.photonmap.gangle", 10.0), 0.0, 360.0, "gatherangle", "Angle for final gather", gui)
  3836                 if rrstrat.get() == "probability":
  3837                     str += luxFloat("rrcontinueprob", luxProp(scn, "sintegrator.photonmap.rrcontinueprob", 0.65), 0.0, 1.0, "rrcontinueprob", "Probability for russian roulette particle tracing termination", gui)
  3838 
  3839             if showadvanced.get()=="true":
  3840                 if gui: gui.newline("  Debug options:")
  3841                 dbg_enabledirect = luxProp(scn, "sintegrator.photonmap.dbg_enabledirect", "false")
  3842                 dbg_enableradiancemap = luxProp(scn, "sintegrator.photonmap.dbg_enableradiancemap", "false")
  3843                 dbg_enableindircaustic = luxProp(scn, "sintegrator.photonmap.dbg_enableindircaustic", "false")
  3844                 dbg_enableindirdiffuse = luxProp(scn, "sintegrator.photonmap.dbg_enableindirdiffuse", "false")
  3845                 dbg_enableindirspecular = luxProp(scn, "sintegrator.photonmap.dbg_enableindirspecular", "false")
  3846                 str += luxBool("dbg_enabledirect", dbg_enabledirect, "Direct lighing", "Enable visual inspection of direct ligting", gui)
  3847                 str += luxBool("dbg_enableradiancemap", dbg_enableradiancemap, "Radiancemap", "Enable visual inspection of radiance map", gui)
  3848                 str += luxBool("dbg_enableindircaustic", dbg_enableindircaustic, "Indirect caustics", "Enable visual inspection of indirect caustic map", gui)
  3849                 str += luxBool("dbg_enableindirdiffuse", dbg_enableindirdiffuse, "Indirect diffuse", "Enable visual inspection of indirect diffuse map", gui)
  3850                 str += luxBool("dbg_enableindirspecular", dbg_enableindirspecular, "Indirect specular", "Enable visual inspection of indirect specular map", gui)
  3851 
  3852         if integratortype.get() == "distributedpath":
  3853             str += luxOption("strategy", luxProp(scn, "sintegrator.distributedpath.strategy", "auto"), ["one", "all", "auto"], "strategy", "select directlighting strategy", gui)
  3854             if gui: gui.newline("  Direct:")
  3855             str += luxBool("directsampleall",luxProp(scn, "sintegrator.distributedpath.directsampleall", "true"), "Direct ALL", "Include diffuse direct light sample at first vertex", gui, 0.7)
  3856             str += luxInt("directsamples", luxProp(scn, "sintegrator.distributedpath.directsamples", 1), 0, 1024, "s", "The number of direct light samples to take at the eye vertex", gui, 0.3)
  3857             str += luxBool("indirectsampleall",luxProp(scn, "sintegrator.distributedpath.indirectsampleall", "false"), "Indirect ALL", "Include diffuse direct light sample at first vertex", gui, 0.7)
  3858             str += luxInt("indirectsamples", luxProp(scn, "sintegrator.distributedpath.indirectsamples", 1), 0, 1024, "s", "The number of direct light samples to take at the remaining vertices", gui, 0.3)
  3859             if gui: gui.newline("  Diffuse:")
  3860             str += luxInt("diffusereflectdepth", luxProp(scn, "sintegrator.distributedpath.diffusereflectdepth", 3), 0, 2048, "Reflect", "The maximum recursion depth for diffuse reflection ray casting", gui, 0.5)
  3861             str += luxInt("diffusereflectsamples", luxProp(scn, "sintegrator.distributedpath.diffusereflectsamples", 1), 0, 1024, "s", "The number of diffuse reflection samples to take at the eye vertex", gui, 0.3)
  3862             str += luxInt("diffuserefractdepth", luxProp(scn, "sintegrator.distributedpath.diffuserefractdepth", 5), 0, 2048, "Refract", "The maximum recursion depth for diffuse refraction ray casting", gui, 0.5)
  3863             str += luxInt("diffuserefractsamples", luxProp(scn, "sintegrator.distributedpath.diffuserefractsamples", 1), 0, 1024, "s", "The number of diffuse refraction samples to take at the eye vertex", gui, 0.3)
  3864             str += luxBool("directdiffuse",luxProp(scn, "sintegrator.distributedpath.directdiffuse", "true"), "DL", "Include diffuse direct light sample at first vertex", gui, 0.20)
  3865             str += luxBool("indirectdiffuse",luxProp(scn, "sintegrator.distributedpath.indirectdiffuse", "true"), "IDL", "Include diffuse indirect light sample at first vertex", gui, 0.20)
  3866             if gui: gui.newline("  Glossy:")
  3867             str += luxInt("glossyreflectdepth", luxProp(scn, "sintegrator.distributedpath.glossyreflectdepth", 2), 0, 2048, "Reflect", "The maximum recursion depth for glossy reflection ray casting", gui, 0.50)
  3868             str += luxInt("glossyreflectsamples", luxProp(scn, "sintegrator.distributedpath.glossyreflectsamples", 1), 0, 1024, "s", "The number of glossy reflection samples to take at the eye vertex", gui, 0.3)
  3869             str += luxInt("glossyrefractdepth", luxProp(scn, "sintegrator.distributedpath.glossyrefractdepth", 5), 0, 2048, "Refract", "The maximum recursion depth for glossy refraction ray casting", gui, 0.50)
  3870             str += luxInt("glossyrefractsamples", luxProp(scn, "sintegrator.distributedpath.glossyrefractsamples", 1), 0, 1024, "s", "The number of glossy refraction samples to take at the eye vertex", gui, 0.3)
  3871             str += luxBool("directglossy",luxProp(scn, "sintegrator.distributedpath.directglossy", "true"), "DL", "Include glossy direct light sample at first vertex", gui, 0.20)
  3872             str += luxBool("indirectglossy",luxProp(scn, "sintegrator.distributedpath.indirectglossy", "true"), "IDL", "Include glossy indirect light sample at first vertex", gui, 0.20)
  3873             if gui: gui.newline("  Specular:")
  3874             str += luxInt("specularreflectdepth", luxProp(scn, "sintegrator.distributedpath.specularreflectdepth", 3), 0, 2048, "Reflect", "The maximum recursion depth for specular reflection ray casting", gui, 1.0)
  3875             str += luxInt("specularrefractdepth", luxProp(scn, "sintegrator.distributedpath.specularrefractdepth", 5), 0, 2048, "Refract", "The maximum recursion depth for specular refraction ray casting", gui, 1.0)
  3876             #if gui: gui.newline("  Caustics:")
  3877             #str += luxBool("causticsondiffuse",luxProp(scn, "sintegrator.distributedpath.causticsondiffuse", "false"), "Caustics on Diffuse", "Enable caustics on diffuse surfaces (warning: might generate bright pixels)", gui, 1.0)
  3878             #str += luxBool("causticsonglossy",luxProp(scn, "sintegrator.distributedpath.causticsonglossy", "true"), "Caustics on Glossy", "Enable caustics on glossy surfaces (warning: might generate bright pixels)", gui, 1.0)
  3879 
  3880             usereject = luxProp(scn, "sintegrator.distributedpath.usereject", "false")
  3881             luxCollapse("usereject", usereject, "Rejection", "Enable Rejection system to eliminate bright contributions", gui, 2.0)
  3882 
  3883             if usereject.get()=="true":
  3884                 if gui: gui.newline("  Diffuse:")
  3885                 
  3886                 diffusereflectreject = luxProp(scn, "sintegrator.distributedpath.difreflreject", "false")
  3887                 str += luxBool("diffusereflectreject", diffusereflectreject, "Reflect", "Enable Rejection for Diffuse Reflection", gui, 0.4)
  3888                 if diffusereflectreject.get()=="true":
  3889                     str += luxFloat("diffusereflectreject_threshold", luxProp(scn, "sintegrator.distributedpath.difreflrejectthr", 10.0), 0.01, 10.0, "Thr", "The Average Threshold to reject", gui, 0.6)
  3890             
  3891                 diffuserefractreject = luxProp(scn, "sintegrator.distributedpath.difrefrreject", "false")
  3892                 str += luxBool("diffuserefractreject", diffuserefractreject, "Refract", "Enable Rejection for Diffuse Refraction", gui, 0.4)
  3893                 if diffuserefractreject.get()=="true":
  3894                     str += luxFloat("diffuserefractreject_threshold", luxProp(scn, "sintegrator.distributedpath.difrefrrejectthr", 10.0), 0.01, 10.0, "Thr", "The Average Threshold to reject", gui, 0.6)
  3895             
  3896                 if gui: gui.newline("  Glossy:")
  3897                 
  3898                 glossyreflectreject = luxProp(scn, "sintegrator.distributedpath.glosreflreject", "false")
  3899                 str += luxBool("glossyreflectreject", glossyreflectreject, "Reflect", "Enable Rejection for Glossy Reflection", gui, 0.4)
  3900                 if glossyreflectreject.get()=="true":
  3901                     str += luxFloat("glossyreflectreject_threshold", luxProp(scn, "sintegrator.distributedpath.glosreflrejectthr", 10.0), 0.01, 10.0, "Thr", "The Average Threshold to reject", gui, 0.6)
  3902             
  3903                 glossyrefractreject = luxProp(scn, "sintegrator.distributedpath.glosrefrreject", "false")
  3904                 str += luxBool("glossyrefractreject", glossyrefractreject, "Refract", "Enable Rejection for Glossy Refraction", gui, 0.4)
  3905                 if glossyrefractreject.get()=="true":
  3906                     str += luxFloat("glossyrefractreject_threshold", luxProp(scn, "sintegrator.distributedpath.glosrefrrejectthr", 10.0), 0.01, 10.0, "Thr", "The Average Threshold to reject", gui, 0.6)
  3907     
  3908         if integratortype.get() == "igi":
  3909             if gui: gui.newline("  Depth:", 8, 0, None, [0.4,0.4,0.4])
  3910             depth = luxProp(scn, "sintegrator.igi.maxdepth", 5)
  3911             luxInt("maxdepth", depth, 1, 32, "maxdepth", "The maximum recursion depth for ray casting", gui, 2.0)
  3912             if showadvanced.get()=="true":
  3913                 # Advanced parameters
  3914                 if gui: gui.newline("  VLights:")
  3915                 str += luxInt("nsets", luxProp(scn, "sintegrator.igi.nsets", 4), 1, 100, "nsets", "The number of virtual lights sets", gui)
  3916                 str += luxInt("nlights", luxProp(scn, "sintegrator.igi.nlights", 64), 1, 1000, "nlights", "The number of light paths per light set", gui)
  3917                 str += luxFloat("strategy", luxProp(scn, "sintegrator.igi.mindist", 0.1), 0.01, 10.0, "mindist", "The minimal distance to a virtual light to take it into account", gui)
  3918 
  3919     
  3920     return str
  3921 
  3922 def luxVolumeIntegrator(scn, gui=None):
  3923     global icon_c_volumeintegrator
  3924     str = ""
  3925     if scn:
  3926         integratortype = luxProp(scn, "vintegrator.type", "single")
  3927         str = luxIdentifier("VolumeIntegrator", integratortype, ["emission", "single"], "VOLUME INT", "select volume integrator type", gui, icon_c_volumeintegrator)
  3928         if integratortype.get() == "emission":
  3929             str += luxFloat("stepsize", luxProp(scn, "vintegrator.emission.stepsize", 1.0), 0.0, 100.0, "stepsize", "Stepsize for volumes", gui)
  3930         if integratortype.get() == "single":
  3931             str += luxFloat("stepsize", luxProp(scn, "vintegrator.emission.stepsize", 1.0), 0.0, 100.0, "stepsize", "Stepsize for volumes", gui)
  3932     return str
  3933 
  3934 def luxEnvironment(scn, gui=None):
  3935     global icon_c_environment
  3936     str = ""
  3937     if scn:
  3938         def componentRotation(key, hint, scn, gui, complimentary=False):
  3939             str = ''
  3940             rotZ = luxProp(scn, key+".rotation", 0.0)
  3941             rotY = luxProp(scn, key+".rotationY", 0.0)
  3942             rotX = luxProp(scn, key+".rotationX", 0.0)
  3943             luxFloat(key+".rotationX", rotX, 0.0, 360.0, "rot X", hint+" rotation X", gui, 0.66)
  3944             luxFloat(key+".rotationY", rotY, 0.0, 360.0, "rot Y", hint+" rotation Y", gui, 0.66)
  3945             luxFloat(key+".rotationZ", rotZ, 0.0, 360.0, "rot Z", hint+" rotation Z", gui, 0.66)
  3946             if rotZ.get() != 0 or rotY.get() != 0 or rotX.get() != 0:
  3947                 if complimentary:
  3948                     str += "\nAttributeEnd\nAttributeBegin\n"
  3949                 str += "\tRotate %d 1 0 0\n"%(rotX.get())
  3950                 str += "\tRotate %d 0 1 0\n"%(rotY.get())
  3951                 str += "\tRotate %d 0 0 1\n"%(rotZ.get())
  3952             if gui: gui.newline()
  3953             return str
  3954         envtype = luxProp(scn, "env.type", "infinite")
  3955         lsstr = luxIdentifier("LightSource", envtype, ["none", "infinite", "sunsky"], "ENVIRONMENT", "select environment light type", gui, icon_c_environment)
  3956         if gui: gui.newline()
  3957         suncomponent = 0
  3958         sunhalo = 0
  3959         str = ""
  3960         sunstr = ""
  3961         skystr = ""
  3962         
  3963         if envtype.get() != "none":
  3964             
  3965             if envtype.get() in ("infinite", "infinitesample"):
  3966                 str += componentRotation('env', 'environment map', scn, gui)
  3967                 env_lg = luxProp(scn, "env.lightgroup", "default")
  3968                 luxString("env.lightgroup", env_lg, "lightgroup", "Environment light group", gui)
  3969                 if luxProp(scn, "nolg", "false").get()!="true":
  3970                     lsstr = '\nLightGroup "' + env_lg.get() + '"' + lsstr
  3971                 str += "\t"+lsstr
  3972                 str += luxFloat("gain", luxProp(scn, "env.infinite.gain", 1.0), 0.0001, 10000.0, "gain", "Infinite Env Gain", gui, 1.0)
  3973                 mapping = luxProp(scn, "env.infinite.mapping", "latlong")
  3974                 mappings = ["latlong","angular","vcross"]
  3975                 mapstr = luxOption("mapping", mapping, mappings, "mapping", "Select mapping type", gui, 1.0)
  3976                 mapstr += luxFloat("gamma", luxProp(scn, "env.infinite.gamma", 1.0), 0.0, 6.0, "gamma", "", gui, 1.0)
  3977                 map = luxProp(scn, "env.infinite.mapname", "")
  3978                 mapstr += luxFile("mapname", map, "map-file", "filename of the environment map", gui, 2.0)
  3979                 
  3980                 if map.get() != "":
  3981                     str += mapstr
  3982                 else:
  3983                     try:
  3984                         worldcolor = Blender.World.Get('World').getHor()
  3985                         str += "\n   \"color L\" [%g %g %g]" %(worldcolor[0], worldcolor[1], worldcolor[2])
  3986                     except: pass
  3987 
  3988             if envtype.get() == "sunsky":
  3989                 str += componentRotation('sunsky', 'sunsky', scn, gui)
  3990                 skycomponentProp = luxProp(scn, "env.sunsky.skycomponent", "true")
  3991                 luxCollapse("skycomponent", skycomponentProp, "Sky Component", "Add Skydome Light Component", gui, 2.0)
  3992                 if skycomponentProp.get() == "true":
  3993                     sun_lg = luxProp(scn, "env.sky_lightgroup", "default")
  3994                     luxString("env.lightgroup", sun_lg, "lightgroup", "Sky component light group", gui)
  3995                     if luxProp(scn, "nolg", "false").get() != "true":
  3996                         skystr += '\nLightGroup "' + sun_lg.get() + '"'
  3997                     skystr += "\nLightSource \"sky\" "
  3998                     skystr += luxFloat("gain", luxProp(scn, "env.sunsky.sky_gain", 1.0), 0.0001, 100.0, "gain", "Sun light gain", gui)
  3999                     sunhaloProp = luxProp(scn, "env.sunsky.halo", "true")
  4000                     luxBool("sunhalo", sunhaloProp, "Sun Halo", "Atmospheric halo around sun disk", gui)
  4001                     skystr += luxFloat("turbidity", luxProp(scn, "env.sunsky.sky_turbidity", 2.2), 2.0, 50.0, "turbidity", "Atmospheric turbidity", gui)
  4002                     if sunhaloProp.get() == "true":
  4003                         sunhalo = 1
  4004 
  4005                 suncomponentProp = luxProp(scn, "env.sunsky.suncomponent", envtype.get() == "sunsky" and "true" or "false")
  4006                 luxCollapse("suncomponent", suncomponentProp, "Sun Component", "Add Sunlight Component", gui, 2.0)
  4007                 if suncomponentProp.get() == "true":
  4008                     sun_lg = luxProp(scn, "env.sun_lightgroup", "default")
  4009                     luxString("env.lightgroup", sun_lg, "lightgroup", "Sun component light group", gui)
  4010                     if luxProp(scn, "nolg", "false").get() != "true":
  4011                         sunstr += '\nLightGroup "' + sun_lg.get() + '"'
  4012                     sunstr += "\nLightSource \"sun\" "
  4013                     suncomponent = 1
  4014 
  4015             if envtype.get() in ("infinite", "infinitesample"):
  4016                 infinitesunProp = luxProp(scn, "env.infinite.suncomponent", "false")
  4017                 luxCollapse("infinitesun", infinitesunProp, "Sun Component", "Add Sunlight Component", gui, 2.0)
  4018                 if infinitesunProp.get() == "true":
  4019                     str += componentRotation('envsun', 'sun component', scn, gui, True)
  4020                     sun_lg = luxProp(scn, "env.sun_lightgroup", "default")
  4021                     luxString("env.lightgroup", sun_lg, "lightgroup", "Sun component light group", gui)
  4022                     if luxProp(scn, "nolg", "false").get() != "true":
  4023                         sunstr += '\nLightGroup "' + sun_lg.get() + '"'
  4024                     sunstr += "\nLightSource \"sun\" "
  4025                     suncomponent = 1
  4026 
  4027             if suncomponent == 1:
  4028                 sun = None
  4029                 for obj in scn.objects:
  4030                     if (obj.getType() == "Lamp") and ((obj.Layers & scn.Layers) > 0):
  4031                         if obj.getData(mesh=1).getType() == 1: # sun object # data
  4032                             sun = obj
  4033                 if sun:
  4034                     sunstr += luxFloat("gain", luxProp(scn, "env.sunsky.sun_gain", 1.0), 0.0001, 100.0, "gain", "Sun light gain", gui)
  4035                     
  4036                     invmatrix = Mathutils.Matrix(sun.getInverseMatrix())
  4037                     sunstr += "\n   \"vector sundir\" [%f %f %f]" %(invmatrix[0][2], invmatrix[1][2], invmatrix[2][2])
  4038                     sunstr += luxFloat("relsize", luxProp(scn, "env.sunsky.sun_relsize", 1.0), 0.0, 100.0, "rel.size", "Relative sun disk size", gui)
  4039                     sunstr += luxFloat("turbidity", luxProp(scn, "env.sunsky.sun_turbidity", 2.2), 2.0, 50.0, "turbidity", "Atmospheric turbidity", gui)
  4040                     
  4041                     if sunhalo == 1 and skystr != "":
  4042                         skystr += "\n   \"vector sundir\" [%f %f %f]" %(invmatrix[0][2], invmatrix[1][2], invmatrix[2][2])
  4043                     
  4044                     showGeo = luxProp(sun, 'sc.show', 'false')
  4045                     if gui: luxCollapse("sc.show", showGeo, "Geographic Sun", "Set sun position by world location, date and time", gui, 2.0)
  4046                     if showGeo.get() == 'true':
  4047                         if gui: gui.newline("Geographic:")
  4048                         sc = sun_calculator(sun)
  4049                         
  4050                         luxInt("sc.day", luxProp(sun, "sc.day", 1), 1, 31, "day", "Local date: day", gui, 0.66)
  4051                         luxInt("sc.month", luxProp(sun, "sc.month", 1), 1, 12, "month", "Local date: month", gui, 0.67)
  4052                         luxInt("sc.year", luxProp(sun, "sc.year", 2009), 1, 2500, "year", "Local date: year", gui, 0.66)
  4053                         
  4054                         sc_hour = luxProp(sun, "sc.hour", 0)
  4055                         sc_minute = luxProp(sun, "sc.minute", 0)
  4056                         sc_dayminutes = luxProp(sun, 'sc.dayminutes', 0.0)
  4057                         sunIpo = luxProp(sun, 'sc.ipo', 'false')
  4058                         if luxProp(scn, 'useparamkeys', 'false').get() == 'true':
  4059                             if gui: luxCollapse('sc.ipo', sunIpo, 'Sun IPO Keyframing', 'Set sun position using IPO keyframes', gui, 2.0)
  4060                             if sunIpo.get() == 'true':
  4061                                 luxFloat('sc.dayminutes', sc_dayminutes, 0.0, 1439.0, 'IPO day minutes', 'Day minutes for IPO keyframing', gui, 2.0)
  4062                                 if int(sc_dayminutes.get()) != sc_hour.get()*60 + sc_minute.get():
  4063                                     sc_hour.set(int(sc_dayminutes.get()/60))
  4064                                     sc_minute.set(int(sc_dayminutes.get()%60))
  4065                                     sc.compute(False)
  4066                         luxInt("sc.hour", sc_hour, 0, 23, "hour", "Local time: hour", gui, 0.72)
  4067                         luxInt("sc.minute", sc_minute, 0, 59, "minute", "Local time: minute", gui, 0.72)
  4068                         luxBool("sc.dst", luxProp(sun, "sc.dst", 'false'), "DST", "DST", gui, 0.28)
  4069                         if gui:
  4070                             r = gui.getRect(0.28,1)
  4071                             Draw.Button("NOW", 0, r[0], r[1], r[2], r[3], "Set to current time", lambda e,v: sc.now())
  4072                         
  4073                             r = gui.getRect(0.3,1)
  4074                             Draw.Button("Preset", 0, r[0], r[1], r[2], r[3], "Choose a preset location", lambda e,v: sc.set_location(
  4075                                 Draw.PupTreeMenu(sun_calculator.location_list)
  4076                             ))
  4077                         
  4078                         luxFloat("sc.lat", luxProp(sun, "sc.lat", 0.0), -90.0, 90.0, "lat", "Location: latitude. Positive value for north hemisphere, negative value for south hemisphere", gui, 0.56)
  4079                         luxFloat("sc.long", luxProp(sun, "sc.long", 0.0), -180.0, 180.0, "long", "Location: longitude. Positive value for west hemisphere, negative value for east hemisphere", gui, 0.56)
  4080                         luxInt("sc.tz", luxProp(sun, "sc.tz", 0), -12, 12, "timezone", "Local time: timezone offset from GMT", gui, 0.56)
  4081                         
  4082                         if gui:
  4083                             r = gui.getRect(2,1)
  4084                             Draw.Button("Calculate", 0, r[0], r[1], r[2], r[3], "Calculate sun's position", lambda e,v: sc.compute())
  4085                 else:
  4086                     sunstr = ""
  4087                     if gui:
  4088                         gui.newline(); r = gui.getRect(2,1); BGL.glRasterPos2i(r[0],r[1]+5) 
  4089                         Draw.Text("create a Blender Sun Lamp")
  4090             
  4091             if skystr != "": str += skystr
  4092             if sunstr != "": str += sunstr
  4093             
  4094             str += "\n"
  4095         #if gui: gui.newline("GLOBAL:", 8, 0, None, [0.75,0.5,0.25])
  4096         #luxFloat("scale", luxProp(scn, "global.scale", 1.0), 0.0, 10.0, "scale", "global world scale", gui)
  4097         if gui: gui.newline("WORLD MEDIUM:", 8, 0, None, [0.75,0.5,0.25])
  4098         luxNamedVolumeTexture(0, gui)
  4099         
  4100     return str
  4101 
  4102 class sun_calculator:
  4103     #Based on SunLight v1.0 by Miguel Kabantsov (miguelkab@gmail.com)
  4104     #Replaces the faulty sun position calculation algorythm with a precise calculation (Source for algorythm: http://de.wikipedia.org/wiki/Sonnenstand),
  4105     #Co-Ordinates: http://www.bcca.org/misc/qiblih/latlong.html
  4106     #Author: Nils-Peter Fischer (Nils-Peter.Fischer@web.de)
  4107     
  4108     sun = None
  4109     
  4110     lat = 0
  4111     long = 0
  4112     
  4113     hour = 0
  4114     min = 0
  4115     tz = 0
  4116     dst = 'false'
  4117     
  4118     day = 0
  4119     month = 0
  4120     year = 0
  4121     
  4122     location_list = [
  4123         ("EUROPE",[
  4124             ("Antwerp, Belgium",          67),
  4125             ("Berlin, Germany",            1),
  4126             ("Bratislava, Slovak Republic", 70),
  4127             ("Brno, Czech Republic",      72),
  4128             ("Brussles, Belgium",         68),
  4129             ("Geneva, Switzerland",       65),
  4130             ("Helsinki, Finland",          7),
  4131             ("Innsbruck, Austria",        62),
  4132             ("Kyiv, Ukraine",             64),
  4133             ("London, England",           10),
  4134             ("Lyon, France",              66),
  4135             ("Nitra, Slovak Republic",    69),
  4136             ("Oslo, Norway",              58),
  4137             ("Paris, France",             15),
  4138             ("Praha, Czech Republic",     71),
  4139             ("Rome, Italy",               18),
  4140             ("Telfs, Austria",            63),
  4141             ("Warsaw, Poland",            74),
  4142             ("Wroclaw, Poland",           73),
  4143             ("Zurich, Switzerland",       21),
  4144         ]),
  4145     
  4146         ("WORLD CITIES", [
  4147             ("Beijing, China",             0),
  4148             ("Bombay, India",              2),
  4149             ("Buenos Aires, Argentina",    3),
  4150             ("Cairo, Egypt",               4),
  4151             ("Cape Town, South Africa",    5),
  4152             ("Caracas, Venezuela",         6),
  4153             ("Curitiba, Brazil",          60),
  4154             ("Hong Kong, China",           8),
  4155             ("Jerusalem, Israel",          9),
  4156             ("Joinville, Brazil",         61),
  4157             ("Mexico City, Mexico",       11),
  4158             ("Moscow, Russia",            12),
  4159             ("New Delhi, India",          13),
  4160             ("Ottawa, Canada",            14),
  4161             ("Rio de Janeiro, Brazil",    16),
  4162             ("Riyadh, Saudi Arabia",      17),
  4163             ("Sao Paulo, Brazil",         59),
  4164             ("Sydney, Australia",         19),
  4165             ("Tokyo, Japan",              20), 
  4166         ]),
  4167         
  4168         ("US CITIES", [
  4169             ("Albuquerque, NM",           22),
  4170             ("Anchorage, AK",             23),
  4171             ("Atlanta, GA",               24),
  4172             ("Austin, TX",                25),
  4173             ("Birmingham, AL",            26),
  4174             ("Bismarck, ND",              27),
  4175             ("Boston, MA",                28),
  4176             ("Boulder, CO",               29),
  4177             ("Chicago, IL",               30),
  4178             ("Dallas, TX",                31),
  4179             ("Denver, CO",                32),
  4180             ("Detroit, MI",               33),
  4181             ("Honolulu, HI",              34),
  4182             ("Houston, TX",               35),
  4183             ("Indianapolis, IN",          36),
  4184             ("Jackson, MS",               37),
  4185             ("Kansas City, MO",           38),
  4186             ("Los Angeles, CA",           39),
  4187             ("Menomonee Falls, WI",       40),
  4188             ("Miami, FL",                 41),
  4189             ("Minneapolis, MN",           42),
  4190             ("New Orleans, LA",           43),
  4191             ("New York City, NY",         44),
  4192             ("Oklahoma City, OK",         45),
  4193             ("Philadelphia, PA",          46),
  4194             ("Phoenix, AZ",               47),
  4195             ("Pittsburgh, PA",            48),
  4196             ("Portland, ME",              49),
  4197             ("Portland, OR",              50),
  4198             ("Raleigh, NC",               51),
  4199             ("Richmond, VA",              52),
  4200             ("Saint Louis, MO",           53),
  4201             ("San Diego, CA",             54),
  4202             ("San Francisco, CA",         55),
  4203             ("Seattle, WA",               56),
  4204             ("Washington DC",             57),
  4205         ])
  4206     ]
  4207 
  4208     location_data = {
  4209         # Europe
  4210         67:   ( 51.2167, -4.4, 1),
  4211         1:    ( 52.33, -13.30, 1),
  4212         70:   ( 48.17, -17.17, 1),
  4213         72:   ( 49.2, -16.63, 1),
  4214         68:   ( 58.8467, -4.3525, 1),
  4215         65:   ( 46.217, -6.150, 1),
  4216         7:    ( 60.1667, -24.9667,2),
  4217         62:   ( 47.2672, -11.3928, 1),
  4218         64:   ( 50.75, -30.0833, 2),
  4219         10:   ( 51.50, 0.0, 0),
  4220         66:   ( 45.767, -4.833, 1),
  4221         69:   ( 48.32, -18.07, 1),
  4222         58:   ( 59.56, -10.41, 1),
  4223         15:   ( 48.8667, -2.667, 1),
  4224         71:   ( 50.08, -14.46, 1),
  4225         18:   ( 41.90, -12.4833, 1),
  4226         63:   ( 47.3, -11.0667, 1),
  4227         74:   ( 52.232, -21.008, 1),
  4228         73:   ( 51.108, -17.038, 1),
  4229         21:   ( 47.3833, -8.5333, 1),
  4230     
  4231         # World Cities
  4232         0:    ( 39.9167, -116.4167, 8),
  4233         2:    ( 18.9333, -72.8333, 5.5),
  4234         3:    (-34.60, 58.45, -3),
  4235         4:    ( 30.10, -31.3667, 2),
  4236         5:    (-33.9167, -18.3667, 2),
  4237         6:    ( 10.50, 66.9333, -4),
  4238         60:   (-25.4278, 49.2731, -3),
  4239         8:    ( 22.25, -114.1667, 8),
  4240         9:    ( 31.7833, -35.2333, 2),
  4241         61:   (-29.3044, 48.8456, -3),
  4242         11:   ( 19.4, 99.15, -6),
  4243         12:   ( 55.75, -37.5833, 3),
  4244         13:   ( 28.6, -77.2, 5.5),
  4245         14:   ( 45.41667, 75.7, -5),
  4246         16:   (-22.90, 43.2333, -3),
  4247         17:   ( 24.633, -46.71667, 3),
  4248         59:   ( -23.5475, 46.6361, -3),
  4249         19:   (-33.8667, -151.2167,10),
  4250         20:   ( 35.70, -139.7667, 9), 
  4251     
  4252         # US Cities
  4253         22:   ( 35.0833, 106.65, -7),
  4254         23:   ( 61.217, 149.90, -9),
  4255         24:   ( 33.733, 84.383, -5),
  4256         25:   ( 30.283, 97.733, -6),
  4257         26:   ( 33.521, 86.8025, -6),
  4258         27:   ( 46.817, 100.783, -6),
  4259         28:   ( 42.35, 71.05, -5),
  4260         29:   ( 40.125, 105.237, -7),
  4261         30:   ( 41.85, 87.65, -6),
  4262         31:   ( 32.46, 96.47, -6),
  4263         32:   ( 39.733, 104.983, -7),
  4264         33:   ( 42.333, 83.05, -5),
  4265         34:   ( 21.30, 157.85, -10),
  4266         35:   ( 29.75, 95.35, -6),
  4267         36:   ( 39.767, 86.15, -5),
  4268         37:   ( 32.283, 90.183, -6),
  4269         38:   ( 39.083, 94.567, -6),
  4270         39:   ( 34.05, 118.233, -8),
  4271         40:   ( 43.11, 88.10, -6),
  4272         41:   ( 25.767, 80.183, -5),
  4273         42:   ( 44.967, 93.25, -6),
  4274         43:   ( 29.95, 90.067, -6),
  4275         44:   ( 40.7167, 74.0167, -5),
  4276         45:   ( 35.483, 97.533, -6),
  4277         46:   ( 39.95, 75.15, -5),
  4278         47:   ( 33.433, 112.067,-7),
  4279         48:   ( 40.433, 79.9833, -5),
  4280         49:   ( 43.666, 70.283, -5),
  4281         50:   ( 45.517, 122.65, -8),
  4282         51:   ( 35.783, 78.65, -5),
  4283         52:   ( 37.5667, 77.450, -5),
  4284         53:   ( 38.6167, 90.1833, -6),
  4285         54:   ( 32.7667, 117.2167, -8),
  4286         55:   ( 37.7667, 122.4167, -8),
  4287         56:   ( 47.60, 122.3167, -8),
  4288         57:   ( 38.8833, 77.0333, -5),
  4289     }
  4290 
  4291     def __init__(self, sun):
  4292         self.sun = sun
  4293     
  4294     def now(self):
  4295         ct = time.localtime()
  4296         
  4297         if ct[8] == 0:
  4298             dst = 'false'
  4299         else:
  4300             dst = 'true'
  4301         
  4302         luxProp(self.sun, 'sc.day', 0).set(ct[2])
  4303         luxProp(self.sun, 'sc.month', 0).set(ct[1])
  4304         luxProp(self.sun, 'sc.year', 0).set(ct[0])
  4305         luxProp(self.sun, 'sc.hour', 0).set(ct[3])
  4306         luxProp(self.sun, 'sc.minute', 0).set(ct[4])
  4307         luxProp(self.sun, 'sc.dst', 0).set(dst)
  4308         luxProp(self.sun, 'sc.dayminutes', 0.0).set(ct[3]*60.0+ct[4])
  4309         
  4310         self.compute()
  4311         
  4312     def set_location(self, location):
  4313         if location < 0: return
  4314         
  4315         lat, long, tz = self.location_data[location]
  4316         luxProp(self.sun, "sc.lat", 0).set(lat)
  4317         luxProp(self.sun, "sc.long", 0).set(long)
  4318         luxProp(self.sun, "sc.tz", 0).set(tz)
  4319         
  4320         self.compute()
  4321     
  4322     def compute(self, redraw=True):
  4323         
  4324         self.lat  = luxProp(self.sun, "sc.lat", 0).get()
  4325         self.long = luxProp(self.sun, "sc.long", 0).get()
  4326         self.tz   = luxProp(self.sun, "sc.tz", 0).get()
  4327         
  4328         self.hour = luxProp(self.sun, "sc.hour", 0).get()
  4329         self.min  = luxProp(self.sun, "sc.minute", 0).get()
  4330         self.dst  = luxProp(self.sun, "sc.dst", 'false').get()
  4331         if self.dst == 'true':
  4332             self.dst = 1
  4333         else:
  4334             self.dst = 0
  4335         
  4336         self.day   = luxProp(self.sun, "sc.day", 0).get()
  4337         self.month = luxProp(self.sun, "sc.month", 0).get()
  4338         self.year  = luxProp(self.sun, "sc.year", 0).get()
  4339         
  4340         
  4341         az,el = self.geoSunData(
  4342             self.lat,
  4343             self.long,
  4344             self.year,
  4345             self.month,
  4346             self.day,
  4347             self.hour + self.min/60.0,
  4348             -self.tz + self.dst
  4349         )
  4350         
  4351         self.sun.rot = math.radians(90-el), 0, math.radians(-az)
  4352         
  4353         if redraw is True: Window.Redraw()
  4354         
  4355         
  4356     # --- THE FOLLOWING METHODS ARE ADAPTED FROM LUXMAYA ---
  4357     
  4358     # mathematical helpers
  4359     def sind(self, deg):
  4360         return math.sin(math.radians(deg))
  4361     
  4362     def cosd(self, deg):
  4363         return math.cos(math.radians(deg))
  4364     
  4365     def tand(self, deg):
  4366         return math.tan(math.radians(deg))
  4367     
  4368     def asind(self, deg):
  4369         return math.degrees(math.asin(deg))
  4370     
  4371     def atand(self, deg):
  4372         return math.degrees(math.atan(deg))
  4373     
  4374     
  4375     def geo_sun_astronomicJulianDate(self, Year, Month, Day, LocalTime, Timezone):
  4376         """
  4377         See quoted source in class header for explanation
  4378         """
  4379         
  4380         if Month > 2.0:
  4381             Y = Year
  4382             M = Month
  4383         else:
  4384             Y = Year - 1.0
  4385             M = Month + 12.0
  4386             
  4387         UT = LocalTime - Timezone
  4388         hour = UT / 24.0
  4389         A = int(Y/100.0)
  4390         
  4391         JD = math.floor(365.25*(Y+4716.0)) + math.floor(30.6001*(M+1.0)) + Day + hour - 1524.4
  4392         
  4393         # The following section is adopted from netCDF4 netcdftime implementation.
  4394         # Copyright: 2008 by Jeffrey Whitaker
  4395         # License: http://www.opensource.org/licenses/mit-license.php
  4396         if JD >= 2299170.5:
  4397             # 1582 October 15 (Gregorian Calendar)
  4398             B = 2.0 - A + int(A/4.0)
  4399         elif JD < 2299160.5:
  4400             # 1582 October 5 (Julian Calendar)
  4401             B = 0
  4402         else:
  4403             Draw.PupMenu('ERROR: Date falls in the gap between Julian and Gregorian calendars%t|OK%x1')
  4404             B = 0
  4405         
  4406         return JD+B
  4407     
  4408     def geoSunData(self, Latitude, Longitude, Year, Month, Day, LocalTime, Timezone):
  4409         """
  4410         See quoted source in class header for explanation
  4411         """
  4412         
  4413         JD = self.geo_sun_astronomicJulianDate(Year, Month, Day, LocalTime, Timezone)
  4414         
  4415         phi = Latitude
  4416         llambda = Longitude
  4417                 
  4418         n = JD - 2451545.0
  4419         LDeg = (280.460 + 0.9856474*n) - (math.floor((280.460 + 0.9856474*n)/360.0) * 360.0)
  4420         gDeg = (357.528 + 0.9856003*n) - (math.floor((357.528 + 0.9856003*n)/360.0) * 360.0)
  4421         LambdaDeg = LDeg + 1.915 * self.sind(gDeg) + 0.02 * self.sind(2.0*gDeg)
  4422         
  4423         epsilonDeg = 23.439 - 0.0000004*n
  4424         
  4425         alphaDeg = self.atand( (self.cosd(epsilonDeg) * self.sind(LambdaDeg)) / self.cosd(LambdaDeg) )
  4426         if self.cosd(LambdaDeg) < 0.0:
  4427             alphaDeg += 180.0
  4428             
  4429         deltaDeg = self.asind( self.sind(epsilonDeg) * self.sind(LambdaDeg) )
  4430         
  4431         JDNull = self.geo_sun_astronomicJulianDate(Year, Month, Day, 0.0, 0.0)
  4432         
  4433         TNull = (JDNull - 2451545.0) / 36525.0
  4434         T = LocalTime - Timezone
  4435         
  4436         thetaGh = 6.697376 + 2400.05134*TNull + 1.002738*T
  4437         thetaGh -= math.floor(thetaGh/24.0) * 24.0
  4438         
  4439         thetaG = thetaGh * 15.0
  4440         theta = thetaG + llambda
  4441         
  4442         tau = theta - alphaDeg
  4443         
  4444         a = self.atand( self.sind(tau) / ( self.cosd(tau)*self.sind(phi) - self.tand(deltaDeg)*self.cosd(phi)) )
  4445         if self.cosd(tau)*self.sind(phi) - self.tand(deltaDeg)*self.cosd(phi) < 0.0:
  4446             a += 180.0
  4447         
  4448         h = self.asind( self.cosd(deltaDeg)*self.cosd(tau)*self.cosd(phi) + self.sind(deltaDeg)*self.sind(phi) )
  4449         
  4450         R = 1.02 / (self.tand (h+(10.3/(h+5.11))))
  4451         hR = h + R/60.0
  4452         
  4453         azimuth = a
  4454         elevation = hR
  4455         
  4456         return azimuth, elevation
  4457 
  4458 def luxAccelerator(scn, gui=None):
  4459     str = ""
  4460     if scn:
  4461         acceltype = luxProp(scn, "accelerator.type", "tabreckdtree")
  4462         str = luxIdentifier("Accelerator", acceltype, ["none", "tabreckdtree", "grid", "bvh", "qbvh"], "ACCEL", "select accelerator type", gui)
  4463         if acceltype.get() == "tabreckdtree":
  4464             if gui: gui.newline()
  4465             str += luxInt("intersectcost", luxProp(scn, "accelerator.kdtree.interscost", 80), 0, 1000, "inters.cost", "specifies how expensive ray-object intersections are", gui)
  4466             str += luxInt("traversalcost", luxProp(scn, "accelerator.kdtree.travcost", 1), 0, 1000, "trav.cost", "specifies how expensive traversing a ray through the kdtree is", gui)
  4467             if gui: gui.newline()
  4468             str += luxFloat("emptybonus", luxProp(scn, "accelerator.kdtree.emptybonus", 0.2), 0.0, 100.0, "empty.b", "promotes kd-tree nodes that represent empty space", gui)
  4469             if gui: gui.newline()
  4470             str += luxInt("maxprims", luxProp(scn, "accelerator.kdtree.maxprims", 1), 0, 1000, "maxprims", "maximum number of primitives in a kdtree volume before further splitting of the volume occurs", gui)
  4471             str += luxInt("maxdepth", luxProp(scn, "accelerator.kdtree.maxdepth", -1), -1, 100, "maxdepth", "If positive, the maximum depth of the tree. If negative this value is set automatically", gui)
  4472         if acceltype.get() == "unsafekdtree":
  4473             if gui: gui.newline()
  4474             str += luxInt("intersectcost", luxProp(scn, "accelerator.kdtree.interscost", 80), 0, 1000, "inters.cost", "specifies how expensive ray-object intersections are", gui)
  4475             str += luxInt("traversalcost", luxProp(scn, "accelerator.kdtree.travcost", 1), 0, 1000, "trav.cost", "specifies how expensive traversing a ray through the kdtree is", gui)
  4476             if gui: gui.newline()
  4477             str += luxFloat("emptybonus", luxProp(scn, "accelerator.kdtree.emptybonus", 0.2), 0.0, 100.0, "empty.b", "promotes kd-tree nodes that represent empty space", gui)
  4478             if gui: gui.newline()
  4479             str += luxInt("maxprims", luxProp(scn, "accelerator.kdtree.maxprims", 1), 0, 1000, "maxprims", "maximum number of primitives in a kdtree volume before further splitting of the volume occurs", gui)
  4480             str += luxInt("maxdepth", luxProp(scn, "accelerator.kdtree.maxdepth", -1), -1, 100, "maxdepth", "If positive, the maximum depth of the tree. If negative this value is set automatically", gui)
  4481         if acceltype.get() == "grid":
  4482             str += luxBool("refineimmediately", luxProp(scn, "accelerator.grid.refine", "false"), "refine immediately", "Makes the primitive intersectable as soon as it is added to the grid", gui)
  4483         if acceltype.get() == "qbvh":
  4484             if gui: gui.newline()
  4485             str += luxInt("maxprimsperleaf", luxProp(scn, "accelerator.qbvh.maxprimsperleaf", 4), 1, 64, "maxprimsperleaf", "Maximum number of primitives to leave in one leaf node", gui)
  4486     return str
  4487 
  4488 def luxSystem(scn, gui=None):
  4489     if scn:
  4490         if gui: gui.newline("PATHS:", 10)
  4491         lp = luxProp(scn, "lux", "")
  4492         lp.set(Blender.sys.dirname(lp.get())+os.sep)
  4493         luxPath("LUX dir", lp, "lux binary dir", "LuxRender installation path", gui, 2.0)
  4494 
  4495 #        luxFile("GUI filename", luxProp(scn, "lux", ""), "lux-file", "filename and path of the lux GUI executable", gui, 2.0)
  4496 #        luxFile("Console filename", luxProp(scn, "luxconsole", ""), "lux-file-console", "filename and path of the lux console executable", gui, 2.0)
  4497         if gui: gui.newline()
  4498         datadir = luxProp(scn, "datadir", "")
  4499         luxFile("datadir", datadir, "default out dir", "default.lxs save path", gui, 2.0)
  4500         datadir.set(datadir.get().rstrip(os.sep))
  4501 
  4502         if gui: gui.newline()
  4503         pm = ["absolute","relative","flat"]
  4504         luxOption("pathmode", luxProp(scn, "pathmode", "absolute"), pm, "path-mode", "select format for paths on export", gui, 2.0)
  4505 
  4506         if gui: gui.newline("PRIORITY:", 10)
  4507         luxnice = luxProp(scn, "luxnice", 10)
  4508         if osys.platform=="win32":
  4509             r = gui.getRect(2, 1)
  4510             Draw.Menu("priority%t|abovenormal%x-10|normal%x0|belownormal%x10|low%x19", evtLuxGui, r[0], r[1], r[2], r[3], luxnice.get(), "", lambda e,v: luxnice.set(v))
  4511         else: luxInt("nice", luxnice, -20, 19, "nice", "nice value. Range goes from -20 (highest priority) to 19 (lowest)", gui)
  4512 
  4513         luxBool("noopengl", luxProp(scn, "noopengl", "false"), "Disable OpenGL", "(workaround for some buggy display drivers)", gui, 1.0)
  4514 
  4515 
  4516         if gui: gui.newline("THREADS:", 10)
  4517         autothreads = luxProp(scn, "autothreads", "true")
  4518         luxBool("autothreads", autothreads, "Auto Detect", "Automatically use all available processors", gui, 1.0)
  4519         if autothreads.get()=="false":
  4520             luxInt("threads", luxProp(scn, "threads", 1), 1, 100, "threads", "number of threads used for rendering", gui, 1.0)
  4521 #        luxBool('export.threaded', luxProp(scn, 'export.threaded', 'true'), 'Pipe in background', 'When using pipe export, do not block Blender UI', gui, 1.0)
  4522 
  4523         if gui: gui.newline("ANIM:", 10)
  4524         useparamkeys = luxProp(scn, "useparamkeys", "false")
  4525         luxBool("useparamkeys", useparamkeys, "Enable Parameter IPO Keyframing", "Enables keyframing of LuxBlend parameters", gui, 2.0)
  4526 
  4527         if gui: gui.newline("PARAMS:", 10)
  4528         parammodeadvanced = luxProp(scn, "parammodeadvanced", "false")
  4529         luxBool("parammodeadvanced", parammodeadvanced, "Default Advanced Parameters", "Always use advanced parameters by default", gui, 2.0)
  4530 
  4531         if gui: gui.newline("PREVIEW:", 10)
  4532         qs = ["low","medium","high","very high"]
  4533         defprevmat = luxProp(scn, "defprevmat", "high")
  4534         luxOption("defprevmat", defprevmat, qs, "Materials", "Select default preview quality in material editor for materials", gui, 1.0)
  4535 
  4536         if gui: gui.newline("GAMMA:", 10)
  4537         luxBool("RGC", luxProp(scn, "RGC", "true"), "RGC", "use reverse gamma correction", gui)
  4538         luxBool("ColClamp", luxProp(scn, "colorclamp", "false"), "ColClamp", "clamp all colors to 0.0-0.9", gui)
  4539         if gui: gui.newline("MESH:", 10)
  4540         luxBool("mesh_optimizing", luxProp(scn, "mesh_optimizing", "true"), "Optimize meshes", "Optimize meshes during export", gui, 2.0)
  4541         #luxInt("trianglemesh thr", luxProp(scn, "trianglemesh_thr", 0), 0, 10000000, "trianglemesh threshold", "Vertex threshold for exporting (wald) trianglemesh object(s)", gui, 2.0)
  4542         #if gui: gui.newline()
  4543         #luxInt("barytrianglemesh thr", luxProp(scn, "barytrianglemesh_thr", 300000), 0, 100000000, "barytrianglemesh threshold", "Vertex threshold for exporting barytrianglemesh object(s) (slower but uses less memory)", gui, 2.0)
  4544         if gui: gui.newline("INSTANCING:", 10)
  4545         luxInt("instancing_threshold", luxProp(scn, "instancing_threshold", 2), 2, 1000000, "Object instancing threshold", "Minimum number of objects to export as instances", gui, 2.0)
  4546         
  4547         # dougal2 packed images, enable this when implemented in Lux itself
  4548         #if gui: gui.newline('TEXTURES:',10)
  4549         #impack = luxProp(scn, 'packtextures', 'false')
  4550         #luxBool('impack', impack, 'Pack All Images', '', gui, 2.0)
  4551         
  4552         if gui: 
  4553             network=luxProp(scn,"network","false")
  4554             gui.newline("NETWORK:", 10)
  4555             luxCollapse("network",network, "Network", "enable network option", gui, 2.0)
  4556             if(network.get() == "true"):
  4557                 network_use_file=luxProp(scn,"network_use_file","false")
  4558                 luxBool ("use file",network_use_file,"use file", "load servers list from a file (one server per line)",gui,2.0)
  4559                 if (network_use_file.get() == "true"):
  4560                     luxFile("file", luxProp(scn, "network_file_path", ""), "file", "file with servers list (one server per line)", gui, 1.0)         
  4561                 else:
  4562                     luxString("servers",luxProp(scn,"network_servers",""),"servers","comma-separated list of servers",gui,1.0)
  4563                 luxInt("network_interval",luxProp(scn,"network_interval",180),15,86400,"update interval","interval between network refresh (in seconds)",gui)
  4564 
  4565 
  4566 def luxRemoveProps(scn, gui=None):
  4567     def doRemove(defs, uids, scenes, cameras, materials, all):
  4568         def wipeProps(objs):
  4569             l = []
  4570             for obj in objs:
  4571                 if obj.lib is not None: continue
  4572                 try:
  4573                     del obj.properties['luxblend']
  4574                     l.append('"%s"' % obj.name)
  4575                 except:
  4576                     pass
  4577             return ', '.join(l) if l else 'none'
  4578         defs = defs.get()
  4579         uids = uids.get()
  4580         scenes = scenes.get()
  4581         cameras = cameras.get()
  4582         materials = materials.get()
  4583         all = all.get()
  4584         if not 'true' in [defs, uids, scenes, cameras, materials]:
  4585             Draw.PupMenu('ERROR: Select at least one category of properties%t|OK%x1')
  4586             Blender.Window.QRedrawAll()
  4587             return
  4588         if Draw.PupMenu('  OK?%t|Are you completely sure to remove defined LuxBlend properties? This action is irreversible.%x1') != 1:
  4589             return
  4590         print 'Removing LuxBlend properties...'
  4591         if defs == 'true':
  4592             print ' - removing default settings'
  4593             try:
  4594                 global luxdefaults
  4595                 luxdefaults = {}
  4596                 Blender.Registry.RemoveKey('luxblend', True)
  4597             except:
  4598                 pass
  4599         if uids == 'true' or scenes == 'true':
  4600             print ' - removing UIDs'
  4601             scns = filter(lambda s: s.lib is None and s != Scene.GetCurrent(), Scene.Get()) + [Scene.GetCurrent()] if all == 'true' else [Scene.GetCurrent()]
  4602             for scn in scns:
  4603                 luxProp(scn, 'UID', '').delete()
  4604             if 'UID' in luxdefaults: del luxdefaults['UID']
  4605             print '   processed scenes:', ', '.join([ '"%s"' % s.name for s in scns ])
  4606             l = []
  4607             for mat in Material.Get():
  4608                 if mat.lib is None:
  4609                     for volume_prop in ['Exterior', 'Interior']:
  4610                         luxProp(mat, '%s_vol_guid' % (volume_prop), '').delete()
  4611                     l.append('"%s"' % mat.name)
  4612             print '   processed UID tags in materials:', (', '.join(l) if l else 'none')
  4613         if scenes == 'true':
  4614             print ' - removing scene properties'
  4615             scns = filter(lambda s: s.lib is None and s != Scene.GetCurrent(), Scene.Get()) + [Scene.GetCurrent()] if all == 'true' else [Scene.GetCurrent()]
  4616             print '   processed scenes:', wipeProps(scns)
  4617         if cameras == 'true':
  4618             print ' - removing camera properties'
  4619             cams = Blender.Camera.Get() if all == 'true' else [Scene.GetCurrent().objects.camera.data]
  4620             print '   processed cameras:', wipeProps(cams)
  4621         if materials == 'true':
  4622             print ' - removing material properties'
  4623             print '   processed materials:', wipeProps(Material.Get())
  4624         print 'LuxBlend properties removed'
  4625         print
  4626         Blender.Window.QRedrawAll()
  4627     
  4628     if scn:
  4629         wipe = luxProp(scn, 'wipe_props', 'false')
  4630         luxCollapse('wipe', wipe, 'Reset settings', 'Completely remove LuxBlend properties from defined objects', gui, 2.0)
  4631         if wipe.get() == 'true':
  4632             wipe_defs = luxProp(scn, 'wipe_defs', 'false')
  4633             wipe_uids = luxProp(scn, 'wipe_uids', 'false')
  4634             wipe_scns = luxProp(scn, 'wipe_scns', 'false')
  4635             wipe_cams = luxProp(scn, 'wipe_cams', 'false')
  4636             wipe_mats = luxProp(scn, 'wipe_mats', 'false')
  4637             wipe_all = luxProp(scn, 'wipe_all', 'true')
  4638             luxBool('switch_wipe_defs', wipe_defs, 'Defaults', 'Reset default settings', gui, 0.5)
  4639             if not wipe_scns.get() == 'true':
  4640                 luxBool('switch_wipe_uids', wipe_uids, 'UIDs', 'Reset scene UID (use this option after forking blend-file or making a new "Full Copy" scene)', gui, 0.25)
  4641             luxBool('switch_wipe_scns', wipe_scns, 'Scenes' if wipe_scns.get() == 'true' else 'Scns', 'Remove global scene properties (including mediums definitions)', gui, 0.5 if wipe_scns.get() == 'true' else 0.25)
  4642             luxBool('switch_wipe_cams', wipe_cams, 'Cameras', 'Remove camera properties', gui, 0.5)
  4643             luxBool('switch_wipe_mats', wipe_mats, 'Materials', 'Remove material properties', gui, 0.5)
  4644             luxBool('switch_wipe_all', wipe_all, 'All scenes/cameras', 'Remove properties from all cameras/scenes or from the current ones only', gui, 1.5)
  4645             if gui:
  4646                 r = gui.getRect(0.5, 1)
  4647                 Draw.Button('REMOVE', 0, r[0], r[1], r[2], r[3], 'Proceed with removing', lambda e,v:doRemove(wipe_defs, wipe_uids, wipe_scns, wipe_cams, wipe_mats, wipe_all))
  4648 
  4649 
  4650 def scalelist(list, factor):
  4651     for i in range(len(list)): list[i] = list[i] * factor
  4652     return list
  4653 
  4654 
  4655 def luxMapping(key, mat, gui, level=0):
  4656     global icon_map2d, icon_map2dparam
  4657     if gui: gui.newline("2Dmap:", -2, level, icon_map2d)
  4658     mapping = luxProp(mat, key+".mapping", "uv")
  4659     mappings = ["uv","spherical","cylindrical","planar"]
  4660     str = luxOption("mapping", mapping, mappings, "mapping", "", gui, 0.5)
  4661     if mapping.get() == "uv":
  4662         if gui: gui.newline()
  4663         str += luxFloat("uscale", luxProp(mat, key+".uscale", 1.0), -100.0, 100.0, "Us", "u-scale", gui, 0.5)
  4664         str += luxFloat("vscale", luxProp(mat, key+".vscale", -1.0), -100.0, 100.0, "Vs", "v-scale", gui, 0.5)
  4665         str += luxFloat("udelta", luxProp(mat, key+".udelta", 0.0), -100.0, 100.0, "Ud", "u-delta", gui, 0.5)
  4666         str += luxFloat("vdelta", luxProp(mat, key+".vdelta", 1.0 if luxProp(mat, key+'.texture', 'imagemap').get() == 'imagemap' else 0.0), -100.0, 100.0, "Vd", "v-delta", gui, 0.5)
  4667     elif mapping.get() == "spherical":
  4668         if gui: gui.newline()
  4669         str += luxFloat("uscale", luxProp(mat, key+".uscale", 1.0), -100.0, 100.0, "Us", "u-scale", gui, 0.5)
  4670         str += luxFloat("vscale", luxProp(mat, key+".vscale", -1.0), -100.0, 100.0, "Vs", "v-scale", gui, 0.5)
  4671         str += luxFloat("udelta", luxProp(mat, key+".udelta", 0.0), -100.0, 100.0, "Ud", "u-delta", gui, 0.5)
  4672         str += luxFloat("vdelta", luxProp(mat, key+".vdelta", 0.0), -100.0, 100.0, "Vd", "v-delta", gui, 0.5)
  4673     elif mapping.get() == "cylindrical":
  4674         if gui: gui.newline()
  4675         str += luxFloat("uscale", luxProp(mat, key+".uscale", 1.0), -100.0, 100.0, "Us", "u-scale", gui, 1.0)
  4676         str += luxFloat("udelta", luxProp(mat, key+".udelta", 0.0), -100.0, 100.0, "Ud", "u-delta", gui, 1.0)
  4677     elif mapping.get() == "planar":
  4678         str += luxFloat("udelta", luxProp(mat, key+".udelta", 0.0), -100.0, 100.0, "Ud", "u-delta", gui, 0.75)
  4679         str += luxFloat("vdelta", luxProp(mat, key+".vdelta", 0.0), -100.0, 100.0, "Vd", "v-delta", gui, 0.75)
  4680         if gui: gui.newline("v1:", -2, level+1, icon_map2dparam)
  4681         str += luxVector("v1", luxProp(mat, key+".v1", "1 0 0"), -100.0, 100.0, "v1", "v1-vector", gui, 2.0)
  4682         if gui: gui.newline("v2:", -2, level+1, icon_map2dparam)
  4683         str += luxVector("v2", luxProp(mat, key+".v2", "0 1 0"), -100.0, 100.0, "v2", "v2-vector", gui, 2.0)
  4684     return str
  4685 
  4686 def lux3DMapping(key, mat, gui, level=0):
  4687     global icon_map3dparam
  4688     str = ""
  4689     if gui: gui.newline("scale:", -2, level, icon_map3dparam)
  4690     str += luxVectorUniform("scale", luxProp(mat, key+".3dscale", 1.0), 0.001, 1000.0, "scale", "scale-vector", gui, 2.0)
  4691     if gui: gui.newline("rot:", -2, level, icon_map3dparam)
  4692     str += luxVector("rotate", luxProp(mat, key+".3drotate", "0 0 0"), -360.0, 360.0, "rotate", "rotate-vector", gui, 2.0)
  4693     if gui: gui.newline("move:", -2, level, icon_map3dparam)
  4694     str += luxVector("translate", luxProp(mat, key+".3dtranslate", "0 0 0"), -1000.0, 1000.0, "move", "translate-vector", gui, 2.0)
  4695     return str
  4696     
  4697 def getTreeNameById(tree, i): # helper function to retrive name of the selected treemenu-item
  4698     for t in tree:
  4699         if type(t)==types.TupleType:
  4700             if type(t[1])==types.ListType: 
  4701                 n=getTreeNameById(t[1], i)
  4702                 if n: return n
  4703             elif t[1]==i: return t[0]
  4704     return None    
  4705 
  4706 def luxTexture(name, parentkey, type, default, min, max, caption, hint, mat, gui, matlevel, texlevel=0, lightsource=0, overrideicon=""):
  4707     global icon_tex, icon_texcol, icon_texmix, icon_texmixcol, icon_texparam, icon_spectex
  4708     def c(t1, t2):
  4709         return (t1[0]+t2[0], t1[1]+t2[1])
  4710     def alternativedefault(type, default):
  4711         if type=="float": return 0.0
  4712         else: return "0.0 0.0 0.0"
  4713     level = matlevel + texlevel
  4714     keyname = "%s:%s"%(parentkey, name)
  4715     texname = "%s:%s"%(mat.getName(), keyname)
  4716 #    if gui: gui.newline(caption+":", 0, level)
  4717     if(lightsource == 0):
  4718         if texlevel == 0: texture = luxProp(mat, keyname+".texture", "imagemap")
  4719         else: texture = luxProp(mat, keyname+".texture", "constant")
  4720     else:
  4721         texture = luxProp(mat, keyname+".texture", "blackbody")
  4722 
  4723     tex_all = ["constant", "imagemap", "mix", "scale", "bilerp", "brick"]
  4724     tex_color = ["blackbody", "lampspectrum", "equalenergy", "frequency", "gaussian", "regulardata", "irregulardata", "tabulateddata", "uv", "harlequin", "marble"]
  4725     tex_float = ["blender_marble", "blender_musgrave", "blender_wood", "blender_clouds", "blender_blend", "blender_distortednoise", "blender_noise", "blender_magic", "blender_stucci", "blender_voronoi", "checkerboard", "dots", "fbm", "windy", "wrinkled"]
  4726     tex_fresnel = ['constant', 'cauchy', 'sellmeier', 'sopra', 'luxpop', 'preset']
  4727     textures = tex_all
  4728     if type=="color":
  4729         textures.extend(tex_color)
  4730     textures.extend(tex_float)
  4731     if type == 'fresnel':
  4732         textures = tex_fresnel
  4733 
  4734     if gui:
  4735         if(overrideicon != ""):
  4736             icon = overrideicon
  4737         else:
  4738             icon = icon_tex
  4739             if texture.get() in ["mix", "scale", "checkerboard", "dots"]:
  4740                 if type=="color": icon = icon_texmixcol
  4741                 else: icon = icon_texmix
  4742             elif type == 'fresnel':
  4743                 icon = icon_tex
  4744             elif texture.get() in ["constant", "blackbody", "equalenergy", "frequency", "gaussian", "regulardata", "irregulardata", "tabulateddata"]:
  4745                 icon = icon_spectex
  4746             else:
  4747                 if type=="color": icon = icon_texcol
  4748                 else: icon = icon_tex
  4749         if (texlevel > 0): gui.newline(caption+":", -2, level, icon, scalelist([0.5,0.5,0.5],2.0/(level+2)))
  4750         else: gui.newline("texture:", -2, level, icon, scalelist([0.5,0.5,0.5],2.0/(level+2)))
  4751     luxOption("texture", texture, textures, "texture", "", gui, 2)
  4752     if type!="float" and texture.get() in tex_float:
  4753         str = "Texture \"%s::amount\" \"float\" \"%s\""%(texname, texture.get())
  4754     else:
  4755         str = "Texture \"%s\" \"%s\" \"%s\""%(texname, type, texture.get())
  4756 
  4757     if gui: Draw.PushButton(">", evtLuxGui, gui.xmax+gui.h, gui.y-gui.h, gui.h, gui.h, "Menu", lambda e,v: showMatTexMenu(mat,keyname,True))
  4758     if gui: # Draw Texture level Material preview
  4759         luxPreview(mat, parentkey, 1, False, False, name, gui, texlevel, [0.5, 0.5, 0.5])
  4760         # Add an offset for next controls
  4761         #r = gui.getRect(1.0, 1)
  4762         #gui.x += 140
  4763 
  4764     if texture.get() == "constant":
  4765         value = luxProp(mat, keyname+".value", default)
  4766         if type == "float": luxFloat("value", value, min, max, "", "", gui, 1.1)
  4767         elif type == "color": luxRGB("value", value, max, "", "", gui, 2)
  4768         elif type == 'fresnel':
  4769             if gui: gui.newline()
  4770             # rude copy&paste from luxIORFloatTexture() with slight modifications
  4771             iorusepreset = luxProp(mat, keyname+".iorusepreset", "true")
  4772             luxBool("iorusepreset", iorusepreset, "Preset", "Select from a list of predefined presets", gui, 0.4)
  4773             if iorusepreset.get() == "true":
  4774                 iortree = [ ("Liquids", [("Acetone", 1), ("Alcohol, Ethyl (grain)", 2), ("Alcohol, Methyl (wood)", 3), ("Beer", 4), ("Benzene", 5), ("Carbon tetrachloride", 6), ("Carbon disulfide", 7), ("Carbonated Beverages", 8), ("Chlorine (liq)", 9), ("Cranberry Juice (25%)", 10), ("Glycerin", 11), ("Honey, 13% water content", 12), ("Honey, 17% water content", 13), ("Honey, 21% water content", 14), ("Ice", 15), ("Milk", 16), ("Oil, Clove", 17), ("Oil, Lemon", 18), ("Oil, Neroli", 19), ("Oil, Orange", 20), ("Oil, Safflower", 21), ("Oil, vegetable (50 C)", 22), ("Oil of Wintergreen", 23), ("Rum, White", 24), ("Shampoo", 25), ("Sugar Solution 30%", 26), ("Sugar Solution 80%", 27), ("Turpentine", 28), ("Vodka", 29), ("Water (0 C)", 30), ("Water (100 C)", 31), ("Water (20 C)", 32), ("Whisky", 33) ] ), ("Gases", [("Vacuum", 101), ("Air @ STP", 102), ("Air", 103), ("Helium", 104), ("Hydrogen", 105), ("Carbon dioxide", 106) ]), ("Transparent\x20", [("Eye, Aqueous humor", 201), ("Eye, Cornea", 202), ("Eye, Lens", 203), ("Eye, Vitreous humor", 204), ("Glass, Arsenic Trisulfide", 205), ("Glass, Crown (common)", 206), ("Glass, Flint, 29% lead", 207), ("Glass, Flint, 55% lead", 208), ("Glass, Flint, 71% lead", 209), ("Glass, Fused Silica", 210), ("Glass, Pyrex", 211), ("Lucite", 212), ("Nylon", 213), ("Obsidian", 214), ("Plastic", 215), ("Plexiglas", 216), ("Salt", 217)  ]), ("Gemstones", [("Agate", 301), ("Alexandrite", 302), ("Almandine", 303), ("Amber", 304), ("Amethyst", 305), ("Ammolite", 306), ("Andalusite", 307), ("Apatite", 308), ("Aquamarine", 309), ("Axenite", 310), ("Beryl", 311), ("Beryl, Red", 312), ("Chalcedony", 313), ("Chrome Tourmaline", 314), ("Citrine", 315), ("Clinohumite", 316), ("Coral", 317), ("Crystal", 318), ("Crysoberyl, Catseye", 319), ("Danburite", 320), ("Diamond", 321), ("Emerald", 322), ("Emerald Catseye", 323), ("Flourite", 324), ("Garnet, Grossular", 325), ("Garnet, Andradite", 326), ("Garnet, Demantiod", 327), ("Garnet, Mandarin", 328), ("Garnet, Pyrope", 329), ("Garnet, Rhodolite", 330), ("Garnet, Tsavorite", 331), ("Garnet, Uvarovite", 332), ("Hauyn", 333), ("Iolite", 334), ("Jade, Jadeite", 335), ("Jade, Nephrite", 336), ("Jet", 337), ("Kunzite", 338), ("Labradorite", 339), ("Lapis Lazuli", 340), ("Moonstone", 341), ("Morganite", 342), ("Obsidian", 343), ("Opal, Black", 344), ("Opal, Fire", 345), ("Opal, White", 346), ("Oregon Sunstone", 347), ("Padparadja", 348), ("Pearl", 349), ("Peridot", 350), ("Quartz", 351), ("Ruby", 352), ("Sapphire", 353), ("Sapphire, Star", 354), ("Spessarite", 355), ("Spinel", 356), ("Spinel, Blue", 357), ("Spinel, Red", 358), ("Star Ruby", 359), ("Tanzanite", 360), ("Topaz", 361), ("Topaz, Imperial", 362), ("Tourmaline", 363), ("Tourmaline, Blue", 364), ("Tourmaline, Catseye", 365), ("Tourmaline, Green", 366), ("Tourmaline, Paraiba", 367), ("Tourmaline, Red", 368), ("Zircon", 369), ("Zirconia, Cubic", 370) ] ), ("Other ", [("Pyrex (Borosilicate glass)", 401), ("Ruby", 402), ("Water ice", 403), ("Cryolite", 404), ("Acetone", 405), ("Ethanol", 406), ("Teflon", 407), ("Glycerol", 408), ("Acrylic glass", 409), ("Rock salt", 410), ("Crown glass (pure)", 411), ("Salt (NaCl)", 412), ("Polycarbonate", 413), ("PMMA", 414), ("PETg", 415), ("PET", 416), ("Flint glass (pure)", 417), ("Crown glass (impure)", 418), ("Fused Quartz", 419), ("Bromine", 420), ("Flint glass (impure)", 421), ("Cubic zirconia", 422), ("Moissanite", 423), ("Cinnabar (Mercury sulfide)", 424), ("Gallium(III) prosphide", 425), ("Gallium(III) arsenide", 426), ("Silicon", 427) ] ) ]
  4775                 iordict = {1:1.36, 2:1.36, 3:1.329, 4:1.345, 5:1.501, 6:1.000132, 7:1.00045, 8:1.34, 9:1.385, 10:1.351, 11:1.473, 12:1.504, 13:1.494, 14:1.484, 15:1.309, 16:1.35, 17:1.535, 18:1.481, 19:1.482, 20:1.473, 21:1.466, 22:1.47, 23:1.536, 24:1.361, 25:1.362, 26:1.38, 27:1.49, 28:1.472, 29:1.363, 30:1.33346, 31:1.31766, 32:1.33283, 33:1.356, 101:1.0, 102:1.0002926, 103:1.000293, 104:1.000036, 105:1.000132, 106:1.00045, 201:1.33, 202:1.38, 203:1.41, 204:1.34, 205:2.04, 206:1.52, 207:1.569, 208:1.669, 209:1.805, 210:1.459, 211:1.474, 212:1.495, 213:1.53, 214:1.50, 215:1.460, 216:1.488, 217:1.516, 301:1.544, 302:1.746, 303:1.75, 304:1.539, 305:1.532, 306:1.52, 307:1.629, 308:1.632, 309:1.567, 310:1.674, 311:1.57, 312:1.570, 313:1.544, 314:1.61, 315:1.532, 316:1.625, 317:1.486, 318:2.000, 319:1.746, 320:1.627, 321:2.417, 322:1.560, 323:1.560, 324:1.434, 325:1.72, 326:1.88, 327:1.880, 328:1.790, 329:1.73, 330:1.740, 331:1.739, 332:1.74, 333:1.490, 334:1.522, 335:1.64, 336:1.600, 337:1.660, 338:1.660, 339:1.560, 340:1.50, 341:1.518, 342:1.585, 343:1.50, 344:1.440, 345:1.430, 346:1.440, 347:1.560, 348:1.760, 349:1.53, 350:1.635, 351:1.544, 352:1.757, 353:1.757, 354:1.760, 355:1.79, 356:1.712, 357:1.712, 358:1.708, 359:1.76, 360:1.690, 361:1.607, 362:1.605, 363:1.603, 364:1.61, 365:1.61, 366:1.61, 367:1.61, 368:1.61, 369:1.777, 370:2.173, 401:1.47, 402:1.76, 403:1.31, 404:1.388, 405:1.36, 406:1.36, 407:1.35, 408:1.4729, 409:1.49, 410:1.516, 411:1.5, 412:1.544, 413:1.584, 414:1.4893, 415:1.57, 416:1.575, 417:1.6, 418:1.485, 419:1.46, 420:1.661, 421:1.523, 422:2.15, 423:2.419, 424:2.65, 425:3.02, 426:3.5, 427:3.927}
  4776                 iorpreset = luxProp(mat, keyname+".iorpreset", "Glass, Fused Silica" if parentkey != 'named_volumes:0.tex' else "Air @ STP")
  4777                 if gui:
  4778                     def setIor(i, value, preset, tree, dict): # callback function to set ior value after selection              
  4779                         if i >= 0:
  4780                             value.set(dict[i])
  4781                             preset.set(getTreeNameById(tree, i))
  4782                     r = gui.getRect(1.6, 1)
  4783                     Draw.Button(iorpreset.get(), evtLuxGui, r[0], r[1], r[2], r[3], "select IOR preset", lambda e,v: setIor(Draw.PupTreeMenu(iortree), value, iorpreset, iortree, iordict))
  4784                 str += luxFloat(name, value, min, max, "IOR", hint, None, 1.6)
  4785             else:
  4786                 str += luxFloat(name, value, min, max, "IOR", 'Uniform index of refraction', gui, 1.6, 1)
  4787             return str, ' "texture %s" ["%s"]' % (type, texname)
  4788 # direct version
  4789         if type == "color": return ("", " \"%s %s\" [%s]"%(type, name, value.getRGC()))
  4790         return ("", " \"%s %s\" [%s]"%(type, name, value.get()))
  4791 # indirect version
  4792 #        if type == "color": str += " \"%s value\" [%s]"%(type, value.getRGC())
  4793 #        else: str += " \"%s value\" [%s]"%(type, value.get())
  4794 
  4795     if texture.get() == 'cauchy':
  4796         if gui: gui.newline()
  4797         cauchya = luxProp(mat, keyname+'.cauchya.value', 1.4580)
  4798         cauchyb = luxProp(mat, keyname+'.cauchyb.value', 0.00354)
  4799         # copy&paste from luxCauchyBFloatTexture() with some modifications
  4800         cauchynames = ["01 - Fused silica glass", "02 - Borosilicate glass BK7", "03 - Hard crown glass K5", "04 - Barium crown glass BaK4", "05 - Barium flint glass BaF10", "06 - Dense flint glass SF10" ]
  4801         cauchyvals = [ (1.4580, 0.00354), (1.5046, 0.00420), (1.5220, 0.00459), (1.5690, 0.00531), (1.6700, 0.00743), (1.7280, 0.01342) ]
  4802         cauchyusepreset = luxProp(mat, keyname+".cauchyusepreset", "true")
  4803         cauchyindex = luxProp(mat, keyname+'.cauchyindex', 'false')
  4804         luxBool("cauchyusepreset", cauchyusepreset, "Preset", "Select from a list of predefined presets", gui, 0.4)
  4805         
  4806         if cauchyusepreset.get() == "true":
  4807             cauchypreset = luxProp(mat, keyname+".cauchypreset", "01 - Fused silica glass")
  4808             luxOption("cauchypreset", cauchypreset, cauchynames, "  PRESET", "Select Cauchy preset", gui, 1.6)
  4809             idx = cauchynames.index(cauchypreset.get())
  4810             cauchyindex.set('false')
  4811             cauchya.set(cauchyvals[idx][0])
  4812             cauchyb.set(cauchyvals[idx][1])
  4813             str += luxFloat('cauchya', cauchya, min, max, 'cauchy a', hint, None)
  4814             str += luxFloat('cauchyb', cauchyb, 0.0, 1.0, 'cauchy b', hint, None)
  4815         else:
  4816             luxBool('cauchyindex', cauchyindex, 'Index', 'Use IOR value instead of Cauchy\'s A', gui, 0.4)
  4817             if cauchyindex.get() == 'true':
  4818                 str += luxFloat('index', luxProp(mat, keyname+'.value', 1.459), min, max, 'ior', 'Index of refraction', gui, 0.6)
  4819             else:
  4820                 str += luxFloat('cauchya', cauchya, min, max, 'a', 'Cauchy\'s A parameter', gui, 0.6)
  4821             str += luxFloat('cauchyb', cauchyb, 0.0, 1.0, 'b', 'Cauchy\'s B parameter', gui, 0.6)
  4822         return str, ' "texture %s" ["%s"]' % (type, texname)
  4823 
  4824     if texture.get() == 'sellmeier':
  4825         def sellmeierStrToFloats(sb, sc, min, max):
  4826             n = ['c', 'b']
  4827             fb, fc = [], []
  4828             for s in [sb, sc]:
  4829                 l = n.pop()
  4830                 for i in s.split(' '):
  4831                     try:
  4832                         if len(i): vars()['f'+l].append(sorted([float(i),min,max])[1])
  4833                     except ValueError:
  4834                         print 'WARNING: Illegal value dropped from sellmeier texture: only floats are allowed'
  4835             if len(fb) > len(fc):
  4836                 fc.extend([0.0] * (len(fb) - len(fc)))
  4837             elif len(fc) > len(fb):
  4838                 fc = fc[:len(fb)]
  4839             return fb, fc
  4840         if gui: gui.newline()
  4841         sellmeiernames = [ '01 - Fused silica', '02 - Borosilicate glass BK7', '03 - Sapphire (ordinary wave)', '04 - Sapphire (extraordinary wave)' ]
  4842         sellmeiervals = [ (1.0, '0.696166300 0.407942600 0.897479400', '4.67914826e-3 1.35120631e-2 97.9340025'), (1.0, '1.03961212 0.231792344 1.01046945', '6.00069867e-3 2.00179144e-2 1.03560653e2'), (1.0, '1.43134930 0.65054713 5.3414021', '5.2799261e-3 1.42382647e-2 3.25017834e2'), (1.0, '1.5039759 0.55069141 6.5927379', '5.48041129e-3 1.47994281e-2 4.0289514e2') ]
  4843         sellmeiera = luxProp(mat, keyname+'.sellmeiera.value', sellmeiervals[0][0])
  4844         sellmeierb = luxProp(mat, keyname+'.sellmeierb.value', sellmeiervals[0][1])
  4845         sellmeierc = luxProp(mat, keyname+'.sellmeierc.value', sellmeiervals[0][2])
  4846         sellmeierb1 = luxProp(mat, keyname+'.sellmeierb1.value', float(sellmeiervals[0][1].split(' ')[0]))
  4847         sellmeierb2 = luxProp(mat, keyname+'.sellmeierb2.value', float(sellmeiervals[0][1].split(' ')[1]))
  4848         sellmeierb3 = luxProp(mat, keyname+'.sellmeierb3.value', float(sellmeiervals[0][1].split(' ')[2]))
  4849         sellmeierc1 = luxProp(mat, keyname+'.sellmeierc1.value', float(sellmeiervals[0][2].split(' ')[0]))
  4850         sellmeierc2 = luxProp(mat, keyname+'.sellmeierc2.value', float(sellmeiervals[0][2].split(' ')[1]))
  4851         sellmeierc3 = luxProp(mat, keyname+'.sellmeierc3.value', float(sellmeiervals[0][2].split(' ')[2]))
  4852         sellmeieradvanced = luxProp(mat, keyname+'sellmeieradvanced', 'false')
  4853         sellmeierusepreset = luxProp(mat, keyname+'.sellmeierusepreset', 'true')
  4854         luxBool('sellmeierusepreset', sellmeierusepreset, 'Preset', 'Select from a list of predefined presets', gui, 0.4)
  4855         
  4856         if sellmeierusepreset.get() == 'true':
  4857             sellmeierpreset = luxProp(mat, keyname+'.sellmeierpreset', sellmeiernames[0])
  4858             luxOption('sellmeierpreset', sellmeierpreset, sellmeiernames, '  PRESET', 'Select Sellmeier preset', gui, 1.6)
  4859             idx = sellmeiernames.index(sellmeierpreset.get())
  4860             sellmeiera.set(sellmeiervals[idx][0])
  4861             sellmeierb.set(sellmeiervals[idx][1])
  4862             sellmeierc.set(sellmeiervals[idx][2])
  4863             sellmeierb1.set(float(sellmeiervals[idx][1].split(' ')[0]))
  4864             sellmeierb2.set(float(sellmeiervals[idx][1].split(' ')[1]))
  4865             sellmeierb3.set(float(sellmeiervals[idx][1].split(' ')[2]))
  4866             sellmeierc1.set(float(sellmeiervals[idx][2].split(' ')[0]))
  4867             sellmeierc2.set(float(sellmeiervals[idx][2].split(' ')[1]))
  4868             sellmeierc3.set(float(sellmeiervals[idx][2].split(' ')[2]))
  4869             str += luxFloat('A', sellmeiera, 0.001, 10.0, 'sellmeier a', 'Sellmeier\'s A parameter (constant, usually 1.0)', None)
  4870         else:
  4871             luxBool('sellmeieradvanced', sellmeieradvanced, 'Advanced', 'Advanced parameters', gui, 1.6 if sellmeieradvanced.get() == 'false' else 0.6)
  4872             if sellmeieradvanced.get() == 'false':
  4873                 str += luxFloat('A', sellmeiera, 0.001, 10.0, 'sellmeier a', 'Sellmeier\'s A parameter (constant, usually 1.0)', None)
  4874                 luxFloat('B1', sellmeierb1, 0, 99.9, 'b', 'Sellmeier\'s first B parameter', gui, 0.66)
  4875                 luxFloat('B2', sellmeierb2, 0, 99.9, 'b', 'Sellmeier\'s second B parameter', gui, 0.66)
  4876                 luxFloat('B3', sellmeierb3, 0, 99.9, 'b', 'Sellmeier\'s third B parameter', gui, 0.66)
  4877                 luxFloat('C1', sellmeierc1, 0, 9.9, 'c', 'Sellmeier\'s first C parameter', gui, 0.66)
  4878                 luxFloat('C2', sellmeierc2, 0, 9.9, 'c', 'Sellmeier\'s second C parameter', gui, 0.66)
  4879                 luxFloat('C3', sellmeierc3, 0, 999.9, 'c', 'Sellmeier\'s third C parameter', gui, 0.66)
  4880                 sellmeierb.set(' '.join([__builtins__['str'](sellmeierb1.get()), types.StringType(sellmeierb2.get()), types.StringType(sellmeierb3.get())]))
  4881                 sellmeierc.set(' '.join([__builtins__['str'](sellmeierc1.get()), types.StringType(sellmeierc2.get()), types.StringType(sellmeierc3.get())]))
  4882             else:
  4883                 str += luxFloat('A', sellmeiera, 0.001, 10.0, 'sellmeier a', 'Sellmeier\'s A parameter (constant, usually 1.0)', gui, 1.0)
  4884                 luxString('sellmeierb', sellmeierb, 'sellmeier b', 'Sellmeier\'s B parameter (space-separated list of floats)', gui,2.0)
  4885                 luxString('sellmeierc', sellmeierc, 'sellmeier c', 'Sellmeier\'s C parameter (space-separated list of floats; same number of floats as in B above)', gui,2.0)
  4886         
  4887         (listb, listc) = sellmeierStrToFloats(sellmeierb.get(), sellmeierc.get(), 0, 10000.0)
  4888         sellmeierb.set(' '.join(map(lambda x: types.StringType(x), listb)))
  4889         sellmeierc.set(' '.join(map(lambda x: types.StringType(x), listc)))
  4890         str += "\n   \"float B\" [%s]" % sellmeierb.get()
  4891         str += "\n   \"float C\" [%s]" % sellmeierc.get()
  4892         return str, ' "texture %s" ["%s"]' % (type, texname)
  4893 
  4894     if texture.get() == 'preset':
  4895         preset = luxProp(mat, keyname+'.preset', '')
  4896         presets = ['aluminium', 'amorphous carbon', 'silver', 'gold', 'copper']
  4897         str += luxOption('name', preset, presets, '  PRESET', 'Select preset from the list', gui, 2.0)
  4898         return str, ' "texture %s" ["%s"]' % (type, texname)
  4899 
  4900     if texture.get() == "blackbody":
  4901         if gui:
  4902             if gui.xmax-gui.x < gui.w: gui.newline()
  4903             r = gui.getRect(1.0, 1)
  4904             gui.newline()
  4905             drawBar(bar_blackbody, gui.xmax-gui.w-7, r[1])
  4906         str += luxFloat("temperature", luxProp(mat, keyname+".bbtemp", 6500.0), 1000.0, 10000.0, "temperature", "Black Body temperature in degrees Kelvin", gui, 2.0, 1)
  4907 
  4908     if texture.get() == "lampspectrum":
  4909         lampstring = luxProp(mat, keyname+".lampstring", "Incandescent2")
  4910         lamppreset = luxProp(mat, keyname+".lampspectrum", "PHILIPS [Argenta] 200W Incandescent Lamp")
  4911         if gui:
  4912             def setLamp(i, value, preset, tree, dict): # callback function to set ior value after selection
  4913                 if i >= 0:
  4914                     value.set(dict[i])
  4915                     preset.set(getTreeNameById(tree, i))
  4916 
  4917             measuredtree = [     ("Natural Daylight",     [ ("Natural Daylight", 1) ] ), ("Incandescent",     [ ("Paraffin Candle Flame", 2), ("Generic 7W Incandescent Lamp", 3), ("PHILIPS [Argenta] 200W Incandescent Lamp", 4), ("Welsbach Gas Mantle (modern, without Thorium)", 5), ("Incandescent Anti-Insect Lamp", 6) ] ), ("Fluorescent/Compact Fluorescent",    [ ("PHILIPS [TL-D 30W/55] Regular Daylight Fluorescent", 7), ("Sylvania [F4T5 4W] Regular Warm White Fluorescent", 8), ("OSRAM [DULUXSTAR 21W/827] Regular Compact Triphosphor Fluorescent", 9), ("Cold Cathode Warm White CFL Triphosphor Fluorescent.", 10), ("NARVA [COLOURLUX plus daylight 20W/860] Daylight CFL Triphosphor Fluorescent", 11), ("Sylvania [GroLux] Fluorescent Aquarium/Plant Lamp", 12), ("Laptop LCD Screen", 13), ("PHILIPS [ActiViva] \"Natural\" Triphosphor Fluorescent", 14), ("PHILIPS [ActiViva] \"Active\" Triphosphor Fluorescent", 16) ] ), ("High Pressure Mercury",        [ ("OSRAM [HQA 80W] Clear HPM Lamp", 17), ("PHILIPS [HPL 125W] HPM Lamp with improved color", 18), ("OSRAM [HQL 80W] HPM Lamp with improved warm deluxe color", 19), ("PHILIPS [ML 160W] Self-Ballasted HPM Vapor Lamp", 20), ("NARVA [160W] Self-ballasted HPM Vapor Lamp", 21) ] ), ("Low/High Pressure Sodium",        [ ("Regular High Pressure Sodium Lamp, warmup after 5-7 sec", 22), ("Regular High Pressure Sodium Lamp, warmup after 10-12 sec", 23), ("SOX Low Pressure Sodium Discharge Lamp", 24), ("Medium Pressure Sodium Discharge Lamp, warmup after ~35 sec", 25), ("GE [Lucalox 35W] High Pressure Sodium Lamp", 26), ("PHILIPS [SDW-T 100W] Super High Pressure White Sodium Lamp", 27) ] ), ("Metal Halide",        [ ("PHILIPS [HPI-T 400W] MH Lamp with Mercury, Sodium, Thallium and Indium iodides", 28), ("OSRAM [HQI-TS 75W/WDL] Metal Halide lamp with Mercury, sodium, thallium, indium and tin iodides, from ", 29), ("GE [MVR325IUWM 325 Watt I-Line Multi-Vapor® Metal Halide - Clear Watt Miser®] MH Lamp with Mercury, Sodium and Scandium iodides", 30), ("OSRAM [HQI-T 400W/D] MH Lamp with Mercury, Thallium, Dysprosium, Holmium, Thulium and Caesium iodides", 31), ("PHILIPS Diazo MH Lamp with Mercury, iron and cobalt iodides", 32), ("Sylvania Diazo MH Lamp with Mercury, gallium and lead iodides", 33), ("OSRAM [HQI-T 400W/Blau] Blue colored MH Lamp with Mercury and indium iodides", 34), ("RADIUM [HRI-T 400W/Planta] Plant growing MH Lamp with Mercury, indium and sodium iodides", 35), ("OSRAM [HQI-T 400W/Grun] Green colored MH Lamp with Mercury and thallium iodides", 36) ] ), ("Diode",        [ ("Regular High Brightness Blue LED", 37), ("Monochromatic emission from a Red Laser diode", 38), ("Monochromatic emission from a Green Laser diode.", 39) ] ), ("Spectral",        [ ("PHILIPS Spectral Xenon Lamp - Continuous Xenon low pressure thermionic discharge", 40), ("PHILIPS spectral Rubidium Lamp - Continuous Rubidium low pressure thermionic discharge", 41), ("PHILIPS spectral Cadmium Lamp - Continuous Cadmium low pressure thermionic discharge", 42), ("PHILIPS spectral zinc Lamp - Continuous Zinc low pressure thermionic discharge", 43) ] ), ("Glow Discharge",        [ ("Neon glow discharge", 44), ("Neon and Krypton glow discharge and green phosphor (night-lights/indicators)", 45), ("Neon and Xenon glow discharge and green phosphor (night-lights/indicators)", 46), ("Neon and Xenon glow discharge and blue phosphor (night-lights/indicators)", 48), ("Argon glow discharge", 49), ("Self-ballasted High Pressure Mercury Vapor Lamp, with yttrium vanadate phosphate fluorescent phosphors, in glow discharge mode", 50) ] ), ("Molecular",        [ ("Butane Gas Flame", 51), ("Alcohol Flame", 52) ] ), ("General Fluorescence",        [ ("Print quality A4 Xerox paper wrapped around a blacklight Lamp", 53), ("Neon green dye, bombarded with black light", 54), ("Regular Modern Color TV CRT", 55) ] ), ("Various",        [ ("Stroboscopic flash. Xenon I, likely II and perhaps III", 56), ("Carbon Arc Spectrum", 57), ("OSRAM [XBO 75W/2] Short Arc Xenon Lamp", 58) ] ), ("Blacklight/Ultraviolet",        [ ("Sylvania [G8T5 8W] Germicidal lamp", 59), ("Sylvania [F6T5/BLB 8W] Black light blue fluorescent", 60), ("PHILIPS [HPW 125W] High Pressure Mercury Black Light", 61), ("Sylvania [Blacklite 350 F8W/BL350] Black Light fluorescent", 62) ] ), ("Mercury UV Spectrum",        [ ("The near visible UVA emissions from a high pressure Mercury clear lamp", 63) ] ), ("Absorption/Mixed Spectra",        [ ("High Pressure Mercury Warm Deluxe light ([1.4.3]) absorbed through blue Cobalt glass", 64), ("Incandescent light ([1.2.3]) absorbed through blue Cobalt glass", 65), ("High Pressure Mercury Warm Deluxe light ([1.4.3]) absorbed through ciel dye #42053", 66), ("Incandescent light ([1.2.3]) absorbed through ciel dye #42053", 67), ("High Pressure Mercury Warm Deluxe light ([1.4.3]) absorbed through red glass", 68), ("Incandescent light ([1.2.3]) absorbed through red glass.m", 69), ("Incandescent light ([1.2.3]) absorbed through olive oil. ", 70) ] ) ] 
  4918 
  4919             measureddict  = {1:"Daylight", 2:"Candle", 3:"Incandescent1", 4:"Incandescent2", 5:"Welsbach", 6:"AntiInsect", 7:"FLD2", 8:"FL37K", 9:"CFL27K", 10:"CFL4K", 11:"CFL6K", 12:"GroLux", 13:"LCDS", 14:"FLAV8K", 15:"none", 16:"FLAV17K", 17:"HPM2", 18:"HPMFL1", 19:"HPMFL2", 20:"HPMSB", 21:"HPMSBFL", 22:"SS1", 23:"SS2", 24:"LPS", 25:"MPS", 26:"HPS", 27:"SHPS", 28:"MHN", 29:"MHWWD", 30:"MHSc", 31:"MHD", 32:"FeCo", 33:"GaPb", 34:"BLAU", 35:"PLANTA", 36:"GRUN", 37:"LEDB", 38:"RedLaser", 39:"GreenLaser", 40:"XeI", 41:"Rb", 42:"Cd", 43:"Zn", 44:"Ne", 45:"NeKrFL", 46:"NeXeFL1", 47:"none", 48:"NeXeFL2", 49:"Ar", 50:"HPMFL2Glow", 51:"Butane", 52:"Alcohol", 53:"BLP", 54:"BLNG", 55:"TV", 56:"Xe", 57:"CarbonArc", 58:"HPX", 59:"LPM2", 60:"FLBLB", 61:"HPMBL", 62:"FLBL", 63:"UVA", 64:"HPMFLCobaltGlass", 65:"CobaltGlass", 66:"HPMFLCL42053", 67:"CL42053", 68:"HPMFLRedGlass", 69:"RedGlass", 70:"OliveOil" }
  4920 
  4921             r = gui.getRect(2.0, 1)
  4922             Draw.Button(lamppreset.get(), evtLuxGui, r[0], r[1], r[2], r[3], "select lamp spectrum", lambda e,v: setLamp(Draw.PupTreeMenu(measuredtree), lampstring, lamppreset, measuredtree, measureddict))
  4923         str += luxString("name", lampstring, "Lamp", "Choose measured Lamp Spectrum", None, 2.0)
  4924 
  4925     if texture.get() == "equalenergy":
  4926         if gui:
  4927             if gui.xmax-gui.x < gui.w: gui.newline()
  4928             r = gui.getRect(1.0, 1)
  4929             gui.newline()
  4930             drawBar(bar_equalenergy, gui.xmax-gui.w-7, r[1])
  4931         str += luxFloat("energy", luxProp(mat, keyname+".energy", 1.0), 0.0, 1.0, "energy", "Energy of each spectral band", gui, 2.0, 1)
  4932 
  4933     if texture.get() == "frequency":
  4934         str += luxFloat("freq", luxProp(mat, keyname+".freq", 0.01), 0.01, 100.0, "frequency", "Frequency in nm", gui, 2.0, 1)
  4935         str += luxFloat("phase", luxProp(mat, keyname+".phase", 0.5), 0.0, 1.0, "phase", "Phase", gui, 1.1, 1)
  4936         str += luxFloat("energy", luxProp(mat, keyname+".energy", 1.0), 0.0, 1.0, "energy", "Amount of mean energy", gui, 0.9, 1)
  4937 
  4938     if texture.get() == "gaussian":
  4939         if gui:
  4940             if gui.xmax-gui.x < gui.w: gui.newline()
  4941             r = gui.getRect(1.0, 1)
  4942             gui.newline()
  4943             drawBar(bar_spectrum, gui.xmax-gui.w-7, r[1])
  4944         str += luxFloat("wavelength", luxProp(mat, keyname+".wavelength", 550.0), 380.0, 720.0, "wavelength", "Mean Wavelength in visible spectrum in nm", gui, 2.0, 1)
  4945         str += luxFloat("width", luxProp(mat, keyname+".width", 50.0), 20.0, 300.0, "width", "Width of gaussian distribution in nm", gui, 1.1, 1)
  4946         str += luxFloat("energy", luxProp(mat, keyname+".energy", 1.0), 0.0, 1.0, "energy", "Amount of mean energy", gui, 0.9, 1)
  4947 
  4948     if texture.get() in ["tabulateddata", "sopra", "luxpop"]:
  4949         if gui: gui.newline("data file:", -2, level)        
  4950         str += luxFile("filename", luxProp(mat, keyname+".filename", ""), "file", "data file path", gui, 2.0)
  4951         if type == 'fresnel':
  4952             return str, ' "texture %s" ["%s"]' % (type, texname)
  4953 
  4954     if texture.get() == "imagemap":
  4955         if gui: gui.newline("IM-clip:", -2, level)
  4956         str += luxOption("wrap", luxProp(mat, keyname+".wrap", "repeat"), ["repeat","black","clamp"], "repeat", "", gui, 1.0)
  4957         if type=="float":
  4958             str += luxOption("channel", luxProp(mat, keyname+".channel", "mean"), ["red", "green", "blue", "alpha", "mean", "colored_mean"], "channel", "Image channel", gui, 1.0)
  4959 
  4960         if gui: gui.newline("IM-source:", -2, level)
  4961 
  4962         # ZANQDO
  4963         texturefilename = luxProp(mat, keyname+".filename", "")
  4964         extimage = luxProp(mat, keyname+'.externalimage', "true")
  4965         luxBool("External Image", extimage, "External Image", "External Image", gui, 1.0)
  4966         if gui: gui.newline("IM-path:", -2, level)
  4967         if extimage.get() == "true":
  4968             luxFile("filename", texturefilename, "file", "texture file path", gui, 2.0)
  4969         else:
  4970             bil = [i.filename for i in Image.Get() if '.' in i.filename]
  4971             try:
  4972                 uti = [i.filename for i in Image.Get() if '.' not in i.filename]
  4973                 if len(uti) > 0:
  4974                     luxLabel("INFO: Images not listed here must be saved first", gui)
  4975             except: pass    
  4976             if len(bil) > 0:
  4977                 luxOption("Image", texturefilename, bil, "Blender Images", "Blender Image", gui, 2.0)
  4978             else:
  4979                 luxLabel("No Blender Images - Load Image in the Image Editor", gui)
  4980         # dougal2 image file packing
  4981         impack = luxProp(Scene.GetCurrent(), 'packtextures', 'false')
  4982         
  4983         if impack.get() == 'false':
  4984             str += luxFile("filename", texturefilename, "file", "texture file path", None, 2.0)
  4985         else:
  4986             import zlib, base64
  4987             def get_image_data(filename):
  4988                 try:
  4989                     f=open(filename,'rb')
  4990                     d=f.read()
  4991                     f.close()
  4992                 except:
  4993                     print('Error reading image data from %s' % filename)
  4994                     d = ''
  4995                 return base64.b64encode(zlib.compress(d))
  4996             imdata = get_image_data(texturefilename.get())
  4997             str += '\r\n   "string imagedata" ["%s"]' % imdata
  4998         
  4999         useseq = luxProp(mat, keyname+".useseq", "false")
  5000         luxCollapse("usesew", useseq, "Sequence", "", gui, 2.0)
  5001     
  5002         if useseq.get() == "true":
  5003             seqframes = luxProp(mat, keyname+".seqframes", 100)
  5004             luxInt("frames", seqframes, 1, 100000, "Frames", "", gui, 0.5)
  5005             seqoffset = luxProp(mat, keyname+".seqoffset", 0)
  5006             luxInt("offset", seqoffset, 0, 100000, "Offset", "", gui, 0.5)
  5007             seqstartframe = luxProp(mat, keyname+".seqsframe", 1)
  5008             luxInt("startframe", seqstartframe, 1, 100000, "StartFr", "", gui, 0.5)
  5009             seqcyclic = luxProp(mat, keyname+".seqcycl", "false")
  5010             luxBool("cyclic", seqcyclic, "Cyclic", "", gui, 0.5)
  5011     
  5012             
  5013             totalframes = seqframes.get()
  5014             currentframe = Blender.Get('curframe')
  5015     
  5016             if(currentframe < seqstartframe.get()):
  5017                 fnumber = 1 + seqoffset.get()
  5018             else:
  5019                 fnumber = (currentframe - (seqstartframe.get()-1)) + seqoffset.get()
  5020     
  5021             if(fnumber > seqframes.get()):
  5022                 if(seqcyclic.get() == "false"):
  5023                     fnumber = seqframes.get()
  5024                 else:
  5025                     fnumber = currentframe % seqframes.get()
  5026     
  5027             import re
  5028             def get_seq_filename(number, filename):
  5029                 m = re.findall(r'(\d+)', filename)
  5030                 if len(m) == 0:
  5031                     return "ERR: Can't find pattern"
  5032     
  5033                 rightmost_number = m[len(m)-1]
  5034                 seq_length = len(rightmost_number)
  5035     
  5036                 nstr = "%i" %number
  5037                 new_seq_number = nstr.zfill(seq_length)
  5038      
  5039                 return filename.replace(rightmost_number, new_seq_number)
  5040      
  5041             texturefilename.set(get_seq_filename(fnumber, texturefilename.get()))
  5042             if gui: gui.newline()
  5043     
  5044         str += luxFloat("gamma", luxProp(mat, keyname+".gamma", texturegamma()), 0.0, 6.0, "gamma", "", gui, 0.75)
  5045         str += luxFloat("gain", luxProp(mat, keyname+".gain", 1.0), 0.0, 10.0, "gain", "", gui, 0.5)
  5046         filttype = luxProp(mat, keyname+".filtertype", "bilinear")
  5047         filttypes = ["mipmap_ewa","mipmap_trilinear","bilinear","nearest"]
  5048         str += luxOption("filtertype", filttype, filttypes, "filtertype", "Choose the filtering method to use for the image texture", gui, 0.75)
  5049         
  5050         if filttype.get() == "mipmap_ewa" or filttype.get() == "mipmap_trilinear":    
  5051             str += luxFloat("maxanisotropy", luxProp(mat, keyname+".maxanisotropy", 8.0), 1.0, 512.0, "maxaniso", "", gui, 1.0)
  5052             str += luxInt("discardmipmaps", luxProp(mat, keyname+".discardmipmaps", 0), 0, 1, "discardmips", "", gui, 1.0)
  5053     
  5054         str += luxMapping(keyname, mat, gui, level+1)
  5055 
  5056     if texture.get() == "mix":
  5057         (s, l) = c(("", ""), luxTexture("amount", keyname, "float", 0.5, 0.0, 1.0, "amount", "The degree of mix between the two textures", mat, gui, matlevel, texlevel+1, lightsource))
  5058         (s, l) = c((s, l), luxTexture("tex1", keyname, type, default, min, max, "tex1", "", mat, gui, matlevel, texlevel+1, lightsource))
  5059         (s, l) = c((s, l), luxTexture("tex2", keyname, type, alternativedefault(type, default), min, max, "tex2", "", mat, gui, matlevel, texlevel+1, lightsource))
  5060         str = s + str + l
  5061 
  5062     if texture.get() == "scale":
  5063         (s, l) = c(("", ""), luxTexture("tex1", keyname, type, default, min, max, "tex1", "", mat, gui, matlevel, texlevel+1, lightsource))
  5064         (s, l) = c((s, l), luxTexture("tex2", keyname, type, alternativedefault(type, default), min, max, "tex2", "", mat, gui, matlevel, texlevel+1, lightsource))
  5065         str = s + str + l
  5066 
  5067     if texture.get() == "bilerp":
  5068         if type == "float":
  5069             str += luxFloat("v00", luxProp(mat, keyname+".v00", 0.0), min, max, "v00", "", gui, 1.0)
  5070             str += luxFloat("v01", luxProp(mat, keyname+".v01", 1.0), min, max, "v01", "", gui, 1.0)
  5071             if gui: gui.newline("", -2)
  5072             str += luxFloat("v10", luxProp(mat, keyname+".v10", 0.0), min, max, "v10", "", gui, 1.0)
  5073             str += luxFloat("v11", luxProp(mat, keyname+".v11", 1.0), min, max, "v11", "", gui, 1.0)
  5074         elif type == "color":
  5075             if gui: gui.newline("          v00:", -2)
  5076             str += luxRGB("v00", luxProp(mat, keyname+".v00", "0.0 0.0 0.0"), max, "v00", "", gui, 2.0)
  5077             if gui: gui.newline("          v01:", -2)
  5078             str += luxRGB("v01", luxProp(mat, keyname+".v01", "1.0 1.0 1.0"), max, "v01", "", gui, 2.0)
  5079             if gui: gui.newline("          v10:", -2)
  5080             str += luxRGB("v10", luxProp(mat, keyname+".v10", "0.0 0.0 0.0"), max, "v10", "", gui, 2.0)
  5081             if gui: gui.newline("          v11:", -2)
  5082             str += luxRGB("v11", luxProp(mat, keyname+".v11", "1.0 1.0 1.0"), max, "v11", "", gui, 2.0)
  5083         str += luxMapping(keyname, mat, gui, level+1)
  5084 
  5085     if texture.get() == "windy":
  5086         str += lux3DMapping(keyname, mat, gui, level+1)
  5087         # this texture has no options 
  5088 
  5089         if type!="float":
  5090             str += "Texture \"%s\" \"%s\" \"mix\" \"texture amount\" [\"%s::amount\"]"%(texname, type, texname)
  5091             (s, l) = c(("", ""), luxTexture("tex1", keyname, type, default, min, max, "tex1", "", mat, gui, matlevel, texlevel+1, lightsource))
  5092             (s, l) = c((s, l), luxTexture("tex2", keyname, type, alternativedefault(type, default), min, max, "tex2", "", mat, gui, matlevel, texlevel+1, lightsource))
  5093             str = s + str + l
  5094 
  5095     if texture.get() == "checkerboard":
  5096         dim = luxProp(mat, keyname+".dim", 2)
  5097         str += luxInt("dimension", dim, 2, 3, "dim", "", gui, 1)
  5098         if dim.get() == 2: str += luxOption("aamode", luxProp(mat, keyname+".aamode", "closedform"), ["closedform","supersample","none"], "aamode", "antialiasing mode", gui, 0.6)
  5099         if gui: gui.newline("", -2)
  5100         if dim.get() == 2: str += luxMapping(keyname, mat, gui, level+1) 
  5101         if dim.get() == 3: str += lux3DMapping(keyname, mat, gui, level+1)
  5102 
  5103         if type!="float":
  5104             str += "Texture \"%s\" \"%s\" \"mix\" \"texture amount\" [\"%s::amount\"]"%(texname, type, texname)
  5105 
  5106         (s, l) = c(("", ""), luxTexture("tex1", keyname, type, default, min, max, "tex1", "", mat, gui, matlevel, texlevel+1, lightsource))
  5107         (s, l) = c((s, l), luxTexture("tex2", keyname, type, alternativedefault(type, default), min, max, "tex2", "", mat, gui, matlevel, texlevel+1, lightsource))
  5108         str = s + str + l
  5109 
  5110     if texture.get() == "dots":
  5111         str += luxMapping(keyname, mat, gui, level+1)
  5112 
  5113         if type!="float":
  5114             str += "Texture \"%s\" \"%s\" \"mix\" \"texture amount\" [\"%s::amount\"]"%(texname, type, texname)
  5115 
  5116         (s, l) = c(("", ""), luxTexture("tex1", keyname, type, default, min, max, "inside", "", mat, gui, matlevel, texlevel+1, lightsource))
  5117         (s, l) = c((s, l), luxTexture("tex2", keyname, type, alternativedefault(type, default), min, max, "outside", "", mat, gui, matlevel, texlevel+1, lightsource))
  5118         str = s + str + l
  5119 
  5120     if texture.get() == "fbm":
  5121         str += luxInt("octaves", luxProp(mat, keyname+".octaves", 8), 1, 100, "octaves", "", gui, 1)
  5122         # if gui: gui.newline("", -2)
  5123         str += luxFloat("roughness", luxProp(mat, keyname+".roughness", 0.5), 0.0, 1.0, "roughness", "", gui, 1, 1)
  5124         if gui: gui.newline("", -2)
  5125         str += lux3DMapping(keyname, mat, gui, level+1)
  5126 
  5127         if type!="float":
  5128             str += "Texture \"%s\" \"%s\" \"mix\" \"texture amount\" [\"%s::amount\"]"%(texname, type, texname)
  5129             (s, l) = c(("", ""), luxTexture("tex1", keyname, type, default, min, max, "tex1", "", mat, gui, matlevel, texlevel+1, lightsource))
  5130             (s, l) = c((s, l), luxTexture("tex2", keyname, type, alternativedefault(type, default), min, max, "tex2", "", mat, gui, matlevel, texlevel+1, lightsource))
  5131             str = s + str + l
  5132 
  5133     if texture.get() == "marble":
  5134         str += luxInt("octaves", luxProp(mat, keyname+".octaves", 8), 1, 100, "octaves", "", gui, 1)
  5135         # if gui: gui.newline("", -2)
  5136         str += luxFloat("roughness", luxProp(mat, keyname+".roughness", 0.5), 0.0, 1.0, "roughness", "", gui, 1, 1)
  5137         if gui: gui.newline("", -2)
  5138         str += luxFloat("nscale", luxProp(mat, keyname+".nscale", 1.0), 0.0, 100.0, "nscale", "Scaling factor for the noise input", gui, 1.0)
  5139         str += luxFloat("variation", luxProp(mat, keyname+".variation", 0.2), 0.0, 100.0, "variation", "A scaling factor for the noise input function", gui, 1.0)
  5140         if gui: gui.newline("", -2)
  5141         str += lux3DMapping(keyname, mat, gui, level+1)
  5142 
  5143     if texture.get() == "wrinkled":
  5144         str += luxInt("octaves", luxProp(mat, keyname+".octaves", 8), 1, 100, "octaves", "", gui, 1)
  5145         # if gui: gui.newline("", -2)
  5146         str += luxFloat("roughness", luxProp(mat, keyname+".roughness", 0.5), 0.0, 1.0, "roughness", "", gui, 1, 1)
  5147         if gui: gui.newline("", -2)
  5148         str += lux3DMapping(keyname, mat, gui, level+1)
  5149 
  5150         if type!="float":
  5151             str += "Texture \"%s\" \"%s\" \"mix\" \"texture amount\" [\"%s::amount\"]"%(texname, type, texname)
  5152             (s, l) = c(("", ""), luxTexture("tex1", keyname, type, default, min, max, "tex1", "", mat, gui, matlevel, texlevel+1, lightsource))
  5153             (s, l) = c((s, l), luxTexture("tex2", keyname, type, alternativedefault(type, default), min, max, "tex2", "", mat, gui, matlevel, texlevel+1, lightsource))
  5154             str = s + str + l
  5155 
  5156     if texture.get() == "brick":
  5157         bonds = ["stacked","running", "flemish", "english", "herringbone", "basket", "chain link"]
  5158         bond = luxProp(mat, keyname+".brickbond", "running")
  5159         running = ["running","flemish"]
  5160         defaultrun = {"running":0.5,"flemish":0.75}
  5161         pavers = ["basket","herringbone"]
  5162         
  5163         if gui: gui.newline("brick:", -2, level+1, icon_texparam)
  5164 
  5165         str += luxFloat("brickwidth", luxProp(mat, keyname+".brickwidth", 0.3), 0.0, 10.0, "brickwidth (X)", "", gui, 1.0)
  5166         str += luxFloat("brickheight", luxProp(mat, keyname+".brickheight", 0.1), 0.0, 10.0, "brickheight (Z)", "", gui, 1.0)
  5167         str += luxFloat("brickdepth", luxProp(mat, keyname+".brickdepth", 0.15), 0.0, 10.0, "brickdepth (Y)", "", gui, 1.0)  
  5168         
  5169         if gui: gui.newline("mortar:", -2, level+1, icon_texparam)
  5170 
  5171         str += luxFloat("mortarsize", luxProp(mat, keyname+".mortarsize", 0.01), 0.0, 1.0, "mortarsize", "", gui, 1.0)
  5172 
  5173         if gui: gui.newline("bond:", -2, level+1, icon_texparam)
  5174         str += luxOption("brickbond", bond, bonds, "bond", "", gui, 0.5)
  5175         if bond.get() in running:
  5176             str += luxFloat("brickrun", luxProp(mat, keyname+".brickrun", defaultrun[bond.get()]), -10.0, 10.0, "brickrun", "", gui, 1.0)
  5177         if bond.get() in pavers and gui:
  5178             gui.newline("This paving bond is only mapped to xy",-2,level+7);
  5179             
  5180         (s, l) = c(("", ""), luxTexture("bricktex", keyname, type, default, min, max, "bricktex", "", mat, gui, matlevel, texlevel+1, lightsource))
  5181         (s, l) = c((s, l), luxTexture("mortartex", keyname, type, alternativedefault(type, default), min, max, "mortartex", "", mat, gui, matlevel, texlevel+1, lightsource))
  5182         (s, l) = c((s, l), luxTexture("brickmodtex", keyname, type, default, min, max, "modulation", "", mat, gui, matlevel, texlevel+1, lightsource))
  5183         str = s + str + l
  5184 
  5185         str += lux3DMapping(keyname, mat, gui, level+1)
  5186 
  5187     if texture.get() == "blender_marble":
  5188         if gui: gui.newline("noise:", -2, level+1, icon_texparam)
  5189 
  5190         mtype = luxProp(mat, keyname+".mtype", "soft")
  5191         mtypes = ["soft","sharp","sharper"]
  5192         str += luxOption("type", mtype, mtypes, "type", "", gui, 0.5)
  5193 
  5194         noisetype = luxProp(mat, keyname+".noisetype", "hard_noise")
  5195         noisetypes = ["soft_noise","hard_noise"]
  5196         str += luxOption("noisetype", noisetype, noisetypes, "noisetypes", "", gui, 0.75)
  5197 
  5198         str += luxInt("noisedepth", luxProp(mat, keyname+".noisedepth", 2), 0, 6, "noisedepth", "", gui, 0.75)
  5199 
  5200         str += luxFloat("noisesize", luxProp(mat, keyname+".noisesize", 0.25), 0.0, 2.0, "noisesize", "", gui, 1.0)
  5201         str += luxFloat("turbulence", luxProp(mat, keyname+".turbulence", 5.0), 0.0, 200.0, "turbulence", "", gui, 1.0)
  5202 
  5203         if gui: gui.newline("basis:", -2, level+1, icon_texparam)
  5204         noisebasis2 = luxProp(mat, keyname+".noisebasis2", "sin")
  5205         noisebasises2 = ["sin","saw","tri"]
  5206         str += luxOption("noisebasis2", noisebasis2, noisebasises2, "noisebasis2", "", gui, 0.7)
  5207 
  5208         noisebasis = luxProp(mat, keyname+".noisebasis", "blender_original")
  5209         noisebasises = ["blender_original","original_perlin", "improved_perlin", "voronoi_f1", "voronoi_f2", "voronoi_f3", "voronoi_f4", "voronoi_f2f1", "voronoi_crackle", "cell_noise"]
  5210         str += luxOption("noisebasis", noisebasis, noisebasises, "noisebasis", "", gui, 1.3)
  5211 
  5212         if gui: gui.newline("level:", -2, level+1, icon_texparam)
  5213         str += luxFloat("bright", luxProp(mat, keyname+".bright", 1.0), 0.0, 2.0, "bright", "", gui, 1.0)
  5214         str += luxFloat("contrast", luxProp(mat, keyname+".contrast", 1.0), 0.0, 10.0, "contrast", "", gui, 1.0)
  5215 
  5216         str += lux3DMapping(keyname, mat, gui, level+1)
  5217 
  5218         if type!="float":
  5219             str += "Texture \"%s\" \"%s\" \"mix\" \"texture amount\" [\"%s::amount\"]"%(texname, type, texname)
  5220 
  5221         (s, l) = c(("", ""), luxTexture("tex1", keyname, type, default, min, max, "tex1", "", mat, gui, matlevel, texlevel+1, lightsource))
  5222         (s, l) = c((s, l), luxTexture("tex2", keyname, type, alternativedefault(type, default), min, max, "tex2", "", mat, gui, matlevel, texlevel+1, lightsource))
  5223         str = s + str + l
  5224 
  5225     if texture.get() == "blender_musgrave":
  5226         if gui: gui.newline("type:", -2, level+1, icon_texparam)
  5227         mtype = luxProp(mat, keyname+".mtype", "multifractal")
  5228         mtypes = ["multifractal","ridged_multifractal", "hybrid_multifractal", "hetero_terrain", "fbm"]
  5229         str += luxOption("type", mtype, mtypes, "type", "", gui, 2.0)
  5230 
  5231         str += luxFloat("h", luxProp(mat, keyname+".h", 1.0), 0.0, 2.0, "h", "", gui, 0.5)
  5232         str += luxFloat("lacu", luxProp(mat, keyname+".lacu", 2.0), 0.0, 6.0, "lacu", "", gui, 0.75)
  5233         str += luxFloat("octs", luxProp(mat, keyname+".octs", 2.0), 0.0, 8.0, "octs", "", gui, 0.75)
  5234 
  5235         if mtype.get() == "hetero_terrain":
  5236             str += luxFloat("offset", luxProp(mat, keyname+".offset", 2.0), 0.0, 6.0, "offset", "", gui, 2.0)
  5237         if mtype.get() == "ridged_multifractal":
  5238             str += luxFloat("offset", luxProp(mat, keyname+".offset", 2.0), 0.0, 6.0, "offset", "", gui, 1.25)
  5239             str += luxFloat("gain", luxProp(mat, keyname+".gain", 2.0), 0.0, 6.0, "gain", "", gui, 0.75)
  5240         if mtype.get() == "hybrid_multifractal":
  5241             str += luxFloat("offset", luxProp(mat, keyname+".offset", 2.0), 0.0, 6.0, "offset", "", gui, 1.25)
  5242             str += luxFloat("gain", luxProp(mat, keyname+".gain", 2.0), 0.0, 6.0, "gain", "", gui, 0.75)
  5243 
  5244         str += luxFloat("outscale", luxProp(mat, keyname+".outscale", 1.0), 0.0, 10.0, "iscale", "", gui, 1.0)
  5245         str += luxFloat("noisesize", luxProp(mat, keyname+".noisesize", 0.25), 0.0, 2.0, "noisesize", "", gui, 1.0)
  5246 
  5247         if gui: gui.newline("basis:", -2, level+1, icon_texparam)
  5248         noisebasis = luxProp(mat, keyname+".noisebasis", "blender_original")
  5249         noisebasises = ["blender_original","original_perlin", "improved_perlin", "voronoi_f1", "voronoi_f2", "voronoi_f3", "voronoi_f4", "voronoi_f2f1", "voronoi_crackle", "cell_noise"]
  5250         str += luxOption("noisebasis", noisebasis, noisebasises, "noisebasis", "", gui, 2.0)
  5251 
  5252         if gui: gui.newline("level:", -2, level+1, icon_texparam)
  5253         str += luxFloat("bright", luxProp(mat, keyname+".bright", 1.0), 0.0, 2.0, "bright", "", gui, 1.0)
  5254         str += luxFloat("contrast", luxProp(mat, keyname+".contrast", 1.0), 0.0, 10.0, "contrast", "", gui, 1.0)
  5255 
  5256         str += lux3DMapping(keyname, mat, gui, level+1)
  5257 
  5258         if type!="float":
  5259             str += "Texture \"%s\" \"%s\" \"mix\" \"texture amount\" [\"%s::amount\"]"%(texname, type, texname)
  5260 
  5261         (s, l) = c(("", ""), luxTexture("tex1", keyname, type, default, min, max, "tex1", "", mat, gui, matlevel, texlevel+1, lightsource))
  5262         (s, l) = c((s, l), luxTexture("tex2", keyname, type, alternativedefault(type, default), min, max, "tex2", "", mat, gui, matlevel, texlevel+1, lightsource))
  5263         str = s + str + l
  5264 
  5265     if texture.get() == "blender_wood":
  5266         if gui: gui.newline("noise:", -2, level+1, icon_texparam)
  5267 
  5268         mtype = luxProp(mat, keyname+".mtype", "bands")
  5269         mtypes = ["bands","rings","bandnoise", "ringnoise"]
  5270         str += luxOption("type", mtype, mtypes, "type", "", gui, 0.5)
  5271 
  5272         noisetype = luxProp(mat, keyname+".noisetype", "hard_noise")
  5273         noisetypes = ["soft_noise","hard_noise"]
  5274         str += luxOption("noisetype", noisetype, noisetypes, "noisetypes", "", gui, 0.75)
  5275 
  5276         str += luxFloat("noisesize", luxProp(mat, keyname+".noisesize", 0.25), 0.0, 2.0, "noisesize", "", gui, 1.0)
  5277         str += luxFloat("turbulence", luxProp(mat, keyname+".turbulence", 5.0), 0.0, 200.0, "turbulence", "", gui, 1.0)
  5278 
  5279         if gui: gui.newline("basis:", -2, level+1, icon_texparam)
  5280         noisebasis2 = luxProp(mat, keyname+".noisebasis2", "sin")
  5281         noisebasises2 = ["sin","saw","tri"]
  5282         str += luxOption("noisebasis2", noisebasis2, noisebasises2, "noisebasis2", "", gui, 0.7)
  5283 
  5284         noisebasis = luxProp(mat, keyname+".noisebasis", "blender_original")
  5285         noisebasises = ["blender_original","original_perlin", "improved_perlin", "voronoi_f1", "voronoi_f2", "voronoi_f3", "voronoi_f4", "voronoi_f2f1", "voronoi_crackle", "cell_noise"]
  5286         str += luxOption("noisebasis", noisebasis, noisebasises, "noisebasis", "", gui, 1.3)
  5287 
  5288         if gui: gui.newline("level:", -2, level+1, icon_texparam)
  5289         str += luxFloat("bright", luxProp(mat, keyname+".bright", 1.0), 0.0, 2.0, "bright", "", gui, 1.0)
  5290         str += luxFloat("contrast", luxProp(mat, keyname+".contrast", 1.0), 0.0, 10.0, "contrast", "", gui, 1.0)
  5291     
  5292         str += lux3DMapping(keyname, mat, gui, level+1)
  5293 
  5294         if type!="float":
  5295             str += "Texture \"%s\" \"%s\" \"mix\" \"texture amount\" [\"%s::amount\"]"%(texname, type, texname)
  5296 
  5297         (s, l) = c(("", ""), luxTexture("tex1", keyname, type, default, min, max, "tex1", "", mat, gui, matlevel, texlevel+1, lightsource))
  5298         (s, l) = c((s, l), luxTexture("tex2", keyname, type, alternativedefault(type, default), min, max, "tex2", "", mat, gui, matlevel, texlevel+1, lightsource))
  5299         str = s + str + l
  5300 
  5301     if texture.get() == "blender_clouds":
  5302         if gui: gui.newline("noise:", -2, level+1, icon_texparam)
  5303 
  5304         mtype = luxProp(mat, keyname+".mtype", "default")
  5305         mtypes = ["default","color"]
  5306         str += luxOption("type", mtype, mtypes, "type", "", gui, 0.5)
  5307 
  5308         noisetype = luxProp(mat, keyname+".noisetype", "hard_noise")
  5309         noisetypes = ["soft_noise","hard_noise"]
  5310         str += luxOption("noisetype", noisetype, noisetypes, "noisetypes", "", gui, 0.75)
  5311 
  5312         str += luxFloat("noisesize", luxProp(mat, keyname+".noisesize", 0.25), 0.0, 2.0, "noisesize", "", gui, 1.0)
  5313         str += luxInt("noisedepth", luxProp(mat, keyname+".noisedepth", 2), 0, 6, "noisedepth", "", gui, 1.0)
  5314 
  5315         if gui: gui.newline("basis:", -2, level+1, icon_texparam)
  5316         noisebasis = luxProp(mat, keyname+".noisebasis", "blender_original")
  5317         noisebasises = ["blender_original","original_perlin", "improved_perlin", "voronoi_f1", "voronoi_f2", "voronoi_f3", "voronoi_f4", "voronoi_f2f1", "voronoi_crackle", "cell_noise"]
  5318         str += luxOption("noisebasis", noisebasis, noisebasises, "noisebasis", "", gui, 1.3)
  5319 
  5320         if gui: gui.newline("level:", -2, level+1, icon_texparam)
  5321         str += luxFloat("bright", luxProp(mat, keyname+".bright", 1.0), 0.0, 2.0, "bright", "", gui, 1.0)
  5322         str += luxFloat("contrast", luxProp(mat, keyname+".contrast", 1.0), 0.0, 10.0, "contrast", "", gui, 1.0)
  5323     
  5324         str += lux3DMapping(keyname, mat, gui, level+1)
  5325 
  5326         if type!="float":
  5327             str += "Texture \"%s\" \"%s\" \"mix\" \"texture amount\" [\"%s::amount\"]"%(texname, type, texname)
  5328 
  5329         (s, l) = c(("", ""), luxTexture("tex1", keyname, type, default, min, max, "tex1", "", mat, gui, matlevel, texlevel+1, lightsource))
  5330         (s, l) = c((s, l), luxTexture("tex2", keyname, type, alternativedefault(type, default), min, max, "tex2", "", mat, gui, matlevel, texlevel+1, lightsource))
  5331         str = s + str + l
  5332 
  5333     if texture.get() == "blender_blend":
  5334         if gui: gui.newline("type:", -2, level+1, icon_texparam)
  5335 
  5336         mtype = luxProp(mat, keyname+".mtype", "lin")
  5337         mtypes = ["lin","quad","ease","diag","sphere","halo","radial"]
  5338         str += luxOption("type", mtype, mtypes, "type", "", gui, 0.5)
  5339         
  5340         mflag = luxProp(mat, keyname+".flag", "false")
  5341         str += luxBool("flipxy", mflag, "flipXY", "", gui, 0.5)
  5342 
  5343         if gui: gui.newline("level:", -2, level+1, icon_texparam)
  5344         str += luxFloat("bright", luxProp(mat, keyname+".bright", 1.0), 0.0, 2.0, "bright", "", gui, 1.0)
  5345         str += luxFloat("contrast", luxProp(mat, keyname+".contrast", 1.0), 0.0, 10.0, "contrast", "", gui, 1.0)
  5346         
  5347         str += lux3DMapping(keyname, mat, gui, level+1)
  5348 
  5349         if type!="float":
  5350             str += "Texture \"%s\" \"%s\" \"mix\" \"texture amount\" [\"%s::amount\"]"%(texname, type, texname)
  5351 
  5352         (s, l) = c(("", ""), luxTexture("tex1", keyname, type, default, min, max, "tex1", "", mat, gui, matlevel, texlevel+1, lightsource))
  5353         (s, l) = c((s, l), luxTexture("tex2", keyname, type, alternativedefault(type, default), min, max, "tex2", "", mat, gui, matlevel, texlevel+1, lightsource))
  5354         str = s + str + l
  5355 
  5356     if texture.get() == "blender_distortednoise":
  5357         if gui: gui.newline("noise:", -2, level+1, icon_texparam)
  5358         
  5359         str += luxFloat("distamount", luxProp(mat, keyname+".distamount", 1.0), 0.0, 10.0, "distamount", "", gui, 1.0)
  5360         str += luxFloat("noisesize", luxProp(mat, keyname+".noisesize", 0.25), 0.0, 2.0, "noisesize", "", gui, 1.0)
  5361         str += luxFloat("nabla", luxProp(mat, keyname+".nabla", 0.025), 0.000, 2.0, "nabla", "", gui, 1.0)
  5362         
  5363         if gui: gui.newline("distortion:", -2, level+1, icon_texparam)
  5364         ntype = luxProp(mat, keyname+".type", "blender_original")
  5365         ntypes = ["blender_original","original_perlin", "improved_perlin", "voronoi_f1", "voronoi_f2", "voronoi_f3", "voronoi_f4", "voronoi_f2f1", "voronoi_crackle", "cell_noise"]
  5366         str += luxOption("type", ntype, ntypes, "type", "", gui, 1.3)
  5367         
  5368         if gui: gui.newline("basis:", -2, level+1, icon_texparam)
  5369         noisebasis = luxProp(mat, keyname+".noisebasis", "blender_original")
  5370         noisebasises = ["blender_original","original_perlin", "improved_perlin", "voronoi_f1", "voronoi_f2", "voronoi_f3", "voronoi_f4", "voronoi_f2f1", "voronoi_crackle", "cell_noise"]
  5371         str += luxOption("noisebasis", noisebasis, noisebasises, "noisebasis", "", gui, 1.3)
  5372 
  5373         if gui: gui.newline("level:", -2, level+1, icon_texparam)
  5374         str += luxFloat("bright", luxProp(mat, keyname+".bright", 1.0), 0.0, 2.0, "bright", "", gui, 1.0)
  5375         str += luxFloat("contrast", luxProp(mat, keyname+".contrast", 1.0), 0.0, 10.0, "contrast", "", gui, 1.0)
  5376         
  5377         str += lux3DMapping(keyname, mat, gui, level+1)
  5378 
  5379         if type!="float":
  5380             str += "Texture \"%s\" \"%s\" \"mix\" \"texture amount\" [\"%s::amount\"]"%(texname, type, texname)
  5381 
  5382         (s, l) = c(("", ""), luxTexture("tex1", keyname, type, default, min, max, "tex1", "", mat, gui, matlevel, texlevel+1, lightsource))
  5383         (s, l) = c((s, l), luxTexture("tex2", keyname, type, alternativedefault(type, default), min, max, "tex2", "", mat, gui, matlevel, texlevel+1, lightsource))
  5384         str = s + str + l
  5385 
  5386     if texture.get() == "blender_noise":        
  5387         if gui: gui.newline("level:", -2, level+1, icon_texparam)
  5388         str += luxFloat("bright", luxProp(mat, keyname+".bright", 1.0), 0.0, 2.0, "bright", "", gui, 1.0)
  5389         str += luxFloat("contrast", luxProp(mat, keyname+".contrast", 1.0), 0.0, 10.0, "contrast", "", gui, 1.0)
  5390         
  5391         str += lux3DMapping(keyname, mat, gui, level+1)
  5392 
  5393         if type!="float":
  5394             str += "Texture \"%s\" \"%s\" \"mix\" \"texture amount\" [\"%s::amount\"]"%(texname, type, texname)
  5395 
  5396         (s, l) = c(("", ""), luxTexture("tex1", keyname, type, default, min, max, "tex1", "", mat, gui, matlevel, texlevel+1, lightsource))
  5397         (s, l) = c((s, l), luxTexture("tex2", keyname, type, alternativedefault(type, default), min, max, "tex2", "", mat, gui, matlevel, texlevel+1, lightsource))
  5398         str = s + str + l
  5399         
  5400     if texture.get() == "blender_magic":
  5401         if gui: gui.newline("noise:", -2, level+1, icon_texparam)
  5402         
  5403         str += luxInt("noisedepth", luxProp(mat, keyname+".noisedepth", 2), 0.0, 10.0, "noisedepth", "", gui, 1.0)
  5404         str += luxFloat("turbulence", luxProp(mat, keyname+".turbulence", 5.0), 0.0, 200.0, "turbulence", "", gui, 1.0)
  5405 
  5406         if gui: gui.newline("level:", -2, level+1, icon_texparam)
  5407         str += luxFloat("bright", luxProp(mat, keyname+".bright", 1.0), 0.0, 2.0, "bright", "", gui, 1.0)
  5408         str += luxFloat("contrast", luxProp(mat, keyname+".contrast", 1.0), 0.0, 10.0, "contrast", "", gui, 1.0)
  5409         
  5410         str += lux3DMapping(keyname, mat, gui, level+1)
  5411 
  5412         if type!="float":
  5413             str += "Texture \"%s\" \"%s\" \"mix\" \"texture amount\" [\"%s::amount\"]"%(texname, type, texname)
  5414 
  5415         (s, l) = c(("", ""), luxTexture("tex1", keyname, type, default, min, max, "tex1", "", mat, gui, matlevel, texlevel+1, lightsource))
  5416         (s, l) = c((s, l), luxTexture("tex2", keyname, type, alternativedefault(type, default), min, max, "tex2", "", mat, gui, matlevel, texlevel+1, lightsource))
  5417         str = s + str + l
  5418         
  5419     if texture.get() == "blender_stucci":
  5420         if gui: gui.newline("noise:", -2, level+1, icon_texparam)
  5421         mtype = luxProp(mat, keyname+".mtype", "Plastic")
  5422         mtypes = ["Plastic","Wall In","Wall Out"]
  5423         str += luxOption("type", mtype, mtypes, "type", "", gui, 0.5)
  5424 
  5425         noisetype = luxProp(mat, keyname+".noisetype", "soft_noise")
  5426         noisetypes = ["soft_noise","hard_noise"]
  5427         str += luxOption("noisetype", noisetype, noisetypes, "noisetypes", "", gui, 0.75)
  5428         
  5429         str += luxFloat("noisesize", luxProp(mat, keyname+".noisesize", 0.25), 0.0, 10.0, "noisesize", "", gui, 1.0)
  5430         str += luxFloat("turbulence", luxProp(mat, keyname+".turbulence", 5.0), 0.0, 200.0, "turbulence", "", gui, 1.0)
  5431 
  5432         noisebasis = luxProp(mat, keyname+".noisebasis", "blender_original")
  5433         noisebasises = ["blender_original","original_perlin", "improved_perlin", "voronoi_f1", "voronoi_f2", "voronoi_f3", "voronoi_f4", "voronoi_f2f1", "voronoi_crackle", "cell_noise"]
  5434         str += luxOption("noisebasis", noisebasis, noisebasises, "noisebasis", "", gui, 1.3)
  5435 
  5436         if gui: gui.newline("level:", -2, level+1, icon_texparam)
  5437         str += luxFloat("bright", luxProp(mat, keyname+".bright", 1.0), 0.0, 2.0, "bright", "", gui, 1.0)
  5438         str += luxFloat("contrast", luxProp(mat, keyname+".contrast", 1.0), 0.0, 10.0, "contrast", "", gui, 1.0)
  5439 
  5440         str += lux3DMapping(keyname, mat, gui, level+1)
  5441 
  5442         if type!="float":
  5443             str += "Texture \"%s\" \"%s\" \"mix\" \"texture amount\" [\"%s::amount\"]"%(texname, type, texname)
  5444 
  5445         (s, l) = c(("", ""), luxTexture("tex1", keyname, type, default, min, max, "tex1", "", mat, gui, matlevel, texlevel+1, lightsource))
  5446         (s, l) = c((s, l), luxTexture("tex2", keyname, type, alternativedefault(type, default), min, max, "tex2", "", mat, gui, matlevel, texlevel+1, lightsource))
  5447         str = s + str + l
  5448 
  5449     if texture.get() == "blender_voronoi":
  5450         #if gui: gui.newline("distmetric:", -2, level+1, icon_texparam)
  5451         mtype = luxProp(mat, keyname+".distmetric", "actual_distance")
  5452         mtypes = ["actual_distance","distance_squared","manhattan", "chebychev", "minkovsky_half", "minkovsky_four", "minkovsky"]
  5453         str += luxOption("distmetric", mtype, mtypes, "distmetric", "", gui, 1.1)
  5454 
  5455         if gui: gui.newline("param:", -2, level+1, icon_texparam)
  5456         str += luxFloat("minkovsky_exp", luxProp(mat, keyname+".minkovsky_exp", 2.5), 0.001, 10.0, "minkovsky_exp", "", gui, 1.0)
  5457         str += luxFloat("outscale", luxProp(mat, keyname+".outscale", 1.0), 0.01, 10.0, "outscale", "", gui, 1.0)
  5458         str += luxFloat("noisesize", luxProp(mat, keyname+".noisesize", 0.25), 0.0, 2.0, "noisesize", "", gui, 1.0)
  5459         str += luxFloat("nabla", luxProp(mat, keyname+".nabla", 0.025), 0.001, 0.1, "nabla", "", gui, 1.0)
  5460         if gui: gui.newline("wparam:", -2, level+1, icon_texparam)
  5461         str += luxFloat("w1", luxProp(mat, keyname+".w1", 1.0), -2.0, 2.0, "w1", "", gui, 1.0)
  5462         str += luxFloat("w2", luxProp(mat, keyname+".w2", 0.0), -2.0, 2.0, "w2", "", gui, 1.0)
  5463         str += luxFloat("w3", luxProp(mat, keyname+".w3", 0.0), -2.0, 2.0, "w3", "", gui, 1.0)
  5464         str += luxFloat("w4", luxProp(mat, keyname+".w4", 0.0), -2.0, 2.0, "w4", "", gui, 1.0)
  5465 
  5466         if gui: gui.newline("level:", -2, level+1, icon_texparam)
  5467         str += luxFloat("bright", luxProp(mat, keyname+".bright", 1.0), 0.0, 2.0, "bright", "", gui, 1.0)
  5468         str += luxFloat("contrast", luxProp(mat, keyname+".contrast", 1.0), 0.0, 10.0, "contrast", "", gui, 1.0)
  5469 
  5470         str += lux3DMapping(keyname, mat, gui, level+1)
  5471 
  5472         if type!="float":
  5473             str += "Texture \"%s\" \"%s\" \"mix\" \"texture amount\" [\"%s::amount\"]"%(texname, type, texname)
  5474 
  5475         (s, l) = c(("", ""), luxTexture("tex1", keyname, type, default, min, max, "tex1", "", mat, gui, matlevel, texlevel+1, lightsource))
  5476         (s, l) = c((s, l), luxTexture("tex2", keyname, type, alternativedefault(type, default), min, max, "tex2", "", mat, gui, matlevel, texlevel+1, lightsource))
  5477         str = s + str + l
  5478 
  5479     return (str+"\n", " \"texture %s\" [\"%s\"]"%(name, texname))
  5480 
  5481 
  5482 def luxSpectrumTexture(name, key, default, max, caption, hint, mat, gui, level=0):
  5483     global icon_col
  5484     if gui: gui.newline(caption, 4, level, icon_col, scalelist([0.5,0.6,0.5],2.0/(level+2)))
  5485     str = ""
  5486     keyname = "%s:%s"%(key, name)
  5487     texname = "%s:%s"%(mat.getName(), keyname)
  5488     value = luxProp(mat, keyname, default)
  5489     link = luxRGB(name, value, max, "", hint, gui, 2.0)
  5490     tex = luxProp(mat, keyname+".textured", False)
  5491     if gui: Draw.Toggle("T", evtLuxGui, gui.x, gui.y-gui.h, gui.h, gui.h, tex.get()=="true", "use texture", lambda e,v:tex.set(["false","true"][bool(v)]))
  5492     if tex.get()=="true":
  5493         if gui: gui.newline("", -2)
  5494         (str, link) = luxTexture(name, key, "color", default, 0, max, caption, hint, mat, gui, level+1)
  5495         if value.getRGB() != (1.0, 1.0, 1.0):
  5496             if str == "": # handle special case if texture is a just a constant
  5497                 str += "Texture \"%s\" \"color\" \"scale\" \"color tex1\" [%s] \"color tex2\" [%s]\n"%(texname+".scale", (link.rpartition("[")[2])[0:-1], value.get())
  5498             else: str += "Texture \"%s\" \"color\" \"scale\" \"texture tex1\" [\"%s\"] \"color tex2\" [%s]\n"%(texname+".scale", texname, value.get())
  5499             link = " \"texture %s\" [\"%s\"]"%(name, texname+".scale")
  5500     return (str, link)
  5501 
  5502 def luxLightSpectrumTexture(name, key, default, max, caption, hint, mat, gui, level=0):
  5503     #if gui: gui.newline(caption, 4, level, icon_emission, scalelist([0.6,0.5,0.5],2.0/(level+2)))
  5504     str = ""
  5505     keyname = "%s:%s"%(key, name)
  5506     texname = "%s:%s"%(mat.getName(), keyname)
  5507     (str, link) = luxTexture(name, key, "color", default, 0, max, caption, hint, mat, gui, level+1, 0, 1)
  5508     return (str, link)
  5509 
  5510 def luxFloatTexture(name, key, default, min, max, caption, hint, mat, gui, level=0):
  5511     global icon_float
  5512     if gui: gui.newline(caption, 4, level, icon_float, scalelist([0.5,0.5,0.6],2.0/(level+2)))
  5513     str = ""
  5514     keyname = "%s:%s"%(key, name)
  5515     texname = "%s:%s"%(mat.getName(), keyname)
  5516     value = luxProp(mat, keyname, default)
  5517     link = luxFloat(name, value, min, max, "", hint, gui, 2.0)
  5518     tex = luxProp(mat, keyname+".textured", False)
  5519     if gui: Draw.Toggle("T", evtLuxGui, gui.x, gui.y-gui.h, gui.h, gui.h, tex.get()=="true", "use texture", lambda e,v:tex.set(["false","true"][bool(v)]))
  5520     if tex.get()=="true":
  5521         if gui: gui.newline("", -2)
  5522         (str, link) = luxTexture(name, key, "float", default, min, max, caption, hint, mat, gui, level+1)
  5523         if value.get() != 1.0:
  5524             if str == "": # handle special case if texture is a just a constant
  5525                 str += "Texture \"%s\" \"float\" \"scale\" \"float tex1\" [%s] \"float tex2\" [%s]\n"%(texname+".scale", (link.rpartition("[")[2])[0:-1], value.get())
  5526             else: str += "Texture \"%s\" \"float\" \"scale\" \"texture tex1\" [\"%s\"] \"float tex2\" [%s]\n"%(texname+".scale", texname, value.get())
  5527             link = " \"texture %s\" [\"%s\"]"%(name, texname+".scale")
  5528     return (str, link)
  5529 
  5530 def luxFloatSliderTexture(name, key, default, min, max, caption, hint, mat, gui, level=0):
  5531         global icon_float
  5532         if gui: gui.newline(caption, 4, level, icon_float, scalelist([0.5,0.5,0.6],2.0/(level+2)))
  5533         str = ""
  5534         keyname = "%s:%s"%(key, name)
  5535         texname = "%s:%s"%(mat.getName(), keyname)
  5536         value = luxProp(mat, keyname, default)
  5537         link = luxFloat(name, value, min, max, caption, hint, gui, 2.0, 1)
  5538         tex = luxProp(mat, keyname+".textured", False)
  5539         if gui: Draw.Toggle("T", evtLuxGui, gui.x, gui.y-gui.h, gui.h, gui.h, tex.get()=="true", "use texture", lambda e,v:tex.set(["false","true"][bool(v)]))
  5540         if tex.get()=="true":
  5541                 if gui: gui.newline("", -2)
  5542                 (str, link) = luxTexture(name, key, "float", default, min, max, caption, hint, mat, gui, level+1)
  5543                 if value.get() != 1.0:
  5544                         if str == "": # handle special case if texture is a just a constant
  5545                                 str += "Texture \"%s\" \"float\" \"scale\" \"float tex1\" [%s] \"float tex2\" [%s]\n"%(texname+".scale", (link.rpartition("[")[2])[0:-1], value.get())
  5546                         else: str += "Texture \"%s\" \"float\" \"scale\" \"texture tex1\" [\"%s\"] \"float tex2\" [%s]\n"%(texname+".scale", texname, value.get())
  5547                         link = " \"texture %s\" [\"%s\"]"%(name, texname+".scale")
  5548         return (str, link)
  5549 
  5550 
  5551 def luxExponentTexture(name, key, default, min, max, caption, hint, mat, gui, level=0):
  5552     global icon_float
  5553     if gui: gui.newline(caption, 4, level, icon_float, scalelist([0.5,0.5,0.6],2.0/(level+2)))
  5554     str = ""
  5555     keyname = "%s:%s"%(key, name)
  5556     texname = "%s:%s"%(mat.getName(), keyname)
  5557     value = luxProp(mat, keyname, default)
  5558 
  5559     if(value.get() == None): value.set(0.002)
  5560 
  5561 #    link = luxFloat(name, value, min, max, "", hint, gui, 2.0)
  5562     if gui:
  5563         r = gui.getRect(2.0, 1)
  5564         Draw.Number("", evtLuxGui, r[0], r[1], r[2], r[3], float(2.0/(value.getFloat() ** 2)-2.0), 1.0, 1000000.0, hint, lambda e,v: value.set(math.sqrt(2.0/(v+2.0))))
  5565     link = " \"float %s\" [%f]"%(name, value.getFloat())
  5566 
  5567     tex = luxProp(mat, keyname+".textured", False)
  5568     if gui: Draw.Toggle("T", evtLuxGui, gui.x, gui.y-gui.h, gui.h, gui.h, tex.get()=="true", "use texture", lambda e,v:tex.set(["false","true"][bool(v)]))
  5569     if tex.get()=="true":
  5570         if gui: gui.newline("", -2)
  5571         (str, link) = luxTexture(name, key, "float", default, min, max, caption, hint, mat, gui, level+1)
  5572         if value.get() != 1.0:
  5573             if str == "": # handle special case if texture is a just a constant
  5574                 str += "Texture \"%s\" \"float\" \"scale\" \"float tex1\" [%s] \"float tex2\" [%s]\n"%(texname+".scale", (link.rpartition("[")[2])[0:-1], value.get())
  5575             else: str += "Texture \"%s\" \"float\" \"scale\" \"texture tex1\" [\"%s\"] \"float tex2\" [%s]\n"%(texname+".scale", texname, value.get())
  5576             link = " \"texture %s\" [\"%s\"]"%(name, texname+".scale")
  5577     return (str, link)
  5578 
  5579 
  5580 def luxDispFloatTexture(name, key, default, min, max, caption, hint, mat, gui, level=0):
  5581     global icon_float
  5582     if gui: gui.newline(caption, 4, level, icon_float, scalelist([0.5,0.5,0.6],2.0/(level+2)))
  5583     str = ""
  5584     keyname = "%s:%s"%(key, name)
  5585     texname = "%s:%s"%(mat.getName(), keyname)
  5586     value = luxProp(mat, keyname, default)
  5587     link = luxFloat(name, value, min, max, "", hint, gui, 2.0)
  5588     tex = luxProp(mat, keyname+".textured", False)
  5589     if gui: Draw.Toggle("T", evtLuxGui, gui.x, gui.y-gui.h, gui.h, gui.h, tex.get()=="true", "use texture", lambda e,v:tex.set(["false","true"][bool(v)]))
  5590     if tex.get()=="true":
  5591         if gui: gui.newline("", -2)
  5592         (str, link) = luxTexture(name, key, "float", default, min, max, caption, hint, mat, gui, level+1)
  5593         str += "Texture \"%s\" \"float\" \"scale\" \"texture tex1\" [\"%s\"] \"float tex2\" [%s]\n"%(texname+".scale", texname, value.get())
  5594         link = " \"texture %s\" [\"%s\"]"%(name, texname+".scale")
  5595     return (str, link)
  5596 
  5597 def luxIORFloatTexture(name, key, default, min, max, caption, hint, mat, gui, level=0):
  5598     # IOR preset data
  5599     iornames = ["0Z *** Gases @ 0 C ***", "01 - Vacuum", "02 - Air @ STP", "03 - Air", "04 - Helium", "05 - Hydrogen", "06 - Carbon dioxide",
  5600     "1Z *** LIQUIDS @ 20 C ***", "11 - Benzene", "12 - Water", "13 - Ethyl alcohol", "14 - Carbon tetrachloride", "15 - Carbon disulfide", 
  5601     "2Z *** SOLIDS at room temperature ***", "21 - Diamond", "22 - Strontium titanate", "23 - Amber", "24 - Fused silica glass", "25 - sodium chloride", 
  5602     "3Z *** OTHER Materials ***", "31 - Pyrex (Borosilicate glass)", "32 - Ruby", "33 - Water ice", "34 - Cryolite", "35 - Acetone", "36 - Ethanol", "37 - Teflon", "38 - Glycerol", "39 - Acrylic glass", "40 - Rock salt", "41 - Crown glass (pure)", "42 - Salt (NaCl)", "43 - Polycarbonate", "44 - PMMA", "45 - PETg", "46 - PET", "47 - Flint glass (pure)", "48 - Crown glass (impure)", "49 - Fused Quartz", "50 - Bromine", "51 - Flint glass (impure)", "52 - Cubic zirconia", "53 - Moissanite", "54 - Cinnabar (Mercury sulfide)", "55 - Gallium(III) prosphide", "56 - Gallium(III) arsenide", "57 - Silicon"]
  5603     iorvals = [1.0, 1.0, 1.0002926, 1.000293, 1.000036, 1.000132, 1.00045,
  5604     1.501, 1.501, 1.333, 1.361, 1.461, 1.628,
  5605     2.419, 2.419, 2.41, 1.55, 1.458, 1.50,
  5606     1.470, 1.470, 1.760, 1.31, 1.388, 1.36, 1.36, 1.35, 1.4729, 1.490, 1.516, 1.50, 1.544, 1.584, 1.4893, 1.57, 1.575, 1.60, 1.485, 1.46, 1.661, 1.523, 2.15, 2.419, 2.65, 3.02, 3.5, 3.927, 4.01]
  5607 
  5608     global icon_float
  5609     if gui: gui.newline(caption, 4, level, icon_float, scalelist([0.5,0.5,0.6],2.0/(level+2)))
  5610     str = ""
  5611     keyname = "%s:%s"%(key, name)
  5612     texname = "%s:%s"%(mat.getName(), keyname)
  5613     value = luxProp(mat, keyname, default)
  5614 
  5615     iorusepreset = luxProp(mat, keyname+".iorusepreset", "true")
  5616     luxBool("iorusepreset", iorusepreset, "Preset", "Select from a list of predefined presets", gui, 0.4)
  5617 
  5618     if(iorusepreset.get() == "true"):
  5619         iorpreset = luxProp(mat, keyname+".iorpreset", "Glass, Fused Silica")
  5620         if gui:
  5621             def setIor(i, value, preset, tree, dict): # callback function to set ior value after selection                
  5622                 if i >= 0:
  5623                     value.set(dict[i])
  5624                     preset.set(getTreeNameById(tree, i))
  5625             iortree = [ ("Liquids", [("Acetone", 1), ("Alcohol, Ethyl (grain)", 2), ("Alcohol, Methyl (wood)", 3), ("Beer", 4), ("Benzene", 5), ("Carbon tetrachloride", 6), ("Carbon disulfide", 7), ("Carbonated Beverages", 8), ("Chlorine (liq)", 9), ("Cranberry Juice (25%)", 10), ("Glycerin", 11), ("Honey, 13% water content", 12), ("Honey, 17% water content", 13), ("Honey, 21% water content", 14), ("Ice", 15), ("Milk", 16), ("Oil, Clove", 17), ("Oil, Lemon", 18), ("Oil, Neroli", 19), ("Oil, Orange", 20), ("Oil, Safflower", 21), ("Oil, vegetable (50 C)", 22), ("Oil of Wintergreen", 23), ("Rum, White", 24), ("Shampoo", 25), ("Sugar Solution 30%", 26), ("Sugar Solution 80%", 27), ("Turpentine", 28), ("Vodka", 29), ("Water (0 C)", 30), ("Water (100 C)", 31), ("Water (20 C)", 32), ("Whisky", 33) ] ), ("Gases", [("Vacuum", 101), ("Air @ STP", 102), ("Air", 103), ("Helium", 104), ("Hydrogen", 105), ("Carbon dioxide", 106) ]), ("Transparent\x20", [("Eye, Aqueous humor", 201), ("Eye, Cornea", 202), ("Eye, Lens", 203), ("Eye, Vitreous humor", 204), ("Glass, Arsenic Trisulfide", 205), ("Glass, Crown (common)", 206), ("Glass, Flint, 29% lead", 207), ("Glass, Flint, 55% lead", 208), ("Glass, Flint, 71% lead", 209), ("Glass, Fused Silica", 210), ("Glass, Pyrex", 211), ("Lucite", 212), ("Nylon", 213), ("Obsidian", 214), ("Plastic", 215), ("Plexiglas", 216), ("Salt", 217)  ]), ("Gemstones", [("Agate", 301), ("Alexandrite", 302), ("Almandine", 303), ("Amber", 304), ("Amethyst", 305), ("Ammolite", 306), ("Andalusite", 307), ("Apatite", 308), ("Aquamarine", 309), ("Axenite", 310), ("Beryl", 311), ("Beryl, Red", 312), ("Chalcedony", 313), ("Chrome Tourmaline", 314), ("Citrine", 315), ("Clinohumite", 316), ("Coral", 317), ("Crystal", 318), ("Crysoberyl, Catseye", 319), ("Danburite", 320), ("Diamond", 321), ("Emerald", 322), ("Emerald Catseye", 323), ("Flourite", 324), ("Garnet, Grossular", 325), ("Garnet, Andradite", 326), ("Garnet, Demantiod", 327), ("Garnet, Mandarin", 328), ("Garnet, Pyrope", 329), ("Garnet, Rhodolite", 330), ("Garnet, Tsavorite", 331), ("Garnet, Uvarovite", 332), ("Hauyn", 333), ("Iolite", 334), ("Jade, Jadeite", 335), ("Jade, Nephrite", 336), ("Jet", 337), ("Kunzite", 338), ("Labradorite", 339), ("Lapis Lazuli", 340), ("Moonstone", 341), ("Morganite", 342), ("Obsidian", 343), ("Opal, Black", 344), ("Opal, Fire", 345), ("Opal, White", 346), ("Oregon Sunstone", 347), ("Padparadja", 348), ("Pearl", 349), ("Peridot", 350), ("Quartz", 351), ("Ruby", 352), ("Sapphire", 353), ("Sapphire, Star", 354), ("Spessarite", 355), ("Spinel", 356), ("Spinel, Blue", 357), ("Spinel, Red", 358), ("Star Ruby", 359), ("Tanzanite", 360), ("Topaz", 361), ("Topaz, Imperial", 362), ("Tourmaline", 363), ("Tourmaline, Blue", 364), ("Tourmaline, Catseye", 365), ("Tourmaline, Green", 366), ("Tourmaline, Paraiba", 367), ("Tourmaline, Red", 368), ("Zircon", 369), ("Zirconia, Cubic", 370) ] ), ("Other ", [("Pyrex (Borosilicate glass)", 401), ("Ruby", 402), ("Water ice", 403), ("Cryolite", 404), ("Acetone", 405), ("Ethanol", 406), ("Teflon", 407), ("Glycerol", 408), ("Acrylic glass", 409), ("Rock salt", 410), ("Crown glass (pure)", 411), ("Salt (NaCl)", 412), ("Polycarbonate", 413), ("PMMA", 414), ("PETg", 415), ("PET", 416), ("Flint glass (pure)", 417), ("Crown glass (impure)", 418), ("Fused Quartz", 419), ("Bromine", 420), ("Flint glass (impure)", 421), ("Cubic zirconia", 422), ("Moissanite", 423), ("Cinnabar (Mercury sulfide)", 424), ("Gallium(III) prosphide", 425), ("Gallium(III) arsenide", 426), ("Silicon", 427) ] ) ]
  5626             iordict = {1:1.36, 2:1.36, 3:1.329, 4:1.345, 5:1.501, 6:1.000132, 7:1.00045, 8:1.34, 9:1.385, 10:1.351, 11:1.473, 12:1.504, 13:1.494, 14:1.484, 15:1.309, 16:1.35, 17:1.535, 18:1.481, 19:1.482, 20:1.473, 21:1.466, 22:1.47, 23:1.536, 24:1.361, 25:1.362, 26:1.38, 27:1.49, 28:1.472, 29:1.363, 30:1.33346, 31:1.31766, 32:1.33283, 33:1.356, 101:1.0, 102:1.0002926, 103:1.000293, 104:1.000036, 105:1.000132, 106:1.00045, 201:1.33, 202:1.38, 203:1.41, 204:1.34, 205:2.04, 206:1.52, 207:1.569, 208:1.669, 209:1.805, 210:1.459, 211:1.474, 212:1.495, 213:1.53, 214:1.50, 215:1.460, 216:1.488, 217:1.516, 301:1.544, 302:1.746, 303:1.75, 304:1.539, 305:1.532, 306:1.52, 307:1.629, 308:1.632, 309:1.567, 310:1.674, 311:1.57, 312:1.570, 313:1.544, 314:1.61, 315:1.532, 316:1.625, 317:1.486, 318:2.000, 319:1.746, 320:1.627, 321:2.417, 322:1.560, 323:1.560, 324:1.434, 325:1.72, 326:1.88, 327:1.880, 328:1.790, 329:1.73, 330:1.740, 331:1.739, 332:1.74, 333:1.490, 334:1.522, 335:1.64, 336:1.600, 337:1.660, 338:1.660, 339:1.560, 340:1.50, 341:1.518, 342:1.585, 343:1.50, 344:1.440, 345:1.430, 346:1.440, 347:1.560, 348:1.760, 349:1.53, 350:1.635, 351:1.544, 352:1.757, 353:1.757, 354:1.760, 355:1.79, 356:1.712, 357:1.712, 358:1.708, 359:1.76, 360:1.690, 361:1.607, 362:1.605, 363:1.603, 364:1.61, 365:1.61, 366:1.61, 367:1.61, 368:1.61, 369:1.777, 370:2.173, 401:1.47, 402:1.76, 403:1.31, 404:1.388, 405:1.36, 406:1.36, 407:1.35, 408:1.4729, 409:1.49, 410:1.516, 411:1.5, 412:1.544, 413:1.584, 414:1.4893, 415:1.57, 416:1.575, 417:1.6, 418:1.485, 419:1.46, 420:1.661, 421:1.523, 422:2.15, 423:2.419, 424:2.65, 425:3.02, 426:3.5, 427:3.927}
  5627             r = gui.getRect(1.6, 1)
  5628             Draw.Button(iorpreset.get(), evtLuxGui, r[0], r[1], r[2], r[3], "select IOR preset", lambda e,v: setIor(Draw.PupTreeMenu(iortree), value, iorpreset, iortree, iordict))
  5629         link = luxFloat(name, value, min, max, "IOR", hint, None, 1.6)
  5630     else:
  5631         link = luxFloat(name, value, min, max, "IOR", hint, gui, 1.6, 1)
  5632 
  5633     tex = luxProp(mat, keyname+".textured", False)
  5634     if gui: Draw.Toggle("T", evtLuxGui, gui.x, gui.y-gui.h, gui.h, gui.h, tex.get()=="true", "use texture", lambda e,v:tex.set(["false","true"][bool(v)]))
  5635     if tex.get()=="true":
  5636         if gui: gui.newline("", -2)
  5637         (str, link) = luxTexture(name, key, "float", default, min, max, caption, hint, mat, gui, level+1)
  5638         if value.get() != 1.0:
  5639             str += "Texture \"%s\" \"float\" \"scale\" \"texture tex1\" [\"%s\"] \"float tex2\" [%s]\n"%(texname+".scale", texname, value.get())
  5640             link = " \"texture %s\" [\"%s\"]"%(name, texname+".scale")
  5641     return (str, link)
  5642 
  5643 def luxCauchyBFloatTexture(name, key, default, min, max, caption, hint, mat, gui, level=0):
  5644     # IOR preset data
  5645     cauchybnames = ["01 - Fused silica glass", "02 - Borosilicate glass BK7", "03 - Hard crown glass K5", "04 - Barium crown glass BaK4", "05 - Barium flint glass BaF10", "06 - Dense flint glass SF10" ]
  5646     cauchybvals = [ 0.00354, 0.00420, 0.00459, 0.00531, 0.00743, 0.01342 ]
  5647 
  5648     global icon_float
  5649     if gui: gui.newline(caption, 4, level, icon_float, scalelist([0.5,0.5,0.6],2.0/(level+2)))
  5650     str = ""
  5651     keyname = "%s:%s"%(key, name)
  5652     texname = "%s:%s"%(mat.getName(), keyname)
  5653     value = luxProp(mat, keyname, default)
  5654 
  5655     cauchybusepreset = luxProp(mat, keyname+".cauchybusepreset", "true")
  5656     luxBool("cauchybusepreset", cauchybusepreset, "Preset", "Select from a list of predefined presets", gui, 0.4)
  5657 
  5658     if(cauchybusepreset.get() == "true"):
  5659         cauchybpreset = luxProp(mat, keyname+".cauchybpreset", "01 - Fused silica glass")
  5660         luxOption("cauchybpreset", cauchybpreset, cauchybnames, "  PRESET", "select CauchyB preset", gui, 1.6)
  5661         idx = cauchybnames.index(cauchybpreset.get())
  5662         value.set(cauchybvals[idx])
  5663         link = luxFloat(name, value, min, max, "cauchyb", hint, None, 1.6)
  5664     else:
  5665         link = luxFloat(name, value, min, max, "cauchyb", hint, gui, 1.6, 1)
  5666 
  5667     tex = luxProp(mat, keyname+".textured", False)
  5668     if gui: Draw.Toggle("T", evtLuxGui, gui.x, gui.y-gui.h, gui.h, gui.h, tex.get()=="true", "use texture", lambda e,v:tex.set(["false","true"][bool(v)]))
  5669     if tex.get()=="true":
  5670         if gui: gui.newline("", -2)
  5671         (str, link) = luxTexture(name, key, "float", default, min, max, caption, hint, mat, gui, level+1)
  5672         if value.get() != 1.0:
  5673             str += "Texture \"%s\" \"float\" \"scale\" \"texture tex1\" [\"%s\"] \"float tex2\" [%s]\n"%(texname+".scale", texname, value.get())
  5674             link = " \"texture %s\" [\"%s\"]"%(name, texname+".scale")
  5675     return (str, link)
  5676 
  5677 def luxScaleUnits(keyname, default, mat, width=0.5, gui=None):
  5678     # Length units widget for bumps, absorption and such.
  5679     # @default can be passed as unit str or scale float
  5680     units = ['m', 'cm', 'mm', 'cm^-1']
  5681     scales = [1, 0.01, 0.001, 100]
  5682     if type(default) is not str:
  5683         try: default = units[scales.index(default)]
  5684         except ValueError: default = 'm'
  5685     scaleunits = luxProp(mat, keyname+'.scaleunits', default)
  5686     luxOption('scaleunits', scaleunits, units, '  LENGTH UNITS', 'Select units from the list', gui, width)
  5687     return scales[units.index(scaleunits.get())]
  5688 
  5689 def listNamedVolumes():
  5690     # returns a dict of volumeName:volumeId pairs
  5691     scn = Scene.GetCurrent()
  5692     d = {}
  5693     s = 'named_volumes:'
  5694     try:
  5695         for k, v in scn.properties['luxblend'].convert_to_pyobject().items():
  5696             if k.startswith(s) and k[k.find('.')+1:] == 'name':
  5697                 d[v] = int(k[len(s):k.find('.')])
  5698     except KeyError:
  5699         pass
  5700     if not d.has_key('0'):
  5701         d['world *'] = 0
  5702         luxProp(scn, 'named_volumes:0.id', 0).set(0)
  5703         luxProp(scn, 'named_volumes:0.name', '').set('world *')
  5704     return d
  5705             
  5706 def getNamedVolume(id, scene=None, filter=[]):
  5707     # returns a dict of volume properties' name:value pairs
  5708     if scene is None:
  5709         scn = Scene.GetCurrent()
  5710     else:
  5711         scn = scene
  5712     d = {}
  5713     s = 'named_volumes:%s.' % id
  5714     for k, v in scn.properties['luxblend'].convert_to_pyobject().items():
  5715         (kn, vl) = (k, v)
  5716         if k[:7] == '__hash:':
  5717             (kn, vl) = v.split(' = ')
  5718         if kn[:len(s)] == s:
  5719             d[kn[len(s):]] = vl
  5720     if not d:
  5721         if scene is None: return None
  5722         d['id'] = 0
  5723         d['name'] = 'world *'
  5724     else:
  5725         d['id'] = int(id)
  5726     for f in filter:
  5727         try: del d[f]
  5728         except KeyError: pass
  5729     return d
  5730 
  5731 def luxNamedVolume(mat, volume_prop, gui=None):
  5732     # Possible values for volume_prop: 'Exterior', 'Interior'
  5733     #
  5734     # References to each volume are stored in two places. First, in regular
  5735     # material in ${volume_prop}_vol_id Lux property which has an int index of
  5736     # the volume assigned to this mat. Second, volume and its texture proper-
  5737     # ties are stored in global scene properties "named_volumes" namespace
  5738     # in the form "namespace:volumeId.volumeProperty". We use ids instead of
  5739     # plane names to facilitate volume renaming.
  5740     volumes = listNamedVolumes()
  5741     if gui: gui.newline('Medium name:', 0, 0, None, [0.4,0.4,0.6])
  5742     volumeId = luxProp(mat, '%s_vol_id' % (volume_prop), 0)
  5743     volumeName = luxProp(mat, '%s_vol_name' % (volume_prop), '')
  5744     volumeUID = luxProp(mat, '%s_vol_guid' % (volume_prop), '')
  5745     unmutable = True if volumeId.get() == 0 else False
  5746     # link volume data to the scene UID if it's not linked already
  5747     if not volumeUID.get():
  5748         volumeUID.set(luxUID)
  5749     # is that a native volume data or maybe we linked material
  5750     # from another scene? lets see and import if necessary
  5751     elif volumeUID.get() != luxUID:
  5752         print 'Global properties for medium "%s" are supposedly linked' % volumeName.get(), "\nfrom another scene. Attempting to import..."
  5753         imported = importNamedVolume(volumeId, volumeName, volumeUID)
  5754         if imported is True:
  5755             print 'Medium properties imported successfully', "\n"
  5756             # reloading volumes data
  5757             volumes = listNamedVolumes()
  5758             volumeId = luxProp(mat, '%s_vol_id' % (volume_prop), 0)
  5759             volumeName = luxProp(mat, '%s_vol_name' % (volume_prop), '')
  5760         elif imported is False:
  5761             print 'Medium properties not found in the linked scenes. Import failed', "\n"
  5762         else:
  5763             print 'Medium properties import is unnecessary', "\n"
  5764     # do we have that volume in the scene data?
  5765     if not volumes.has_key(volumeName.get()):
  5766         # seems selected volume was renamed or deleted.
  5767         # lets try to update its name from id (if it still exists)
  5768         try:
  5769             volumeName.set(volumes.keys()[volumes.values().index(volumeId.get())])
  5770         except ValueError:
  5771             volumeId.set(0)
  5772             Blender.Window.QRedrawAll()
  5773     luxOption('%s_vol_name'%(volume_prop), volumeName, volumes.keys(), '  AVAILABLE MEDIUMS', 'Select medium from the list', gui, 1.1 if not unmutable else 1.5)
  5774     try:
  5775         if volumeName.get(): volumeId.set(volumes[volumeName.get()])
  5776         else: volumeId.set(0)   # no volume was selected yet for that property
  5777     except KeyError:
  5778         volumeId.set(0)
  5779     if gui:
  5780         if not unmutable:
  5781             r = gui.getRect(0.2, 1)
  5782             Draw.Button('X', evtLuxGui, r[0], r[1], r[2], r[3], 'Delete a link to this medium', lambda e,v: showVolumesMenu(mat,volume_prop,4))
  5783             luxBool('%s_vol_fixed' % (volume_prop), luxProp(Scene.GetCurrent(), 'named_volumes:%s.fixed' % volumeId.get(), 'false'), 'F', 'Saves this medium even if it has no users', gui, 0.2)
  5784         r = gui.getRect(0.5, 1)
  5785         Draw.Button('Options', evtLuxGui, r[0], r[1], r[2], r[3], 'Manage mediums', lambda e,v: showVolumesMenu(mat,volume_prop))
  5786     
  5787     volId = volumeId.get()
  5788     if volId != 0:
  5789         luxNamedVolumeTexture(volId, gui)
  5790     elif gui:
  5791         gui.newline(); r = gui.getRect(2,1); BGL.glRasterPos2i(r[0],r[1]+5) 
  5792         Draw.Text("use Cam/Env tab to configure world medium")
  5793     
  5794     return "\n\t%s \"%s\"" % (volume_prop, getNamedVolume(volId)['name'])
  5795 
  5796 def luxNamedVolumeTexture(volId, gui=None):
  5797     def c(t1, t2):
  5798        return (t1[0]+t2[0], t1[1]+t2[1])
  5799     scn = Scene.GetCurrent()
  5800     keyname = 'named_volumes:%s.' % volId
  5801     s = l = ''
  5802     volume_types = ['clear']
  5803     volume_type = luxProp(scn, keyname+'type', volume_types[0])
  5804     if gui: gui.newline('type:', 0, 0)
  5805     luxOption(keyname+'type', volume_type, volume_types, '  MEDIUM TYPES', 'Select medium type from the list', gui, 2.0)
  5806     volType = '"%s"' % volume_type.get()
  5807     
  5808     if volume_type.get() == 'clear':
  5809         usecolor = luxProp(scn, keyname+'usecolor', 'true')
  5810         usedepth = luxProp(scn, keyname+'usedepth', 'true')
  5811         (s, l) = c((s, l), luxTexture('value', keyname+'tex', 'fresnel', 1.459 if volId != 0 else 1.0002926, 1.0, 6.0, 'IOR', 'ior', scn, gui, 0, 1))
  5812         (s1, l1) = luxSpectrumTexture('absorption', keyname+'absorption', '1.0 1.0 1.0', 1.0 if usedepth.get() == 'true' else 1000.0, 'absorption:', '', scn, gui, 1)
  5813         absorb_tex = luxProp(scn, keyname+'absorption:absorption.textured', 'false')
  5814         if usedepth.get() == 'true' and absorb_tex.get() != 'true':
  5815             luxBool('usecolor', usecolor, 'Color', 'Resulting light color at given depth of the medium', gui, 0.5)
  5816         if absorb_tex.get() != 'true':
  5817             luxBool('usedepth', usedepth, 'at depth' if usedepth.get() == 'true' else 'Color at depth', 'Resulting light color at given depth of the medium' if usecolor.get() == 'true' else 'Amount of light absorbed at given depth of the medium', gui, 0.5 if usedepth.get() == 'true' else 1.0)
  5818         if usedepth.get() == 'true' and absorb_tex.get() != 'true':
  5819             texkey = 'named_volumes:%s.absorption:absorption' % volId
  5820             usetex = luxProp(scn, texkey+'.textured', None)
  5821             depth = luxProp(scn, keyname+'depth', 1.0)
  5822             luxFloat('depth', depth, 0.001, 1000.0, 'depth', 'Depth of the fixed point inside the medium', gui, 0.5)
  5823             scale = luxScaleUnits(keyname+'scale', 'm', scn, 0.5, gui)
  5824             if usecolor.get() == 'true':
  5825                 factor = lambda rgb: [ (-math.log(max([rg(float(i)),1e-30]))/(depth.get()*scale)) * (rg(float(i))==1.0 and -1 or 1) for i in rgb ]
  5826             else:
  5827                 factor = lambda rgb: [ rg(float(i)) * (1/(depth.get()*scale)) for i in rgb ]
  5828             if l1[l1.find('"')+1:].startswith('color'):
  5829                 rgb = factor(l1[l1.find('[')+1:l1.rfind(']')].split(' '))
  5830                 l1 = l1[:l1.find('[')] + '[%s %s %s]' % (rgb[0], rgb[1], rgb[2])
  5831             elif usetex.get() == 'true':
  5832                 tex = s1.split("\n")
  5833                 for t in tex:
  5834                     if t.startswith('Texture "Scene:%s.scale"' % texkey):
  5835                         rgb = factor(t[t.rfind('[')+1:t.rfind(']')].split(' '))
  5836                         tex[tex.index(t)] = t[:t.rfind('[')] + '[%s %s %s]' % (rgb[0], rgb[1], rgb[2])
  5837                 s1 = "\n".join(tex)
  5838         (s, l) = c((s, l), (s1, l1))
  5839     
  5840     return s, volType+l
  5841 
  5842 importedVolumeIdsTranslation = {}
  5843 def importNamedVolume(volumeId, volumeName, volumeUID):
  5844     # scans all linked libraries and imports volume
  5845     # data from another scene object if possible.
  5846     def assignName(name, names):
  5847         # helper function to set a proper name for imported
  5848         # object without overwriting existing ones
  5849         if not name in names:
  5850             return name
  5851         else:
  5852             if not (name[-4] == '.' and name[-3:].isdigit): subname = name
  5853             else: subname = name[:-4]
  5854             for i in range(1, 1000):
  5855                 newname = subname+'.'+str(i).rjust(3, '0')
  5856                 if not newname in names: return newname
  5857     def importFromScene(scn, linkedScn, volumeId, volumeName, volumeUID):
  5858         # helper function to scan a passed scene object for
  5859         # matching uid and volume properties data and import
  5860         # them in the active scene if found
  5861         global importedVolumeIdsTranslation
  5862         imported = False
  5863         linkedUID = luxProp(linkedScn, 'UID', '')
  5864         if volumeUID.get() == linkedUID.get():
  5865             print '         scene UID matched, loading mediums data'
  5866             linkedVolumeData = getNamedVolume(volumeId.get(), linkedScn)
  5867             currentVolumeData = getNamedVolume(volumeId.get(), scn)
  5868             linkedDataFiltered = getNamedVolume(volumeId.get(), linkedScn, filter=['id', 'name'])
  5869             print '          - target medium found: ID %s, name "%s"' % (linkedVolumeData['id'], linkedVolumeData['name'])
  5870             if linkedVolumeData != currentVolumeData and linkedVolumeData is not None:
  5871                 volumes = listNamedVolumes()
  5872                 newId = max(volumes.values())+1
  5873                 prefix = 'named_volumes:%s.' % newId
  5874                 importedVolumeIdsTranslation[linkedVolumeData['id']] = newId
  5875                 try:
  5876                     i = [ getNamedVolume(vol, filter=['id', 'name']) for vol in volumes.values() ].index(linkedDataFiltered)
  5877                     print '            properties are the same, updating ID'
  5878                     volumeId.set(volumes.values()[i])
  5879                     return None
  5880                 except ValueError:
  5881                     pass
  5882                 print '            importing medium global properties'
  5883                 for k, v in linkedVolumeData.items():
  5884                     if k == 'id':
  5885                         volumeId.set(newId)  # material property
  5886                     elif k == 'name':
  5887                         newName = assignName(v, volumes.keys())
  5888                         volumeName.set(newName)  # material property
  5889                         luxProp(scn, prefix+k, '').set(newName)
  5890                     else:
  5891                         luxProp(scn, prefix+k, '').set(v)
  5892                 print '            medium properties imported under name "%s"' % newName
  5893                 imported = True
  5894             else:
  5895                 print '            properties are the same or empty, skipping import'
  5896                 imported = None
  5897         else:
  5898             print '         scene UID mismatch, skipping'
  5899         return imported
  5900     
  5901     imported = False
  5902     Library = Blender.Library
  5903     scn = Scene.GetCurrent()
  5904     allscenes = Scene.Get()
  5905     linkedLibs = Library.LinkedLibs()
  5906     print ' - active scene "%s" UID %s' % (scn.name, luxUID)
  5907     print ' - searching for scene UID', volumeUID.get()
  5908     if linkedLibs:
  5909         print ' - searching in linked libraries'
  5910     for lib in linkedLibs:
  5911         print '    - opening Blender library path', sys.expandpath(lib)
  5912         try:
  5913             Library.Open(sys.expandpath(lib))
  5914         except IOError:
  5915             print '      error opening library file, skipping'
  5916             continue
  5917         for scnName in Library.Datablocks('Scene'):
  5918             print '       - loading library scene "%s"' % scnName
  5919             Library.Load(scnName, 'Scene', 0)
  5920             # we aren't getting a scene obj explicitly by name because after linking we'll
  5921             # likely end up with two or more scenes with the same name, thus Blender will
  5922             # return the first one which is the active scene
  5923             for i, s in enumerate(Scene.Get()):
  5924                 # we cannot use 's in allscenes' directly as objects will be
  5925                 # compared by their str representations and not hashes in that case
  5926                 if not True in [ s == a for a in allscenes ]:
  5927                     linkedScn = Scene.Get()[i] ; break
  5928             imported = importFromScene(scn, linkedScn, volumeId, volumeName, volumeUID)
  5929             print '       - unlinking library scene "%s"' % scnName
  5930             for obj in linkedScn.objects: linkedScn.objects.unlink(obj)
  5931             Scene.Unlink(linkedScn)
  5932             if imported: break
  5933         print '    - closing library path', sys.expandpath(lib)
  5934         Library.Close()
  5935         if imported: break
  5936     if imported is False and len(allscenes) > 1:
  5937         print ' - searching in the current blend-file scenes'
  5938         for linkedScn in allscenes:
  5939             if linkedScn != scn:
  5940                 print '       - looking in scene "%s"' % linkedScn.name
  5941                 imported = importFromScene(scn, linkedScn, volumeId, volumeName, volumeUID)
  5942                 if imported: break
  5943     volumeUID.set(luxUID)  # material property
  5944     return imported
  5945 
  5946 def gcNamedVolumes(scn, gui=True):
  5947     # garbage collector for named volumes
  5948     used = set()
  5949     mats = Material.Get()
  5950     vols = listNamedVolumes()
  5951     # searching volumes directly linked from materials
  5952     for mat in mats:
  5953         for volume_prop in ['Exterior', 'Interior']:
  5954             if luxProp(mat, '%s_vol_used' % (volume_prop), 'false').get() == 'true':
  5955                 used.add(luxProp(mat, '%s_vol_id' % (volume_prop), 0).get())
  5956     # searching fixed volumes, protected from gc
  5957     for vol in vols.values():
  5958         if luxProp(scn, 'named_volumes:%s.fixed' % vol, 'false').get() == 'true':
  5959             used.add(vol)
  5960     # cleaning unused after asking a user
  5961     unused = set(vols.values()).difference(used)
  5962     unused.discard(0)
  5963     for vol in unused:
  5964         data = getNamedVolume(vol)
  5965         if gui: r = Draw.PupMenu('  LuxRender medium "' + data['name'] + '" is currently unused:%t|Delete medium%x1|Keep medium%x2')
  5966         if not gui or r == 1:
  5967             for n in data.keys():
  5968                 luxProp(scn, 'named_volumes:%s.%s' % (vol,n), '').delete()
  5969             print 'Unused medium "%s" removed from the scene' % data['name']
  5970         Blender.Window.QRedrawAll()
  5971     print
  5972 
  5973 def luxLight(name, kn, mat, gui, level):
  5974     if gui:
  5975         if name != "": gui.newline(name+":", 10, level)
  5976         else: gui.newline("color:", 0, level+1)
  5977     (str,link) = luxLightSpectrumTexture("L", kn+"light", "1.0 1.0 1.0", 1.0, "Spectrum", "", mat, gui, level+1)
  5978     if gui: gui.newline("")
  5979     link += luxFloat("power", luxProp(mat, kn+"light.power", 100.0), 0.0, 10000.0, "Power(W)", "AreaLight Power in Watts", gui)
  5980     link += luxFloat("efficacy", luxProp(mat, kn+"light.efficacy", 17.0), 0.0, 100.0, "Efficacy(lm/W)", "Efficacy Luminous flux/watt", gui)
  5981     if gui: gui.newline("")
  5982     link += luxFloat("gain", luxProp(mat, kn+"light.gain", 1.0), 0.0, 100.0, "gain", "Gain/scale multiplier", gui)
  5983     lightgroup = luxProp(mat, kn+"light.lightgroup", "default")
  5984     luxString("lightgroup", lightgroup, "group", "assign light to a named light-group", gui, 0.8)
  5985     lg_disable = luxProp(Scene.GetCurrent(), "lightgroup.disable."+lightgroup.get(), "false")
  5986     luxBool("lg_disable", lg_disable, "D", "Disable lightgroup during export", gui, 0.2)
  5987     if lg_disable.get() == "true":
  5988         link = ""
  5989 
  5990     if gui: gui.newline("Photometric")
  5991     pm = luxProp(mat, kn+"light.usepm", "false")
  5992     luxCollapse("photometric", pm, "Photometric Diagram", "Enable Photometric Diagram options", gui, 2.0)
  5993 
  5994     if(pm.get()=="true"):
  5995         pmtype = luxProp(mat, kn+"light.pmtype", "IESna")
  5996         pmtypes = ["IESna", "imagemap"]
  5997         luxOption("type", pmtype, pmtypes, "type", "Choose Photometric data type to use", gui, 0.6)
  5998         if(pmtype.get() == "imagemap"):
  5999             map = luxProp(mat, kn+"light.pmmapname", "")
  6000             link += luxFile("mapname", map, "map-file", "filename of the photometric map", gui, 1.4)
  6001         if(pmtype.get() == "IESna"):
  6002             map = luxProp(mat, kn+"light.pmiesname", "")
  6003             link += luxFile("iesname", map, "ies-file", "filename of the IES photometric data file", gui, 1.4)
  6004 
  6005     if gui: gui.newline("Hints")
  6006     hints = luxProp(mat, kn+"light.usehints", "false")
  6007     luxCollapse("hints", hints, "Rendering Hints", "Enable Rendering Hints options", gui, 2.0)
  6008 
  6009     if(hints.get()=="true"):
  6010         link += luxFloat("importance", luxProp(mat, kn+"light.hints.importance", 1.0), 0.0, 100.0, "Importance", "User defined light importance for Light Strategies", gui, 2.0)
  6011 
  6012     has_bump_options = 0
  6013     has_object_options = 1
  6014     return (str, link)
  6015 
  6016 def luxLamp(name, kn, mat, gui, level):
  6017     if gui:
  6018         if name != "": gui.newline(name+":", 10, level)
  6019         else: gui.newline("color:", 0, level+1)
  6020 #    if gui: gui.newline("", 10, level)
  6021     (str,link) = luxLightSpectrumTexture("L", kn+"light", "1.0 1.0 1.0", 1.0, "Spectrum", "", mat, gui, level+1)
  6022     if gui: gui.newline("")
  6023     link += luxFloat("gain", luxProp(mat, kn+"light.gain", 1.0), 0.0, 100.0, "gain", "Gain/scale multiplier", gui)
  6024     lightgroup = luxProp(mat, kn+"light.lightgroup", "default")
  6025     luxString("lightgroup", lightgroup, "group", "assign light to a named light-group", gui, 0.8)
  6026     lg_disable = luxProp(Scene.GetCurrent(), "lightgroup.disable."+lightgroup.get(), "false")
  6027     luxBool("lg_disable", lg_disable, "D", "Disable lightgroup during export", gui, 0.2)
  6028 
  6029     if gui: gui.newline("Photometric")
  6030     pm = luxProp(mat, kn+"light.usepm", "false")
  6031     luxBool("photometric", pm, "Photometric Diagram", "Enable Photometric Diagram options", gui, 2.0)
  6032 
  6033     if(pm.get()=="true"):
  6034         pmtype = luxProp(mat, kn+"light.pmtype", "IESna")
  6035         pmtypes = ["IESna", "imagemap"]
  6036         luxOption("type", pmtype, pmtypes, "type", "Choose Photometric data type to use", gui, 0.6)
  6037         if(pmtype.get() == "imagemap"):
  6038             map = luxProp(mat, kn+"light.pmmapname", "")
  6039             link += luxFile("mapname", map, "map-file", "filename of the photometric map", gui, 1.4)
  6040         if(pmtype.get() == "IESna"):
  6041             map = luxProp(mat, kn+"light.pmiesname", "")
  6042             link += luxFile("iesname", map, "ies-file", "filename of the IES photometric data file", gui, 1.4)
  6043 
  6044         link += luxBool("flipz", luxProp(mat, kn+"light.flipZ", "true"), "Flip Z", "Flip Z direction in mapping", gui, 2.0)
  6045 
  6046     if gui: gui.newline("Hints")
  6047     hints = luxProp(mat, kn+"light.usehints", "false")
  6048     luxCollapse("hints", hints, "Rendering Hints", "Enable Rendering Hints options", gui, 2.0)
  6049 
  6050     if(hints.get()=="true"):
  6051         link += luxFloat("importance", luxProp(mat, kn+"light.hints.importance", 1.0), 0.0, 100.0, "Importance", "User defined light importance for Light Strategies", gui, 2.0)
  6052 
  6053     return (str, link)
  6054 
  6055 def luxSpot(name, kn, mat, gui, level):
  6056     if gui:
  6057         if name != "": gui.newline(name+":", 10, level)
  6058         else: gui.newline("color:", 0, level+1)
  6059 #    if gui: gui.newline("", 10, level)
  6060     (str,link) = luxLightSpectrumTexture("L", kn+"light", "1.0 1.0 1.0", 1.0, "Spectrum", "", mat, gui, level+1)
  6061     if gui: gui.newline("")
  6062     link += luxFloat("gain", luxProp(mat, kn+"light.gain", 1.0), 0.0, 100.0, "gain", "Gain/scale multiplier", gui)
  6063     lightgroup = luxProp(mat, kn+"light.lightgroup", "default")
  6064     luxString("lightgroup", lightgroup, "group", "assign light to a named light-group", gui, 0.8)
  6065     lg_disable = luxProp(Scene.GetCurrent(), "lightgroup.disable."+lightgroup.get(), "false")
  6066     luxBool("lg_disable", lg_disable, "D", "Disable lightgroup during export", gui, 0.2)
  6067 
  6068     if gui: gui.newline("Projection")
  6069     proj = luxProp(mat, kn+"light.usetexproj", "false")
  6070     luxBool("projection", proj, "Texture Projection", "Enable imagemap texture projection", gui, 2.0)
  6071 
  6072     if(proj.get() == "true"):
  6073         map = luxProp(mat, kn+"light.pmmapname", "")
  6074         link += luxFile("mapname", map, "map-file", "filename of the photometric map", gui, 2.0)
  6075 
  6076     if gui: gui.newline("Hints")
  6077     hints = luxProp(mat, kn+"light.usehints", "false")
  6078     luxCollapse("hints", hints, "Rendering Hints", "Enable Rendering Hints options", gui, 2.0)
  6079 
  6080     if(hints.get()=="true"):
  6081         link += luxFloat("importance", luxProp(mat, kn+"light.hints.importance", 1.0), 0.0, 100.0, "Importance", "User defined light importance for Light Strategies", gui, 2.0)
  6082 
  6083     return (str, link)
  6084 
  6085 
  6086 def Preview_Sphereset(mat, kn, state):
  6087     if state=="true":
  6088         luxProp(mat, kn+"prev_sphere", "true").set("true")
  6089         luxProp(mat, kn+"prev_plane", "false").set("false")
  6090         luxProp(mat, kn+"prev_torus", "false").set("false")
  6091 def Preview_Planeset(mat, kn, state):
  6092     if state=="true":
  6093         luxProp(mat, kn+"prev_sphere", "true").set("false")
  6094         luxProp(mat, kn+"prev_plane", "false").set("true")
  6095         luxProp(mat, kn+"prev_torus", "false").set("false")
  6096 def Preview_Torusset(mat, kn, state):
  6097     if state=="true":
  6098         luxProp(mat, kn+"prev_sphere", "true").set("false")
  6099         luxProp(mat, kn+"prev_plane", "false").set("false")
  6100         luxProp(mat, kn+"prev_torus", "false").set("true")
  6101 
  6102 
  6103     
  6104 
  6105 def Preview_Update(mat, kn, defLarge, defType, texName, name, level):
  6106     #print("%s %s %s %s %s %s %s" % (mat, kn, defLarge, defType, texName, name, level))
  6107 
  6108     global previewing
  6109     previewing = True
  6110     
  6111     Blender.Window.WaitCursor(True)
  6112     scn = Scene.GetCurrent()
  6113     
  6114     # set path mode to absolute for preview
  6115     pm_prop = luxProp(scn, "pathmode", "absolute")
  6116     pm = pm_prop.get()
  6117     pm_prop.set('absolute')
  6118     
  6119 
  6120     # Size of preview thumbnail
  6121     thumbres = 110 # default 110x110
  6122     if(defLarge):
  6123         large = luxProp(mat, kn+"prev_large", "true")
  6124     else:
  6125         large = luxProp(mat, kn+"prev_large", "false")
  6126     if(large.get() == "true"):
  6127         thumbres = 140 # small 140x140
  6128 
  6129     thumbbuf = thumbres*thumbres*3
  6130 
  6131 #        consolebin = luxProp(scn, "luxconsole", "").get()
  6132     
  6133     p = get_lux_pipe(scn, buf=thumbbuf, type="luxconsole")
  6134 
  6135     # Unremark to write debugging output to file
  6136     # p.stdin = open('c:\preview.lxs', 'w')
  6137     # p.stdin = open('/home/blender/renders/preview.lxs', 'w')
  6138 
  6139     if defType == 0:    
  6140         prev_sphere = luxProp(mat, kn+"prev_sphere", "true")
  6141         prev_plane = luxProp(mat, kn+"prev_plane", "false")
  6142         prev_torus = luxProp(mat, kn+"prev_torus", "false")
  6143     elif defType == 1:
  6144         prev_sphere = luxProp(mat, kn+"prev_sphere", "false")
  6145         prev_plane = luxProp(mat, kn+"prev_plane", "true")
  6146         prev_torus = luxProp(mat, kn+"prev_torus", "false")
  6147     else:
  6148         prev_sphere = luxProp(mat, kn+"prev_sphere", "false")
  6149         prev_plane = luxProp(mat, kn+"prev_plane", "false")
  6150         prev_torus = luxProp(mat, kn+"prev_torus", "true")
  6151 
  6152     # Zoom
  6153     if prev_plane.get() != "true":
  6154         if luxProp(mat, kn+"prev_zoom", "false").get() == "true":
  6155             p.stdin.write('LookAt 0.250000 -1.500000 0.750000 0.250000 -0.500000 0.750000 0.000000 0.000000 1.000000\nCamera "perspective" "float fov" [22.5]\n')
  6156         else:
  6157             p.stdin.write('LookAt 0.0 -3.0 0.5 0.0 -2.0 0.5 0.0 0.0 1.0\nCamera "perspective" "float fov" [22.5]\n')
  6158     else:
  6159         orientation = luxProp(mat, kn+"prev_orientation", "XY")
  6160         if orientation.get() == "XY":
  6161             p.stdin.write('LookAt 0.0 0.0 1.0  0.0 0.0 0.0  0.0 1.0 0.0\n')
  6162         elif orientation.get() == "XZ":
  6163             p.stdin.write('LookAt 0.0 1.0 0.0  0.0 0.0 0.0  0.0 0.0 1.0\n')
  6164         else:
  6165             p.stdin.write('LookAt 1.0 0.0 0.0  0.0 0.0 0.0  0.0 0.0 1.0\n')
  6166         if luxProp(mat, kn+"prev_zoom", "false").get() == "true":
  6167             p.stdin.write('Camera "orthographic" "float screenwindow" [0.0 0.5 0.0 0.5]\n')
  6168         else:
  6169             p.stdin.write('Camera "orthographic" "float screenwindow" [-0.5 0.5 -0.5 0.5]\n')
  6170     # Fleximage
  6171     p.stdin.write('Film "fleximage" "integer xresolution" [%i] "integer yresolution" [%i] "integer displayinterval" [3] "integer ldr_writeinterval" [3600] "string tonemapkernel" ["linear"] "integer haltspp" [1] "integer reject_warmup" [64] "bool write_tonemapped_tga" ["false"] "bool write_untonemapped_exr" ["false"] "bool write_tonemapped_exr" ["false"] "bool write_untonemapped_igi" ["false"] "bool write_tonemapped_igi" ["false"] "bool write_png" ["false"] "string filename" ["luxblend-preview"] \n'%(thumbres, thumbres))
  6172     p.stdin.write('PixelFilter "mitchell" "float xwidth" [1.500000] "float ywidth" [1.500000] "float B" [0.333330] "float C" [0.333330] "bool supersample" ["true"]\n')
  6173     # Quality
  6174     scn = Scene.GetCurrent()
  6175     defprevmat = luxProp(scn, "defprevmat", "high")
  6176     quality = luxProp(mat, kn+"prev_quality", defprevmat.get())
  6177     if quality.get()=="low":
  6178         p.stdin.write('Sampler "lowdiscrepancy" "string pixelsampler" ["hilbert"] "integer pixelsamples" [2]\n')
  6179     elif quality.get()=="medium":
  6180         p.stdin.write('Sampler "lowdiscrepancy" "string pixelsampler" ["hilbert"] "integer pixelsamples" [4]\n')
  6181     elif quality.get()=="high":
  6182         p.stdin.write('Sampler "lowdiscrepancy" "string pixelsampler" ["hilbert"] "integer pixelsamples" [8]\n')
  6183     else: 
  6184         p.stdin.write('Sampler "lowdiscrepancy" "string pixelsampler" ["hilbert"] "integer pixelsamples" [32]\n')
  6185     # SurfaceIntegrator
  6186     if(prev_plane.get()=="false"):
  6187         p.stdin.write('SurfaceIntegrator "distributedpath" "integer directsamples" [1] "integer diffusereflectdepth" [1] "integer diffusereflectsamples" [4] "integer diffuserefractdepth" [4] "integer diffuserefractsamples" [1] "integer glossyreflectdepth" [1] "integer glossyreflectsamples" [2] "integer glossyrefractdepth" [4] "integer glossyrefractsamples" [1] "integer specularreflectdepth" [2] "integer specularrefractdepth" [4]\n')
  6188     else:
  6189         p.stdin.write('SurfaceIntegrator "distributedpath" "integer directsamples" [1] "integer diffusereflectdepth" [0] "integer diffusereflectsamples" [0] "integer diffuserefractdepth" [0] "integer diffuserefractsamples" [0] "integer glossyreflectdepth" [0] "integer glossyreflectsamples" [0] "integer glossyrefractdepth" [0] "integer glossyrefractsamples" [0] "integer specularreflectdepth" [1] "integer specularrefractdepth" [1]\n')
  6190     # World
  6191     p.stdin.write('WorldBegin\n')
  6192     if(prev_sphere.get()=="true"):
  6193         p.stdin.write('AttributeBegin\nTransform [0.5 0.0 0.0 0.0  0.0 0.5 0.0 0.0  0.0 0.0 0.5 0.0  0.0 0.0 0.5 1.0]\n')
  6194     elif (prev_plane.get()=="true"):
  6195         pass
  6196     else:
  6197         p.stdin.write('AttributeBegin\nTransform [0.35 -0.35 0.0 0.0  0.25 0.25 0.35 0.0  -0.25 -0.25 0.35 0.0  0.0 0.0 0.5 1.0]\n')
  6198     obwidth = luxProp(mat, kn+"prev_obwidth", 1.0)
  6199     obw = obwidth.get()
  6200     p.stdin.write('TransformBegin\n')
  6201     p.stdin.write('Scale %f %f %f\n'%(obw,obw,obw))
  6202     if texName:
  6203         print("texture "+texName+"  "+name)
  6204         (str, link) = luxTexture(texName, name, "color", "1.0 1.0 1.0", None, None, "", "", mat, None, 0, level)
  6205         link = link.replace(" "+texName+"\"", " Kd\"") # swap texture name to "Kd"
  6206         p.stdin.write(str+"\n")
  6207         p.stdin.write("Material \"matte\" "+link+"\n") 
  6208     else:
  6209         # named volumes
  6210         for volume_prop in ['Exterior', 'Interior']:
  6211             if luxProp(mat, '%s_vol_used'%(volume_prop), 'false').get() == 'true':
  6212                 volData = getNamedVolume(luxProp(mat, '%s_vol_id'%(volume_prop), 'false').get())
  6213                 volTex = luxNamedVolumeTexture(volData['id'])
  6214                 p.stdin.write("%s\nMakeNamedVolume \"%s\" %s\n" % (volTex[0], volData['name'], volTex[1]))
  6215         # Material
  6216         p.stdin.write(luxMaterial(mat))
  6217         link = luxProp(mat,"link","").get()
  6218         if kn!="": link = link.rstrip("\"")+":"+kn.strip(".:")+"\""
  6219         p.stdin.write(link+'\n')
  6220     p.stdin.write('TransformEnd\n')
  6221     # Shape
  6222     if(prev_sphere.get()=="true"):
  6223         p.stdin.write('Shape "sphere" "float radius" [1.0]\n')
  6224     elif (prev_plane.get()=="true"):
  6225         _points = "%(p)s %(p)s %(p)s %(p)s" % {'p': "%(x)s %(y)s %(z)s"} % \
  6226             {
  6227                 'x': '%.1f' if 'X' in orientation.get() else '0.0',
  6228                 'y': '%.1f' if 'Y' in orientation.get() else '0.0',
  6229                 'z': '%.1f' if 'Z' in orientation.get() else '0.0'
  6230             } % (0.5,0.5,0.5,-0.5,-0.5,-0.5,-0.5,0.5)
  6231         _uv = ' 0.0 1.0  0.0 0.0  1.0 0.0  1.0 1.0 ' if orientation.get() == 'XZ' else ' 1.0 1.0  1.0 0.0  0.0 0.0  0.0 1.0 '
  6232         p.stdin.write('Shape "trianglemesh" "integer indices" [ 0 1 2 0 2 3 ] "point P" [%s] "float uv" [%s]\n' % (_points, _uv))
  6233     elif (prev_torus.get()=="true"):
  6234         p.stdin.write('Shape "torus" "float radius" [1.0]\n')
  6235     p.stdin.write('AttributeEnd\n')
  6236     # Checkerboard floor
  6237     if(prev_plane.get()=="false"):
  6238         p.stdin.write('AttributeBegin\nTransform [5.0 0.0 0.0 0.0  0.0 5.0 0.0 0.0  0.0 0.0 5.0 0.0  0.0 0.0 0.0 1.0]\n')
  6239         p.stdin.write('Texture "checks::pattern" "float" "checkerboard"')
  6240         p.stdin.write('"integer dimension" [2] "string aamode" ["supersample"]')
  6241         p.stdin.write('"string mapping" ["uv"] "float uscale" [36.8] "float vscale" [36.0]\n')
  6242         p.stdin.write('Texture "checks" "color" "mix" "texture amount" ["checks::pattern"] "color tex1" [0.9 0.9 0.9] "color tex2" [0.0 0.0 0.0]\n')
  6243         p.stdin.write('Material "matte" "texture Kd" ["checks"]\n')
  6244         p.stdin.write('Shape "loopsubdiv" "integer nlevels" [3] "bool dmnormalsmooth" ["true"] "bool dmsharpboundary" ["false"] ')
  6245         p.stdin.write('"integer indices" [ 0 1 2 0 2 3 1 0 4 1 4 5 5 4 6 5 6 7 ]')
  6246         p.stdin.write('"point P" [ 1.000000 1.000000 0.000000 -1.000000 1.000000 0.000000 -1.000000 -1.000000 0.000000 1.000000 -1.000000 0.000000 1.000000 3.000000 0.000000 -1.000000 3.000000 0.000000 1.000000 3.000000 2.000000 -1.000000 3.000000 2.000000')
  6247         p.stdin.write('] "normal N" [ 0.000000 0.000000 1.000000 0.000000 0.000000 1.000000 0.000000 0.000000 1.000000 0.000000 0.000000 1.000000 0.000000 -0.707083 0.707083 0.000000 -0.707083 0.707083 0.000000 -1.000000 0.000000 0.000000 -1.000000 0.000000')
  6248         p.stdin.write('] "float uv" [ 0.333334 0.000000 0.333334 0.333334 0.000000 0.333334 0.000000 0.000000 0.666667 0.000000 0.666667 0.333333 1.000000 0.000000 1.000000 0.333333 ]\n')
  6249         p.stdin.write('AttributeEnd\n')
  6250     # Lightsource
  6251     area = luxProp(mat, kn+"prev_arealight", "false")
  6252     if(prev_plane.get()=="false"):
  6253         p.stdin.write('AttributeBegin\nTransform [1.0 0.0 0.0 0.0  0.0 1.0 0.0 0.0  0.0 0.0 1.0 0.0  1.0 -1.0 4.0 1.0]\n')
  6254     else:
  6255         _area = area.get() ; area.set('false')
  6256         p.stdin.write('AttributeBegin\nTransform [1.0 0.0 0.0 0.0  0.0 1.0 0.0 0.0  0.0 0.0 1.0 0.0  1.0 -4.0 1.0 1.0]\n')
  6257         _m = Mathutils.TranslationMatrix(Mathutils.Vector(0.5,0.5,3.5 if area.get() == "false" else 6.0))*Mathutils.RotationMatrix({'XY':0,'XZ':-90,'YZ':90}[orientation.get()],4,'x' if orientation.get() == "XZ" else 'y')
  6258         _s = ' '.join(['%.5f' % b for a in _m for b in a])
  6259         p.stdin.write('AttributeBegin\nTransform [%s]\n'%_s)
  6260     if(area.get() == "false"):
  6261         p.stdin.write('Texture "pL" "color" "blackbody" "float temperature" [6500.0]\n')
  6262         p.stdin.write('LightSource "point" "texture L" ["pL"] "float gain" [0.002]')
  6263     else:
  6264         p.stdin.write('ReverseOrientation\n')
  6265         p.stdin.write('AreaLightSource "area" "color L" [1.0 1.0 1.0]\n')
  6266         if(prev_plane.get()=="false"):
  6267             p.stdin.write(' "float gain" [0.3]\n')
  6268         p.stdin.write('Shape "disk" "float radius" [1.0]\nAttributeEnd\n')
  6269     if prev_plane.get() == 'true':
  6270         area.set(_area)
  6271     p.stdin.write('WorldEnd\n')
  6272     
  6273     previewing = False
  6274 
  6275     data = p.communicate()[0]
  6276     p.stdin.close()
  6277     
  6278     # restore path mode
  6279     pm_prop.set(pm)    
  6280     
  6281     datalen = len(data)
  6282     if(datalen < thumbbuf): 
  6283         print("error on preview: got %i bytes, expected %i" % (datalen, thumbbuf))
  6284         return
  6285     global previewCache
  6286     image = luxImage()
  6287     image.decodeLuxConsole(thumbres, thumbres, data)
  6288     previewCache[(mat.name+":"+kn).__hash__()] = image
  6289     Draw.Redraw()
  6290     Blender.Window.WaitCursor(False)
  6291 
  6292 def luxPreview(mat, name, defType=0, defEnabled=False, defLarge=False, texName=None, gui=None, level=0, color=None):
  6293     
  6294     if gui:
  6295         kn = name
  6296         if texName: kn += ":"+texName
  6297         if kn != "": kn += "."
  6298         if(defEnabled == True):
  6299             showpreview = luxProp(mat, kn+"prev_show", "true")
  6300         else:
  6301             showpreview = luxProp(mat, kn+"prev_show", "false")
  6302         Draw.Toggle("P", evtLuxGui, gui.xmax, gui.y-gui.h, gui.h, gui.h, showpreview.get()=="true", "Preview", lambda e,v: showpreview.set(["false","true"][bool(v)]))
  6303         if showpreview.get()=="true": 
  6304             if(defLarge):
  6305                 large = luxProp(mat, kn+"prev_large", "true")
  6306             else:
  6307                 large = luxProp(mat, kn+"prev_large", "false")
  6308             voffset = -8
  6309             rr = 5.65 
  6310             if(large.get() == "true"):
  6311                 rr = 7
  6312                 voffset = 22
  6313             gui.newline()
  6314             r = gui.getRect(1.1, rr)
  6315             if(color != None):
  6316                 BGL.glColor3f(color[0],color[1],color[2]); BGL.glRectf(r[0]-110, r[1], 418, r[1]+128+voffset); BGL.glColor3f(0.9, 0.9, 0.9)
  6317             try: previewCache[(mat.name+":"+kn).__hash__()].draw(r[0]-82, r[1]+4)
  6318             except: pass
  6319 
  6320             prev_sphere = luxProp(mat, kn+"prev_sphere", "true")
  6321             prev_plane = luxProp(mat, kn+"prev_plane", "false")
  6322             prev_torus = luxProp(mat, kn+"prev_torus", "false")
  6323             if defType == 1:
  6324                 prev_sphere = luxProp(mat, kn+"prev_sphere", "false")
  6325                 prev_plane = luxProp(mat, kn+"prev_plane", "true")
  6326                 prev_torus = luxProp(mat, kn+"prev_torus", "false")
  6327             elif defType == 2:
  6328                 prev_sphere = luxProp(mat, kn+"prev_sphere", "false")
  6329                 prev_plane = luxProp(mat, kn+"prev_plane", "false")
  6330                 prev_torus = luxProp(mat, kn+"prev_torus", "true")
  6331 
  6332             # preview mode toggle buttons
  6333             Draw.Toggle("S", evtLuxGui, r[0]-108, r[1]+100+voffset, 22, 22, prev_sphere.get()=="true", "Draw Sphere", lambda e,v: Preview_Sphereset(mat, kn, ["false","true"][bool(v)]))
  6334             Draw.Toggle("P", evtLuxGui, r[0]-108, r[1]+74+voffset, 22, 22, prev_plane.get()=="true", "Draw 2D Plane", lambda e,v: Preview_Planeset(mat, kn, ["false","true"][bool(v)]))
  6335             Draw.Toggle("T", evtLuxGui, r[0]-108, r[1]+48+voffset, 22, 22, prev_torus.get()=="true", "Draw Torus", lambda e,v: Preview_Torusset(mat, kn, ["false","true"][bool(v)]))
  6336 
  6337             # Zoom toggle
  6338             zoom = luxProp(mat, kn+"prev_zoom", "false")
  6339             Draw.Toggle("Zoom", evtLuxGui, r[0]+66, r[1]+100+voffset, 62, 18, zoom.get()=="true", "Zoom in to preview object", lambda e,v: zoom.set(["false","true"][bool(v)]))
  6340             
  6341             area = luxProp(mat, kn+"prev_arealight", "false")
  6342             if not prev_plane.get()=="true":
  6343                 Draw.Toggle("Area", evtLuxGui, r[0]+133, r[1]+100+voffset, 62, 18, area.get()=="true", "Use area lightsource instead of point light", lambda e,v: area.set(["false","true"][bool(v)]))
  6344 
  6345             # Object width
  6346             obwidth = luxProp(mat, kn+"prev_obwidth", 1.0)
  6347             Draw.Number("Width:", evtLuxGui, r[0]+66, r[1]+78+voffset, 129, 18, float(obwidth.get()), 0.001, 10, "The width of the preview object in Blender/LuxRender 1m units", lambda e,v: obwidth.set(v))
  6348 
  6349             # Orientation controls for plane obj
  6350             if prev_plane.get() == "true":
  6351                 orientation = luxProp(mat, kn+"prev_orientation", "XY")
  6352                 Draw.Toggle("XY", evtLuxGui, r[0]+66,  r[1]+56+voffset, 41, 18, orientation.get()=="XY", "Position plane over XY axes ('top' view)", lambda e,v: orientation.set("XY"))
  6353                 Draw.Toggle("XZ", evtLuxGui, r[0]+110, r[1]+56+voffset, 41, 18, orientation.get()=="XZ", "Position plane over XZ axes ('front' view)", lambda e,v: orientation.set("XZ"))
  6354                 Draw.Toggle("YZ", evtLuxGui, r[0]+154, r[1]+56+voffset, 41, 18, orientation.get()=="YZ", "Position plane over YZ axes ('side' view)", lambda e,v: orientation.set("YZ"))
  6355             
  6356             # large/small size
  6357             Draw.Toggle("Large", evtLuxGui, r[0]+200, r[1]+78+voffset, 88, 18, large.get()=="true", "Show larger preview image", lambda e,v: large.set(["false","true"][bool(v)]))
  6358 
  6359             # Preview Quality
  6360             qs = ["low","medium","high","very high"]
  6361             scn = Scene.GetCurrent()
  6362             defprevmat = luxProp(scn, "defprevmat", "high")
  6363             quality = luxProp(mat, kn+"prev_quality", defprevmat.get())
  6364             luxOptionRect("quality", quality, qs, "  Quality", "Select preview quality (higher quality > less noise > longer update)", gui, r[0]+200, r[1]+100+voffset, 88, 18)
  6365 
  6366             # Update preview
  6367             Draw.Button("Update Preview", evtLuxGui, r[0]+133, r[1]+5, 154, 18, "Update Material Preview", lambda e,v: Preview_Update(mat, kn, defLarge, defType, texName, name, level))
  6368 
  6369             # Reset depths after getRect()
  6370             gui.y -= 92+voffset
  6371             gui.y -= gui.h
  6372             gui.hmax = 18 + 4
  6373 
  6374 def luxMaterialBlock(name, luxname, key, mat, gui=None, level=0, str_opt=""):
  6375     global icon_mat, icon_matmix, icon_map3dparam
  6376     def c(t1, t2):
  6377         return (t1[0]+t2[0], t1[1]+t2[1])
  6378     str = ""
  6379     if key == "": keyname = kn = name
  6380     else: keyname = kn = "%s:%s"%(key, name)
  6381     if kn != "": kn += "."
  6382     if keyname == "": matname = mat.getName()
  6383     else: matname = "%s:%s"%(mat.getName(), keyname)
  6384 
  6385     if mat:
  6386         mattype = luxProp(mat, kn+"type", "matte")
  6387         # Set backwards compatibility of glossy material from plastic and substrate
  6388         if(mattype.get() == "substrate" or mattype.get() == "plastic"):
  6389             mattype.set("glossy")
  6390 
  6391         # this is reverse order than in shown in the dropdown list
  6392         materials = ["null","mix","mirror","shinymetal","metal","mattetranslucent","matte","glossy_lossy","glossy","roughglass","glass","glass2","carpaint"]
  6393         
  6394         if level == 0: materials = ["portal", "light", "boundvolume"]+materials
  6395         if gui:
  6396             icon = icon_mat
  6397             if mattype.get() == "mix": icon = icon_matmix
  6398             if level == 0: gui.newline("Material type:", 12, level, icon, [0.75,0.5,0.25])
  6399             else: gui.newline(name+":", 12, level, icon, scalelist([0.75,0.6,0.25],2.0/(level+2)))
  6400 
  6401 
  6402         link = luxOption("type", mattype, materials, "  TYPE", "select material type", gui)
  6403         showadvanced = luxProp(mat, kn+"showadvanced", "false")
  6404         luxBool("advanced", showadvanced, "Advanced", "Show advanced options", gui, 0.6)
  6405         showhelp = luxProp(mat, kn+"showhelp", "false")
  6406         luxHelp("help", showhelp, "Help", "Show Help Information", gui, 0.4)
  6407 
  6408         # show copy/paste menu button
  6409         if gui: Draw.PushButton(">", evtLuxGui, gui.xmax+gui.h, gui.y-gui.h, gui.h, gui.h, "Menu", lambda e,v: showMatTexMenu(mat,keyname,False))
  6410 
  6411         # Draw Material preview option
  6412         showmatprev = False
  6413         if level == 0:
  6414             showmatprev = True
  6415         if gui: luxPreview(mat, keyname, 0, showmatprev, True, None, gui, level, [0.746, 0.625, 0.5])
  6416 
  6417 
  6418         if gui: gui.newline()
  6419         has_object_options   = 0 # disable object options by default
  6420         has_volume_options   = 0 # disable named volume options by default
  6421         has_bump_options     = 0 # disable bump mapping options by default
  6422         has_emission_options = 0 # disable emission options by default
  6423         has_compositing_options = 0 # disable compositing options by default
  6424 
  6425         if mattype.get() == "mix":
  6426             (str,link) = c((str,link), luxFloatTexture("amount", keyname, 0.5, 0.0, 1.0, "amount", "The degree of mix between the two materials", mat, gui, level+1))
  6427             if gui:
  6428                 r = gui.getRect(2, 1)
  6429                 Draw.Button("Flip material slots", evtLuxGui, r[0], r[1], r[2], r[3], "Flip mat1 and mat2 contents", lambda e,v: flipMixMat(mat,keyname))
  6430             (str,link) = c((str,link), luxMaterialBlock("mat1", "namedmaterial1", keyname, mat, gui, level+1))
  6431             (str,link) = c((str,link), luxMaterialBlock("mat2", "namedmaterial2", keyname, mat, gui, level+1))
  6432             has_volume_options = 1
  6433             has_bump_options = 0
  6434             has_object_options = 1
  6435             has_emission_options = 1
  6436             has_compositing_options = 0
  6437 
  6438         if mattype.get() == "light":
  6439             lightgroup = luxProp(mat, kn+"light.lightgroup", "default")
  6440             if luxProp(Scene.GetCurrent(), "lightgroup.disable."+lightgroup.get(), "false").get() == "true":
  6441                 # pass dummy mat instead of light material if lightgroup is disabled
  6442                 link = "Material \"matte\" # dummy material\n"
  6443             else:
  6444                 if luxProp(Scene.GetCurrent(), "nolg", "false").get()!="true":
  6445                     link = "LightGroup \"%s\"\n"%lightgroup.get()
  6446                 else:
  6447                     link = ''
  6448                 link += "AreaLightSource \"area\""
  6449             (str,link) = c((str,link), luxLight("", kn, mat, gui, level))
  6450             has_volume_options = 1
  6451             has_bump_options = 0
  6452             has_object_options = 1
  6453             has_emission_options = 0
  6454             has_compositing_options = 1
  6455 
  6456         if mattype.get() == "boundvolume":
  6457             link = ""
  6458             voltype = luxProp(mat, kn+"vol.type", "homogeneous")
  6459             vols = ["homogeneous", "exponential", "cloud"]
  6460             vollink = luxOption("type", voltype, vols, "type", "", gui)
  6461             if voltype.get() == "homogeneous":
  6462                 link = "Volume \"homogeneous\""
  6463             if voltype.get() == "exponential":
  6464                 link = "Volume \"exponential\""
  6465             if voltype.get() == "cloud":
  6466                 link = "Volume \"cloud\""
  6467 
  6468             if gui: gui.newline("absorption:", 0, level+1)
  6469             link += luxRGB("sigma_a", luxProp(mat, kn+"vol.sig_a", "1.0 1.0 1.0"), 1.0, "sigma_a", "The absorption cross section", gui)
  6470             if gui: gui.newline("scattering:", 0, level+1)
  6471             link += luxRGB("sigma_s", luxProp(mat, kn+"vol.sig_b", "0.0 0.0 0.0"), 1.0, "sigma_b", "The scattering cross section", gui)
  6472             if gui: gui.newline("emission:", 0, level+1)
  6473             link += luxRGB("Le", luxProp(mat, kn+"vol.le", "0.0 0.0 0.0"), 1.0, "Le", "The volume's emission spectrum", gui)
  6474             if gui: gui.newline("assymetry:", 0, level+1)
  6475             link += luxFloat("g", luxProp(mat, kn+"vol.g", 0.0), 0.0, 100.0, "g", "The phase function asymmetry parameter", gui)
  6476 
  6477             if voltype.get() == "exponential":
  6478                 if gui: gui.newline("form:", 0, level+1)
  6479                 link += luxFloat("a", luxProp(mat, kn+"vol.a", 1.0), 0.0, 100.0, "a/scale", "exponential::a parameter in the ae^{-bh} formula", gui)
  6480                 link += luxFloat("b", luxProp(mat, kn+"vol.b", 2.0), 0.0, 100.0, "b/falloff", "exponential::b parameter in the ae^{-bh} formula", gui)
  6481                 if gui: gui.newline("updir:", 0, level+1)
  6482                 link += luxVector("updir", luxProp(mat, kn+"vol.updir", "0 0 1"), -1.0, 1.0, "updir", "Up direction vector", gui, 2.0)
  6483 
  6484             if voltype.get() == "cloud":
  6485                 if gui: gui.newline("cloud:", 0, level+1)
  6486                 link += luxFloat("radius", luxProp(mat, kn+"vol.radius", 0.5), 0.01, 2.0, "radius", "Radius of hemisphere used as basis for cloud shape", gui)
  6487                 link += luxFloat("noisescale", luxProp(mat, kn+"vol.noisescale", 0.3), 0.1, 2.0, "noisesize", "Size of cloud noise", gui)
  6488                 link += luxFloat("turbulence", luxProp(mat, kn+"vol.turbulence", 0.5), 0.0, 3.0, "turbulence", "Extent to which the noise effects the cloud shape", gui)
  6489                 link += luxFloat("noiseoffset", luxProp(mat, kn+"vol.noiseoffset", 0.0), 0.0, 1000.0, "noiseoffset", "Useful for creating unique clouds", gui )
  6490                 link += luxInt("octaves", luxProp(mat, kn+"vol.octaves", 3), 1, 8, "octaves", "Sets the amount of detail for the noise", gui )
  6491                 link += luxFloat("omega", luxProp(mat, kn+"vol.omega", 0.75), 0.1, 1.0, "omega", "Sets the scale difference of each successive octave", gui )
  6492                 link += luxFloat("sharpness", luxProp(mat, kn+"vol.sharpness", 6.0), 0.2, 10.0, "sharpness", "Sets the sharpness of the noise", gui)
  6493                 link += luxFloat("variability", luxProp(mat, kn+"vol.variability", 0.9), 0.0, 1.0, "mask amount", "Noise mask amount. 0 means noise everywhere, 1 means only some spots have noise.", gui)
  6494                 link += luxFloat("baseflatness", luxProp(mat, kn+"vol.baseflatness", 0.8), 0.0, 1.0, "baseflatness", "Flatness of the cloud's base. (0.0 makes a round cloud.)", gui)
  6495                 link += luxInt("spheres", luxProp(mat, kn+"vol.spheres", 2000), 0, 10000, "spheres", "Number of small spheres for cumulus shape. 0 is non-cumulus.", gui )
  6496                 link += luxFloat("spheresize", luxProp(mat, kn+"vol.spheresize", 0.15), 0.05, 0.55, "spheresize", "Size of cumulus spheres", gui)
  6497  
  6498             link += str_opt
  6499 
  6500             has_bump_options = 0
  6501             has_object_options = 0
  6502             has_emission_options = 0
  6503 
  6504             return (str, link)
  6505 
  6506         if mattype.get() == "carpaint":
  6507             carpaintData = {        #        Kd RGB                    Ks1 RGB                   Ks2 RGB                   Ks3 RGB            R1      R2      R3      M1      M2      M3
  6508                 "ford f8":        [ [0.0012, 0.0015, 0.0018], [0.0049, 0.0076, 0.0120], [0.0100, 0.0130, 0.0180], [0.0070, 0.0065, 0.0077], 0.1500, 0.0870, 0.9000, 0.3200, 0.1100, 0.0130 ],
  6509                 "polaris silber": [ [0.0550, 0.0630, 0.0710], [0.0650, 0.0820, 0.0880], [0.1100, 0.1100, 0.1300], [0.0080, 0.0130, 0.0150], 1.0000, 0.9200, 0.9000, 0.3800, 0.1700, 0.0130 ],
  6510                 "opel titan":     [ [0.0110, 0.0130, 0.0150], [0.0570, 0.0660, 0.0780], [0.1100, 0.1200, 0.1300], [0.0095, 0.0140, 0.0160], 0.8500, 0.8600, 0.9000, 0.3800, 0.1700, 0.0140 ],
  6511                 "bmw339":         [ [0.0120, 0.0150, 0.0160], [0.0620, 0.0760, 0.0800], [0.1100, 0.1200, 0.1200], [0.0083, 0.0150, 0.0160], 0.9200, 0.8700, 0.9000, 0.3900, 0.1700, 0.0130 ],
  6512                 "2k acrylack":    [ [0.4200, 0.3200, 0.1000], [0.0000, 0.0000, 0.0000], [0.0280, 0.0260, 0.0060], [0.0170, 0.0075, 0.0041], 1.0000, 0.9000, 0.1700, 0.8800, 0.8000, 0.0150 ],
  6513                 "white":          [ [0.6100, 0.6300, 0.5500], [2.6e-6, 3.1e-4, 3.1e-8], [0.0130, 0.0110, 0.0083], [0.0490, 0.0420, 0.0370], 0.0490, 0.4500, 0.1700, 1.0000, 0.1500, 0.0150 ],
  6514                 "blue":           [ [0.0079, 0.0230, 0.1000], [0.0011, 0.0015, 0.0019], [0.0250, 0.0300, 0.0430], [0.0590, 0.0740, 0.0820], 1.0000, 0.0940, 0.1700, 0.1500, 0.0430, 0.0200 ],
  6515                 "blue matte":     [ [0.0099, 0.0360, 0.1200], [0.0032, 0.0045, 0.0059], [0.1800, 0.2300, 0.2800], [0.0400, 0.0490, 0.0510], 1.0000, 0.0460, 0.1700, 0.1600, 0.0750, 0.0340 ]
  6516                 }
  6517             if gui: gui.newline("Preset:", 0, level+1)
  6518             carname = luxProp(mat, kn+"carpaint.name", "Custom")
  6519             cars = ["Custom","ford f8","polaris silber","opel titan","bmw339","2k acrylack","white","blue","blue matte"]
  6520             carlink = luxOption("name", carname, cars, "name", "", gui)
  6521             if carname.get() == "Custom":
  6522                 (str,link) = c((str,link), luxSpectrumTexture("Kd", keyname, "1.0 1.0 1.0", 1.0, "diffuse", "", mat, gui, level+1))
  6523                 (str,link) = c((str,link), luxSpectrumTexture("Ks1", keyname, "1.0 1.0 1.0", 1.0, "specular1", "", mat, gui, level+1))
  6524                 (str,link) = c((str,link), luxSpectrumTexture("Ks2", keyname, "1.0 1.0 1.0", 1.0, "specular2", "", mat, gui, level+1))
  6525                 (str,link) = c((str,link), luxSpectrumTexture("Ks3", keyname, "1.0 1.0 1.0", 1.0, "specular3", "", mat, gui, level+1))
  6526                 (str,link) = c((str,link), luxFloatTexture("R1", keyname, 1.0, 0.0, 1.0, "R1", "", mat, gui, level+1))
  6527                 (str,link) = c((str,link), luxFloatTexture("R2", keyname, 1.0, 0.0, 1.0, "R2", "", mat, gui, level+1))
  6528                 (str,link) = c((str,link), luxFloatTexture("R3", keyname, 1.0, 0.0, 1.0, "R3", "", mat, gui, level+1))
  6529                 (str,link) = c((str,link), luxFloatTexture("M1", keyname, 1.0, 0.0, 1.0, "M1", "", mat, gui, level+1))
  6530                 (str,link) = c((str,link), luxFloatTexture("M2", keyname, 1.0, 0.0, 1.0, "M2", "", mat, gui, level+1))
  6531                 (str,link) = c((str,link), luxFloatTexture("M3", keyname, 1.0, 0.0, 1.0, "M3", "", mat, gui, level+1))
  6532             else:
  6533                 luxProp(mat, keyname+':Kd', '1.0 1.0 1.0').set(' '.join(map(__builtins__['str'], carpaintData[carname.get()][0])))
  6534                 luxProp(mat, keyname+':Ks1', '1.0 1.0 1.0').set(' '.join(map(__builtins__['str'], carpaintData[carname.get()][1])))
  6535                 luxProp(mat, keyname+':Ks2', '1.0 1.0 1.0').set(' '.join(map(__builtins__['str'], carpaintData[carname.get()][2])))
  6536                 luxProp(mat, keyname+':Ks3', '1.0 1.0 1.0').set(' '.join(map(__builtins__['str'], carpaintData[carname.get()][3])))
  6537                 luxProp(mat, keyname+':R1', 1.0).set(carpaintData[carname.get()][4])
  6538                 luxProp(mat, keyname+':R2', 1.0).set(carpaintData[carname.get()][5])
  6539                 luxProp(mat, keyname+':R3', 1.0).set(carpaintData[carname.get()][6])
  6540                 luxProp(mat, keyname+':M1', 1.0).set(carpaintData[carname.get()][7])
  6541                 luxProp(mat, keyname+':M2', 1.0).set(carpaintData[carname.get()][8])
  6542                 luxProp(mat, keyname+':M3', 1.0).set(carpaintData[carname.get()][9])
  6543                 link += carlink
  6544             absorption = luxProp(mat, keyname+".useabsorption", "false")
  6545             luxCollapse("absorption", absorption, "Absorption", "Enable Coating Absorption", gui, 2.0)
  6546             if absorption.get() == "true":
  6547                 (str,link) = c((str,link), luxSpectrumTexture("Ka", keyname, "0.2 0.2 0.2", 1.0, "absorption", "", mat, gui, level+1))
  6548                 (str,link) = c((str,link), luxFloatTexture("d", keyname, 5.0, 0.0, 15.0, "depth", "", mat, gui, level+1))
  6549             has_volume_options = 1
  6550             has_bump_options = 1
  6551             has_object_options = 1
  6552             has_emission_options = 1
  6553             has_compositing_options = 1
  6554         
  6555         if mattype.get() == "glass":
  6556             (str,link) = c((str,link), luxSpectrumTexture("Kr", keyname, "1.0 1.0 1.0", 1.0, "reflection", "", mat, gui, level+1))
  6557             (str,link) = c((str,link), luxSpectrumTexture("Kt", keyname, "1.0 1.0 1.0", 1.0, "transmission", "", mat, gui, level+1))
  6558             (str,link) = c((str,link), luxIORFloatTexture("index", keyname, 1.5, 1.0, 6.0, "IOR", "", mat, gui, level+1))
  6559             architectural = luxProp(mat, keyname+".architectural", "false")
  6560             link += luxBool("architectural", architectural, "Architectural", "Enable architectural glass", gui, 2.0)
  6561             if architectural.get() == "false":
  6562                 chromadisp = luxProp(mat, keyname+".chromadisp", "false")
  6563                 luxCollapse("chromadisp", chromadisp, "Dispersive Refraction", "Enable Chromatic Dispersion", gui, 2.0)
  6564                 if chromadisp.get() == "true":
  6565                     (str,link) = c((str,link), luxCauchyBFloatTexture("cauchyb", keyname, 0.0, 0.0, 1.0, "cauchyb", "", mat, gui, level+1))
  6566                 thinfilm = luxProp(mat, keyname+".thinfilm", "false")
  6567                 luxCollapse("thinfilm", thinfilm, "Thin Film Coating", "Enable Thin Film Coating", gui, 2.0)
  6568                 if thinfilm.get() == "true":
  6569                     (str,link) = c((str,link), luxFloatSliderTexture("film", keyname, 200.0, 1.0, 1500.0, "film", "thickness of film coating in nanometers", mat, gui, level+1))
  6570                     (str,link) = c((str,link), luxIORFloatTexture("filmindex", keyname, 1.5, 1.0, 6.0, "film IOR", "film coating index of refraction", mat, gui, level+1))
  6571             has_volume_options = 1
  6572             has_bump_options = 1
  6573             has_object_options = 1
  6574             has_emission_options = 1
  6575             has_compositing_options = 1
  6576             
  6577         if mattype.get() == 'glass2':
  6578             architectural = luxProp(mat, keyname+".architectural", "false")
  6579             link += luxBool("architectural", architectural, "Architectural", "Enable architectural glass", gui, 2.0)
  6580             if architectural.get() == "false":
  6581                 chromadisp = luxProp(mat, keyname+".dispersion", "false")
  6582                 link += luxBool("dispersion", chromadisp, "Dispersive Refraction", "Enable Chromatic Dispersion", gui, 2.0)
  6583             has_volume_options = 1
  6584             has_bump_options = 1
  6585             has_object_options = 1
  6586             has_emission_options = 1
  6587             has_compositing_options = 1
  6588             
  6589         if mattype.get() == "matte":
  6590             orennayar = luxProp(mat, keyname+".orennayar", "false")
  6591             (str,link) = c((str,link), luxSpectrumTexture("Kd", keyname, "1.0 1.0 1.0", 1.0, "diffuse", "", mat, gui, level+1))
  6592             luxCollapse("orennayar", orennayar, "Oren-Nayar", "Enable Oren-Nayar BRDF", gui, 2.0)
  6593             if orennayar.get() == "true":
  6594                 (str,link) = c((str,link), luxFloatTexture("sigma", keyname, 0.0, 0.0, 100.0, "sigma", "sigma value for Oren-Nayar BRDF", mat, gui, level+1))
  6595             has_volume_options = 1
  6596             has_bump_options = 1
  6597             has_object_options = 1
  6598             has_emission_options = 1
  6599             has_compositing_options = 1
  6600         
  6601         if mattype.get() == "mattetranslucent":
  6602             orennayar = luxProp(mat, keyname+".orennayar", "false")
  6603             (str,link) = c((str,link), luxSpectrumTexture("Kr", keyname, "1.0 1.0 1.0", 1.0, "reflection", "", mat, gui, level+1))
  6604             (str,link) = c((str,link), luxSpectrumTexture("Kt", keyname, "1.0 1.0 1.0", 1.0, "transmission", "", mat, gui, level+1))
  6605             luxCollapse("orennayar", orennayar, "Oren-Nayar", "Enable Oren-Nayar BRDF", gui, 2.0)
  6606             if orennayar.get() == "true":
  6607                 (str,link) = c((str,link), luxFloatTexture("sigma", keyname, 0.0, 0.0, 100.0, "sigma", "", mat, gui, level+1))
  6608             has_volume_options = 1
  6609             has_bump_options = 1
  6610             has_object_options = 1
  6611             has_emission_options = 1
  6612             has_compositing_options = 1
  6613         
  6614         if mattype.get() == "metal":
  6615             if gui: gui.newline("name:", 0, level+1)
  6616             metalname = luxProp(mat, kn+"metal.name", "")
  6617             metalnames = ["aluminium","amorphous carbon","silver","gold","copper"]
  6618             metals = [n for n in metalnames]
  6619 
  6620             if not(metalname.get() in metals):
  6621                 metals.append(metalname.get())
  6622             metallink = luxOption("name", metalname, metals, "name", "", gui, 1.88)
  6623             if gui: Draw.Button("...", evtLuxGui, gui.x, gui.y-gui.h, gui.h, gui.h, "click to select a nk file",lambda e,v:Window.FileSelector(lambda s:metalname.set(s), "Select nk file"))
  6624             link += luxstr(metallink)
  6625             if not metalname.get() in metalnames:
  6626                 link = link.replace('"string name"', '"string filename"')
  6627             anisotropic = luxProp(mat, kn+"metal.anisotropic", "false")
  6628             if gui:
  6629                 gui.newline("")
  6630                 Draw.Toggle("A", evtLuxGui, gui.x-gui.h, gui.y-gui.h, gui.h, gui.h, anisotropic.get()=="true", "anisotropic roughness", lambda e,v:anisotropic.set(["false","true"][bool(v)]))
  6631             if anisotropic.get()=="true":
  6632                 (str,link) = c((str,link), luxExponentTexture("uroughness", keyname, 0.002, 0.0, 1.0, "u-exponent", "", mat, gui, level+1))
  6633                 (str,link) = c((str,link), luxExponentTexture("vroughness", keyname, 0.002, 0.0, 1.0, "v-exponent", "", mat, gui, level+1))
  6634             else:
  6635                 (s, l) = luxExponentTexture("uroughness", keyname, 0.002, 0.0, 1.0, "exponent", "", mat, gui, level+1)
  6636                 (str,link) = c((str,link), (s, l))
  6637                 link += l.replace("uroughness", "vroughness", 1)
  6638             has_volume_options = 1
  6639             has_bump_options = 1
  6640             has_object_options = 1
  6641             has_emission_options = 1
  6642             has_compositing_options = 1
  6643             
  6644         if mattype.get() == "mirror":
  6645             (str,link) = c((str,link), luxSpectrumTexture("Kr", keyname, "1.0 1.0 1.0", 1.0, "reflection", "", mat, gui, level+1))
  6646             thinfilm = luxProp(mat, keyname+".thinfilm", "false")
  6647             luxCollapse("thinfilm", thinfilm, "Thin Film Coating", "Enable Thin Film Coating", gui, 2.0)
  6648             if thinfilm.get() == "true":
  6649                 (str,link) = c((str,link), luxFloatSliderTexture("film", keyname, 200.0, 1.0, 1500.0, "film", "thickness of film coating in nanometers", mat, gui, level+1))
  6650                 (str,link) = c((str,link), luxIORFloatTexture("filmindex", keyname, 1.5, 1.0, 6.0, "film IOR", "film coating index of refraction", mat, gui, level+1))
  6651             has_volume_options = 1
  6652             has_bump_options = 1
  6653             has_object_options = 1
  6654             has_emission_options = 1
  6655             has_compositing_options = 1
  6656             
  6657         if mattype.get() == "roughglass":
  6658             (str,link) = c((str,link), luxSpectrumTexture("Kr", keyname, "1.0 1.0 1.0", 1.0, "reflection", "", mat, gui, level+1))
  6659             (str,link) = c((str,link), luxSpectrumTexture("Kt", keyname, "1.0 1.0 1.0", 1.0, "transmission", "", mat, gui, level+1))
  6660             anisotropic = luxProp(mat, kn+"roughglass.anisotropic", "false")
  6661             if gui:
  6662                 gui.newline("")
  6663                 Draw.Toggle("A", evtLuxGui, gui.x-gui.h, gui.y-gui.h, gui.h, gui.h, anisotropic.get()=="true", "anisotropic roughness", lambda e,v:anisotropic.set(["false","true"][bool(v)]))
  6664             if anisotropic.get()=="true":
  6665                 (str,link) = c((str,link), luxExponentTexture("uroughness", keyname, 0.002, 0.0, 1.0, "u-exponent", "", mat, gui, level+1))
  6666                 (str,link) = c((str,link), luxExponentTexture("vroughness", keyname, 0.002, 0.0, 1.0, "v-exponent", "", mat, gui, level+1))
  6667             else:
  6668                 (s, l) = luxExponentTexture("uroughness", keyname, 0.002, 0.0, 1.0, "exponent", "", mat, gui, level+1)
  6669                 (str,link) = c((str,link), (s, l))
  6670                 link += l.replace("uroughness", "vroughness", 1)
  6671             (str,link) = c((str,link), luxIORFloatTexture("index", keyname, 1.5, 1.0, 6.0, "IOR", "", mat, gui, level+1))
  6672             chromadisp = luxProp(mat, keyname+".chromadisp", "false")
  6673             luxCollapse("chromadisp", chromadisp, "Dispersive Refraction", "Enable Chromatic Dispersion", gui, 2.0)
  6674             if chromadisp.get() == "true":
  6675                 (str,link) = c((str,link), luxCauchyBFloatTexture("cauchyb", keyname, 0.0, 0.0, 1.0, "cauchyb", "", mat, gui, level+1))
  6676             has_volume_options = 1
  6677             has_bump_options = 1
  6678             has_object_options = 1
  6679             has_emission_options = 1
  6680             has_compositing_options = 1
  6681             
  6682         if mattype.get() == "shinymetal":
  6683             (str,link) = c((str,link), luxSpectrumTexture("Kr", keyname, "1.0 1.0 1.0", 1.0, "reflection", "", mat, gui, level+1))
  6684             (str,link) = c((str,link), luxSpectrumTexture("Ks", keyname, "1.0 1.0 1.0", 1.0, "specular", "", mat, gui, level+1))
  6685             anisotropic = luxProp(mat, kn+"shinymetal.anisotropic", "false")
  6686             if gui:
  6687                 gui.newline("")
  6688                 Draw.Toggle("A", evtLuxGui, gui.x-gui.h, gui.y-gui.h, gui.h, gui.h, anisotropic.get()=="true", "anisotropic roughness", lambda e,v:anisotropic.set(["false","true"][bool(v)]))
  6689             if anisotropic.get()=="true":
  6690                 (str,link) = c((str,link), luxExponentTexture("uroughness", keyname, 0.002, 0.0, 1.0, "u-exponent", "", mat, gui, level+1))
  6691                 (str,link) = c((str,link), luxExponentTexture("vroughness", keyname, 0.002, 0.0, 1.0, "v-exponent", "", mat, gui, level+1))
  6692             else:
  6693                 (s, l) = luxExponentTexture("uroughness", keyname, 0.002, 0.0, 1.0, "exponent", "", mat, gui, level+1)
  6694                 (str,link) = c((str,link), (s, l))
  6695                 link += l.replace("uroughness", "vroughness", 1)
  6696 
  6697             thinfilm = luxProp(mat, keyname+".thinfilm", "false")
  6698             luxCollapse("thinfilm", thinfilm, "Thin Film Coating", "Enable Thin Film Coating", gui, 2.0)
  6699             if thinfilm.get() == "true":
  6700                 (str,link) = c((str,link), luxFloatSliderTexture("film", keyname, 200.0, 1.0, 1500.0, "film", "thickness of film coating in nanometers", mat, gui, level+1))
  6701                 (str,link) = c((str,link), luxIORFloatTexture("filmindex", keyname, 1.5, 1.0, 6.0, "film IOR", "film coating index of refraction", mat, gui, level+1))
  6702             has_volume_options = 1
  6703             has_bump_options = 1
  6704             has_object_options = 1
  6705             has_emission_options = 1
  6706             has_compositing_options = 1
  6707             
  6708         if mattype.get() in ("glossy", "glossy_lossy"):
  6709             (str,link) = c((str,link), luxSpectrumTexture("Kd", keyname, "1.0 1.0 1.0", 1.0, "diffuse", "", mat, gui, level+1))
  6710             useior = luxProp(mat, keyname+".useior", "false")
  6711             if gui:
  6712                 gui.newline("")
  6713                 Draw.Toggle("I", evtLuxGui, gui.x-gui.h, gui.y-gui.h, gui.h, gui.h, useior.get()=="true", "Use IOR/Reflective index input", lambda e,v:useior.set(["false","true"][bool(v)]))
  6714             if useior.get() == "true":
  6715                 (str,link) = c((str,link), luxIORFloatTexture("index", keyname, 1.5, 1.0, 50.0, "IOR", "", mat, gui, level+1))
  6716                 link += " \"color Ks\" [1.0 1.0 1.0]"    
  6717             else:
  6718                 (str,link) = c((str,link), luxSpectrumTexture("Ks", keyname, "1.0 1.0 1.0", 1.0, "specular", "", mat, gui, level+1))
  6719                 link += " \"float index\" [0.0]"    
  6720             anisotropic = luxProp(mat, kn+"glossy.anisotropic", "false")
  6721             if gui:
  6722                 gui.newline("")
  6723                 Draw.Toggle("A", evtLuxGui, gui.x-gui.h, gui.y-gui.h, gui.h, gui.h, anisotropic.get()=="true", "anisotropic roughness", lambda e,v:anisotropic.set(["false","true"][bool(v)]))
  6724             if anisotropic.get()=="true":
  6725                 (str,link) = c((str,link), luxExponentTexture("uroughness", keyname, 0.002, 0.0, 1.0, "u-exponent", "", mat, gui, level+1))
  6726                 (str,link) = c((str,link), luxExponentTexture("vroughness", keyname, 0.002, 0.0, 1.0, "v-exponent", "", mat, gui, level+1))
  6727             else:
  6728                 (s, l) = luxExponentTexture("uroughness", keyname, 0.002, 0.0, 1.0, "exponent", "", mat, gui, level+1)
  6729                 (str,link) = c((str,link), (s, l))
  6730                 link += l.replace("uroughness", "vroughness", 1)
  6731 
  6732             absorption = luxProp(mat, keyname+".useabsorption", "false")
  6733             luxCollapse("absorption", absorption, "Absorption", "Enable Coating Absorption", gui, 2.0)
  6734             if absorption.get() == "true":
  6735                 (str,link) = c((str,link), luxSpectrumTexture("Ka", keyname, "0.2 0.2 0.2", 1.0, "absorption", "", mat, gui, level+1))
  6736                 (str,link) = c((str,link), luxFloatTexture("d", keyname, 0.15, 0.0, 15.0, "depth", "", mat, gui, level+1))
  6737             has_volume_options = 1
  6738             has_bump_options = 1
  6739             has_object_options = 1
  6740             has_emission_options = 1
  6741             has_compositing_options = 1
  6742             
  6743         if mattype.get() == 'null':
  6744             has_emission_options = 1
  6745 
  6746         # Bump mapping options (common)
  6747         if (has_bump_options == 1):
  6748             usebump = luxProp(mat, keyname+".usebump", "false")
  6749             luxCollapse("usebump", usebump, "Bump Map", "Enable Bump Mapping options", gui, 2.0)
  6750             if usebump.get() == "true":
  6751                 (str,link) = c((str,link), luxFloatTexture("bumpmap", keyname, 0.0, -1.0, 1.0, "bumpmap", "bumpmap scale in meters - i.e. 0.01 = 1 cm", mat, gui, level+1))
  6752 
  6753         # volume options (common)
  6754         if has_volume_options == 1 and level == 0:
  6755             for volume_prop in ['Exterior', 'Interior']:
  6756                 volume_used = luxProp(mat, '%s_vol_used'%(volume_prop), 'false')
  6757                 if gui: gui.newline('', 2, level, None, [0.4,0.4,0.6])
  6758                 luxCollapse('%s_vol_used'%(volume_prop), volume_used, "%s Medium"%(volume_prop), "%s medium settings"%(volume_prop), gui, 2.0)
  6759                 if volume_used.get() == "true":
  6760                     luxNamedVolume(mat, volume_prop, gui)
  6761 
  6762         # emission options (common)
  6763         if (level == 0):
  6764             if (has_emission_options == 1):
  6765                 if gui: gui.newline("", 2, level, None, [0.6,0.6,0.4])
  6766                 useemission = luxProp(mat, "emission", "false")
  6767                 luxCollapse("useemission", useemission, "Emission", "Enable emission options", gui, 2.0)
  6768                 if useemission.get() == "true":
  6769                     # emission GUI is here but lux export will be done later 
  6770                     luxLight("", "", mat, gui, level)
  6771             else: luxProp(mat, "emission", "false").set("false") # prevent from exporting later
  6772 
  6773 
  6774         # Compositing options (common)
  6775         # Note - currently only display options when using distributedpath integrator
  6776         integratortype = luxProp(Scene.GetCurrent(), "sintegrator.type", "bidirectional")
  6777         if (integratortype.get() == "distributedpath" and level == 0):
  6778             if (has_compositing_options == 1):
  6779                 if gui: gui.newline("", 2, level, None, [0.4,0.4,0.6])
  6780                 usecompo = luxProp(mat, "compo", "false")
  6781                 luxCollapse("compo", usecompo, "Compositing", "Enable Compositing options", gui, 2.0)
  6782                 if usecompo.get() == "true":
  6783                     if gui: gui.newline("", 2, level, None, [0.35,0.35,0.55])
  6784                     usecompoviz = luxProp(mat, "compo_viz", "false")
  6785                     luxCollapse("compo_viz", usecompoviz, "Visibility", "Enable Visibility Compositing options", gui, 2.0)
  6786                     if usecompoviz.get() == "true":
  6787                         if gui: gui.newline("View", 2, level, None, [0.35,0.35,0.55])
  6788                         compovizmat = luxProp(mat, "compo_viz_mat", "true")
  6789                         link += luxBool("compo_visible_material", compovizmat, "Material", "Enable View Visibility of Material", gui, 1.0)
  6790                         compovizemi = luxProp(mat, "compo_viz_emi", "true")
  6791                         link += luxBool("compo_visible_emission", compovizemi, "Emission", "Enable View Visibility of Emission", gui, 1.0)
  6792                         
  6793                         if gui: gui.newline("Indirect", 2, level, None, [0.35,0.35,0.55])
  6794                         compovizmati = luxProp(mat, "compo_viz_mati", "true")
  6795                         link += luxBool("compo_visible_indirect_material", compovizmati, "Material", "Enable InDirect Visibility of Material", gui, 1.0)
  6796                         compovizemii = luxProp(mat, "compo_viz_emii", "true")
  6797                         link += luxBool("compo_visible_indirect_emission", compovizemii, "Emission", "Enable InDirect Visibility of Emission", gui, 1.0)
  6798                     
  6799                     if gui: gui.newline("", 2, level, None, [0.4,0.4,0.6])
  6800                     overridealpha = luxProp(mat, "compo_o_alpha", "false")
  6801                     link += luxCollapse("compo_override_alpha", overridealpha, "Override Alpha", "Enable Manual control of alpha value", gui, 2.0)
  6802                     if overridealpha.get() == "true":
  6803                         if gui: gui.newline("Alpha", 2, level, None, [0.4,0.4,0.6])
  6804                         link += luxFloat("compo_override_alpha_value", luxProp(mat, "compo_o_alpha_v", 0.0), 0.0, 1.0, "Alpha", "Alpha Value", gui, 2.0, 1)
  6805                     usecolorkey = luxProp(mat, "compo_usekey", "false")
  6806                     if gui: gui.newline("", 2, level, None, [0.35,0.35,0.55])
  6807                     link += luxCollapse("compo_use_key", usecolorkey, "Chroma Key", "Enable Chroma Object key", gui, 2.0)
  6808                     if usecolorkey.get() == "true":
  6809                         if gui: gui.newline("Key", 2, level, None, [0.35,0.35,0.55])
  6810                         link += luxRGB("compo_key_color", luxProp(mat, "compo_key_color", "0.0 0.0 1.0"), 1.0, "key", "", gui, 2.0)
  6811 
  6812         # transformation options (common)
  6813         if (level == 0) and mattype.get() not in ['portal', 'null']:
  6814             if gui: gui.newline("", 2, level, None, [0.6,0.6,0.4])
  6815             usetransformation = luxProp(mat, "transformation", "false")
  6816             luxCollapse("usetransformation", usetransformation, "Texture Transformation", "Enable transformation option", gui, 2.0)
  6817             if usetransformation.get() == "true":
  6818                 scale = luxProp(mat, "3dscale", 1.0)
  6819                 rotate = luxProp(mat, "3drotate", "0 0 0")
  6820                 translate = luxProp(mat, "3dtranslate", "0 0 0")
  6821                 if gui:
  6822                     gui.newline("scale:", -2, level, icon_map3dparam)
  6823                     luxVectorUniform("scale", scale, 0.001, 1000.0, "scale", "scale-vector", gui, 2.0)
  6824                     gui.newline("rot:", -2, level, icon_map3dparam)
  6825                     luxVector("rotate", rotate, -360.0, 360.0, "rotate", "rotate-vector", gui, 2.0)
  6826                     gui.newline("move:", -2, level, icon_map3dparam)
  6827                     luxVector("translate", translate, -1000.0, 1000.0, "move", "translate-vector", gui, 2.0)
  6828                 str = ("TransformBegin\n\tScale %f %f %f\n"%( 1.0/scale.getVector()[0],1.0/scale.getVector()[1],1.0/scale.getVector()[2] ))+("\tRotate %f 1 0 0\n\tRotate %f 0 1 0\n\tRotate %f 0 0 1\n"%rotate.getVector())+("\tTranslate %f %f %f\n"%translate.getVector()) + str + "TransformEnd\n"
  6829 
  6830         # Object options (common)
  6831         if (level == 0) and (has_object_options == 1):
  6832             if gui: gui.newline("Mesh:", 2, level, icon, [0.6,0.6,0.4])
  6833             usesubdiv = luxProp(mat, "subdiv", "false")
  6834             luxBool("usesubdiv", usesubdiv, "Subdivision", "Enable Loop Subdivision options", gui, 1.0)
  6835             usedisp = luxProp(mat, "dispmap", "false")
  6836             luxBool("usedisp", usedisp, "Displacement Map", "Enable Displacement mapping options", gui, 1.0)
  6837             if usesubdiv.get() == "true" or usedisp.get() == "true":
  6838                 luxInt("sublevels", luxProp(mat, "sublevels", 2), 0, 12, "sublevels", "The number of levels of object subdivision", gui, 2.0)
  6839                 sharpbound = luxProp(mat, "sharpbound", "false")
  6840                 luxBool("sharpbound", sharpbound, "Sharpen Bounds", "Sharpen boundaries during subdivision", gui, 1.0)
  6841                 nsmooth = luxProp(mat, "nsmooth", "true")
  6842                 luxBool("nsmooth", nsmooth, "Smooth", "Smooth faces during subdivision", gui, 1.0)
  6843             if usedisp.get() == "true":
  6844                 (str,ll) = c((str,link), luxDispFloatTexture("dispmap", keyname, 0.1, -10, 10.0, "dispmap", "Displacement Mapping amount", mat, gui, level+1))
  6845                 luxFloat("sdoffset",  luxProp(mat, "sdoffset", 0.0), 0.0, 1.0, "Offset", "Offset for displacement map", gui, 2.0)
  6846                 usesubdiv.set("true")
  6847             if gui: gui.newline('Hair:', 2, level, None, [0.6,0.6,0.4])
  6848             luxFloat('hair_thickness',  luxProp(mat, 'hair_thickness', 0.5), 0.001, 100.0, 'hair thickness', 'Hair strand diameter', gui, 1.5)
  6849             luxScaleUnits('hair_thickness', 'mm', mat, 0.5, gui)
  6850 
  6851         if mattype.get() == "light":
  6852             return (str, link)
  6853 
  6854         str += "MakeNamedMaterial \"%s\"%s\n"%(matname, link)
  6855     return (str, " \"string %s\" [\"%s\"]"%(luxname, matname))
  6856 
  6857 
  6858 def luxMaterial(mat, gui=None):
  6859     str = ""
  6860     if mat:
  6861         if luxProp(mat, "type", "").get()=="": # lux material not defined yet
  6862             print("Blender material \"%s\" has no LuxRender material definition, converting..."%(mat.getName()))
  6863             try:
  6864                 convertMaterial(mat) # try converting the blender material to a lux material
  6865             except: pass
  6866         (str, link) = luxMaterialBlock("", "", "", mat, gui, 0)
  6867         if luxProp(mat, "type", "matte").get() != "light":
  6868             link = "NamedMaterial \"%s\""%(mat.getName())
  6869             # volume properties
  6870             for volume_prop in ['Exterior', 'Interior']:
  6871                 if luxProp(mat, '%s_vol_used'%(volume_prop), 'false').get() == 'true':
  6872                     link += luxNamedVolume(mat, volume_prop)
  6873         # export emission options (no gui)
  6874         useemission = luxProp(mat, "emission", "false")
  6875         if useemission.get() == "true":
  6876             lightgroup = luxProp(mat, "light.lightgroup", "default")
  6877             if luxProp(Scene.GetCurrent(), "lightgroup.disable."+lightgroup.get(), "false").get() == "true":
  6878                 # skip export of emission component if lightgroup is disabled
  6879                 pass
  6880             else:
  6881                 if luxProp(Scene.GetCurrent(), "nolg", "false").get()!="true":
  6882                     link += "\n\tLightGroup \"%s\"\n"%lightgroup.get()
  6883                 
  6884                 (estr, elink) = luxLight("", "", mat, None, 0)
  6885                 str += estr
  6886                 link += "\n\tAreaLightSource \"area\" "+elink 
  6887             
  6888         luxProp(mat, "link", "").set("".join(link))
  6889         
  6890     return str
  6891         
  6892 
  6893 def luxVolume(mat, gui=None):
  6894     str = ""
  6895     if mat:
  6896         (str, link) = luxMaterialBlock("", "", "", mat, gui, 0)
  6897         luxProp(mat, "link", "").set("".join(link))
  6898     return str
  6899 
  6900 runRenderAfterExport = None
  6901 def CBluxExport(default, run):
  6902     global runRenderAfterExport
  6903     runRenderAfterExport = run
  6904     if default:
  6905         datadir = luxProp(Scene.GetCurrent(), "datadir", "").get()
  6906         if datadir=="": datadir = Blender.Get("datadir")
  6907         import os.path
  6908         if not os.path.exists(datadir):
  6909             Draw.PupMenu("ERROR: output directory does not exist!")
  6910             if LuxIsGUI:
  6911                 Draw.Redraw()
  6912             return
  6913         filename = datadir + os.sep + "default.lxs"
  6914         save_still(filename)
  6915     else:
  6916         Window.FileSelector(save_still, "Export", sys.makename(Blender.Get("filename"), ".lxs"))
  6917 
  6918 
  6919 def CBluxAnimExport(default, run, fileselect=True):
  6920     if default:
  6921         datadir = luxProp(Scene.GetCurrent(), "datadir", "").get()
  6922         if datadir=="": datadir = Blender.Get("datadir")
  6923         import os.path
  6924         if not os.path.exists(datadir):
  6925             Draw.PupMenu("ERROR: output directory does not exist!")
  6926             if LuxIsGUI:
  6927                 Draw.Redraw()
  6928             return
  6929         filename = datadir + os.sep + "default.lxs"
  6930         save_anim(filename)
  6931     else:
  6932         if fileselect:
  6933             Window.FileSelector(save_anim, "Export", sys.makename(Blender.Get("filename"), ".lxs"))
  6934         else:
  6935             datadir = luxProp(Scene.GetCurrent(), "datadir", "").get()
  6936             if datadir=="": datadir = Blender.Get("datadir")
  6937             filename = sys.makename(Blender.Get("filename") , ".lxs")
  6938             save_anim(filename)
  6939 
  6940 
  6941 # convert a Blender material to lux material
  6942 def convertMaterial(mat):
  6943     def dot(str):
  6944         if str != "": return str+"."
  6945         return str
  6946     def ddot(str):
  6947         if str != "": return str+":"
  6948         return str
  6949     def mapConstDict(value, constant_dict, lux_dict, default=None):
  6950         for k,v in constant_dict.items():
  6951             if (v == value) and (lux_dict.has_key(k)):
  6952                 return lux_dict[k]
  6953         return default
  6954     def getTexFlags(value, constant_dict):
  6955         constant_dict = sorted(constant_dict, lambda x,y: cmp(y[1], x[1]))
  6956         flags_dict = []
  6957         for f, v in constant_dict:
  6958             if value < v:
  6959                 continue
  6960             else:
  6961                 value = value-v
  6962                 flags_dict.append(f)
  6963         return flags_dict
  6964     
  6965     def convertMapping(name, tex):
  6966         if tex.texco == Texture.TexCo["UV"]:
  6967             luxProp(mat, dot(name)+"mapping","").set("uv")
  6968             luxProp(mat, dot(name)+"uscale", 1.0).set(tex.size[0])
  6969             luxProp(mat, dot(name)+"vscale", 1.0).set(-tex.size[1])
  6970             luxProp(mat, dot(name)+"udelta", 0.0).set(tex.ofs[0]+0.5*(1.0-tex.size[0]))
  6971             luxProp(mat, dot(name)+"vdelta", 0.0).set(-tex.ofs[1]+1-(0.5*(1.0-tex.size[1])))
  6972             if tex.mapping != Texture.Mappings["FLAT"]:
  6973                 print("Material Conversion Warning: for UV-texture-input only FLAT mapping is supported\n") 
  6974         else:
  6975             if tex.mapping == Texture.Mappings["FLAT"]:
  6976                 luxProp(mat, dot(name)+"mapping","").set("planar") # make planar-mapping convert correctly from blender(WYSIWYG)- jens
  6977                 luxProp(mat, dot(name)+"v1", "1.0 1.0 1.0").setVector((0.5*tex.size[0], 0.0, 0.0))
  6978                 luxProp(mat, dot(name)+"v2", "0.0 0.0 0.0").setVector((0.0, -0.5*tex.size[1], -0.0))
  6979                 luxProp(mat, dot(name)+"udelta", 0.0).set(tex.ofs[0]+0.5)
  6980                 luxProp(mat, dot(name)+"vdelta", 0.0).set(-tex.ofs[1]-0.5)
  6981             elif tex.mapping == Texture.Mappings["TUBE"]:
  6982                 luxProp(mat, dot(name)+"mapping","").set("cylindrical")
  6983             elif tex.mapping == Texture.Mappings["SPHERE"]:
  6984                 luxProp(mat, dot(name)+"mapping","").set("spherical")
  6985             else: luxProp(mat, dot(name)+"mapping","").set("planar")
  6986 
  6987         luxProp(mat, dot(name)+"3dscale", "1.0 1.0 1.0").setVector((1.0/tex.size[0], 1.0/tex.size[1], 1.0/tex.size[2]))
  6988         luxProp(mat, dot(name)+"3dtranslate", "0.0 0.0 0.0").setVector((-tex.ofs[0], -tex.ofs[1], -tex.ofs[2]))
  6989 
  6990     def convertColorband(colorband):
  6991         # colorbands are not supported in lux - so lets extract a average low-side and high-side color
  6992         cb = [colorband[0]] + colorband[:] + [colorband[-1]]
  6993         cb[0][4], cb[-1][4] = 0.0, 1.0
  6994         low, high = [0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]
  6995         for i in range(1, len(cb)):
  6996             for c in range(4):
  6997                 low[c] += (cb[i-1][c]*(1.0-cb[i-1][4]) + cb[i][c]*(1.0-cb[i][4])) * (cb[i][4]-cb[i-1][4])
  6998                 high[c] += (cb[i-1][c]*cb[i-1][4] + cb[i][c]*cb[i][4]) * (cb[i][4]-cb[i-1][4])
  6999         return low, high
  7000 
  7001     def createLuxTexture(name, tex):
  7002         texture = tex.tex
  7003         convertMapping(name, tex)
  7004         if (texture.type == Texture.Types["IMAGE"]) and (texture.image) and (texture.image.filename!=""):
  7005             luxProp(mat, dot(name)+"texture", "").set("imagemap")
  7006             luxProp(mat, dot(name)+"filename", "").set(texture.image.filename)
  7007             luxProp(mat, dot(name)+"wrap", "").set(mapConstDict(texture.extend, Texture.ExtendModes, {"REPEAT":"repeat", "EXTEND":"clamp", "CLIP":"black"}, ""))
  7008         else:
  7009             if tex.texco != Texture.TexCo["GLOB"]:
  7010                 print("Material Conversion Warning: procedural textures supports global mapping only\n")
  7011             noiseDict = {"BLENDER":"blender_original", "CELLNOISE":"cell_noise", "IMPROVEDPERLIN":"improved_perlin", "PERLIN":"original_perlin", "VORONOICRACKLE":"voronoi_crackle", "VORONOIF1":"voronoi_f1", "VORONOIF2":"voronoi_f2", "VORONOIF2F1":"voronoi_f2f1", "VORONOIF3":"voronoi_f3", "VORONOIF4":"voronoi_f4"}
  7012             luxProp(mat, dot(name)+"bright", 1.0).set(texture.brightness)
  7013             luxProp(mat, dot(name)+"contrast", 1.0).set(texture.contrast)
  7014             if texture.type == Texture.Types["CLOUDS"]:
  7015                 luxProp(mat, dot(name)+"texture", "").set("blender_clouds")
  7016                 luxProp(mat, dot(name)+"mtype", "").set(mapConstDict(texture.stype, Texture.STypes, {"CLD_DEFAULT":"default", "CLD_COLOR":"color"}, ""))
  7017                 luxProp(mat, dot(name)+"noisetype", "").set({"soft":"soft_noise", "hard":"hard_noise"}[texture.noiseType])
  7018                 luxProp(mat, dot(name)+"noisesize", 0.25).set(texture.noiseSize)
  7019                 luxProp(mat, dot(name)+"noisedepth", 2).set(texture.noiseDepth)
  7020                 luxProp(mat, dot(name)+"noisebasis", "").set(mapConstDict(texture.noiseBasis, Texture.Noise, noiseDict, ""))
  7021             elif texture.type == Texture.Types["WOOD"]:
  7022                 luxProp(mat, dot(name)+"texture", "").set("blender_wood")
  7023                 luxProp(mat, dot(name)+"mtype", "").set(mapConstDict(texture.stype, Texture.STypes, {"WOD_BANDS":"bands", "WOD_RINGS":"rings", "WOD_BANDNOISE":"bandnoise", "WOD_RINGNOISE":"ringnoise"}, ""))
  7024                 luxProp(mat, dot(name)+"noisebasis2", "").set(mapConstDict(texture.noiseBasis2, Texture.Noise, {"SINE":"sin", "SAW":"saw", "TRI":"tri"}, ""))
  7025                 luxProp(mat, dot(name)+"noisebasis", "").set(mapConstDict(texture.noiseBasis, Texture.Noise, noiseDict, ""))
  7026                 luxProp(mat, dot(name)+"noisetype", "").set({"soft":"soft_noise", "hard":"hard_noise"}[texture.noiseType])
  7027                 luxProp(mat, dot(name)+"noisesize", 0.25).set(texture.noiseSize)
  7028                 luxProp(mat, dot(name)+"turbulence", 0.25).set(texture.turbulence)
  7029             elif texture.type == Texture.Types["MUSGRAVE"]:
  7030                 luxProp(mat, dot(name)+"texture", "").set("blender_musgrave")
  7031                 luxProp(mat, dot(name)+"mtype", "").set(mapConstDict(texture.stype, Texture.STypes, {"MUS_MFRACTAL":"multifractal", "MUS_RIDGEDMF":"ridged_multifractal", "MUS_HYBRIDMF":"hybrid_multifractal", "MUS_HTERRAIN":"hetero_terrain", "MUS_FBM":"fbm"}, ""))
  7032                 luxProp(mat, dot(name)+"noisebasis", "").set(mapConstDict(texture.noiseBasis, Texture.Noise, noiseDict, ""))
  7033                 luxProp(mat, dot(name)+"noisesize", 0.25).set(texture.noiseSize)
  7034                 # bug in blender python API: value of "hFracDim" is casted to Integer instead of Float (reported to Ideasman42 - will be fixed after Blender 2.47)
  7035                 if texture.hFracDim != 0.0: luxProp(mat, dot(name)+"h", 1.0).set(texture.hFracDim) # bug in blender API, "texture.hFracDim" returns a Int instead of a Float
  7036                 else: luxProp(mat, dot(name)+"h", 1.0).set(0.5) # use a default value
  7037                 # bug in blender python API: values "offset" and "gain" are missing in Python-API (reported to Ideasman42 - will be fixed after Blender 2.47)
  7038                 try:
  7039                     luxProp(mat, dot(name)+"offset", 1.0).set(texture.offset)
  7040                     luxProp(mat, dot(name)+"gain", 1.0).set(texture.gain)
  7041                 except AttributeError: pass
  7042                 luxProp(mat, dot(name)+"lacu", 2.0).set(texture.lacunarity)
  7043                 luxProp(mat, dot(name)+"octs", 2.0).set(texture.octs)
  7044                 luxProp(mat, dot(name)+"outscale", 1.0).set(texture.iScale)
  7045             elif texture.type == Texture.Types["MARBLE"]:
  7046                 luxProp(mat, dot(name)+"texture", "").set("blender_marble")
  7047                 luxProp(mat, dot(name)+"mtype", "").set(mapConstDict(texture.stype, Texture.STypes, {"MBL_SOFT":"soft", "MBL_SHARP":"sharp", "MBL_SHARPER":"sharper"}, ""))
  7048                 luxProp(mat, dot(name)+"noisetype", "").set({"soft":"soft_noise", "hard":"hard_noise"}[texture.noiseType])
  7049                 luxProp(mat, dot(name)+"turbulence", 0.25).set(texture.turbulence)
  7050                 luxProp(mat, dot(name)+"noisedepth", 2).set(texture.noiseDepth)
  7051                 luxProp(mat, dot(name)+"noisebasis", "").set(mapConstDict(texture.noiseBasis, Texture.Noise, noiseDict, ""))
  7052                 luxProp(mat, dot(name)+"noisebasis2", "").set(mapConstDict(texture.noiseBasis2, Texture.Noise, {"SINE":"sin", "SAW":"saw", "TRI":"tri"}, ""))
  7053                 luxProp(mat, dot(name)+"noisesize", 0.25).set(texture.noiseSize)
  7054             elif texture.type == Texture.Types["VORONOI"]:
  7055                 luxProp(mat, dot(name)+"texture", "").set("blender_voronoi")
  7056                 luxProp(mat, dot(name)+"distmetric", "").set({0:"actual_distance", 1:"distance_squared", 2:"manhattan", 3:"chebychev", 4:"minkovsky_half", 5:"minkovsky_four", 6:"minkovsky"}[texture.distMetric])
  7057                 luxProp(mat, dot(name)+"outscale", 1.0).set(texture.iScale)
  7058                 luxProp(mat, dot(name)+"noisesize", 0.25).set(texture.noiseSize)
  7059                 luxProp(mat, dot(name)+"minkosky_exp", 2.5).set(texture.exp)
  7060                 luxProp(mat, dot(name)+"w1", 1.0).set(texture.weight1)
  7061                 luxProp(mat, dot(name)+"w2", 0.0).set(texture.weight2)
  7062                 luxProp(mat, dot(name)+"w3", 0.0).set(texture.weight3)
  7063                 luxProp(mat, dot(name)+"w4", 0.0).set(texture.weight4)
  7064             elif texture.type == Texture.Types["NOISE"]:
  7065                 luxProp(mat, dot(name)+"texture", "").set("blender_noise")
  7066             elif texture.type == Texture.Types["DISTNOISE"]:
  7067                 luxProp(mat, dot(name)+"texture", "").set("blender_distortednoise")
  7068                 luxProp(mat, dot(name)+"distamount", 1.0).set(texture.distAmnt)
  7069                 luxProp(mat, dot(name)+"noisesize", 0.25).set(texture.noiseSize)
  7070                 luxProp(mat, dot(name)+"type", "").set(mapConstDict(texture.noiseBasis, Texture.Noise, noiseDict, ""))
  7071                 luxProp(mat, dot(name)+"noisebasis", "").set(mapConstDict(texture.noiseBasis2, Texture.Noise, noiseDict, ""))
  7072             elif texture.type == Texture.Types["MAGIC"]:
  7073                 luxProp(mat, dot(name)+"texture", "").set("blender_magic")
  7074                 luxProp(mat, dot(name)+"turbulence", 0.25).set(texture.turbulence)
  7075                 luxProp(mat, dot(name)+"noisedepth", 2).set(texture.noiseDepth)
  7076             elif texture.type == Texture.Types["STUCCI"]:
  7077                 luxProp(mat, dot(name)+"texture", "").set("blender_stucci")
  7078                 luxProp(mat, dot(name)+"mtype", "").set(mapConstDict(texture.stype, Texture.STypes, {"STC_PLASTIC":"Plastic", "MSTC_WALLIN":"Wall In", "STC_WALLOUT":"Wall Out"}, ""))
  7079                 luxProp(mat, dot(name)+"noisetype", "").set({"soft":"soft_noise", "hard":"hard_noise"}[texture.noiseType])
  7080                 luxProp(mat, dot(name)+"noisesize", 0.25).set(texture.noiseSize)
  7081                 luxProp(mat, dot(name)+"turbulence", 0.25).set(texture.turbulence)
  7082                 luxProp(mat, dot(name)+"noisebasis", "").set(mapConstDict(texture.noiseBasis, Texture.Noise, noiseDict, ""))
  7083             elif texture.type == Texture.Types["BLEND"]:
  7084                 luxProp(mat, dot(name)+"texture", "").set("blender_blend")
  7085                 luxProp(mat, dot(name)+"mtype", "").set(mapConstDict(texture.stype, Texture.STypes, {"BLN_LIN":"lin", "BLN_QUAD":"quad", "BLN_EASE":"ease", "BLN_DIAG":"diag", "BLN_SPHERE":"sphere", "BLN_HALO":"halo", "BLN_RADIAL":"radial"}, ""))
  7086                 luxProp(mat, dot(name)+"flag", "false").set(str('FLIPBLEND' in getTexFlags(texture.flags, Texture.Flags.items())).lower())
  7087             else:
  7088                 print("Material Conversion Warning: SORRY, this procedural texture isn\'t implemented in conversion\n")
  7089 
  7090     def convertTextures(basename, texs, type="float", channel="col", val=1.0):
  7091         tex = texs.pop()
  7092         texture = tex.tex
  7093         isImagemap = (texture.type == Texture.Types["IMAGE"]) and (texture.image) and (texture.image.filename!="")
  7094         if channel == "col":
  7095             if texture.flags & Texture.Flags["COLORBAND"] > 0:
  7096                 cbLow, cbHigh = convertColorband(texture.colorband)
  7097                 val1, alpha1, val2, alpha2 = (cbLow[0],cbLow[1],cbLow[2]), cbLow[3]*tex.colfac, (cbHigh[0], cbHigh[1], cbHigh[2]), cbHigh[3]*tex.colfac
  7098                 if tex.noRGB:
  7099                     lum1, lum2 = (val1[0]+val1[1]+val1[2])/3.0, (val2[0]+val2[1]+val2[2])/3.0
  7100                     val1, val2 = (tex.col[0]*lum1,tex.col[1]*lum1,tex.col[2]*lum1), (tex.col[0]*lum2,tex.col[1]*lum2,tex.col[2]*lum2)
  7101             elif isImagemap and not(tex.noRGB): val1, alpha1, val2, alpha2 = (0.0,0.0,0.0), tex.colfac, (1.0,1.0,1.0), tex.colfac
  7102             else: val1, alpha1, val2, alpha2 = tex.col, 0.0, tex.col, tex.colfac
  7103         elif channel == "nor": val1, alpha1, val2, alpha2 = tex.norfac * 0.01, 0.0, tex.norfac * 0.01, 1.0
  7104         else: val1, alpha1, val2, alpha2 = 1.0, 0.0, 1.0, tex.varfac
  7105         if (tex.neg)^((channel=="nor") and (tex.mtNor<0)): val1, alpha1, val2, alpha2 = val2, alpha2, val1, alpha1
  7106         luxProp(mat, dot(basename)+"textured", "").set("true")
  7107 
  7108         name = basename
  7109         if (alpha1 < 1.0) or (alpha2 < 1.0): # texture with transparency
  7110             luxProp(mat, dot(basename)+"texture", "").set("mix")
  7111             if alpha1 == alpha2: # constant alpha
  7112                 luxProp(mat, ddot(basename)+"amount.value", 1.0).set(alpha1)
  7113             else:
  7114                 createLuxTexture(ddot(basename)+"amount", tex)
  7115                 luxProp(mat, ddot(basename)+"amount:tex1.value", 1.0).set(alpha1)
  7116                 luxProp(mat, ddot(basename)+"amount:tex2.value", 1.0).set(alpha2)
  7117             # transparent to next texture
  7118             name = ddot(basename)+"tex1"
  7119             if len(texs) > 0:
  7120                 convertTextures(ddot(basename)+"tex1", texs, type, channel, val)
  7121             else:
  7122                 if type=="float": luxProp(mat, ddot(basename)+"tex1.value", 1.0).set(val)
  7123                 else: luxProp(mat, ddot(basename)+"tex1.value", "1.0 1.0 1.0").setRGB((val[0], val[1], val[2]))
  7124             name = ddot(basename)+"tex2"
  7125         if val1 == val2: # texture with different colors / value
  7126             if type == "col": luxProp(mat, dot(name)+"value", "1.0 1.0 1.0").setRGB(val1)
  7127             else: luxProp(mat, dot(name)+"value", 1.0).set(val1)
  7128         else:
  7129             createLuxTexture(name, tex)
  7130             if type == "col": luxProp(mat, ddot(name)+"tex1.value", "1.0 1.0 1.0").setRGB(val1)
  7131             else: luxProp(mat, ddot(name)+"tex1.value", 1.0).set(val1)
  7132             if type == "col": luxProp(mat, ddot(name)+"tex2.value", "1.0 1.0 1.0").setRGB(val2)
  7133             else: luxProp(mat, ddot(name)+"tex2.value", 1.0).set(val2)
  7134 
  7135 
  7136     def convertDiffuseTexture(name):
  7137         texs = []
  7138         for tex in mat.getTextures():
  7139             if tex and (tex.mapto & Texture.MapTo["COL"] > 0) and (tex.tex) and (tex.tex.type != Texture.Types["NONE"]): texs.append(tex)
  7140         if len(texs) > 0:
  7141             luxProp(mat, name, "").setRGB((mat.ref, mat.ref, mat.ref))
  7142             convertTextures(name, texs, "col", "col", (mat.R, mat.G, mat.B))
  7143     def convertSpecularTexture(name):
  7144         texs = []
  7145         for tex in mat.getTextures():
  7146             if tex and (tex.mapto & Texture.MapTo["CSP"] > 0) and (tex.tex) and (tex.tex.type != Texture.Types["NONE"]): texs.append(tex)
  7147         if len(texs) > 0:
  7148             luxProp(mat, name, "").setRGB((mat.ref*mat.spec, mat.ref*mat.spec, mat.ref*mat.spec))
  7149             convertTextures(name, texs, "col", "col", (mat.specR, mat.specG, mat.specB))
  7150     def convertMirrorTexture(name):
  7151         texs = []
  7152         for tex in mat.getTextures():
  7153             if tex and (tex.mapto & Texture.MapTo["CMIR"] > 0) and (tex.tex) and (tex.tex.type != Texture.Types["NONE"]): texs.append(tex)
  7154         if len(texs) > 0:
  7155             luxProp(mat, name, "").setRGB((mat.ref, mat.ref, mat.ref))
  7156             convertTextures(name, texs, "col", "col", (mat.mirR, mat.mirG, mat.mirB))
  7157     def convertBumpTexture(basename):
  7158         texs = []
  7159         for tex in mat.getTextures():
  7160             if tex and (tex.mapto & Texture.MapTo["NOR"] > 0) and (tex.tex) and (tex.tex.type != Texture.Types["NONE"]): texs.append(tex)
  7161         if len(texs) > 0:
  7162             name = basename+":bumpmap"
  7163             luxProp(mat, basename+".usebump", "").set("true")
  7164             luxProp(mat, dot(name)+"textured", "").set("true")
  7165             luxProp(mat, name, "").set(1.0)
  7166             convertTextures(name, texs, "float", "nor", 0.0)
  7167 
  7168     def makeMatte(name):
  7169         luxProp(mat, dot(name)+"type", "").set("matte")
  7170         luxProp(mat, name+":Kd", "").setRGB((mat.R*mat.ref, mat.G*mat.ref, mat.B*mat.ref))
  7171         convertDiffuseTexture(name+":Kd")
  7172         convertBumpTexture(name)
  7173     def makeGlossy(name, roughness):
  7174         luxProp(mat, dot(name)+"type", "").set("glossy")
  7175         luxProp(mat, name+":Kd", "").setRGB((mat.R*mat.ref, mat.G*mat.ref, mat.B*mat.ref))
  7176         luxProp(mat, name+":Ks", "").setRGB((mat.specR*mat.spec*0.5, mat.specG*mat.spec*0.5, mat.specB*mat.spec*0.5))
  7177         luxProp(mat, name+":uroughness", 0.0).set(roughness)
  7178         luxProp(mat, name+":vroughness", 0.0).set(roughness)
  7179         convertDiffuseTexture(name+":Kd")
  7180         convertSpecularTexture(name+":Ks")
  7181         convertBumpTexture(name)
  7182     def makeMirror(name):
  7183         luxProp(mat, dot(name)+"type", "").set("mirror")
  7184         luxProp(mat, name+":Kr", "").setRGB((mat.mirR, mat.mirG, mat.mirB))
  7185         convertMirrorTexture(name+":Kr")
  7186         convertBumpTexture(name)
  7187     def makeGlass(name):
  7188         luxProp(mat, dot(name)+"type", "").set("glass")
  7189         luxProp(mat, name+":Kr", "").setRGB((0.0, 0.0, 0.0))
  7190         luxProp(mat, name+":Kt", "").setRGB((mat.R, mat.G, mat.B))
  7191         luxProp(mat, name+":index.iorusepreset", "").set("false")
  7192         luxProp(mat, name+":index", 0.0).set(mat.getIOR())
  7193         convertMirrorTexture(name+":Kr")
  7194         convertDiffuseTexture(name+":Kt")
  7195         convertBumpTexture(name)
  7196     def makeRoughglass(name, roughness):
  7197         luxProp(mat, dot(name)+"type", "").set("roughglass")
  7198         luxProp(mat, name+":Kr", "").setRGB((0.0, 0.0, 0.0))
  7199         luxProp(mat, name+":Kt", "").setRGB((mat.R, mat.G, mat.B))
  7200         luxProp(mat, name+":index.iorusepreset", "").set("false")
  7201         luxProp(mat, name+":index", 0.0).set(mat.getIOR())
  7202         luxProp(mat, name+":uroughness", 0.0).set(roughness)
  7203         luxProp(mat, name+":vroughness", 0.0).set(roughness)
  7204         convertMirrorTexture(name+":Kr")
  7205         convertDiffuseTexture(name+":Kt")
  7206         convertBumpTexture(name)
  7207     print("convert Blender material \"%s\" to LuxRender material"%(mat.name))
  7208     mat.properties['luxblend'] = {}
  7209     if mat.emit > 0.0001:
  7210         luxProp(mat, "type", "").set("light")
  7211         luxProp(mat, "light.l", "").setRGB((mat.R, mat.G, mat.B))
  7212         luxProp(mat, "light.gain", 1.0).set(mat.emit)
  7213         return
  7214     alpha = mat.alpha
  7215     if not(mat.mode & Material.Modes.RAYTRANSP): alpha = 1.0
  7216     alpha0name, alpha1name = "", ""
  7217     if (alpha > 0.0) and (alpha < 1.0):
  7218         luxProp(mat, "type", "").set("mix")
  7219         luxProp(mat, ":amount", 0.0).set(alpha)
  7220         alpha0name, alpha1name = "mat2", "mat1"
  7221     if alpha > 0.0:
  7222         mirror = mat.rayMirr
  7223         if not(mat.mode & Material.Modes.RAYMIRROR): mirror = 0.0
  7224         mirror0name, mirror1name = alpha1name, alpha1name
  7225         if (mirror > 0.0) and (mirror < 1.0):
  7226             luxProp(mat, dot(alpha1name)+"type", "").set("mix")
  7227             luxProp(mat, alpha1name+":amount", 0.0).set(1.0 - mirror)
  7228             mirror0name, mirror1name = ddot(alpha1name)+"mat1", ddot(alpha1name)+"mat2"
  7229         if mirror > 0.0:
  7230             if mat.glossMir < 1.0: makeGlossy(mirror1name, 1.0-mat.glossMir)
  7231             else: makeMirror(mirror1name)
  7232         if mirror < 1.0:
  7233             if mat.spec > 0.0: makeGlossy(mirror0name, math.sqrt(2.0/(mat.hard+2.0)))
  7234             else: makeMatte(mirror0name)
  7235     if alpha < 1.0:
  7236         if mat.glossTra < 1.0: makeRoughnessGlass(alpha0name, 1.0-mat.glossTra**2)
  7237         else: makeGlass(alpha0name)
  7238 
  7239 def convertAllMaterials():
  7240     if Draw.PupMenu('  OK?%t|Are you sure to convert all materials, replacing the current LuxRender material definitions?%x1') == 1:
  7241         for mat in Material.Get(): convertMaterial(mat)
  7242 
  7243 
  7244 
  7245 
  7246 ### Connect LRMDB ###
  7247 ConnectLrmdb = False
  7248 try:
  7249     import socket  # try import of socket library
  7250     ConnectLrmdb = True
  7251     def downloadLRMDB(mat, id):
  7252         if id.isalnum():
  7253             DrawProgressBar(0.0,'Getting Material #'+id)
  7254             try:
  7255                 HOST = 'webserver' #'www.luxrender.net'
  7256                 GET = '/lrmdb/en/material/download/'+id+'/'+LBX_VERSION
  7257                 PORT = 80
  7258                 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  7259                 sock.connect((HOST, PORT))
  7260                 sock.send("GET %s HTTP/1.0\r\nHost: %s\r\n\r\n" % (GET, HOST))
  7261                 data = sock.recv(1024)
  7262                 str = ""
  7263                 while len(data):
  7264                     str += data
  7265                     data = sock.recv(1024)
  7266                 sock.close()
  7267                 if str.split("\n", 1)[0].find("200") < 0:
  7268                     print("ERROR: server error: %s"%(str.split("\n",1)[0]))
  7269                     return None
  7270                 str = (str.split("\r\n\r\n")[1]).strip()
  7271                 if (str[0]=="{") and (str[-1]=="}"):
  7272                     return str2MatTex(str)
  7273                 print("ERROR: downloaded data is not a material or texture:\n%s"%str)
  7274             except:
  7275                 print("ERROR: download failed")
  7276                 
  7277             DrawProgressBar(1.0,'')
  7278         else:
  7279             print("ERROR: material id is not valid")
  7280         return None
  7281     
  7282         
  7283     #===========================================================================
  7284     # COOKIETRANSPORT
  7285     #===========================================================================
  7286     
  7287     #--------------------------------------------------------------------------- 
  7288     # IMPORTS
  7289     import cookielib, urllib2, xmlrpclib
  7290     
  7291     #---------------------------------------------------------------------------
  7292     # pilfered from
  7293     # https://fedorahosted.org/python-bugzilla/browser/bugzilla.py?rev=e6f699f06e92b1e49b1b8d2c8fbe89d9425a4a9a
  7294     class CookieTransport(xmlrpclib.Transport):
  7295         '''
  7296         A subclass of xmlrpclib.Transport that supports cookies.
  7297         '''
  7298         
  7299         cookiejar = None
  7300         scheme = 'http'
  7301         verbose = None
  7302     
  7303         # Cribbed from xmlrpclib.Transport.send_user_agent 
  7304         def send_cookies(self, connection, cookie_request):
  7305             '''
  7306             Send all the cookie data that we have received
  7307             '''
  7308             
  7309             if self.cookiejar is None:
  7310                 self.cookiejar = cookielib.CookieJar()
  7311             elif self.cookiejar:
  7312                 # Let the cookiejar figure out what cookies are appropriate
  7313                 self.cookiejar.add_cookie_header(cookie_request)
  7314                 # Pull the cookie headers out of the request object...
  7315                 cookielist = list()
  7316                 for header, value in cookie_request.header_items():
  7317                     if header.startswith('Cookie'):
  7318                         cookielist.append([header, value])
  7319                 # ...and put them over the connection
  7320                 for header, value in cookielist:
  7321                     connection.putheader(header, value)
  7322     
  7323         # This is the same request() method from xmlrpclib.Transport,
  7324         # with a couple additions noted below
  7325         def request(self, host, handler, request_body, verbose=0):
  7326             '''
  7327             Handle the request
  7328             '''
  7329             
  7330             host_connection = self.make_connection(host)
  7331             if verbose:
  7332                 host_connection.set_debuglevel(1)
  7333     
  7334             # ADDED: construct the URL and Request object for proper cookie handling
  7335             request_url = "%s://%s/" % (self.scheme, host)
  7336             cookie_request  = urllib2.Request(request_url) 
  7337     
  7338             self.send_request(host_connection, handler, request_body)
  7339             self.send_host(host_connection, host) 
  7340             
  7341             # ADDED. creates cookiejar if None.
  7342             self.send_cookies(host_connection, cookie_request)
  7343             self.send_user_agent(host_connection)
  7344             self.send_content(host_connection, request_body)
  7345     
  7346             errcode, errmsg, headers = host_connection.getreply()
  7347     
  7348             # ADDED: parse headers and get cookies here
  7349             class CookieResponse:
  7350                 '''
  7351                 fake a response object that we can fill with the headers above
  7352                 '''
  7353                 
  7354                 def __init__(self, headers):
  7355                     self.headers = headers
  7356                     
  7357                 def info(self):
  7358                     return self.headers
  7359                 
  7360             cookie_response = CookieResponse(headers)
  7361             
  7362             # Okay, extract the cookies from the headers
  7363             self.cookiejar.extract_cookies(cookie_response, cookie_request)
  7364             
  7365             # And write back any changes
  7366             # DH THIS DOESN'T WORK
  7367             # self.cookiejar.save(self.cookiejar.filename)
  7368     
  7369             if errcode != 200:
  7370                 raise xmlrpclib.ProtocolError(
  7371                     host + handler,
  7372                     errcode, errmsg,
  7373                     headers
  7374                 )
  7375     
  7376             self.verbose = verbose
  7377     
  7378             try:
  7379                 sock = host_connection._conn.sock
  7380             except AttributeError:
  7381                 sock = None
  7382     
  7383             return self._parse_response(host_connection.getfile(), sock)
  7384     
  7385 
  7386     #===========================================================================
  7387     # LRMDB Integration
  7388     #===========================================================================
  7389     class lrmdb:
  7390         host              = 'http://webserver/lrmdb/ixr' #'http://www.luxrender.net/lrmdb/ixr'
  7391         
  7392         username          = ""
  7393         password          = ""
  7394         logged_in         = False
  7395         
  7396         SERVER            = None
  7397         
  7398         last_error_str    = None
  7399         
  7400         def last_error(self):
  7401             return self.last_error_str #'LRMDB Connector: %s' %
  7402         
  7403         def login(self):
  7404             try:
  7405                 result = self.SERVER.user.login(
  7406                     self.username,
  7407                     self.password
  7408                 )
  7409                 if not result:
  7410                     raise
  7411                 else:
  7412                     self.logged_in = True
  7413                     return True
  7414             except:
  7415                 self.last_error_str = 'Login Failed'
  7416                 self.logged_in = False
  7417                 return False
  7418             
  7419         def submit_object(self, mat, basekey, tex):
  7420             if not self.check_creds(): return False
  7421             
  7422             try:
  7423                 result = 'Unknown Error'
  7424                 
  7425                 if tex:
  7426                     name = Draw.PupStrInput('Name: ', '', 32)
  7427                 else:
  7428                     name = mat.name
  7429                 
  7430                 result = self.SERVER.object.submit(
  7431                     name,
  7432                     MatTex2dict( getMatTex(mat, basekey, tex), tex )
  7433                 )
  7434                 if result is not True:
  7435                     raise Exception()
  7436                 else:
  7437                     return True
  7438             except Exception, err:
  7439                 self.last_error_str = 'Submit failed: %s' % result
  7440                 print (result)
  7441                 print str(err)
  7442                 return False
  7443         
  7444         def check_creds(self):
  7445             if self.SERVER is None:
  7446                 try:
  7447                     self.SERVER = xmlrpclib.ServerProxy(self.host, transport=CookieTransport())
  7448                 except:
  7449                     self.last_error_str = 'ServerProxy init failed'
  7450                     return False
  7451             
  7452             
  7453             if not self.logged_in:
  7454                 #if self.username is "":
  7455                 self.request_username()
  7456                 
  7457                 #if self.password is "":
  7458                 self.request_password()
  7459                     
  7460                 return self.login()
  7461             else:
  7462                 return True
  7463                 
  7464         def request_username(self):
  7465             self.username = Draw.PupStrInput("Username:", self.username, 32)
  7466             
  7467         def request_password(self):
  7468             self.password = Draw.PupStrInput("Password:", self.password, 32)
  7469 
  7470     lrmdb_connector = lrmdb()
  7471         
  7472     
  7473 except: print("WARNING: LRMDB support not available")
  7474 
  7475 
  7476 
  7477 ### MatTex functions ###
  7478 ### MatTex : is a dictionary of material or texture properties
  7479 
  7480 def getMatTex(mat, basekey='', tex=False):
  7481     global usedproperties, usedpropertiesfilterobj
  7482     usedproperties = {}
  7483     usedpropertiesfilterobj = mat
  7484     if basekey.startswith('named_volumes:'):
  7485         luxNamedVolumeTexture(basekey[basekey.find(':')+1:basekey.find('.')])
  7486     else:
  7487         luxMaterial(mat)
  7488     dict = {}
  7489     for k,v in usedproperties.items():
  7490         if k[:len(basekey)]==basekey:
  7491             if k[-9:] != '.textured':
  7492                 name = k[len(basekey):]
  7493                 if name == ".type": name = "type"
  7494                 dict[name] = v
  7495     dict["__type__"] = ["material","texture"][bool(tex)]
  7496     return dict
  7497 
  7498 def putMatTex(mat, dict, basekey='', tex=None):
  7499     if dict and (tex!=None) and (tex ^ (dict.has_key("__type__") and (dict["__type__"]=="texture"))):
  7500         print("ERROR: Can't apply %s as %s"%(["texture","material"][bool(tex)],["material","texture"][bool(tex)]))
  7501         return
  7502     if dict:
  7503         # remove all current properties in mat that starts with basekey
  7504         try:
  7505             d = mat.properties['luxblend']
  7506             for k,v in d.convert_to_pyobject().items():
  7507                 kn = k
  7508                 if k[:7]=="__hash:":    # decode if entry is hashed (cause of 32chars limit)
  7509                     l = v.split(" = ")
  7510                     kn = l[0]
  7511                 if kn[:len(basekey)]==basekey:
  7512                     del mat.properties['luxblend'][k]
  7513         except: print("error") # pass
  7514         # volume properties
  7515         scn = Scene.GetCurrent()
  7516         if not basekey:
  7517             for k, v in dict.items():
  7518                 if k == '__volumes__':
  7519                     for volume_prop, volume_data in v.items():
  7520                         volumes = listNamedVolumes()
  7521                         # new id and name if existing are occupied
  7522                         newId = max(volumes.values())+1 if volumes else 1
  7523                         newName = volume_data['name'] if not volumes.has_key(volume_data['name']) else volume_data['name']+'_new'
  7524                         volPrefix = 'named_volumes:%s.' % newId
  7525                         # replacing dict items for the next pass ('assign loaded' below)
  7526                         dict['%s_vol_id' % volume_prop] = newId
  7527                         dict['%s_vol_name' % volume_prop] = newName
  7528                         # assigning global properties
  7529                         luxProp(scn, volPrefix+'id', 0).set(newId)
  7530                         luxProp(scn, volPrefix+'name', '').set(newName)
  7531                         for volKey, volVal in volume_data.items():
  7532                             if not volKey in ['id', 'name']:
  7533                                 luxProp(scn, volPrefix+volKey, None).set(volVal)
  7534         # assign loaded properties
  7535         for k,v in dict.items():
  7536             if k in ['Exterior_vol_guid', 'Interior_vol_guid']:
  7537                 continue
  7538             try:
  7539                 if (basekey!="") and (k=="type"): k = ".type"
  7540                 # zuegs: following two lines should fix issue http://www.luxrender.net/forum/viewtopic.php?f=16&t=1618&p=14512#p14512
  7541                 if (basekey!="") and ((k[0]!=".") and (k[0]!=":")): k = ":"+k
  7542                 if (basekey=="") and (k[0:4]==":mat"): k = k[1:]
  7543                 luxProp(mat, basekey+k, None).set(v)
  7544                 if k[-8:] == '.texture':
  7545                     luxProp(mat, basekey+k[:-8]+'.textured', 'false').set('true')
  7546             except: pass
  7547 
  7548 
  7549 LBX_VERSION = '0.71'
  7550 
  7551 def MatTex2dict(d, tex = None):
  7552     global LBX_VERSION
  7553     
  7554     if LBX_VERSION == '0.6':
  7555     
  7556         if tex is not None and tex == True:
  7557             d['LUX_DATA'] = 'TEXTURE'
  7558         else:
  7559             d['LUX_DATA'] = 'MATERIAL'
  7560         
  7561         d['LUX_VERSION'] = '0.6'
  7562         
  7563         return d
  7564     
  7565     elif LBX_VERSION in ['0.7', '0.71']:
  7566         def makeDefinition(d):
  7567             o = []
  7568             for k in d.keys():
  7569                 if type(d[k]) == types.IntType:
  7570                     t = 'integer'
  7571                 if type(d[k]) == types.FloatType:
  7572                     t = 'float'
  7573                 if type(d[k]) == types.BooleanType:
  7574                     t = 'bool'
  7575                 if type(d[k]) == types.StringType:
  7576                     l=None
  7577                     try:
  7578                         l = d[k].split(" ")
  7579                     except: pass
  7580                     if l==None or len(l)!=3:
  7581                         t = 'string'
  7582                     else:
  7583                         t = 'vector'
  7584                     
  7585                 o.append([ t, k, d[k] ])
  7586             return o
  7587         
  7588         if LBX_VERSION == '0.71':
  7589             lbx = {
  7590                 'type': d['__type__'],
  7591                 'version': LBX_VERSION,
  7592                 'definition': makeDefinition(d),
  7593                 'volumes': [],
  7594                 'metadata': [
  7595                     ['string', 'generator', 'luxblend'],
  7596                 ]
  7597             }
  7598             
  7599             for volume_prop in ['Exterior', 'Interior']:
  7600                 if d.has_key('%s_vol_id' % volume_prop):
  7601                     volume = makeDefinition(getNamedVolume(d['%s_vol_id' % volume_prop]))
  7602                     lbx['volumes'].append({
  7603                         'type': volume_prop,
  7604                         'definition': volume,
  7605                         'version': LBX_VERSION
  7606                     })
  7607         
  7608         elif LBX_VERSION == '0.7':
  7609             lbx = {
  7610                 'type': d['__type__'],
  7611                 'version': LBX_VERSION,
  7612                 'definition': makeDefinition(d),
  7613                 'metadata': [
  7614                     ['string', 'generator', 'luxblend'],
  7615                 ]
  7616             }
  7617         
  7618         return lbx
  7619 
  7620 def format_dictStr(dictStr):
  7621     result = ''
  7622     pos = 0
  7623     indentStr = '  '
  7624     newLine = '\n'
  7625     
  7626     for char in dictStr:
  7627         if char in ['}', ']']:
  7628             result += newLine
  7629             pos -= 1
  7630             for j in range(0,pos):
  7631                 result += indentStr
  7632                 
  7633         result += char
  7634         
  7635         if char in [',', '{', '[']:
  7636             result += newLine
  7637             if char in ['{', '[']:
  7638                 pos += 1
  7639             for j in range(0,pos):
  7640                 result += indentStr
  7641             
  7642     return result
  7643 
  7644 
  7645 def MatTex2str(d, tex = None):
  7646     global LBX_VERSION
  7647     
  7648     if LBX_VERSION == '0.6':
  7649         return format_dictStr(str( MatTex2dict(d, tex) )) #.replace(", \'", ",\n\'")
  7650     
  7651     elif LBX_VERSION in ['0.7', '0.71']:
  7652         return format_dictStr(str( MatTex2dict(d, tex) )) #.replace("], \'", "],\r\n\'").replace("[","\r\n\t[")
  7653         
  7654 
  7655 def str2MatTex(s, tex = None):    # todo: this is not absolutely save from attacks!!!
  7656     global LBX_VERSION
  7657     
  7658     s = s.strip()
  7659     if (s[0]=='{') and (s[-1]=='}'):
  7660         d = eval(s, dict(__builtins__=None))
  7661         if type(d)==types.DictType:
  7662             def lb_list_to_dict(list):
  7663                 d = {}
  7664                 for t, k, v in list:
  7665                     if t == 'float':
  7666                         v = float(v)
  7667                         
  7668                     d[k] = v
  7669                 return d
  7670             
  7671             if LBX_VERSION == '0.6':
  7672             
  7673                 if tex is not None and tex == True:
  7674                     test_str = 'TEXTURE'
  7675                 else:
  7676                     test_str = 'MATERIAL'
  7677                     
  7678                 if   ('LUX_DATA' in d.keys() and d['LUX_DATA'] == test_str) \
  7679                 and  ('LUX_VERSION' in d.keys() and (d['LUX_VERSION'] == '0.6' or d['LUX_VERSION'] == 0.6)):
  7680                     return d
  7681                 else:
  7682                     reason = 'Missing/incorrect metadata'
  7683                     
  7684             elif LBX_VERSION == '0.7':
  7685                 
  7686                 if   ('version' in d.keys() and d['version'] in ['0.6', '0.7']) \
  7687                 and  ('type' in d.keys() and d['type'] in ['material', 'texture']) \
  7688                 and  ('definition' in d.keys()):
  7689                     try:
  7690                         definition = lb_list_to_dict(d['definition'])
  7691                         
  7692                         if 'metadata' in d.keys():
  7693                             definition.update( lb_list_to_dict(d['metadata']) )
  7694                         return definition
  7695                     except:
  7696                         reason = 'Incorrect LBX definition data'
  7697                 else: 
  7698                     reason = 'Missing/incorrect metadata'
  7699             
  7700             elif LBX_VERSION == '0.71':
  7701                 
  7702                 if   ('version' in d.keys() and d['version'] in ['0.6', '0.7', '0.71']) \
  7703                 and  ('type' in d.keys() and d['type'] in ['material', 'texture']) \
  7704                 and  ('definition' in d.keys()):
  7705                     try:
  7706                         definition = lb_list_to_dict(d['definition'])
  7707                         
  7708                         if 'metadata' in d.keys():
  7709                             definition.update( lb_list_to_dict(d['metadata']) )
  7710                         if 'volumes' in d.keys():
  7711                             definition['__volumes__'] = {}
  7712                             for volume in d['volumes']:
  7713                                 definition['__volumes__'][volume['type']] = lb_list_to_dict(volume['definition'])
  7714                         return definition
  7715                     except:
  7716                         reason = 'Incorrect LBX definition data'
  7717                 else: 
  7718                     reason = 'Missing/incorrect metadata'
  7719             else:
  7720                 reason = 'Unknown LBX version'
  7721         else:
  7722             reason = 'Not a parsed dict'
  7723     else:
  7724         reason = 'Not a stored dict'
  7725             
  7726             
  7727     print("ERROR: string to material/texture conversion failed: %s" % reason)
  7728     return None
  7729 
  7730 
  7731 luxclipboard = None # global variable for copy/paste content
  7732 def showMatTexMenu(mat, basekey='', tex=False):
  7733     global luxclipboard, ConnectLrmdb
  7734     if tex: menu="Texture menu:%t"
  7735     else: menu="Material menu:%t"
  7736     menu += "|Copy%x1"
  7737     try:
  7738         if luxclipboard and (not(tex) ^ (luxclipboard["__type__"]=="texture")): menu +="|Paste%x2"
  7739     except: pass
  7740     if (tex):
  7741         menu += "|Load LBT%x3|Save LBT%x4"
  7742     else:
  7743         menu += "|Load LBM%x3|Save LBM%x4"
  7744     if  ConnectLrmdb:
  7745         menu += "|Download from DB%x5" #not(tex) and
  7746         # XXX temporarily disabling for glass2
  7747         if luxProp(mat, basekey+'type', '').get() != 'glass2': menu += "|Upload to DB%x6"
  7748 	menu += '|Reset%x7'
  7749 
  7750 #    menu += "|%l|dump material%x99|dump clipboard%x98"
  7751     r = Draw.PupMenu(menu)
  7752     if r==1:
  7753         luxclipboard = getMatTex(mat, basekey, tex)
  7754     elif r==2: putMatTex(mat, luxclipboard, basekey, tex)
  7755     elif r==3: 
  7756         scn = Scene.GetCurrent()
  7757         if (tex):
  7758             Window.FileSelector(lambda fn:loadMatTex(mat, fn, basekey, tex), "load texture", luxProp(scn, "lux", "").get()+os.sep+".lbt")
  7759         else:
  7760             Window.FileSelector(lambda fn:loadMatTex(mat, fn, basekey, tex), "load material", luxProp(scn, "lux", "").get()+os.sep+".lbm")
  7761     elif r==4:
  7762         scn = Scene.GetCurrent()
  7763         if (tex):
  7764             Window.FileSelector(lambda fn:saveMatTex(mat, fn, basekey, tex), "save texture", luxProp(scn, "lux", "").get()+os.sep+".lbt")
  7765         else:
  7766             Window.FileSelector(lambda fn:saveMatTex(mat, fn, basekey, tex), "save material", luxProp(scn, "lux", "").get()+os.sep+".lbm")
  7767     elif r==5:
  7768         if not tex:
  7769             id = Draw.PupStrInput("Material ID:", "", 32)
  7770         else:
  7771             id = Draw.PupStrInput("Texture ID:", "", 32)
  7772         if id: putMatTex(mat, downloadLRMDB(mat, id), basekey, tex)
  7773     elif r==6:
  7774         global lrmdb_connector
  7775         if not lrmdb_connector.submit_object(mat, basekey, tex):
  7776             msg = lrmdb_connector.last_error()
  7777         else:
  7778             msg = 'OK'
  7779         Draw.PupMenu("Upload: "+msg+".%t|OK")
  7780     elif r == 7:
  7781         resetMatTex(mat, basekey)
  7782 #    elif r==99:
  7783 #        for k,v in mat.properties['luxblend'].convert_to_pyobject().items(): print(k+"="+repr(v))
  7784 #    elif r==98:
  7785 #        for k,v in luxclipboard.items(): print(k+"="+repr(v))
  7786 #    prin()
  7787     Draw.Redraw()
  7788 
  7789 
  7790 def resetMatTex(mat, basekey=''):
  7791     if not mat.properties.has_key('luxblend'): return
  7792     for k, v in mat.properties['luxblend'].convert_to_pyobject().items():
  7793         kn = k
  7794         if k[:7] == '__hash:':
  7795             kn = v.split(' = ')[0]
  7796         if kn.startswith(basekey) and kn != basekey:
  7797             del mat.properties['luxblend'][k]
  7798 
  7799 
  7800 def saveMatTex(mat, fn, basekey='', tex=False):
  7801     global LuxIsGUI
  7802     d = getMatTex(mat, basekey, tex)
  7803     file = open(fn, 'w')
  7804     file.write(MatTex2str(d, tex))
  7805     file.close()
  7806     if LuxIsGUI: Draw.Redraw()
  7807 
  7808 
  7809 def loadMatTex(mat, fn, basekey='', tex=None):
  7810     global LuxIsGUI
  7811     file = open(fn, 'rU')
  7812     data = file.read()
  7813     file.close()
  7814     data = str2MatTex(data, tex)
  7815     putMatTex(mat, data, basekey, tex) 
  7816     if LuxIsGUI: Draw.Redraw()
  7817 
  7818 
  7819 def batchRenamePaths(scn):
  7820     dryrun = luxProp(scn, 'batch_rename_dryrun', False)
  7821     searchSaved = luxProp(scn, 'batch_rename_search', '')
  7822     search = Draw.PupStrInput('path substring to search for: ', searchSaved.get(), 100)
  7823     searchSaved.set(search)
  7824     
  7825     if search == '':
  7826         Blender.Window.QRedrawAll()
  7827         return False
  7828     else:
  7829         collection = []
  7830         misses = []
  7831         metals = ['aluminium', 'amorphous carbon', 'silver', 'gold', 'copper']
  7832         for item in [scn] + Material.Get():
  7833             if item.properties.has_key('luxblend'):
  7834                 for k, v in item.properties['luxblend'].convert_to_pyobject().items():
  7835                     kn = k
  7836                     if k[:7] == '__hash:':
  7837                         kn, v = v.split(' = ')
  7838                     if type(v) is str and kn.endswith(('.filename', '.name')) and not (kn.endswith('.name') and v in metals) \
  7839                      and not (kn.endswith('.name') and kn.startswith('named_volumes:')):
  7840                         if v.find(search) < 0: misses.append([item, kn])
  7841                         else: collection.append([item, kn])
  7842     
  7843     if not len(collection):
  7844         Draw.PupMenu('Search substring not found in resources paths%t|OK%x1')
  7845         Blender.Window.QRedrawAll()
  7846         return False
  7847     
  7848     replaceSaved = luxProp(scn, 'batch_rename_replace', '')
  7849     replace = Draw.PupStrInput('replace with: ', replaceSaved.get(), 100)
  7850     replaceSaved.set(replace)
  7851     
  7852     r = Draw.PupMenu('  OK?%t|Replace "'+search+'" substring with "'+replace+'" in '+str(len(collection))+' of '+str(len(collection)+len(misses))+' path properties%x1')
  7853     if r == 1:
  7854         print
  7855         if dryrun.get() == 'true':
  7856             print '--------------- DRY RUN ---------------'
  7857         print 'Begin batch search & replace in paths:'
  7858         print '("%s" to "%s" in %s properties)' % (search, replace, len(collection))
  7859         for item, kn in collection:
  7860             v_old = luxProp(item, kn, '').get()
  7861             v_new = v_old.replace(search, replace)
  7862             print '%s, %s' % (item, kn)
  7863             print "\told:", v_old
  7864             print "\tnew:", v_new
  7865             if dryrun.get() != 'true':
  7866                 luxProp(item, kn, '').set(v_new)
  7867         if len(misses):
  7868             print '---------------------------------------'
  7869             print '%s properties not affected:' % len(misses)
  7870             for item, kn in misses:
  7871                 print '%s, %s' % (item, kn)
  7872         if dryrun.get() == 'true':
  7873             print '--------------- DRY RUN ---------------'
  7874         return True
  7875     else:
  7876         Blender.Window.QRedrawAll()
  7877         return False
  7878 
  7879 
  7880 def flipMixMat(mat, basekey):
  7881     # flip mix material slots
  7882     if luxProp(mat, 'type', '').get() == 'mix':
  7883         import re
  7884         global previewCache
  7885         if basekey != '':
  7886             r = re.compile(basekey+r'(\:mat[12])+')
  7887             basekey = basekey+':'
  7888         else:
  7889             r = re.compile(r'mat[12](\:mat[12])*')
  7890         
  7891         s = [{}, {}]  # we'll store slots property items here for later processing
  7892         p = [{}, {}]  # and this is for cached preview images
  7893         d = mat.properties['luxblend']
  7894         for k, v in d.convert_to_pyobject().items():
  7895             kn = k
  7896             if k[:7] == '__hash:':
  7897                 l = v.split(' = ')
  7898                 kn = l[0]
  7899             # select required slot properties at the appropriate level
  7900             for i in range(0, 2):
  7901                 if kn[:len(basekey)+4] == basekey+'mat'+str(i+1):
  7902                     # mat property
  7903                     #print 'slot'+str(i+1)+' (saved to dict '+str(i^1)+'):', k, '=', v
  7904                     s[i^1][k] = str(v)
  7905                     del mat.properties['luxblend'][k]  # remove original item
  7906                     # preview image
  7907                     try:
  7908                         pk = k[0:r.match(k).end()]
  7909                         hashkey = (mat.name+':'+pk+'.').__hash__()
  7910                         if not p[i^1].has_key(pk) and previewCache.has_key(hashkey):
  7911                             #print 'preview for slot'+str(i+1)+' key:  ', pk, '(saved to dict '+str(i^1)+')'
  7912                             p[i^1][pk] = previewCache[hashkey]
  7913                             del previewCache[hashkey]  # remove original item
  7914                     except AttributeError:
  7915                         pass
  7916         # processing items
  7917         for i in range(0, 2):
  7918             # renaming mat keys
  7919             for k, v in s[i].items():
  7920                 if k[:7] == '__hash:':
  7921                     l = v.split(' = ')
  7922                     newkey = l[0].replace(basekey+'mat'+str((i+1)^3), basekey+'mat'+str(i+1), 1)
  7923                     hexkey = '__hash:'+hex(newkey.__hash__()).replace('0x', '')
  7924                     v = l[1]
  7925                     #print k, '>>>', hexkey, '=', newkey, '=', v
  7926                     mat.properties['luxblend'][hexkey] = newkey+' = '+str(v)
  7927                 else:
  7928                     newkey = k.replace(basekey+'mat'+str((i+1)^3), basekey+'mat'+str(i+1), 1)
  7929                     #print k, '>>>', newkey, '=', v
  7930                     mat.properties['luxblend'][newkey] = str(v)
  7931             # renaming cached previews keys
  7932             for k, v in p[i].items():
  7933                 pk = mat.name+':'+k+'.'
  7934                 newpk = k.replace(basekey+'mat'+str((i+1)^3), basekey+'mat'+str(i+1), 1)
  7935                 newpk = mat.name+':'+newpk+'.'
  7936                 #print pk, '('+str(pk.__hash__())+')', '>>>', newpk, '('+str(newpk.__hash__())+')'
  7937                 previewCache[newpk.__hash__()] = v
  7938 
  7939 def showVolumesMenu(mat, volume_prop, r=None):
  7940     scn = Scene.GetCurrent()
  7941     active_volume = getNamedVolume(luxProp(mat, '%s_vol_id' % (volume_prop), 0).get())
  7942     menu = "Manage mediums:%t|Create new medium%x1|Copy selected%x2"
  7943     if active_volume['name'] != 'world *':
  7944         menu += "|Rename selected%x3"
  7945     
  7946     if not r: r = Draw.PupMenu(menu)
  7947     if r==1:
  7948         # create new volume
  7949         name = Draw.PupStrInput('medium name: ', '')
  7950         vols = listNamedVolumes()
  7951         if vols.has_key(name) or name == 'world *':
  7952             Draw.PupMenu('ERROR: Medium name already exists%t|OK%x1')
  7953             Blender.Window.QRedrawAll()
  7954             return False
  7955         elif name != '':
  7956             newId = vols and max(vols.values())+1 or 1
  7957             luxProp(scn, 'named_volumes:%s.name' % newId, 0).set(name)
  7958             luxProp(mat, '%s_vol_id' % (volume_prop), 0).set(newId)
  7959             luxProp(mat, '%s_vol_name' % (volume_prop), '').set(name)
  7960             luxProp(mat, '%s_vol_guid' % (volume_prop), '').set(luxUID)
  7961             Blender.Window.QRedrawAll()
  7962             return True
  7963     elif r == 2:
  7964         # copy existing volume
  7965         name = Draw.PupStrInput('copy to name: ', '')
  7966         vols = listNamedVolumes()
  7967         if name == active_volume['name'] or name == '':
  7968             Blender.Window.QRedrawAll()
  7969             return False
  7970         if vols.has_key(name):
  7971             r = Draw.PupMenu('  OK?%t|Replace existing medium%x1')
  7972             if r == 1:
  7973                 volProps = getNamedVolume(vols[name])
  7974                 volId = vols[name]
  7975                 for n in volProps.keys():
  7976                     if not n in ['name', 'id']:
  7977                         luxProp(scn, 'named_volumes:%s.%s'%(volId,n), '').delete()
  7978             else:
  7979                 Blender.Window.QRedrawAll()
  7980                 return False
  7981         else:
  7982             volId = vols and max(vols.values())+1 or 1
  7983             luxProp(scn, 'named_volumes:%s.name' % volId, 0).set(name)
  7984         for k, v in active_volume.items():
  7985             if not k in ['name', 'id']:
  7986                 luxProp(scn, 'named_volumes:%s.%s'%(volId,k), '').set(v)
  7987         luxProp(mat, '%s_vol_id' % (volume_prop), 0).set(volId)
  7988         luxProp(mat, '%s_vol_name' % (volume_prop), '').set(name)
  7989         Blender.Window.QRedrawAll()
  7990         return True
  7991     elif r == 3:
  7992         # rename existing volume
  7993         name = Draw.PupStrInput('new name: ', active_volume['name'])
  7994         vols = listNamedVolumes()
  7995         if name == 'world *':
  7996             Draw.PupMenu('ERROR: Impossible to rename to the world medium, use Copy instead%t|OK%x1')
  7997             Blender.Window.QRedrawAll()
  7998             return False
  7999         elif name == active_volume['name'] or name == '':
  8000             Blender.Window.QRedrawAll()
  8001             return False
  8002         if vols.has_key(name):
  8003             r = Draw.PupMenu('  OK?%t|Replace existing medium%x1')
  8004             if r == 1:
  8005                 volProps = getNamedVolume(vols[name])
  8006                 for n in volProps.keys():
  8007                     luxProp(scn, 'named_volumes:%s.%s'%(vols[name],n), '').delete()
  8008             else:
  8009                 Blender.Window.QRedrawAll()
  8010                 return False
  8011         luxProp(scn, 'named_volumes:%s.name' % active_volume['id'], 0).set(name)
  8012         luxProp(mat, '%s_vol_name' % (volume_prop), '').set(name)
  8013         Blender.Window.QRedrawAll()
  8014         return True
  8015     elif r == 4:
  8016         # unlinking a volume
  8017         luxProp(mat, '%s_vol_name' % (volume_prop), '').set('world *')
  8018         luxProp(mat, '%s_vol_id' % (volume_prop), 0).set(0)
  8019         Blender.Window.QRedrawAll()
  8020         return True
  8021 
  8022 
  8023 activemat = None
  8024 def setactivemat(mat):
  8025     global activemat
  8026     activemat = mat
  8027 
  8028 
  8029 # scrollbar
  8030 class scrollbar:
  8031     def __init__(self):
  8032         self.position = 0 # current position at top (inside 0..height-viewHeight)
  8033         self.height = 0 # total height of the content
  8034         self.viewHeight = 0 # height of window
  8035         self.x = 0 # horizontal position of the scrollbar
  8036         self.scrolling = self.over = False # start without scrolling ;)
  8037     def calcRects(self):
  8038         # Blender doesn't give us direct access to the window size yet, but it does set the
  8039         # GL scissor box for it, so we can get the size from that. (thx to Daniel Dunbar)
  8040         size = BGL.Buffer(BGL.GL_FLOAT, 4)
  8041         BGL.glGetFloatv(BGL.GL_SCISSOR_BOX, size)
  8042         size = size.list # [winx, winy, width, height]
  8043         self.winrect = size[:]
  8044         self.viewHeight = size[3]
  8045         size[0], size[1] = size[2]-20, 0 # [scrollx1, scrolly1, scrollx2, scrolly2]
  8046         self.rect = size[:]
  8047         if self.position < 0: self.position = 0
  8048         if self.height < self.viewHeight: self.height = self.viewHeight
  8049         if self.position > self.height-self.viewHeight: self.position = self.height-self.viewHeight
  8050         self.factor = (size[3]-size[1]-4)/self.height
  8051         self.sliderRect = [size[0]+2, size[3]-2-(self.position+self.viewHeight)*self.factor, size[2]-2, size[3]-2-self.position*self.factor]
  8052     def draw(self):
  8053         self.calcRects()
  8054         BGL.glColor3f(0.5,0.5,0.5); BGL.glRectf(self.rect[0],self.rect[1],self.rect[2],self.rect[3])
  8055         if self.over or self.scrolling: BGL.glColor3f(1.0,1.0,0.7)
  8056         else: BGL.glColor3f(0.7,0.7,0.7)
  8057         BGL.glRectf(self.sliderRect[0],self.sliderRect[1],self.sliderRect[2],self.sliderRect[3])
  8058     def getTop(self):
  8059         return self.viewHeight+self.position
  8060     def scroll(self, delta):
  8061         self.position = self.position + delta
  8062         self.calcRects()
  8063         Draw.Redraw()
  8064     def Mouse(self):
  8065         self.calcRects()
  8066         coord, buttons = Window.GetMouseCoords(), Window.GetMouseButtons()
  8067         over = (coord[0]>=self.winrect[0]+self.rect[0]) and (coord[0]<=self.winrect[0]+self.rect[2]) and \
  8068                (coord[1]>=self.winrect[1]+self.rect[1]) and (coord[1]<=self.winrect[1]+self.rect[3])
  8069         if Window.MButs.L and buttons > 0:
  8070             if self.scrolling:
  8071                 if self.factor > 0: self.scroll((self.lastcoord[1]-coord[1])/self.factor)
  8072                 Draw.Redraw()
  8073             elif self.over:
  8074                 self.scrolling = True
  8075             self.lastcoord = coord
  8076         elif self.scrolling:
  8077             self.scrolling = False
  8078             Draw.Redraw()
  8079         if self.over != over: Draw.Redraw()
  8080         self.over = over
  8081 
  8082 scrollbar = scrollbar()
  8083 
  8084 
  8085 # gui main draw
  8086 def luxDraw():
  8087     global icon_luxblend
  8088     global luxUID
  8089 
  8090     BGL.glClear(BGL.GL_COLOR_BUFFER_BIT)
  8091 
  8092     y = int(scrollbar.getTop()) # 420
  8093     BGL.glColor3f(0.1,0.1,0.1); BGL.glRectf(0,0,440,y)
  8094     BGL.glColor3f(1.0,0.5,0.0); BGL.glRasterPos2i(130,y-21); Draw.Text("v%s"%__version__)
  8095     BGL.glColor3f(0.9,0.9,0.9)
  8096 
  8097     drawLogo(icon_luxblend, 6, y-25)
  8098 
  8099     scn = Scene.GetCurrent()
  8100     if scn:
  8101         luxUID = luxGenUID(scn)
  8102         luxpage = luxProp(scn, "page", 0)
  8103         gui = luxGui(y-70)
  8104 
  8105         # render presets
  8106         BGL.glRasterPos2i(10,y-45); Draw.Text("Render presets:")
  8107         luxpreset = luxProp(scn, "preset", "1C - Final - medium MLT/Path Tracing (indoor) (recommended)")
  8108         presets = getScenePresets()
  8109         presetskeys = presets.keys()
  8110         presetskeys.sort()
  8111         presetskeys.insert(0, "")
  8112         presetsstr = "presets: %t"
  8113         for i, v in enumerate(presetskeys): presetsstr = "%s %%x%d|%s"%(v, i, presetsstr)
  8114         try: i = presetskeys.index(luxpreset.get())
  8115         except ValueError: i = 0
  8116         Draw.Menu(presetsstr, evtLuxGui, 110, y-50, 220, 18, i, "", lambda e,v: luxpreset.set(presetskeys[v]))
  8117         Draw.Button("save", evtSavePreset, 330, y-50, 40, 18, "create a render-settings preset")
  8118         Draw.Button("del", evtDeletePreset, 370, y-50, 40, 18, "delete a render-settings preset")
  8119 
  8120         # if preset is selected load values
  8121         if luxpreset.get() != "":
  8122             try:
  8123                 d = presets[luxpreset.get()]
  8124                 for k,v in d.items(): scn.properties['luxblend'][k] = v
  8125             except: pass
  8126 
  8127         Draw.Button("Material", evtLuxGui, 10, y-70, 80, 16, "", lambda e,v:luxpage.set(0))
  8128         Draw.Button("Cam/Env", evtLuxGui, 90, y-70, 80, 16, "", lambda e,v:luxpage.set(1))
  8129         Draw.Button("Render", evtLuxGui, 170, y-70, 80, 16, "", lambda e,v:luxpage.set(2))
  8130         Draw.Button("Output", evtLuxGui, 250, y-70, 80, 16, "", lambda e,v:luxpage.set(3))
  8131         Draw.Button("System", evtLuxGui, 330, y-70, 80, 16, "", lambda e,v:luxpage.set(4))
  8132         if luxpage.get() == 0:
  8133             BGL.glColor3f(1.0,0.5,0.0);BGL.glRectf(10,y-74,90,y-70);BGL.glColor3f(0.9,0.9,0.9)
  8134             obj = scn.objects.active
  8135             if obj:
  8136                 if (obj.getType() == "Lamp"):
  8137                     ltype = obj.getData(mesh=1).getType() # data
  8138                     if (ltype == Lamp.Types["Area"]): luxLight("Area LIGHT", "", obj, gui, 0)
  8139                     elif (ltype == Lamp.Types["Spot"]): luxSpot("Spot LIGHT", "", obj, gui, 0)
  8140                     elif (ltype == Lamp.Types["Lamp"]): luxLamp("Point LIGHT", "", obj, gui, 0)
  8141                 else:
  8142                     matfilter = luxProp(scn, "matlistfilter", "false")
  8143                     mats = getMaterials(obj, True)
  8144                     if (activemat == None) and (len(mats) > 0):
  8145                         setactivemat(mats[0])
  8146                     if matfilter.get() == "false":
  8147                         mats = Material.Get()
  8148                     matindex = 0
  8149                     for i, v in enumerate(mats):
  8150                         if v==activemat: matindex = i
  8151                     matnames = [m.getName() for m in mats]
  8152                     menustr = "Material: %t"
  8153                     for i, v in enumerate(matnames): menustr = "%s %%x%d|%s"%(v, i, menustr)
  8154                     gui.newline("MATERIAL:", 8) 
  8155                     r = gui.getRect(1.1, 1)
  8156                     Draw.Button("C", evtConvertMaterial, r[0]-gui.h, gui.y-gui.h, gui.h, gui.h, "convert Blender material to LuxRender material")
  8157                     Draw.Menu(menustr, evtLuxGui, r[0], r[1], r[2], r[3], matindex, "", lambda e,v: setactivemat(mats[v]))
  8158                     luxBool("", matfilter, "filter", "only show active object materials", gui, 0.5)
  8159 
  8160                     Draw.Button("L", evtLoadMaterial, gui.x, gui.y-gui.h, gui.h, gui.h, "load a material preset")
  8161                     Draw.Button("S", evtSaveMaterial, gui.x+gui.h, gui.y-gui.h, gui.h, gui.h, "save a material preset")
  8162                     Draw.Button("D", evtDeleteMaterial, gui.x+gui.h*2, gui.y-gui.h, gui.h, gui.h, "delete a material preset")
  8163                     if len(mats) > 0:
  8164                         setactivemat(mats[matindex])
  8165                         luxMaterial(activemat, gui)
  8166         if luxpage.get() == 1:
  8167             BGL.glColor3f(1.0,0.5,0.0);BGL.glRectf(90,y-74,170,y-70);BGL.glColor3f(0.9,0.9,0.9)
  8168             #cam = scn.getCurrentCamera()
  8169             cam = scn.objects.camera
  8170             if cam:
  8171                 r = gui.getRect(1.1, 1)
  8172                 luxCamera(cam.data, scn.getRenderingContext(), gui)
  8173             gui.newline("", 10)
  8174             luxEnvironment(scn, gui)
  8175         if luxpage.get() == 2:
  8176             BGL.glColor3f(1.0,0.5,0.0);BGL.glRectf(170,y-74,250,y-70);BGL.glColor3f(0.9,0.9,0.9)
  8177             r = gui.getRect(1.1, 1)
  8178             luxSampler(scn, gui)
  8179             gui.newline("", 10)
  8180             luxSurfaceIntegrator(scn, gui)
  8181             gui.newline("", 10)
  8182             luxVolumeIntegrator(scn, gui)
  8183             gui.newline("", 10)
  8184             luxPixelFilter(scn, gui)
  8185         if luxpage.get() == 3:
  8186             BGL.glColor3f(1.0,0.5,0.0);BGL.glRectf(250,y-74,330,y-70);BGL.glColor3f(0.9,0.9,0.9)
  8187             r = gui.getRect(1.1, 1)
  8188             luxFilm(scn, gui)
  8189         if luxpage.get() == 4:
  8190             BGL.glColor3f(1.0,0.5,0.0);BGL.glRectf(330,y-74,410,y-70);BGL.glColor3f(0.9,0.9,0.9)
  8191             luxSystem(scn, gui)
  8192             gui.newline("", 10)
  8193             luxAccelerator(scn, gui)
  8194             gui.newline("MATERIALS:", 10)
  8195             r = gui.getRect(2,1)
  8196             Draw.Button("Convert all Blender materials", 0, r[0], r[1], r[2], r[3], "Convert all Blender materials in the scene to LuxRender materials", lambda e,v:convertAllMaterials())
  8197             r = gui.getRect(1.5,1)
  8198             Draw.Button('Batch replace of resources paths', 0, r[0], r[1], r[2], r[3], 'Batch search & replace of paths to imagemaps and other external resources', lambda e,v:batchRenamePaths(scn))
  8199             luxBool('batch_rename_dryrun', luxProp(scn, 'batch_rename_dryrun', False), 'dry run', 'Print all normal output to the Blender console without actually replacing any path properties', gui, 0.5)
  8200             gui.newline("SETTINGS:", 10)
  8201             r = gui.getRect(2,1)
  8202             Draw.Button("Save defaults", 0, r[0], r[1], r[2], r[3], "Save current settings as defaults", lambda e,v:saveluxdefaults())
  8203             luxRemoveProps(scn, gui)
  8204         y = gui.y - 80
  8205         if y > 0: y = 0 # bottom align of render button
  8206         run = luxProp(scn, "run", "true")
  8207         dlt = luxProp(scn, "default", "true")
  8208         pipe = luxProp(scn, "pipe", "false")
  8209         clay = luxProp(scn, "clay", "false")
  8210         nolg = luxProp(scn, "nolg", "false")
  8211         lxs = luxProp(scn, "lxs", "true")
  8212         lxo = luxProp(scn, "lxo", "true")
  8213         lxm = luxProp(scn, "lxm", "true")
  8214         lxv = luxProp(scn, "lxv", "true")
  8215         net = luxProp(scn, "netrenderctl", "false")
  8216         donet = luxProp(scn, "donetrender", "true")
  8217         
  8218         global render_status_text
  8219         global render_status
  8220         
  8221         if render_status == True:
  8222             BGL.glRasterPos2i(10,y+20)
  8223             Draw.Text(render_status_text)
  8224         else:
  8225             BGL.glRasterPos2i(10,y+5)
  8226             Draw.Text(render_status_text, "tiny")
  8227             
  8228             def check_pipe_def_exclusion(m, v):
  8229                 if m == 'd':
  8230                     dlt.set(["false","true"][bool(v)])
  8231                     
  8232                     if dlt.get() == 'true':
  8233                         pipe.set('false')
  8234                 elif m == 'p':
  8235                     pipe.set(["false","true"][bool(v)])
  8236                     
  8237                     if pipe.get() == 'true':
  8238                         dlt.set('false')
  8239             
  8240             if (run.get()=="true"):
  8241                 Draw.Button("Render", 0, 10, y+20, 100, 36, "Render image with LuxRender", lambda e,v:CBluxExport(dlt.get()=="true" or pipe.get()=="true", True))
  8242                 Draw.Button("Render Anim", 0, 110, y+20, 100, 36, "Render animation with LuxRender", lambda e,v:CBluxAnimExport(dlt.get()=="true" or pipe.get()=="true", True))
  8243             else:
  8244                 Draw.Button("Export", 0, 10, y+20, 100, 36, "Export", lambda e,v:CBluxExport(dlt.get()=="true" or pipe.get()=="true", False))
  8245                 Draw.Button("Export Anim", 0, 110, y+20, 100, 36, "Export animation", lambda e,v:CBluxAnimExport(dlt.get()=="true" or pipe.get()=="true", False))
  8246             
  8247             Draw.Toggle("run", evtLuxGui, 265, y+40, 30, 16, run.get()=="true", "Start LuxRender after export", lambda e,v: run.set(["false","true"][bool(v)]))
  8248             
  8249             if (pipe.get() == 'false' and dlt.get() == 'true') or run.get()=='false':
  8250                 Draw.Toggle("def", evtLuxGui, 295, y+40, 55, 16, dlt.get()=="true", "Write to default lxs file", lambda e,v: check_pipe_def_exclusion('d',v))
  8251             elif pipe.get() == 'true' and dlt.get() == 'false':
  8252                 Draw.Toggle("pipe", evtLuxGui, 295, y+40, 55, 16, pipe.get()=="true", "Do not write any lxs file", lambda e,v: check_pipe_def_exclusion('p',v))
  8253             else:
  8254                 Draw.Toggle("def", evtLuxGui, 295, y+40, 25, 16, dlt.get()=="true", "Write to default lxs file", lambda e,v: check_pipe_def_exclusion('d',v))
  8255                 Draw.Toggle("pipe", evtLuxGui, 320, y+40, 30, 16, pipe.get()=="true", "Do not write any lxs file", lambda e,v: check_pipe_def_exclusion('p',v))
  8256             
  8257             Draw.Toggle("clay", evtLuxGui, 350, y+40, 30, 16, clay.get()=="true", "All materials are rendered as white-matte", lambda e,v: clay.set(["false","true"][bool(v)]))
  8258             Draw.Toggle("noLG", evtLuxGui, 380, y+40, 35, 16, nolg.get()=="true", "Disables all light groups", lambda e,v: nolg.set(["false","true"][bool(v)]))
  8259             
  8260             if pipe.get() == "false":
  8261                 Draw.Toggle(".lxs", 0, 265, y+20, 37, 16, lxs.get()=="true", "Export .lxs scene file", lambda e,v: lxs.set(["false","true"][bool(v)]))
  8262                 Draw.Toggle(".lxo", 0, 302, y+20, 38, 16, lxo.get()=="true", "Export .lxo geometry file", lambda e,v: lxo.set(["false","true"][bool(v)]))
  8263                 Draw.Toggle(".lxm", 0, 340, y+20, 37, 16, lxm.get()=="true", "Export .lxm material file", lambda e,v: lxm.set(["false","true"][bool(v)]))
  8264                 Draw.Toggle(".lxv", 0, 377, y+20, 38, 16, lxv.get()=="true", "Export .lxv volume file", lambda e,v: lxv.set(["false","true"][bool(v)]))
  8265     
  8266     BGL.glColor3f(0.9, 0.9, 0.9)
  8267     
  8268     BGL.glRasterPos2i(330,y+5) ; Draw.Text("Press Q or ESC to quit.", "tiny")
  8269     scrollbar.height = scrollbar.getTop() - y
  8270     scrollbar.draw()
  8271 
  8272 render_status_text = ''
  8273 render_status = False
  8274 
  8275 mouse_xr=1 
  8276 mouse_yr=1 
  8277 
  8278 activeObject = None
  8279 activeEvent = None
  8280 lastEventTime = 0
  8281 key_tabs = {
  8282     Draw.ONEKEY:     0,
  8283     Draw.TWOKEY:     1,
  8284     Draw.THREEKEY:   2,
  8285     Draw.FOURKEY:    3,
  8286     Draw.FIVEKEY:    4,
  8287 }
  8288 def luxEvent(evt, val):  # function that handles keyboard and mouse events
  8289     global activeObject, activemat, activeEvent, lastEventTime, key_tabs
  8290     if evt == Draw.ESCKEY or evt == Draw.QKEY:
  8291         stop = Draw.PupMenu("OK?%t|Cancel export %x1")
  8292         if stop == 1:
  8293             Draw.Exit()
  8294             return
  8295     scn = Scene.GetCurrent()
  8296     if scn:
  8297         if scn.objects.active != activeObject:
  8298             activeObject = scn.objects.active
  8299             activemat = None
  8300             Window.QRedrawAll()
  8301     if (evt == Draw.MOUSEX) or (evt == Draw.MOUSEY): scrollbar.Mouse()
  8302     if evt == Draw.WHEELUPMOUSE: scrollbar.scroll(-16)
  8303     if evt == Draw.WHEELDOWNMOUSE: scrollbar.scroll(16)
  8304     if evt == Draw.PAGEUPKEY: scrollbar.scroll(-50)
  8305     if evt == Draw.PAGEDOWNKEY: scrollbar.scroll(50)
  8306 
  8307     # scroll to [T]op and [B]ottom
  8308     if evt == Draw.TKEY:
  8309         scrollbar.scroll(-scrollbar.position)
  8310     if evt == Draw.BKEY:
  8311         scrollbar.scroll(100000)   # Some large number should be enough ?!
  8312 
  8313     # R key shortcut to launch render
  8314     # E key shortcut to export current scene (not render)
  8315     # P key shortcut to preview current material
  8316     # These keys need time and process-complete locks
  8317     if evt in [Draw.RKEY, Draw.EKEY, Draw.PKEY]:
  8318         if activeEvent == None and (sys.time() - lastEventTime) > 5:
  8319             lastEventTime = sys.time()
  8320             if evt == Draw.RKEY:
  8321                 activeEvent = 'RKEY'
  8322                 CBluxExport(luxProp(scn, "default", "true").get() == "true" or luxProp(scn, "pipe", "false").get() == "true", True)
  8323                 activeEvent = None
  8324             if evt == Draw.EKEY:
  8325                 activeEvent = 'EKEY'
  8326                 CBluxExport(luxProp(scn, "default", "true").get() == "true" or luxProp(scn, "pipe", "false").get() == "true", False)
  8327                 activeEvent = None
  8328             if evt == Draw.PKEY:
  8329                 activeEvent = 'PKEY'
  8330                 if activemat != None:
  8331                     Preview_Update(activemat, '', True, 0, None, None, None)
  8332                 activeEvent = None
  8333         
  8334     # Switch GUI tabs with number keys
  8335     if evt in key_tabs.keys():
  8336         luxProp(scn, "page", 0).set(key_tabs[evt])        
  8337         luxDraw()
  8338         Window.QRedrawAll()
  8339           
  8340 
  8341     # Handle icon button events - note - radiance - this is a work in progress! :)
  8342 #    if evt == Draw.LEFTMOUSE and not val: 
  8343 #           size=BGL.Buffer(BGL.GL_FLOAT, 4) 
  8344 #           BGL.glGetFloatv(BGL.GL_SCISSOR_BOX, size) 
  8345 #            size= [int(s) for s in size] 
  8346 #        mx, my = Window.GetMouseCoords()
  8347 #        mousex = mx - size[0]
  8348 #        print("mousex = %i"%mousex)
  8349 #        #if((mousex > 2) and (mousex < 25)):
  8350 #            # Mouse clicked in left button bar
  8351 #        if((mousex > 399) and (mousex < 418)):
  8352 #            # Mouse clicked in right button bar
  8353 #            mousey = my - size[1] - scrollbar.position
  8354 #            print("mousey = %i"%mousey)
  8355             
  8356     
  8357 def luxButtonEvt(evt):  # function that handles button events
  8358     global usedproperties, usedpropertiesfilterobj
  8359     if evt == evtLuxGui:
  8360         Draw.Redraw()
  8361     if evt == evtSavePreset:
  8362         scn = Scene.GetCurrent()
  8363         if scn:
  8364             name = Draw.PupStrInput("preset name: ", "")
  8365             if name != "":
  8366                 usedproperties = {}
  8367                 usedpropertiesfilterobj = None
  8368                 luxSurfaceIntegrator(scn)
  8369                 luxSampler(scn)
  8370                 luxPixelFilter(scn)
  8371                 # luxFilm(scn)
  8372                 luxAccelerator(scn)
  8373                 # luxEnvironment(scn)
  8374                 saveScenePreset(name, usedproperties.copy())
  8375                 luxProp(scn, "preset", "").set(name)
  8376                 Draw.Redraw()
  8377     if evt == evtDeletePreset:
  8378         presets = getScenePresets().keys()
  8379         presets.sort()
  8380         presetsstr = "delete preset: %t"
  8381         for i, v in enumerate(presets): presetsstr += "|%s %%x%d"%(v, i)
  8382         r = Draw.PupMenu(presetsstr, 20)
  8383         if r >= 0:
  8384             saveScenePreset(presets[r], None)
  8385             Draw.Redraw()
  8386 
  8387     if evt == evtLoadMaterial:
  8388         if activemat:
  8389             mats = getMaterialPresets()
  8390             matskeys = mats.keys()
  8391             matskeys.sort()
  8392             matsstr = "load preset: %t"
  8393             for i, v in enumerate(matskeys): matsstr += "|%s %%x%d"%(v, i)
  8394             r = Draw.PupMenu(matsstr, 20)
  8395             if r >= 0:
  8396                 name = matskeys[r]
  8397                 try:
  8398 #                    for k,v in mats[name].items(): activemat.properties['luxblend'][k] = v
  8399                     for k,v in mats[name].items(): luxProp(activemat, k, None).set(v)
  8400                 except: pass
  8401                 Draw.Redraw()
  8402     if evt == evtSaveMaterial:
  8403         if activemat:
  8404             name = Draw.PupStrInput("preset name: ", "")
  8405             if name != "":
  8406                 usedproperties = {}
  8407                 usedpropertiesfilterobj = activemat
  8408                 luxMaterial(activemat)
  8409                 saveMaterialPreset(name, usedproperties.copy())
  8410                 Draw.Redraw()
  8411     if evt == evtDeleteMaterial:
  8412         matskeys = getMaterialPresets().keys()
  8413         matskeys.sort()
  8414         matsstr = "delete preset: %t"
  8415         for i, v in enumerate(matskeys): matsstr += "|%s %%x%d"%(v, i)
  8416         r = Draw.PupMenu(matsstr, 20)
  8417         if r >= 0:
  8418             saveMaterialPreset(matskeys[r], None)
  8419             Draw.Redraw()
  8420     if evt == evtConvertMaterial:
  8421         if activemat: convertMaterial(activemat)
  8422         Draw.Redraw()
  8423     if evt == evtLoadMaterial2:
  8424         if activemat:
  8425             scn = Scene.GetCurrent()
  8426             Window.FileSelector(lambda fn:loadMatTex(activemat, fn), "load material", luxProp(scn, "lux", "").get()+os.sep+".lbm")
  8427     if evt == evtSaveMaterial2:
  8428         if activemat:
  8429             scn = Scene.GetCurrent()
  8430             Window.FileSelector(lambda fn:saveMaterial(activemat, fn), "save material", luxProp(scn, "lux", "").get()+os.sep+".lbm")
  8431     
  8432 
  8433 def setFocus(target):
  8434     currentscene = Scene.GetCurrent()
  8435     camObj = currentscene.objects.camera # currentscene.getCurrentCamera()
  8436     if target == "S":
  8437         try:
  8438             refLoc = (Object.GetSelected()[0]).getLocation()
  8439         except:
  8440             print("select an object to focus\n")
  8441     elif target == "C":
  8442         refLoc = Window.GetCursorPos()
  8443     else:
  8444         refLoc = (Object.Get(target)).getLocation()
  8445     dist = Mathutils.Vector(refLoc) - Mathutils.Vector(camObj.getLocation())
  8446     camDir = camObj.getMatrix()[2]*(-1.0)
  8447     camObj.getData(mesh=1).dofDist = (camDir[0]*dist[0]+camDir[1]*dist[1]+camDir[2]*dist[2])/camDir.length # data
  8448 
  8449 
  8450 # Parse command line arguments for batch mode rendering if supplied
  8451 global luxUID
  8452 luxUID = None
  8453 
  8454 try:
  8455     batchindex = osys.argv.index('--batch')
  8456     pyargs = osys.argv[osys.argv.index('--batch')+1:]
  8457 except: pyargs = []
  8458 
  8459 if (pyargs != []) and (batchindex != 0):
  8460     print("\n\nLuxBlend v%s - BATCH mode\n"%__version__)
  8461 
  8462     LuxIsGUI = False
  8463     scene = Scene.GetCurrent()
  8464     context = scene.getRenderingContext()
  8465     luxUID = luxGenUID(scene)
  8466     gcNamedVolumes(scene, LuxIsGUI)
  8467 
  8468     luxpath = ""
  8469     import getopt
  8470     o, a = getopt.getopt(pyargs, 's:e:o:t:l:',["scale=","haltspp=","run=", "lbm=", "lbt="])
  8471 
  8472     opts = {}
  8473     for k,v in o:
  8474         opts[k] = v
  8475 
  8476     if (opts.has_key('--run')) and (opts['--run'] == 'false'):
  8477         print("Run: false")
  8478         luxProp(scene, "run", "true").set("false")
  8479     else:
  8480         luxProp(scene, "run", "true").set("true")
  8481 
  8482     if opts.has_key('--scale'):
  8483         print("Zoom: %s" %opts['--scale'])
  8484         luxProp(scene, "film.scale", "100 %").set(opts['--scale'])
  8485 
  8486     if opts.has_key('--haltspp'):
  8487         print("haltspp: %s" %opts['--haltspp'])
  8488         luxProp(scene, "haltspp", 0).set(int(opts['--haltspp']))
  8489 
  8490     if opts.has_key('-s'):
  8491         print("Start frame: %s" %opts['-s'])
  8492         context.startFrame(int(opts['-s']))
  8493     else:
  8494         print("Error: Start frame not supplied (-s)"); osys.exit(1)
  8495     if opts.has_key('-e'):
  8496         print("End frame: %s" %opts['-e'])
  8497         context.endFrame(int(opts['-e']))
  8498     else:
  8499         print("Error: End frame not supplied (-e)")
  8500     if opts.has_key('-l'):
  8501         print("Path to LuxRender binary: %s" %opts['-l'])
  8502         luxbatchconsolemode = luxProp(scene, "luxbatchc", "false")
  8503         luxbatchconsolemode.set("true")
  8504         luxpathprop = luxProp(scene, "lux", "")
  8505         luxpathprop.set(opts['-l'])
  8506     else:
  8507         print("Error: path to LuxRender binary not supplied (-l)"); osys.exit(1)
  8508     if opts.has_key('-o'):
  8509         print("Image output path: %s" %opts['-o'])
  8510         luxProp(scene, "overrideoutputpath", "").set(opts['-o'])
  8511     else:
  8512         print("Error: image output path not supplied (-o)"); osys.exit(1)
  8513     if opts.has_key('-t'):
  8514         print("Temporary export path: %s" %opts['-t'])
  8515         luxProp(scene, "datadir", "").set(opts['-t'])
  8516     else:
  8517         print("Error: Temporary export path not supplied (-t)"); osys.exit(1)
  8518     
  8519     if opts.has_key('--lbm'):
  8520         print("Load material: %s" %opts['--lbm'])
  8521         mat = Material.Get("Material")
  8522         if mat: loadMatTex(mat, opts['--lbm'])
  8523         else:
  8524             print("Error: No material with name \"Material\" found (--lbm)"); osys.exit(1)
  8525             
  8526     if opts.has_key('--lbt'):
  8527         print("Load material: %s" %opts['--lbt'])
  8528         mat = Material.Get("Material")
  8529         if mat: loadMatTex(mat, opts['--lbt'], ':Kd')
  8530         else:
  8531             print("Error: No material with name \"Material\" found (--lbt)"); osys.exit(1)
  8532 
  8533 #    CBluxAnimExport(True, True)
  8534     CBluxAnimExport(True, True, False) # as by zukazuka (http://www.luxrender.net/forum/viewtopic.php?f=11&t=1288)
  8535     osys.exit(0)
  8536 
  8537 else:
  8538     print("\n\nLuxBlend v%s - UI mode\n"%__version__)
  8539     from Blender.Window import DrawProgressBar
  8540     LuxIsGUI = True
  8541     scn = Scene.GetCurrent()
  8542     
  8543     Draw.Register(luxDraw, luxEvent, luxButtonEvt) # init GUI
  8544     gcNamedVolumes(scn, LuxIsGUI)
  8545 
  8546     luxpathprop = luxProp(scn, "lux", "")
  8547     luxpath = luxpathprop.get()
  8548     luxrun = luxProp(scn, "run", True).get()
  8549     checkluxpath = luxProp(scn, "checkluxpath", True).get()
  8550 
  8551     if checkluxpath and luxrun:
  8552         if (luxpath is None) or (sys.exists(luxpath)<=0):
  8553             # luxpath not valid, so delete entry from .blend scene file
  8554             luxpathprop.delete()
  8555             # and re-get luxpath, so we get the path from default-settings
  8556             luxpath = luxpathprop.get()
  8557             #
  8558             LUXRENDER_ROOT = os.getenv('LUXRENDER_ROOT')
  8559             if LUXRENDER_ROOT is not None:
  8560                 LUXRENDER_ROOT = LUXRENDER_ROOT + os.sep
  8561                 luxpathprop.set(LUXRENDER_ROOT)
  8562                 luxpath = LUXRENDER_ROOT
  8563                 if sys.exists(luxpath)>0:
  8564                     print('LuxRender path set from LUXRENDER_ROOT environment variable')
  8565                     saveluxdefaults()
  8566             
  8567             if (luxpath is None) or (sys.exists(luxpath)<=0):
  8568                 print("WARNING: LuxRender path \"%s\" is not valid\n"%(luxpath))
  8569                 if scn:
  8570                     r = Draw.PupMenu("Installation: Set path to the LuxRender software?%t|Yes%x1|No%x0|Never%x2")
  8571                     if r == 1:
  8572                         Window.FileSelector(lambda s:luxProp(scn, "lux", "").set(Blender.sys.dirname(s)+os.sep), "Select file in LuxRender path")
  8573                         saveluxdefaults()
  8574                     if r == 2:
  8575                         newluxdefaults["checkluxpath"] = False
  8576                         saveluxdefaults()
  8577     else:
  8578         print("LuxRender path check disabled\n")