#!BPY #************************************************************************************************** # Supreme Commander Exporter for Blender3D - www.blender3d.org # # Written by dan - www.sup-com.net), Brent (www.scmods.net) # # further improvements by GeomanNL and Darius # # History # # 0.1.0 2006-07-02 Dan Initial version. # # 0.2.0 2007-03-11 Brent Fixed UV coords, V was inverted. # Support for exporting quads. # Fixed a padding issue. # # 0.3.0 2007-03-18 Dan Code refactoring / Clean up. # Fixed 'INFO' section size in header. # # 0.3.3 2007-09-25 GeomanNL fixed a file-write bug # orientation fix, changed to matrix rotation # other excellent stuff # (me darius took the freedom to write your entry:D) # # 0.3.5 2009-03-20 Darius_ tangent and binormal calc # vertex optimation # blender front to supcom front # some more fixes and reorganizing/cleanup code # # # Todo # - GUI improvements # - Support for LOD exporting. Eg. not merging all meshes for an armature into one mech but rather only # sub-meshes and export the top level meshes to different files. # - Validation, ensure that # - Prompt before overwriting files & check that directories exists # - Second UV set? # - Set animation time per frame # - Export LUA script for use in the animation viewer (eg, start anim, set texture etc).. # - Set root rot/pos for sca # #************************************************************************************************** """ Name: 'Supreme Commander 3.5' Blender: 242 Group: 'Export' Tooltip: 'Model / Animation Exporter for Supreme Commander' """ import Blender import struct from Blender.BGL import * from Blender import Draw from Blender.Draw import * from Blender import Mathutils from Blender.Mathutils import * from Blender import Window import os from os import path VERSION = '3.5' ###################################################### # User defined behaviour, Select as you need ###################################################### #if you want to leave an info in your scm file put it in there USER_INFO = "" #Enable Progress Bar ( 0 = faster ) PROG_BAR_ENABLE = 1 #how many steps a progress bar has (the lesser the faster) PROG_BAR_STEP = 25 #slower - reduce vertex amount VERTEX_OPTIMIZE = 1 #LOG File for debuging #Enable LOG File (0 = Disabled , 1 = Enabled ) LOG_ENABLE = 0 #Filename / Path. Default is blender directory Filename SC-E_LOG.txt LOG_FILENAME = "SC-E_LOG.txt" LOG_BONE = 1 LOG_VERT = 0 ###################################################### # Init Supreme Commander SCM( _bone, _vertex, _mesh), SCA(_bone, _frame, _anim) Layout ###################################################### #Transform matrix z -> yx -> xy -> z xy_to_xz_transform = Matrix([ 1, 0, 0], [ 0, 0, -1], [ 0, 1, 0]) #xy_to_xz_transform = Matrix([ 1, 0, 0], [ 0, 1, 0], [ 0, 0, 1]) # Armature world matrix MArmatureWorld = Matrix() BONES = [] ANIMATION_DURATION = 1.5 class scm_bone : rest_pose = [] rest_pose_inv = [] rotation = [] position = [] parent_index = 0 used = False name = "" def __init__(self, name, rest_pose_inv, rotation, position, parent_index): self.rest_pose_inv = rest_pose_inv self.rotation = rotation self.position = position self.parent_index = parent_index self.used = False self.name = name def save(self, file): bonestruct = '16f3f4f4i' #bonestruct = '16f3f4f4L' #Deprecation warning L and mistyrious binary output rp_inv = [0] * 16 icount = 0 for irow in xrange(4): #rest pose_inv for icol in xrange(4): rp_inv[icount] = self.rest_pose_inv[irow][icol] icount = icount + 1 bonedata = struct.pack(bonestruct, rp_inv[0], rp_inv[1], rp_inv[2], rp_inv[3], rp_inv[4], rp_inv[5], rp_inv[6], rp_inv[7], rp_inv[8], rp_inv[9], rp_inv[10],rp_inv[11], rp_inv[12],rp_inv[13],rp_inv[14],rp_inv[15], self.position[0],self.position[1],self.position[2], self.rotation.w,self.rotation.x,self.rotation.y,self.rotation.z, #Quaternion (w,x,y,z)#w,x,y,z self.name_offset, self.parent_index, 0,0) #print self.name #print self.rest_pose_inv if LOG_BONE : LOGn(" %s rp_inv: [%.3f, %.3f, %.3f, %.3f],\t [%.3f, %.3f, %.3f, %.3f],\t [%.3f, %.3f, %.3f, %.3f],\t [%.3f, %.3f, %.3f, %.3f] \tpos: [%.3f, %.3f, %.3f] \trot: [%.3f, %.3f, %.3f, %.3f] %d" % ( self.name, rp_inv[0], rp_inv[1], rp_inv[2], rp_inv[3], rp_inv[4], rp_inv[5], rp_inv[6], rp_inv[7], rp_inv[8], rp_inv[9], rp_inv[10],rp_inv[11], rp_inv[12],rp_inv[13],rp_inv[14],rp_inv[15], self.position[0],self.position[1],self.position[2], self.rotation[0],self.rotation[1],self.rotation[2],self.rotation[3], self.parent_index)) file.write(bonedata) class scm_vertex : global xy_to_xz_transform position = [] tangent = [] normal = [] binormal = [] uvc = 0 uv1 = [] uv2 = [] bone_index = [] def __init__(self, pos , no , uv1, bone_index): self.position = pos self.normal = no #tangent and binormal wil be calculated by face self.tangent = Vector( 0, 0, 0) self.binormal = Vector( 0, 0, 0) self.uvc = 1 self.uv1 = uv1 self.uv2 = uv1# Vector(0,0) #uv1 #better results with copy ... strange, where is the use of that? self.bone_index = bone_index def save(self, file): vertstruct = '3f3f3f3f2f2f4B' #so finaly we can norm because here it is sure that no tang norm will be added #self.normal = CrossVecs(self.tangent, self.binormal).normalize() self.tangent.normalize() self.binormal.normalize() self.normal.normalize() if False : self.tangent = Vector(0,0,0) self.binormal= Vector(0,0,0) #self.normal = Vector(0,0,0) if LOG_VERT : LOGn( " pos: [%.3f, %.3f, %.3f] \tn: [%.3f, %.3f, %.3f] \tt: [%.3f, %.3f, %.3f] \tb: [%.3f, %.3f, %.3f] \tuv [ %.3f, %.3f | %.3f, %.3f ] \tbi: [%d, %d, %d, %d]" % ( self.position[0], self.position[1], self.position[2], self.normal[0], self.normal[1], self.normal[2], self.tangent[0], self.tangent[1], self.tangent[2], self.binormal[0], self.binormal[1], self.binormal[2], self.uv1[0], self.uv1[1], self.uv2[0], self.uv2[1], self.bone_index[0], self.bone_index[1], self.bone_index[2], self.bone_index[3]) ) # so you store in this order: # pos, normal, tangent, binormal, uv1, uv2, ibone vertex = struct.pack(vertstruct, self.position[0], self.position[1], self.position[2], self.normal[0], self.normal[1], self.normal[2], self.tangent[0], self.tangent[1], self.tangent[2], self.binormal[0], self.binormal[1], self.binormal[2], self.uv1[0], self.uv1[1], self.uv2[0], self.uv2[1], self.bone_index[0], self.bone_index[1], self.bone_index[2], self.bone_index[3]) file.write(vertex) #helper the real scm face 'tupel is stored in mesh #quad face class qFace : vertex_cont = [] def __init__(self): self.vertex_cont = [] def addVert(self, vertex): self.vertex_cont.extend( vertex ) def addToMesh(self, mesh): face1 = Face() face1.addVert([ self.vertex_cont[0], self.vertex_cont[1], self.vertex_cont[2] ]) face1.CalcTB() face2 = Face() face2.addVert([ self.vertex_cont[2], self.vertex_cont[3], self.vertex_cont[0] ]) face2.CalcTB() mesh.addQFace(face1, face2) #helper the real scm face 'tupel is stored in mesh #tri face class Face : vertex_cont = [] def __init__(self): self.vertex_cont = [] def addVert(self, vertex): self.vertex_cont.extend(vertex) #now contains 3 vertexes calculate bi and ta and add to mesh def CalcTB( self ) : vert1 = self.vertex_cont[0] vert2 = self.vertex_cont[1] vert3 = self.vertex_cont[2] uv = [ vert1.uv1, vert2.uv1, vert3.uv1] # Calculate Tangent and Binormal # (v3 - v1).(p2 - p1) - (v2 - v1).(p3 - p1) # T = ------------------------------------------------ # (u2 - u1).(v3 - v1) - (v2 - v1).(u3 - u1) # (u3 - u1).(p2 - p1) - (u2 - u1).(p3 - p1) # B = ------------------------------------------------- # (v2 - v1).(u3 - u1) - (u2 - u1).(v3 - v1) P2P1 = vert2.position - vert1.position P3P1 = vert3.position - vert1.position #UV2UV1 = [ uv[1][0]-uv[0][0], uv[1][1]-uv[0][1] ] #UV3UV1 = [ uv[2][0]-uv[0][0], uv[2][1]-uv[0][1] ] UV2UV1 = uv[1] - uv[0] UV3UV1 = uv[2] - uv[0] divide = (UV2UV1[1]*UV3UV1[0] - UV2UV1[0]*UV3UV1[1]) if ( divide != 0.0 ) : tangent = Vector((UV3UV1[1]*P2P1 - UV2UV1[1]*P3P1)/(divide)) binormal = Vector((UV3UV1[0]*P2P1 - UV2UV1[0]*P3P1)/(-divide)) else : countLOG(" 0: infooffset = pad_file(scm, 'INFO') for i in range(len(self.info)): info = self.info[i] infolen = len(info) + 1 buffer = struct.pack(str(infolen)+'s', info) scm.write(buffer) infosize = scm.tell() - infooffset; # Now we can update the header scm.seek(0, 0) header = struct.pack(headerstruct, marker, version, boneoffset, bonecount, vertoffset, extravertoffset, vertcount, indexoffset, indexcount, infooffset, infosize, totalbonecount) scm.write(header) scm.close() class sca_bone: position = Vector( 0, 0, 0) rotation = Quaternion( 0, 0, 0, 0 ) def __init__(self, pos, rot): self.position = pos self.rotation = rot class sca_frame: keytime = 0.0 keyflags = 0 bones = [] anim = None def __init__(self, anim): self.keytime = 0.0 self.keyflags = 0 self.anim = anim self.bones = [] def save(self, file): frameheader_fmt = 'fl' frameheader_size = struct.calcsize(frameheader_fmt) posrot_fmt = '3f4f' posrot_size = struct.calcsize(posrot_fmt) # Frame header buffer = struct.pack(frameheader_fmt, self.keytime, self.keyflags) file.write(buffer) #Log(":%d:" % (len(self.bones))) # Bones for bone in self.bones: buffer = struct.pack( posrot_fmt, bone.position.x, bone.position.y, bone.position.z, bone.rotation.w, bone.rotation.x, bone.rotation.y, bone.rotation.z) file.write(buffer) class sca_anim : frames = [] bonelinks = [] bonenames = [] duration = 0.0 def __init__(self): global ANIMATION_DURATION self.frames = [] self.bonelinks = [] self.bonenames = [] self.duration = ANIMATION_DURATION def save(self, filename): LOGp('Writing SCA...') #self.filename = filename sca = file(filename, 'wb') headerstruct = '4sllflllll' # Write temp header magic = 'ANIM' version = 5 numframes = len(self.frames) numbones = len(self.bonenames) namesoffset = 0 linksoffset = 0 animoffset = 0 framesize = 0 header = struct.pack(headerstruct, magic, version, numframes, self.duration, numbones, namesoffset, linksoffset, animoffset, framesize) #note: this header is seen correctly by the GPG dumpsca.py sca.write(header) # Write bone names namesoffset = pad_file(sca, 'NAME') for bone_name in self.bonenames: buffer = struct.pack(str(len(bone_name) + 1)+'s', bone_name) sca.write(buffer) # Write bone links linksoffset = pad_file(sca, 'LINK') for link in self.bonelinks: buffer = struct.pack('l', link) sca.write(buffer) # Write data animoffset = pad_file(sca, 'DATA') #the space occupied by postion and rotation info on the bones. posrot_fmt = '3f4f' posrot_size = struct.calcsize(posrot_fmt) #this writes the position/rotation of the root bone in the first animation, as if it is at position 0, and no rotation #note: it looks like rot=1,0,0,0 is needed to indicate no rotation. buffer = struct.pack(posrot_fmt, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0) sca.write(buffer) for frame in self.frames: framesize = sca.tell() frame.save(sca) framesize = sca.tell() - framesize # Update header sca.seek(0, 0) header = struct.pack(headerstruct, magic, version, numframes, self.duration, numbones, namesoffset, linksoffset, animoffset, framesize) sca.write(header) sca.close() LOGp( "Bones: %d, Frames: %d;\n" % (numbones, numframes) ) #Log('OFFSETS: names = %d links = %d anim = %d framesize = %d' % (namesoffset, linksoffset, animoffset, framesize)) ###################################################### # Exporter Functions ###################################################### # Helper methods ###################################################### def pad(size): val = 32 - (size % 32) if (val < 4): val = val + 32 return val def pad_file(file, s4comment): N = pad(file.tell()) - 4 filldata = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX' padding = struct.pack(str(N)+'s4s', filldata[0:N], s4comment) file.write(padding) return file.tell() # ###################################################### # Helper method for itterating through the bone tree def itterate_bones(mesh, bone, parent = None, scm_parent_index = -1): global MArmatureWorld global BONES global xy_to_xz_transform if (parent != None and bone.parent.name != parent.name): PupMenu("Error: Invalid parenting in bone ... multiple parents?!%t|OK") Exit() #print "Invalid parenting in bone", bone.name," and parent ", parent.name return b_rest_pose = Matrix([0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]) b_rest_pose_inv = Matrix([0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]) b_rotation = Quaternion( 0,0,0,0 ) b_position = Vector( 0,0,0 ) b_index = len(mesh.bones) #MArmatureWorld = Matrix(arm_obj.matrixWorld) bone_matrix = Matrix(bone.matrix['ARMATURESPACE']) # Calculate the inverse rest pose for the bone #instead bonearmmat*worldmat = Matrix['BONESPACE'] b_rest_pose = Matrix( bone_matrix * MArmatureWorld ) b_rest_pose_inv = Matrix( b_rest_pose * xy_to_xz_transform ).invert() if (parent == None): rel_mat = b_rest_pose * xy_to_xz_transform #root pos is the same as the rest-pose else: parent_matrix_inv = Matrix( parent.matrix['ARMATURESPACE'] ).invert() rel_mat = Matrix(bone_matrix * parent_matrix_inv) # must be BM * PMI in that order # do not use an extra (absolute) extra rotation here, cause this is only relative # Position & Rotation relative to parent (if there is a parent) b_rotation = rel_mat.toQuat()#.normalize() #row 3, cols 0,1,2 indicate position b_position = Vector( rel_mat[3] ) #def __init__(self, name, rest_pose_inv, rotation, position, parent_index): sc_bone = scm_bone( bone.name, b_rest_pose_inv, b_rotation, b_position, scm_parent_index ) BONES.append(sc_bone) mesh.bones.append(sc_bone) # recursive call for all children if (bone.children != None): for child in bone.children: itterate_bones( mesh, child, bone, b_index ) def make_scm(arm_obj): global MArmatureWorld global xy_to_xz_transform arm = arm_obj.getData() scn = Blender.Scene.GetCurrent() # Get all mesh objects for the selected armature & calculate progbar length pb_length = 0 mesh_objs = [] for obj in scn.objects: if obj.parent == arm_obj and obj.getType() == 'Mesh': #calculate progbar length bmesh_data = obj.getData(False, True) pb_length += len(bmesh_data.faces) mesh_objs.append(obj) ProgBarFaces = ProgressBar( "Exp: Verts", pb_length ) # Create SCM Mesh supcom_mesh = scm_mesh() # Traverse the bone tree and check if there is one root bone numroots = 0 for bone in arm.bones.values(): if (bone.parent == None): numroots += 1 itterate_bones(supcom_mesh, bone) if numroots > 1: PupMenu("Error: there are multiple root bones -> check you bone relations!%t|OK") Exit() return #this inserts a converted armature back into the existing model, to see #if the bone locations are correct (was used for debugging) #test_the_armature = 1 #if test_the_armature: # #create an extra test armature # testarm_data= Blender.Armature.New('testArmature') # testarm_ob = scn.objects.new(testarm_data) # testarm_data.makeEditable() # prev_eb = Non # for bone in supcom_mesh.bones: # print "mesh bone: ", bone.name # eb = Blender.Armature.Editbone() # eb.name = bone.name # rest_pose = bone.rest_pose[3] # eb.head = Vector(rest_pose[0], rest_pose[1], rest_pose[2]) # eb.tail = eb.head + Vector(1,0,0) # if bone.parent_index != -1: # eb.parent = prev_eb # prev_eb = eb # testarm_data.bones[eb.name]= eb # testarm_data.update() # Process all the meshes for mesh_obj in mesh_objs: bmesh_data = mesh_obj.getData(False, True) if not bmesh_data.faceUV : PupMenu("Error: Mesh has no texture values -> Please set your UV!%t|OK") Exit() return MatrixMesh = Matrix(mesh_obj.matrixWorld) mesh_name = mesh_obj.name for face in bmesh_data.faces: ProgBarFaces.do() vertList = [] for i in xrange(len(face.verts)): vert = face.verts[i] v_nor = Vector( 0, 0, 0 ) v_pos = Vector( 0, 0, 0 ) v_uv1 = Vector( 0, 0) #SC allows 2 uv's v_boneIndex = [0]*4 # SC supports up to 4 bones we will use only one #v_boneIndex = [-1,0,0,0] #Find controling bone v_boneIndex[0] = -1 inf = bmesh_data.getVertexInfluences(vert.index) for j in xrange(len(inf)) : bonename = inf[j][0] for b in range(len(supcom_mesh.bones)): bone = supcom_mesh.bones[b] if bone.name == bonename: bone.used = True v_boneIndex[0] = b break if (v_boneIndex[0] == -1): v_boneIndex[0] = 0 Blender.Window.EditMode(0) vert.sel = 1 countLOG("Warning: Verticle without Bone Influence in %s. Selected " % (mesh_name)) v_pos = Vector( vert.co * (MatrixMesh * xy_to_xz_transform)) v_nor = vert.no * (MatrixMesh * xy_to_xz_transform) #needed cause supcom scans an image in the opposite vertical direction or something?. v_uv1 = Vector(face.uv[i][0], 1.0 - face.uv[i][1]) vertList.append( scm_vertex( v_pos, v_nor, v_uv1 , v_boneIndex) ) if len(vertList) > 3: newFace = qFace() else: newFace = Face() newFace.addVert(vertList) newFace.addToMesh(supcom_mesh) return supcom_mesh def make_sca(arm_obj, action): global BONES global MArmatureWorld global xy_to_xz_transform action.setActive(arm_obj) scene = Blender.Scene.GetCurrent() render_context = scene.getRenderingContext() endframe = render_context.endFrame() animation = sca_anim() #animation.duration = 1.5 # Add bone names & links for bone in BONES: animation.bonenames.append(bone.name) animation.bonelinks.append(bone.parent_index) LOGn('adding bone: %s with parent %d ' % (bone.name, bone.parent_index)) ProgBarAnimation = ProgressBar( "Exp: Anim", endframe) # Add frames frame_counter = 1 while frame_counter <= endframe: LOGn('adding frame %d of %d' % (frame_counter, endframe)) ProgBarAnimation.do() frame = sca_frame(animation) arm_obj.evaluatePose(frame_counter) frame_counter += 1 POSED_BONES = {} for posebone in arm_obj.getPose().bones.values(): POSED_BONES[posebone.name] = posebone.poseMatrix for bone in BONES: pose_bone_matrix = POSED_BONES[bone.name] if (bone.parent_index == -1): rel_mat = (Matrix(pose_bone_matrix) * MArmatureWorld) * xy_to_xz_transform else: rel_mat = Matrix(pose_bone_matrix) * Matrix(POSED_BONES[BONES[bone.parent_index].name]).invert() rotation = rel_mat.toQuat().normalize() #rot = rotation #[ rotation.w, rotation.x, rotation.y, rotation.z ] pos = Vector( rel_mat[3][0], rel_mat[3][1], rel_mat[3][2] ) anim_bone = sca_bone(pos, rotation) frame.bones.append(anim_bone) animation.frames.append(frame) return animation def export(outdir): global VERSION, USER_INFO global MArmatureWorld global xy_to_xz_transform w_edit_mode = Blender.Window.EditMode() #No animation export in editmode if w_edit_mode : Blender.Window.EditMode(0) Blender.Window.WaitCursor(1) xy_to_xz_transform.resize4x4() scn = Blender.Scene.GetCurrent() # Get Selected object(s) selected_objects = Blender.Object.GetSelected() # Look for an armature arm = None arm_obj = None for obj in selected_objects: data = obj.getData() if type(data) is Blender.Types.ArmatureType: arm = data arm_obj = obj break # Is there one armature? Take this one if arm == None : for obj in scn.objects: data = obj.getData() if obj.getType() == 'Armature' : if arm == None: arm = data arm_obj = obj else : arm = None break if arm == None: PupMenu("Error: Please select your armature.%t|OK") Exit() return # this defines the ARMATURE_SPACE. # all bones in the armature are positioned relative to this space. MArmatureWorld = Matrix(arm_obj.matrixWorld) # SCM LOGp(' ') LOGp(' ') LOGp('Exporting model: ' + arm_obj.name ) LOGp('----------------------------------') mesh = make_scm(arm_obj) if mesh == None : LOGp('Aborted!') return mesh.info.append('Exported with Blender SupCom-Exporter ' + VERSION) if USER_INFO != "" : mesh.info.append( USER_INFO ) mesh.save(outdir + arm_obj.name + '.scm') mesh = None # SCA actions = Blender.Armature.NLA.GetActions().iteritems() for action in actions: #action[0] = the key, action[1] = the dictionary ####maybe this could help? LOGp(' ') LOGp('Animation: ' + action[0]) animation = make_sca(arm_obj, action[1]) animation.save(outdir + action[0] + '.sca') Blender.Window.EditMode(w_edit_mode) Blender.Window.WaitCursor(0) LOGp('----------------------------------') closeLog() ###################################################### # GUI ###################################################### log = [] log_max_lines = 14 LOG_FILE = None #log to file def LOGn(message): global LOG_FILE global LOG_ENABLE global LOG_FILENAME if LOG_ENABLE : if LOG_FILE == None : LOG_FILE = open( LOG_FILENAME, 'w') LOG_FILE.write('SupCom Exporter LOG File:\n\n') LOGp( "LOG enabled: %s" % (LOG_FILENAME)) Log(message + '\n') else : LOG_FILE.write(message + '\n') #Log to file, to console and exp window def LOGp(message): global log, log_max_lines LOGn(message) print message log.append(message) if len(log) > log_max_lines: del log[0] counter = [] cLog = [] #log for a amount of errors like vertex errors def countLOG(message): global cLog global counter cont = False for i in xrange(len(cLog)): if cLog[i] == message: cont = True counter[i] +=1 break if not cont : cLog.append( message) counter.append( 1) def closeLog(): global cLog, LOG_FILE global counter for i in xrange(len(cLog)): LOGp("%s (Times:%d)" % (cLog[i], counter[i])) if LOG_FILE != None : LOG_FILE.close() Blender.Window.RedrawAll() class ProgressBar : global PROG_BAR_STEP global PROG_BAR_ENABLE progress = 0 progressold = 0 current = 0 end = 0 div = 0 text = "None" def __init__(self, text, end): self.progress = 0 self.progressold = 0 self.current = 0 self.end = end self.text = text self.div = PROG_BAR_STEP #it looks like blender needs to init this progress bar with 0.0 if PROG_BAR_ENABLE : Blender.Window.DrawProgressBar ( 0.0 , text) def do(self): if PROG_BAR_ENABLE : self.current += 1 self.progress = (self.current*self.div)/self.end if self.progress != self.progressold : self.progressold = self.progress Blender.Window.DrawProgressBar ( float(self.progress)/self.div , self.text) # Events EVENT_NOEVENT = 1 EVENT_DRAW = 2 EVENT_EXIT = 3 EVENT_CLOSE_LOG = 4 EVENT_EXPORTDIR_TEXT = 5 EVENT_EXPORTDIR = 6 export_directory = Blender.Draw.Create("sds") show_log = 0 def fileselector_callback(filename): modelpath, modelfile = os.path.split(filename) export_directory.val = modelpath + '/' def draw(): global EVENT_NOEVENT, EVENT_DRAW, EVENT_EXIT, EVENT_CLOSE_LOG global export_directory, show_log global log_max_lines, log global VERSION # Titles glClear(GL_COLOR_BUFFER_BIT) top = 60 + log_max_lines * 12 + 8 #top = 500 top_x = 304 glColor3f(0.8, 0.8, 1) glRecti(top_x, top, 4, 4) glBegin(GL_LINES) glColor3f(0.8, 0.8, 0.8) glVertex2d(4, top) glVertex2d(4, 4) glVertex2d(4, 4) glVertex2d(top_x, 4) glColor3f(0.5, 0.5, 0.5) glVertex2d(top_x, top) glVertex2d(4, top) glVertex2d(top_x, top) glVertex2d(top_x, top-1) glEnd() glColor3f(0, 0, 0) glRasterPos2d(10, top-16) Text("Supreme Commander Exporter " + VERSION) # Show exporting log if show_log: for index in range(0, len(log)): i = (len(log) - 1) - index glRasterPos2i(10, 40 + i*12) Text(log[index]) #, 'tiny') Button("Close", EVENT_EXIT , 10, 10, 80, 18) #Blender.Window.RedrawAll() # Exporter GUI else: Blender.Draw.Button("Browse...", EVENT_EXPORTDIR, 10, 40, 80, 18, "") #if export_directory.val == 'sds': #automatically launch the browse window once # Blender.Window.FileSelector (fileselector_callback, "Select output DIR") # Blender.Redraw() export_directory = Blender.Draw.String("", EVENT_EXPORTDIR_TEXT, 100, 40, 200, 18, export_directory.val, 255, "Where to save the exported files") # Draw and Exit Buttons Button("Export", EVENT_DRAW , 10, 10, 80, 18) Button("Exit", EVENT_EXIT , 100, 10, 80, 18) def event(evt, val): if (evt == QKEY and not val): Exit() def bevent(evt): global EVENT_NOEVENT,EVENT_DRAW,EVENT_EXIT global export_directory global show_log if (evt == EVENT_EXIT): show_log = 1 Exit() elif (evt == EVENT_EXPORTDIR): Blender.Window.FileSelector (fileselector_callback, "Select output DIR") #Blender.Redraw() elif (evt == EVENT_DRAW): if (export_directory.val == ""): return show_log = 1 modelpath, modelfile = os.path.split(export_directory.val) export(modelpath + '/') #changed cause filename was included in the dir selection #Blender.Redraw() Register(draw, event, bevent) #automatically launch the browse window once Blender.Window.FileSelector (fileselector_callback, "Select output DIR")