#!BPY """ Name: 'Sculpteo...' Blender: 249 Group: 'Export' Tooltip: 'Upload to Sculpteo' """ __author__ = "Vivien Chappelier" __url__ = ['http://www.sculpteo.com'] __version__ = "1.0" __bpydoc__ = """\ This script uploads a design on the Sculpteo website. Usage: Select the objects you wish to export and run this script from "File->Export" menu. Selecting the default options from the popup box will be good in most cases. All objects that can be represented as a mesh (mesh, curve, metaball, surface, text3d) will be exported. """ # ***** BEGIN GPL LICENSE BLOCK ***** # # Script copyright (C) Vivien Chappelier, Sculpteo 2010 # - based heavily on export_obj by Campbell J Barton # - V1.22- bspline import/export added (funded by PolyDimensions GmbH) # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # # ***** END GPL LICENCE BLOCK ***** # -------------------------------------------------------------------------- # # - stripped unrelevant obj export options # - added units import Blender from Blender import Mesh, Scene, Window, sys, Image, Draw import BPyMesh import BPyObject import BPySys import BPyMessages # Returns a tuple - path,extension. # 'hello.obj' > ('hello', '.obj') def splitExt(path): dotidx = path.rfind('.') if dotidx == -1: return path, '' else: return path[:dotidx], path[dotidx:] def fixName(name): if name == None: return 'None' else: return name.replace(' ', '_') # A Dict of Materials # (material.name, image.name):matname_imagename # matname_imagename has gaps removed. MTL_DICT = {} def write_mtl(filename): world = Blender.World.GetCurrent() if world: worldAmb = world.getAmb() else: worldAmb = (0,0,0) # Default value file = open(filename, "w") file.write('# Blender3D MTL File: %s\n' % Blender.Get('filename').split('\\')[-1].split('/')[-1]) file.write('# Material Count: %i\n' % len(MTL_DICT)) # Write material/image combinations we have used. for key, (mtl_mat_name, mat, img) in MTL_DICT.iteritems(): # Get the Blender data for the material and the image. # Having an image named None will make a bug, dont do it :) file.write('newmtl %s\n' % mtl_mat_name) # Define a new material: matname_imgname if mat: file.write('Ns %.6f\n' % ((mat.getHardness()-1) * 1.9607843137254901) ) # Hardness, convert blenders 1-511 to MTL's file.write('Ka %.6f %.6f %.6f\n' % tuple([c*mat.amb for c in worldAmb]) ) # Ambient, uses mirror colour, file.write('Kd %.6f %.6f %.6f\n' % tuple([c*mat.ref for c in mat.rgbCol]) ) # Diffuse file.write('Ks %.6f %.6f %.6f\n' % tuple([c*mat.spec for c in mat.specCol]) ) # Specular file.write('Ni %.6f\n' % mat.IOR) # Refraction index file.write('d %.6f\n' % mat.alpha) # Alpha (obj uses 'd' for dissolve) # 0 to disable lighting, 1 for ambient & diffuse only (specular color set to black), 2 for full lighting. if mat.getMode() & Blender.Material.Modes['SHADELESS']: file.write('illum 0\n') # ignore lighting elif mat.getSpec() == 0: file.write('illum 1\n') # no specular. else: file.write('illum 2\n') # light normaly else: #write a dummy material here? file.write('Ns 0\n') file.write('Ka %.6f %.6f %.6f\n' % tuple([c for c in worldAmb]) ) # Ambient, uses mirror colour, file.write('Kd 0.8 0.8 0.8\n') file.write('Ks 0.8 0.8 0.8\n') file.write('d 1\n') # No alpha file.write('illum 2\n') # light normaly # Write images! if img: # We have an image on the face! file.write('map_Kd %s\n' % img.filename.split('\\')[-1].split('/')[-1]) # Diffuse mapping image elif mat: # No face image. if we havea material search for MTex image. for mtex in mat.getTextures(): if mtex and mtex.tex.type == Blender.Texture.Types.IMAGE: try: filename = mtex.tex.image.filename.split('\\')[-1].split('/')[-1] file.write('map_Kd %s\n' % filename) # Diffuse mapping image break except: # Texture has no image though its an image type, best ignore. pass file.write('\n\n') file.close() def copy_file(source, dest): file = open(source, 'rb') data = file.read() file.close() file = open(dest, 'wb') file.write(data) file.close() def copy_images(dest_dir): if dest_dir[-1] != sys.sep: dest_dir += sys.sep # Get unique image names uniqueImages = {} for matname, mat, image in MTL_DICT.itervalues(): # Only use image name # Get Texface images if image: uniqueImages[image] = image # Should use sets here. wait until Python 2.4 is default. # Get MTex images if mat: for mtex in mat.getTextures(): if mtex and mtex.tex.type == Blender.Texture.Types.IMAGE: image_tex = mtex.tex.image if image_tex: try: uniqueImages[image_tex] = image_tex except: pass # Now copy images copyCount = 0 for bImage in uniqueImages.itervalues(): image_path = sys.expandpath(bImage.filename) if sys.exists(image_path): # Make a name for the target path. dest_image_path = dest_dir + image_path.split('\\')[-1].split('/')[-1] if not sys.exists(dest_image_path): # Image isnt alredy there print '\tCopying "%s" > "%s"' % (image_path, dest_image_path) copy_file(image_path, dest_image_path) copyCount+=1 print '\tCopied %d images' % copyCount def write(filename, objects,\ EXPORT_TRI=False,\ EXPORT_UV=True, EXPORT_MTL=True, \ EXPORT_APPLY_MODIFIERS=True, \ EXPORT_UNIT=1.0): ''' Basic write function. The context and options must be alredy set This can be accessed externaly eg. write( 'c:\\test\\foobar.obj', Blender.Object.GetSelected() ) # Using default options. ''' def veckey3d(v): return round(v.x, 6), round(v.y, 6), round(v.z, 6) def veckey2d(v): return round(v.x, 6), round(v.y, 6) def findVertexGroupName(face, vWeightMap): """ Searches the vertexDict to see what groups is assigned to a given face. We use a frequency system in order to sort out the name because a given vetex can belong to two or more groups at the same time. To find the right name for the face we list all the possible vertex group names with their frequency and then sort by frequency in descend order. The top element is the one shared by the highest number of vertices is the face's group """ weightDict = {} for vert in face: vWeights = vWeightMap[vert.index] for vGroupName, weight in vWeights: weightDict[vGroupName] = weightDict.get(vGroupName, 0) + weight if weightDict: alist = [(weight,vGroupName) for vGroupName, weight in weightDict.iteritems()] # sort least to greatest amount of weight alist.sort() return(alist[-1][1]) # highest value last else: return '(null)' print 'OBJ Export path: "%s"' % filename temp_mesh_name = '~tmp-mesh' time1 = sys.time() scn = Scene.GetCurrent() file = open(filename, "w") # Write Header file.write('# Blender3D v%s OBJ File: %s\n' % (Blender.Get('version'), Blender.Get('filename').split('/')[-1].split('\\')[-1] )) file.write('# www.blender3d.org\n') # Tell the obj file what material file to use. if EXPORT_MTL: mtlfilename = '%s.mtl' % '.'.join(filename.split('.')[:-1]) file.write('mtllib %s\n' % ( mtlfilename.split('\\')[-1].split('/')[-1] )) # Get the container mesh. - used for applying modifiers and non mesh objects. containerMesh = meshName = tempMesh = None for meshName in Blender.NMesh.GetNames(): if meshName.startswith(temp_mesh_name): tempMesh = Mesh.Get(meshName) if not tempMesh.users: containerMesh = tempMesh if not containerMesh: containerMesh = Mesh.New(temp_mesh_name) del meshName del tempMesh # Initialize totals, these are updated each object totverts = totuvco = totno = 1 face_vert_index = 1 globalNormals = {} # Get all meshes for ob_main in objects: for ob, ob_mat in BPyObject.getDerivedObjects(ob_main): # Will work for non meshes now! :) # getMeshFromObject(ob, container_mesh=None, apply_modifiers=True, vgroups=True, scn=None) me= BPyMesh.getMeshFromObject(ob, containerMesh, EXPORT_APPLY_MODIFIERS, False, scn) if not me: continue if EXPORT_UV: faceuv= me.faceUV else: faceuv = False # We have a valid mesh if EXPORT_TRI and me.faces: # Add a dummy object to it. has_quads = False for f in me.faces: if len(f) == 4: has_quads = True break if has_quads: oldmode = Mesh.Mode() Mesh.Mode(Mesh.SelectModes['FACE']) me.sel = True tempob = scn.objects.new(me) me.quadToTriangle(0) # more=0 shortest length oldmode = Mesh.Mode(oldmode) scn.objects.unlink(tempob) Mesh.Mode(oldmode) # Make our own list so it can be sorted to reduce context switching faces = [ f for f in me.faces ] edges = [] if not (len(faces)+len(edges)+len(me.verts)): # Make sure there is somthing to write continue # dont bother with this mesh. me.transform(ob_mat) # # Crash Blender #materials = me.getMaterials(1) # 1 == will return None in the list. materials = me.materials materialNames = [] materialItems = materials[:] if materials: for mat in materials: if mat: # !=None materialNames.append(mat.name) else: materialNames.append(None) # Cant use LC because some materials are None. # materialNames = map(lambda mat: mat.name, materials) # Bug Blender, dosent account for null materials, still broken. # Possible there null materials, will mess up indicies # but at least it will export, wait until Blender gets fixed. materialNames.extend((16-len(materialNames)) * [None]) materialItems.extend((16-len(materialItems)) * [None]) # Sort by Material, then images # so we dont over context switch in the obj file. if faceuv: try: faces.sort(key = lambda a: (a.mat, a.image, a.smooth)) except: faces.sort(lambda a,b: cmp((a.mat, a.image, a.smooth), (b.mat, b.image, b.smooth))) elif len(materials) > 1: try: faces.sort(key = lambda a: (a.mat, a.smooth)) except: faces.sort(lambda a,b: cmp((a.mat, a.smooth), (b.mat, b.smooth))) else: # no materials try: faces.sort(key = lambda a: a.smooth) except: faces.sort(lambda a,b: cmp(a.smooth, b.smooth)) # Set the default mat to no material and no image. contextMat = (0, 0) # Can never be this, so we will label a new material teh first chance we get. contextSmooth = None # Will either be true or false, set bad to force initialization switch. name1 = ob.name name2 = ob.getData(1) if name1 == name2: obnamestring = fixName(name1) else: obnamestring = '%s_%s' % (fixName(name1), fixName(name2)) file.write('o %s\n' % obnamestring) # Write Object name # Vert for v in me.verts: vs = (v.co[0] * EXPORT_UNIT, v.co[1] * EXPORT_UNIT, v.co[2] * EXPORT_UNIT) file.write('v %.6f %.6f %.6f\n' % tuple(vs)) # UV if faceuv: uv_face_mapping = [[0,0,0,0] for f in faces] # a bit of a waste for tri's :/ uv_dict = {} # could use a set() here for f_index, f in enumerate(faces): for uv_index, uv in enumerate(f.uv): uvkey = veckey2d(uv) try: uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] except: uv_face_mapping[f_index][uv_index] = uv_dict[uvkey] = len(uv_dict) file.write('vt %.6f %.6f\n' % tuple(uv)) uv_unique_count = len(uv_dict) del uv, uvkey, uv_dict, f_index, uv_index # Only need uv_unique_count and uv_face_mapping if not faceuv: f_image = None for f_index, f in enumerate(faces): f_v= f.v f_smooth= f.smooth f_mat = min(f.mat, len(materialNames)-1) if faceuv: f_image = f.image f_uv= f.uv # MAKE KEY if faceuv and f_image: # Object is always true. key = materialNames[f_mat], f_image.name else: key = materialNames[f_mat], None # No image, use None instead. # CHECK FOR CONTEXT SWITCH if key == contextMat: pass # Context alredy switched, dont do anything else: if key[0] == None and key[1] == None: # Write a null material, since we know the context has changed. file.write('usemtl (null)\n') # mat, image else: mat_data= MTL_DICT.get(key) if not mat_data: # First add to global dict so we can export to mtl # Then write mtl # Make a new names from the mat and image name, # converting any spaces to underscores with fixName. # If none image dont bother adding it to the name if key[1] == None: mat_data = MTL_DICT[key] = ('%s'%fixName(key[0])), materialItems[f_mat], f_image else: mat_data = MTL_DICT[key] = ('%s_%s' % (fixName(key[0]), fixName(key[1]))), materialItems[f_mat], f_image file.write('usemtl %s\n' % mat_data[0]) # can be mat_image or (null) contextMat = key if f_smooth != contextSmooth: if f_smooth: # on now off file.write('s 1\n') contextSmooth = f_smooth else: # was off now on file.write('s off\n') contextSmooth = f_smooth file.write('f') if faceuv: for vi, v in enumerate(f_v): file.write( ' %d/%d' % (\ v.index+totverts,\ totuvco + uv_face_mapping[f_index][vi])) # vert, uv face_vert_index += len(f_v) else: # No UV's for v in f_v: file.write( ' %d' % (\ v.index+totverts)) file.write('\n') # Make the indicies global rather then per mesh totverts += len(me.verts) if faceuv: totuvco += uv_unique_count me.verts= None file.close() # Now we have all our materials, save them if EXPORT_MTL: write_mtl(mtlfilename) if True: dest_dir = filename # Remove chars until we are just the path. while dest_dir and dest_dir[-1] not in '\\/': dest_dir = dest_dir[:-1] if dest_dir: copy_images(dest_dir) else: print '\tError: "%s" could not be used as a base for an image path.' % filename print "OBJ Export time: %.2f" % (sys.time() - time1) def write_ui(filename): if not filename.lower().endswith('.obj'): filename += '.obj' if not BPyMessages.Warning_SaveOver(filename): return global EXPORT_APPLY_MODIFIERS, EXPORT_TRI,\ EXPORT_UV,\ EXPORT_MTL, EXPORT_SEL_ONLY, EXPORT_ALL_SCENES,\ EXPORT_ANIMATION,\ EXPORT_UNIT_MM, EXPORT_UNIT_CM, EXPORT_UNIT_M, EXPORT_UNIT_IN, EXPORT_UNIT_FT, EXPORT_UNIT_YD EXPORT_APPLY_MODIFIERS = Draw.Create(1) EXPORT_TRI = Draw.Create(0) EXPORT_UV = Draw.Create(1) EXPORT_MTL = Draw.Create(1) EXPORT_SEL_ONLY = Draw.Create(0) EXPORT_ALL_SCENES = Draw.Create(1) EXPORT_ANIMATION = Draw.Create(0) EXPORT_UNIT_MM = Draw.Create(0) EXPORT_UNIT_CM = Draw.Create(1) EXPORT_UNIT_M = Draw.Create(0) EXPORT_UNIT_IN = Draw.Create(0) EXPORT_UNIT_FT = Draw.Create(0) EXPORT_UNIT_YD = Draw.Create(0) # BEGIN ALTERNATIVE UI ******************* if True: EVENT_NONE = 0 EVENT_EXIT = 1 EVENT_REDRAW = 2 EVENT_EXPORT = 3 UNIT_MM = 1 UNIT_CM = 2 UNIT_M = 3 UNIT_IN = 4 UNIT_FT = 5 UNIT_YD = 6 GLOBALS = {} GLOBALS['EVENT'] = EVENT_REDRAW #GLOBALS['MOUSE'] = Window.GetMouseCoords() GLOBALS['MOUSE'] = [i/2 for i in Window.GetScreenSize()] GLOBALS['UNIT'] = UNIT_CM def obj_ui_set_event(e,v): GLOBALS['EVENT'] = e def do_help(e,v): url = __url__[0] print 'Trying to open web browser with documentation at this address...' print '\t' + url try: import webbrowser webbrowser.open(url) except: print '...could not open a browser window.' def do_units(e,v): global EXPORT_UNIT_MM, EXPORT_UNIT_CM, EXPORT_UNIT_M, EXPORT_UNIT_FT, EXPORT_UNIT_YD GLOBALS['UNIT'] = e EXPORT_UNIT_MM.val = EXPORT_UNIT_CM.val = EXPORT_UNIT_M.val = EXPORT_UNIT_IN.val = EXPORT_UNIT_FT.val = EXPORT_UNIT_YD.val = 0 if e == UNIT_MM: EXPORT_UNIT_MM.val = 1 elif e == UNIT_CM: EXPORT_UNIT_CM.val = 1 elif e == UNIT_M: EXPORT_UNIT_M.val = 1 elif e == UNIT_IN: EXPORT_UNIT_IN.val = 1 elif e == UNIT_FT: EXPORT_UNIT_FT.val = 1 elif e == UNIT_YD: EXPORT_UNIT_YD.val = 1 def obj_ui(): ui_x, ui_y = GLOBALS['MOUSE'] # Center based on overall pup size ui_x -= 165 ui_y -= 140 global EXPORT_APPLY_MODIFIERS, EXPORT_TRI,\ EXPORT_UV,\ EXPORT_MTL, EXPORT_SEL_ONLY, EXPORT_ALL_SCENES,\ EXPORT_ANIMATION,\ EXPORT_UNIT_MM, EXPORT_UNIT_CM, EXPORT_UNIT_M, EXPORT_UNIT_FT, EXPORT_UNIT_IN, EXPORT_UNIT_YD Draw.Label('Context...', ui_x+9, ui_y+239, 220, 20) Draw.BeginAlign() EXPORT_SEL_ONLY = Draw.Toggle('Selection Only', EVENT_NONE, ui_x+9, ui_y+219, 110, 20, EXPORT_SEL_ONLY.val, 'Only export objects in visible selection. Else export whole scene.') EXPORT_ALL_SCENES = Draw.Toggle('All Scenes', EVENT_NONE, ui_x+119, ui_y+219, 110, 20, EXPORT_ALL_SCENES.val, 'Each scene as a separate OBJ file.') EXPORT_ANIMATION = Draw.Toggle('Animation', EVENT_NONE, ui_x+229, ui_y+219, 110, 20, EXPORT_ANIMATION.val, 'Each frame as a numbered OBJ file.') Draw.EndAlign() Draw.Label('Output Options...', ui_x+9, ui_y+189, 220, 20) Draw.BeginAlign() EXPORT_APPLY_MODIFIERS = Draw.Toggle('Apply Modifiers', EVENT_REDRAW, ui_x+9, ui_y+170, 110, 20, EXPORT_APPLY_MODIFIERS.val, 'Use transformed mesh data from each object. May break vert order for morph targets.') EXPORT_TRI = Draw.Toggle('Triangulate', EVENT_NONE, ui_x+119, ui_y+170, 70, 20, EXPORT_TRI.val, 'Triangulate quads.') Draw.EndAlign() Draw.Label('Export...', ui_x+9, ui_y+139, 220, 20) Draw.BeginAlign() EXPORT_MTL = Draw.Toggle('Materials', EVENT_NONE, ui_x+9, ui_y+120, 70, 20, EXPORT_MTL.val, 'Write a separate MTL file with the OBJ.') EXPORT_UV = Draw.Toggle('UVs', EVENT_NONE, ui_x+79, ui_y+120, 31, 20, EXPORT_UV.val, 'Export texface UV coords.') Draw.EndAlign() Draw.Label('Dimensions...', ui_x+9, ui_y+89, 220, 20) Draw.BeginAlign() EXPORT_UNIT_MM = Draw.Toggle('mm', UNIT_MM, ui_x+9, ui_y+70, 30, 20, EXPORT_UNIT_MM.val, 'millimeter', do_units) EXPORT_UNIT_CM = Draw.Toggle('cm', UNIT_CM, ui_x+39, ui_y+70, 30, 20, EXPORT_UNIT_CM.val, 'centimeter', do_units) EXPORT_UNIT_M = Draw.Toggle('m', UNIT_M, ui_x+69, ui_y+70, 30, 20, EXPORT_UNIT_M.val, 'meter', do_units) EXPORT_UNIT_IN= Draw.Toggle('in', UNIT_IN, ui_x+99, ui_y+70, 30, 20, EXPORT_UNIT_IN.val, 'inch', do_units) EXPORT_UNIT_FT = Draw.Toggle('ft', UNIT_FT, ui_x+129, ui_y+70, 30, 20, EXPORT_UNIT_FT.val, 'foot', do_units) EXPORT_UNIT_YD = Draw.Toggle('yd', UNIT_YD, ui_x+159, ui_y+70, 30, 20, EXPORT_UNIT_YD.val, 'yard', do_units) Draw.EndAlign() Draw.BeginAlign() Draw.PushButton('Online Help', EVENT_REDRAW, ui_x+9, ui_y+9, 110, 20, 'Load the wiki page for this script', do_help) Draw.PushButton('Cancel', EVENT_EXIT, ui_x+119, ui_y+9, 110, 20, '', obj_ui_set_event) Draw.PushButton('Export', EVENT_EXPORT, ui_x+229, ui_y+9, 110, 20, 'Export with these settings', obj_ui_set_event) Draw.EndAlign() # hack so the toggle buttons redraw. this is not nice at all while GLOBALS['EVENT'] not in (EVENT_EXIT, EVENT_EXPORT): Draw.UIBlock(obj_ui, 0) if GLOBALS['EVENT'] != EVENT_EXPORT: return False # END ALTERNATIVE UI ********************* Window.EditMode(0) Window.WaitCursor(1) EXPORT_APPLY_MODIFIERS = EXPORT_APPLY_MODIFIERS.val EXPORT_TRI = EXPORT_TRI.val EXPORT_UV = EXPORT_UV.val EXPORT_MTL = EXPORT_MTL.val EXPORT_SEL_ONLY = EXPORT_SEL_ONLY.val EXPORT_ALL_SCENES = EXPORT_ALL_SCENES.val EXPORT_ANIMATION = EXPORT_ANIMATION.val EXPORT_UNIT_MM = EXPORT_UNIT_MM.val EXPORT_UNIT_CM = EXPORT_UNIT_CM.val EXPORT_UNIT_M = EXPORT_UNIT_M.val EXPORT_UNIT_IN = EXPORT_UNIT_IN.val EXPORT_UNIT_FT = EXPORT_UNIT_FT.val EXPORT_UNIT_YD = EXPORT_UNIT_YD.val EXPORT_UNIT = 1.0 if EXPORT_UNIT_MM: EXPORT_UNIT = 0.1 if EXPORT_UNIT_M: EXPORT_UNIT = 100 if EXPORT_UNIT_IN: EXPORT_UNIT = 2.54 if EXPORT_UNIT_FT: EXPORT_UNIT = 30.48 if EXPORT_UNIT_YD: EXPORT_UNIT = 91.44 base_name, ext = splitExt(filename) context_name = [base_name, '', '', ext] # basename, scene_name, framenumber, extension # Use the options to export the data using write() # def write(filename, objects, EXPORT_MTL=True, EXPORT_APPLY_MODIFIERS=True): orig_scene = Scene.GetCurrent() if EXPORT_ALL_SCENES: export_scenes = Scene.Get() else: export_scenes = [orig_scene] # Export all scenes. for scn in export_scenes: scn.makeCurrent() # If alredy current, this is not slow. context = scn.getRenderingContext() orig_frame = Blender.Get('curframe') if EXPORT_ALL_SCENES: # Add scene name into the context_name context_name[1] = '_%s' % BPySys.cleanName(scn.name) # WARNING, its possible that this could cause a collision. we could fix if were feeling parranoied. # Export an animation? if EXPORT_ANIMATION: scene_frames = xrange(context.startFrame(), context.endFrame()+1) # up to and including the end frame. else: scene_frames = [orig_frame] # Dont export an animation. # Loop through all frames in the scene and export. for frame in scene_frames: if EXPORT_ANIMATION: # Add frame to the filename. context_name[2] = '_%.6d' % frame Blender.Set('curframe', frame) if EXPORT_SEL_ONLY: export_objects = scn.objects.context else: export_objects = scn.objects full_path= ''.join(context_name) # erm... bit of a problem here, this can overwrite files when exporting frames. not too bad. # EXPORT THE FILE. write(full_path, export_objects,\ EXPORT_TRI, EXPORT_UV, EXPORT_MTL,\ EXPORT_APPLY_MODIFIERS, EXPORT_UNIT) Blender.Set('curframe', orig_frame) # Restore old active scene. orig_scene.makeCurrent() Window.WaitCursor(0) return True # workaround a runtime error message caused by uuid using ctypes on windows # see http://groups.google.com/group/pyinstaller/browse_thread/thread/a2de39dda63c1508 for details from sys import platform if platform == 'win32': try: import ctypes, ctypes.util ctypes.util.find_library = None except: pass from uuid import uuid1 import webbrowser import tempfile import os import urllib import urllib2 import cookielib import mimetools import mimetypes import stat from shutil import rmtree from zipfile import ZipFile # MultipartPostHandler #### # 02/2006 Will Holcomb # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # class Callable: def __init__(self, anycallable): self.__call__ = anycallable # Controls how sequences are uncoded. If true, elements may be given # multiple values by assigning a sequence. doseq = 1 class MultipartPostHandler(urllib2.BaseHandler): handler_order = urllib2.HTTPHandler.handler_order - 10 # needs to run first def http_request(self, request): data = request.get_data() if data is not None and type(data) != str: v_files = [] v_vars = [] try: for(key, value) in data.items(): if type(value) == file: v_files.append((key, value)) else: v_vars.append((key, value)) except TypeError: systype, value, traceback = sys.exc_info() raise TypeError, "not a valid non-string sequence or mapping object", traceback if len(v_files) == 0: data = urllib.urlencode(v_vars, doseq) else: boundary, data = self.multipart_encode(v_vars, v_files) contenttype = 'multipart/form-data; boundary=%s' % boundary if(request.has_header('Content-Type') and request.get_header('Content-Type').find('multipart/form-data') != 0): print "Replacing %s with %s" % (request.get_header('content-type'), 'multipart/form-data') request.add_unredirected_header('Content-Type', contenttype) request.add_data(data) return request def multipart_encode(vars, files, boundary = None, buffer = None): if boundary is None: boundary = mimetools.choose_boundary() if buffer is None: buffer = '' for(key, value) in vars: buffer += '--%s\r\n' % boundary buffer += 'Content-Disposition: form-data; name="%s"' % key buffer += '\r\n\r\n' + value + '\r\n' for(key, fd) in files: file_size = os.fstat(fd.fileno())[stat.ST_SIZE] filename = os.path.basename(fd.name) contenttype = mimetypes.guess_type(filename)[0] or 'application/octet-stream' buffer += '--%s\r\n' % boundary buffer += 'Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key, filename) buffer += 'Content-Type: %s\r\n' % contenttype # buffer += 'Content-Length: %s\r\n' % file_size fd.seek(0) buffer += '\r\n' + fd.read() + '\r\n' buffer += '--%s--\r\n\r\n' % boundary return boundary, buffer multipart_encode = Callable(multipart_encode) https_request = http_request cookies = cookielib.CookieJar() opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookies), MultipartPostHandler) urllib2.install_opener(opener) # upload #### SCULPTEO_SERVER = 'http://www.sculpteo.com' #SCULPTEO_SERVER = 'http://localhost:8000' SCULPTEO_UPLOADFILE = 'gallery/uploadfile' SCULPTEO_UPLOAD = 'gallery/3D/upload' def post_design(filename, name, server, lang='en'): uuid = uuid1().int # post the file upload_post = { 'file': open(filename, 'rb') } upload_url = '%s/%s/%s/?X-Progress-ID=%s' % (server, lang, SCULPTEO_UPLOADFILE, uuid) print 'POST %s arg %s' % (upload_url, upload_post) page = urllib2.urlopen(upload_url, upload_post) # redirect to upload page to add name, description, etc... webbrowser.open_new('%s/%s/%s/?X-Progress-ID=%s&name=%s' % (server, lang, SCULPTEO_UPLOAD, uuid, name)) if __name__ == '__main__': scenename = Scene.GetCurrent().getName() # export to obj filename = 'upload.obj' subdir = tempfile.mkdtemp() try: tmpfile = os.path.join(subdir, filename) if write_ui(tmpfile): # zip the directory zipfd, zipfilename = tempfile.mkstemp('.zip') try: zipfile = ZipFile(zipfilename, mode='w') for name in os.listdir(subdir): zipfile.write(os.path.join(subdir, name), arcname = name) zipfile.close() os.close(zipfd) # send it post_design(zipfilename, scenename, SCULPTEO_SERVER) finally: os.unlink(zipfilename) finally: rmtree(subdir)