#!BPY """ Registration info for Blender menus: Name: 'Super Tux Kart Track' Blender: 233 Group: 'Export' Submenu: 'All meshes and empties ...' all Submenu: 'Only selected ...' sel Submenu: 'Configure' config Tip: 'Create Super Tux Kart racing tracks.' """ # -------------------------------------------------------------------------- # This script turns the AC3D exporter script into a TuxKart track exporter. # -------------------------------------------------------------------------- # ***** BEGIN GPL LICENSE BLOCK ***** # # Copyright (C) 2004: Willian P. Germano, wgermano _at_ ig.com.br # Copyright (C) 2006: Eduardo Hernández, coz.eduardo.hernandez _at_ gmail.com # # 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 ***** # -------------------------------------------------------------------------- VERSION = '0.2' HELPTEXT = HELPTEXT_INTRO = [ 'This script can export racing tracks with all needed data for TuxKart.', '', 'TuxKart is an open source 3D kart racing game by Steve and Oliver Baker.', 'It is written in portable C++ using OpenGL and Steve\'s PLib 3D gaming library.', '', 'Links:', 'TuxKart: tuxkart.sf.net PLib: plib.sf.net OpenGL: opengl.org', '', 'The game\'s site contains a text describing how to add new tracks.', 'Please read it to understand better how to use this script.', ] HELPTEXT_DRV = [ 'To export the drive path file you need to create two mesh objects,', 'which are the left and right borders of the track. They must be only', 'linked 2D line segments (aka a curve). The left border mesh object must', 'be called "DRV_LEFT" and the right border mesh object is to be', 'called "DRV_RIGHT".', '', 'Hints to create the drive line in the 3D View window (use TOP VIEW):', '', '- select the track mesh (the actual path);', '- enter edit mode, and select all the vertex of one of the borders;', '- use the P-KEY to separate that border from the main mesh;', '- select the other border and separate it;', '- change the name of the mesh objects to DRV_LEFT and DRV_RIGHT;', '', 'To write the vertices in the correct order, the script starts with the', 'closest one to (0,0,0), looks for the edge that goes the most "up" (' 'increasing y coordinate) then follows the line segments till the last one.' ] HELPTEXT_LOC = [ 'To put items on the track add Empties (SPACEBAR->ADD->EMPTY)', 'where you want them to appear and give them a proper name.', 'Examples:', '(x,y,z are coordinates and h,p,r are rotation angles)' '', '- sign.ac is exported as .loc line: "sign.ac",x,y,z,h,p,r', '- sign.ac{Z}: "sign.ac",x,y,{},h,p,r', '- sign.ac{ZPR}: "sign.ac",x,y,{},h,{},{}', '- RHERRING exported as: RHERRING,x,y,z', '', 'So (did you read the text about this at TuxKart\'s site?):', '- to have z,p or r calculated by the game, append them inside {};', '- names without an extension (.something) are considered hardcoded', ' game objects, like GHERRING (default if name is left as Empty);', '- item examples: zipper.ac, RHERRING, GHERRING, YHERRING;', '- don\'t worry about the .001 etc. extensions appended by Blender.', '', 'You can assign a song file for your track in the Config screen.' ] HELPTEXT_AC = [ 'The script uses the AC3D exporter.', '', 'AC3D is a shareware 3D modeler with a simple text format (.ac).', '', 'There are a few options you can configure:', '- skip data: set it if you don\'t want mesh names (ME:, not OB: field)', ' to be exported as strings for AC\'s "data" tags (19 chars max);', '- rgb mirror color can be exported as ambient and/or emissive if needed,', ' since Blender handles these differently;', '- default mat: a default (white) material is added if some mesh was', ' left without mats -- it\'s better to always add your own materials...', '', 'To export mesh groups, parent each set to an Empty that has the suffix "GR_".' ] import Blender from Blender import sys as bsys from Blender import Mathutils from math import sqrt # Globals / config options: ARG = __script__['arg'] # user selected argument HELPME = CONFIG = 0 # secondary screens TK_ALL = 0 TK_DRV = 1 TK_LOC = 0 TK_TRK = 0 TK_PATH = bsys.dirname(Blender.Get('filename')) + bsys.sep TK_TUNE = '' AC_SKIP_DATA = 0 AC_MIRCOL_AS_AMB = 0 AC_MIRCOL_AS_EMIS = 0 AC_ADD_DEFAULT_MAT = 1 # Looking for a saved key in Blender.Registry dict: rd = Blender.Registry.GetKey('TuxKart') if rd: TK_ALL = rd['TK_ALL'] TK_DRV = rd['TK_DRV'] TK_LOC = rd['TK_LOC'] TK_TRK = rd['TK_TRK'] TK_PATH = rd['TK_PATH'] TK_TUNE = rd['TK_TUNE'] AC_SKIP_DATA = rd['AC_SKIP_DATA'] AC_MIRCOL_AS_AMB = rd['AC_MIRCOL_AS_AMB'] AC_MIRCOL_AS_EMIS = rd['AC_MIRCOL_AS_EMIS'] AC_ADD_DEFAULT_MAT = rd['AC_ADD_DEFAULT_MAT'] if "DRV_LEFT" and "DRV_RIGHT" in Blender.Object.Get(): TK_DRV = 1 # found both DRV meshes def update_RegistryInfo(): d = {} d['TK_ALL'] = TK_ALL d['TK_DRV'] = TK_DRV d['TK_LOC'] = TK_LOC d['TK_TRK'] = TK_TRK d['TK_PATH'] = TK_PATH d['TK_TUNE'] = TK_TUNE d['AC_SKIP_DATA'] = AC_SKIP_DATA d['AC_MIRCOL_AS_AMB'] = AC_MIRCOL_AS_AMB d['AC_MIRCOL_AS_EMIS'] = AC_MIRCOL_AS_EMIS d['AC_ADD_DEFAULT_MAT'] = AC_ADD_DEFAULT_MAT Blender.Registry.SetKey('TuxKart', d) # The default material to be used when necessary (see AC_ADD_DEFAULT_MAT) DEFAULT_MAT = \ 'MATERIAL "DefaultWhite" rgb 1 1 1 amb 1 1 1 emis 0 0 0 \ spec 0.5 0.5 0.5 shi 64 trans 0' # This transformation aligns Blender and AC3D coordinate systems: acmatrix = Mathutils.Matrix([1,0,0,0], [0,0,-1,0], [0,1,0,0], [0,0,0,1]) def Round(f): r = round(f,6) # precision set to 10e-06 if r == int(r): return str(int(r)) else: return str(r) def transform_verts(verts, m): vecs = [] for v in verts: vec = Mathutils.Vector([v[0],v[1],v[2], 1]) vecs.append(vec * m) return vecs # --- def WriteLocation(objs, filename): "Write the .loc file" filename = Blender.sys.splitext(filename)[0] trackname = Blender.sys.basename(filename)+".ac" filename += '.loc' locfile = file(filename, "w") print "Writing %s file..." % filename locfile.write("# Created by tuxkart.py v%s Blender Python script.\n#\n" \ % VERSION) locfile.write('"%s",0,0,0,0,0,0\n' % trackname) rad2deg = 180/3.1415926535 #from radians to degrees for item in objs: x,y,z = map(str, map(Round, item.loc)) rx,ry,rz = map(lambda x: rad2deg*x, item.rot) h,p,r = map(str, map(Round, [rz,rx,ry])) name = item.name.split(".") hardcoded = 1 # name has extension (like .ac) ? if len(name) == 1: name = name[0] else: if name[-1].isdigit(): name = name[:-1] if name == 'Empty': name = 'GHERRING' if len(name) == 1: name = name[0] else: name = ".".join(name) hardcoded = 0 # has extension, isn't harcoded if name.find("{") > 0: name, encoded = name.split("{", 1) encoded = encoded.split("}")[0].lower() if encoded.find("z") >= 0: z = '{}' if encoded.find("p") >= 0: p = '{}' if encoded.find("r") >= 0: r = '{}' locline = '%s,%s,%s' % (x,y,z) if not hardcoded: name = '"'+name+'"' locline += ',%s,%s,%s' % (h,p,r) locfile.write('%s,%s\n' % (name, locline)) if TK_TUNE: locfile.write('MUSIC "%s"\n' % TK_TUNE) locfile.close() def WriteLeftDriveLine(obj, filename): "Write the .drvl file" mesh = obj.getData() mesh.transform(obj.getMatrix()) for f in mesh.faces: if len(f.v) != 2: print "Can't export Drive Line .drvl file: invalid mesh." print "The mesh must be made of linked line segments only." return filename = Blender.sys.splitext(filename)[0] filename += '.drvl' drvfile = file(filename, "w") print "Writing drive line file: %s" % filename verts = mesh.verts shortest_distance = 100000 for v in verts: #find vertex closest to 0,0 distance = sqrt(v[0]**2 + v[1]**2) if distance < shortest_distance: shortest_distance = distance first_vertex = v #Find the two vertex that are linked to the first vertex e_v = [] edges = mesh.edges for e in edges: if first_vertex == e.v1: e_v.append(e.v2) elif first_vertex == e.v2: e_v.append(e.v1) second_vertex = first_vertex #Find the vertex which has the higher Y value for v in e_v: if v[1] > second_vertex[1]: second_vertex = v if second_vertex == first_vertex: print "Can't export Drive Line .drvl file: invalid mesh." print "The mesh only has one point." return sorted_vertex = [first_vertex, second_vertex] prev_vertex = first_vertex curr_vertex = second_vertex count = 2 #Find the edge that contains the second vertex but does not has first vertex while count != len(verts): for e in edges: if prev_vertex != e.v1 and prev_vertex != e.v2: #Find which vertex is the next_vertex if curr_vertex == e.v1: prev_vertex = curr_vertex curr_vertex = e.v2 sorted_vertex.append(curr_vertex) count = count + 1 break elif curr_vertex == e.v2: prev_vertex = curr_vertex curr_vertex = e.v1 sorted_vertex.append(curr_vertex) count = count + 1 break for v in sorted_vertex: drvfile.write('%f,%f,%f\n' % (v[0], v[1], v[2])) drvfile.close() def WriteRightDriveLine(obj, filename): "Write the .drvl file" mesh = obj.getData() mesh.transform(obj.getMatrix()) for f in mesh.faces: if len(f.v) != 2: print "Can't export Drive Line .drvr file: invalid mesh." print "The mesh must be made of linked line segments only." return filename = Blender.sys.splitext(filename)[0] filename += '.drvr' drvfile = file(filename, "w") print "Writing drive line file: %s" % filename verts = mesh.verts shortest_distance = 100000 for v in verts: #find vertex closest to 0,0 distance = sqrt(v[0]**2 + v[1]**2) if distance < shortest_distance: shortest_distance = distance first_vertex = v #Find the two vertex that are linked to the first vertex e_v = [] edges = mesh.edges for e in edges: if first_vertex == e.v1: e_v.append(e.v2) elif first_vertex == e.v2: e_v.append(e.v1) second_vertex = first_vertex #Find the vertex which has the higher Y value for v in e_v: if v[1] > second_vertex[1]: second_vertex = v if second_vertex == first_vertex: print "Can't export Drive Line .drvr file: invalid mesh." print "The mesh only has one point." return sorted_vertex = [first_vertex, second_vertex] prev_vertex = first_vertex curr_vertex = second_vertex count = 2 #Find the edge that contains the second vertex but does not has first vertex while count != len(verts): for e in edges: if prev_vertex != e.v1 and prev_vertex != e.v2: #Find which vertex is the next_vertex if curr_vertex == e.v1: prev_vertex = curr_vertex curr_vertex = e.v2 sorted_vertex.append(curr_vertex) count = count + 1 break elif curr_vertex == e.v2: prev_vertex = curr_vertex curr_vertex = e.v1 sorted_vertex.append(curr_vertex) count = count + 1 break for v in sorted_vertex: drvfile.write('%f,%f,%f\n' % (v[0], v[1], v[2])) drvfile.close() class Track: # the ac3d exporter part def __init__(self, bl_objlist, filename): from Blender.sys import time TIME = time() global ARG, AC_SKIP_DATA, AC_ADD_DEFAULT_MAT, DEFAULT_MAT print 'Exporting track model(s) to AC3D format...' header = 'AC3Db' self.buf = '' self.mbuf = '' world_kids = 0 self.mlist = [] kids_dict = {} objlist = [] bl_objlist2 = [] if filename.find('.ac', -3) <= 0: filename += '.ac' try: file = open(filename, 'w') except IOError, (errno, strerror): errmsg = "IOError #%s" % errno errmsg = errmsg + "%t|" + strerror Blender.Draw.PupMenu(errmsg) return None file.write(header+'\n') for obj in bl_objlist: if obj.getType() != 'Mesh' and obj.getType() != 'Empty': continue else: kids_dict[obj.name] = 0 if obj.getParent() == None: objlist.append(obj) else: bl_objlist2.append(obj) bl_objlist = bl_objlist2[:] world_kids = len(objlist) while bl_objlist2: for obj in bl_objlist: obj2 = obj dad = obj.getParent() kids_dict[dad.name] += 1 while dad.name not in objlist: obj2 = dad dad = dad.getParent() kids_dict[dad.name] += 1 objlist.insert(objlist.index(dad.name)+1, obj2.name) bl_objlist2.remove(obj2) meshlist = [o for o in objlist if o.getType() == 'Mesh'] self.MATERIALS(meshlist) if not self.mbuf or AC_ADD_DEFAULT_MAT: self.mbuf = DEFAULT_MAT + '\n' + self.mbuf file.write(self.mbuf) file.write('OBJECT world\nkids %s\n' % world_kids) for obj in objlist: self.obj = obj if obj.getType == 'Empty': file.write('OBJECT group\n') emptyname = obj.name # skip the GR_ prefix if len(emptyname) > 3: emptyname = emptyname[3:] file.write(emptyname+'\n') #self.rot(obj.rot) #self.loc(obj.loc) else: mesh = self.mesh = obj.getData() file.write('OBJECT poly\nname "%s"\n' % obj.name) if not AC_SKIP_DATA: file.write('data %s\n%s\n' % (len(mesh.name), mesh.name)) texline = self.texture(mesh.faces) if texline: file.write(texline) self.numvert(mesh.verts, obj.getMatrix(), file) self.numsurf(mesh.faces, mesh.hasFaceUV(), file) file.write('kids %s\n' % kids_dict[obj.name]) file.close() print "Done. Saved to %s\n" % filename def MATERIALS(self, meshlist): for meobj in meshlist: me = meobj.getData() mat = me.materials mbuf = [] mlist = self.mlist for m in xrange(len(mat)): name = mat[m].name try: mlist.index(name) except ValueError: mlist.append(name) M = Blender.Material.Get(name) material = 'MATERIAL "%s"' % name mirCol = "%s %s %s" % (Round(M.mirCol[0]), Round(M.mirCol[1]), Round(M.mirCol[2])) rgb = "rgb %s %s %s" % (Round(M.R), Round(M.G), Round(M.B)) amb = "amb %s %s %s" % (Round(M.amb), Round(M.amb), Round(M.amb)) if AC_MIRCOL_AS_AMB: amb = "amb %s" % mirCol emis = "emis 0 0 0" if AC_MIRCOL_AS_EMIS: emis = "emis %s" % mirCol spec = "spec %s %s %s" % (Round(M.specCol[0]), Round(M.specCol[1]), Round(M.specCol[2])) shi = "shi 72" trans = "trans %s" % (Round(1 - M.alpha)) mbuf.append("%s %s %s %s %s %s %s\n" \ % (material, rgb, amb, emis, spec, shi, trans)) self.mlist = mlist self.mbuf += "".join(mbuf) def texture(self, faces): tex = [] for f in faces: if f.image and f.image.name not in tex: tex.append(f.image.name) if tex: if len(tex) > 1: print "\nAC3Db format supports only one texture per object." print "Object %s -- using only the first one: %s\n" % \ (self.obj.name, tex[0]) image = Blender.Image.Get(tex[0]) buf = 'texture "%s"\n' % image.filename xrep = image.xrep yrep = image.yrep buf += 'texrep %s %s\n' % (xrep, yrep) return buf def rot(self, matrix): rot = '' not_I = 0 for i in [0, 1, 2]: r = map(Round, matrix[i]) not_I += (r[0] != '0.0')+(r[1] != '0.0')+(r[2] != '0.0') not_I -= (r[i] == '1.0') for j in [0, 1, 2]: rot = "%s %s" % (rot, r[j]) if not_I: rot = rot.strip() buf = 'rot %s\n' % rot self.buf = self.buf + buf def loc(self, loc): loc = map(Round, loc) if loc[0] or loc[1] or loc[2]: buf = 'loc %s %s %s\n' % (loc[0], loc[1], loc[2]) self.buf = self.buf + buf def numvert(self, verts, matrix, file): file.write("numvert %s\n" % len(verts)) m = matrix * acmatrix verts = transform_verts(verts, m) for v in verts: v0, v1, v2 = Round(v[0]), Round(v[1]), Round(v[2]) file.write("%s %s %s\n" % (v0, v1, v2)) def numsurf(self, faces, hasFaceUV, file): global AC_ADD_DEFAULT_MAT file.write("numsurf %s\n" % len(faces)) mlist = self.mlist indexerror = 0 omlist = {} objmats = self.mesh.materials for i in range(len(objmats)): objmats[i] = objmats[i].name for f in faces: m_idx = f.materialIndex try: m_idx = mlist.index(objmats[m_idx]) except IndexError: if not indexerror: print "\nNotice: object " + self.obj.name + \ " has at least one material *index* assigned\n" \ "\tbut not defined (not linked to an existing material).\n" \ "\tThis can make some faces be exported with a wrong color.\n" \ "\tYou can fix the problem in the Edit Buttons Window (F9).\n" indexerror = 1 m_idx = 0 refs = len(f) flaglow = (refs == 2) << 1 two_side = f.mode & Blender.NMesh.FaceModes['TWOSIDE'] two_side = (two_side > 0) << 1 flaghigh = f.smooth | two_side surfstr = "SURF 0x%d%d\n" % (flaghigh, flaglow) if AC_ADD_DEFAULT_MAT and objmats: m_idx += 1 matstr = "mat %s\n" % m_idx refstr = "refs %s\n" % refs u, v, vi = 0, 0, 0 fvstr = '' for vert in f.v: fvstr = fvstr + str(self.mesh.verts.index(vert)) if hasFaceUV: u = f.uv[vi][0] v = f.uv[vi][1] vi += 1 fvstr = fvstr + " %s %s\n" % (u, v) file.write(surfstr + matstr + refstr + fvstr) # End of Class Track from Blender import Draw, BGL from Blender.Window import FileSelector # Special Buttons BUTPATH = Draw.Create(TK_PATH) BUTTUNE = Draw.Create(TK_TUNE) BUTTRK = Draw.Create(TK_TRK) BUTDRV = Draw.Create(TK_DRV) BUTALL = Draw.Create(TK_ALL) BUTLOC = Draw.Create(TK_LOC) HTRK = Draw.Create(0) HDRV = Draw.Create(0) HLOC = Draw.Create(0) HINTRO = Draw.Create(0) HTOG = HTOG_INTRO = 0 HTOG_TRK = 1 HTOG_LOC = 2 HTOG_DRV = 3 # Button Events EVT_CONFIG = 1 EVT_AC_DMAT = 2 EVT_AC_SKIP = 3 EVT_AC_MIR = 4 EVT_AC_EMIS = 5 EVT_AC_ALL = 6 EVT_AC_SEL = 7 EVT_HELP = 10 EVT_HELP_DRV = 11 EVT_HELP_LOC = 12 EVT_HELP_TRK = 13 EVT_HELP_INTRO = 14 EVT_ALL = 20 EVT_TRK = 21 EVT_DRV = 22 EVT_LOC = 23 EVT_PATH = 24 EVT_BROWSE = 25 EVT_TUNE = 26 EVT_TUNEBROWSE = 27 EVT_EXPORT = 30 EVT_EXIT = 31 def gui(): # gui drawing callback global AC_SKIP_DATA, AC_MIRCOL_AS_AMB, AC_MIRCOL_AS_EMIS, AC_ADD_DEFAULT_MAT global HELPME, CONFIG, HELPTEXT, HTRK, HDRV, HLOC, HINTRO global HTOG, HTOG_INTRO, HTOG_TRK, HTOG_LOC, HTOG_DRV global BUTPATH, BUTTRK, BUTDRV, BUTALL, BUTLOC, BUTTUNE if HELPME: # help screen BGL.glClearColor(0.4,0.5,0.8,1) BGL.glClear(BGL.GL_COLOR_BUFFER_BIT) BGL.glColor3f(1,1,1) x = 55 y = 50+20*len(HELPTEXT) for line in HELPTEXT: y -= 20 BGL.glRasterPos2i(x, y) Draw.Text(line) w = 35 h = 20 y = 10 HINTRO = Draw.Toggle("intro", EVT_HELP_INTRO, x, y, w, h,(HTOG==HTOG_INTRO), "Intro Help screen.") HLOC = Draw.Toggle("loc", EVT_HELP_LOC, x+w, y, w, h, (HTOG==HTOG_LOC), "Help for location (.loc) file.") HTRK = Draw.Toggle("track", EVT_HELP_TRK, x+2*w, y, w, h, (HTOG==HTOG_TRK), "Help for the mesh exporter.") HDRV = Draw.Toggle("drv", EVT_HELP_DRV, x+3*w, y, w, h, (HTOG==HTOG_DRV), "Help for drive line (.drv) file.") Draw.Button("back", EVT_HELP, x+4*w, y, w, h, "Return to main screen.") elif CONFIG: # config screen BGL.glClearColor(0.4,0.5,0.8,1) BGL.glClear(BGL.GL_COLOR_BUFFER_BIT) BGL.glColor3f(0.9,0.85,0.75) BUTTUNE = Draw.String('song: ', EVT_TUNE, 10, 10, 210, 20, \ BUTTUNE.val, 150, 'Song filename for your race track.') Draw.PushButton('browse...', EVT_TUNEBROWSE, 222, 10, 53, 20, 'Open file selector (select any file in the chosen dir)') x = 15 y = 40 w = 80 h = 20 BGL.glRecti(x-4,y-5,x+205,y+84) BGL.glColor3f(1,1,1) Draw.Button("BACK", EVT_CONFIG, x+w+127, y-5, 53, 30, "Click to return to main screen.") Draw.Button("Export Selected...", EVT_AC_SEL, x+w+10, y+4, 110, 30, "Export now selected meshes to an AC3D file.") Draw.Button("Export All...", EVT_AC_ALL, x+w+10, y+44, 110, 30, "Export now all meshes to an AC3D file.") Draw.Toggle("Mir2Emis", EVT_AC_EMIS, x, y, w, h, AC_MIRCOL_AS_EMIS, "Get AC3D's emissive RGB color for each object from its mirror color " "in Blender.") Draw.Toggle("Mir2Amb", EVT_AC_MIR, x, y+h, w, h, AC_MIRCOL_AS_AMB, "Get AC3D's ambient RGB color for each object from its mirror color " "in Blender.") Draw.Toggle("Skip data", EVT_AC_SKIP, x, y+2*h, w, h, AC_SKIP_DATA, "Don't export mesh names as 'data' info.") Draw.Toggle("Default mat", EVT_AC_DMAT, x, y+3*h, w, h, AC_ADD_DEFAULT_MAT, "Objects without materials assigned get a default (white) one " "automatically.") BGL.glRasterPos2i(x-4, y+5*h) Draw.Text("Additional Configuration") else: # main screen # let's count our objects first: meshnr = meshsel = emptynr = emptysel = 0 ob = Blender.Object.Get() for o in ob: if o.getType() == "Mesh": meshnr += 1 if o.sel: meshsel += 1 elif o.getType() == "Empty": emptynr += 1 if o.sel: emptysel += 1 obinfo = "%d/%d meshes" % (meshsel, meshnr) if emptynr: obinfo += ", %d/%d empties" % (emptysel, emptynr) if len(obinfo) < 27: obinfo = 'Selected: ' + obinfo BGL.glClearColor(0.4,0.5,0.8,1) BGL.glClear(BGL.GL_COLOR_BUFFER_BIT) BGL.glColor3f(1,1,1) x = y = 20 w = 60 h = 25 BGL.glRasterPos2i(x, y + 9) Draw.Text(obinfo) Draw.PushButton('EXPORT', EVT_EXPORT, x+202, y, w, h, 'Write track file(s).') y += 30 w = 40 h = 20 pathmsg = 'Base dir where files will be saved.' if BUTPATH.val[-1] != bsys.sep: pathmsg += ' And data filename head.' BUTPATH = Draw.String('path: ', EVT_PATH, x, y, 5*w, h, BUTPATH.val, 100, pathmsg) Draw.PushButton('browse...', EVT_BROWSE, x+202, y, 60, 20, 'Open file selector (select any file in the chosen dir)') y += 24 w = 26 h = 15 BUTALL = Draw.Toggle('all', EVT_ALL, x, y, w, h, BUTALL.val, "Export all meshes and empties, not only selected ones.") x += 7 BUTTRK = Draw.Toggle('trk', EVT_TRK, x+w, y, w, h, BUTTRK.val, "Write track (.ac) file.") BUTLOC = Draw.Toggle('loc', EVT_LOC, x+2*w, y, w, h, BUTLOC.val, "Write location (.loc) file.") BUTDRV = Draw.Toggle('drv', EVT_DRV, x+3*w, y, w, h, BUTDRV.val, "Write drive line (.drv) file.") x += 7 + 4*w w = 41 Draw.PushButton('config', EVT_CONFIG, x, y, w, h, 'Additional configurations (see help).') Draw.PushButton('help', EVT_HELP, x+w, y, w, h, 'About, links and instructions.') Draw.PushButton('exit', EVT_EXIT, x+2*w+2, y, 60, h, 'Exit from script (shortcuts: Esc, q).') x = 20 y += 46 BGL.glRasterPos2i(x, y) title = "TuxKart Track Exporter v%s" % VERSION tlen = Draw.Text(title) BGL.glBegin(BGL.GL_LINE_LOOP) BGL.glVertex2i(x+2+tlen, y+18) BGL.glVertex2i(x+2+tlen, y-10) BGL.glVertex2i(x-4, y-10) BGL.glVertex2i(x-4, y+18) BGL.glEnd() def event(evt, val): # input event callback global HELPME, CONFIG if not val: return if HELPME or CONFIG: if evt == Draw.ESCKEY or evt == Draw.QKEY: HELPME = CONFIG = 0 Draw.Redraw() return if evt == Draw.ESCKEY or evt == Draw.QKEY: update_RegistryInfo() Draw.Exit() return def b_event(evt): # gui button events callback global AC_SKIP_DATA, AC_MIRCOL_AS_AMB, AC_MIRCOL_AS_EMIS, AC_ADD_DEFAULT_MAT global CONFIG, HELPTEXT, TK_ALL, TK_DRV, TK_LOC, TK_TRK, TK_PATH, TK_TUNE global HELPME, ARG, BUTPATH, BUTTRK, BUTDRV, BUTALL, BUTLOC, BUTTUNE global HTOG, HTOG_TRK, HTOG_DRV, HTOG_LOC, HTOG_INTRO if evt == EVT_EXPORT: fname = TK_PATH if bsys.exists(TK_PATH) == 2: fname += bsys.makename(Blender.Get('filename'), strip=1) FileSelector(fs_callback, "Export TuxKart track", fname) else: TK_Export(fname, 0) elif evt == EVT_ALL: TK_ALL = BUTALL.val if TK_ALL: ARG = 'all' else: ARG = 'sel' elif evt == EVT_DRV: TK_DRV = BUTDRV.val elif evt == EVT_LOC: TK_LOC = BUTLOC.val elif evt == EVT_TRK: TK_TRK = BUTTRK.val elif evt == EVT_PATH: TK_PATH = BUTPATH.val pathtype = bsys.exists(TK_PATH) if pathtype: if pathtype == 2 and TK_PATH[-1] != bsys.sep: TK_PATH += bsys.sep else: TKPATH = bsys.splitext(TK_PATH)[0] BUTPATH.val = TK_PATH elif evt == EVT_BROWSE: FileSelector(fs_cbPath, "Ok", TK_PATH) elif evt == EVT_TUNE: TK_TUNE = BUTTUNE.val if bsys.exists(TK_TUNE) != 1: # 1 is file Draw.PupMenu("ERROR%t|No such file.") TK_TUNE = '' BUTTUNE.val = TK_TUNE elif evt == EVT_TUNEBROWSE: FileSelector(fs_cbTune, "Select Song", TK_TUNE) elif evt == EVT_AC_DMAT: AC_ADD_DEFAULT_MAT = 1 - AC_ADD_DEFAULT_MAT elif evt == EVT_AC_SKIP: AC_SKIP_DATA = 1 - AC_SKIP_DATA elif evt == EVT_AC_MIR: AC_MIRCOL_AS_AMB = 1 - AC_MIRCOL_AS_AMB elif evt == EVT_AC_EMIS: AC_MIRCOL_AS_EMIS = 1 - AC_MIRCOL_AS_EMIS elif evt == EVT_AC_ALL: ARG = 'AC_all' fname = bsys.makename(ext=".ac") FileSelector(fs_callback, "Export AC3D", fname) return elif evt == EVT_AC_SEL: ARG = 'AC_sel' fname = bsys.makename(ext=".ac") FileSelector(fs_callback, "Export AC3D", fname) return elif evt == EVT_CONFIG: CONFIG = 1 - CONFIG elif evt == EVT_HELP: HELPME = 1 - HELPME elif evt == EVT_HELP_DRV: HELPTEXT = HELPTEXT_DRV HTOG = HTOG_DRV elif evt == EVT_HELP_LOC: HELPTEXT = HELPTEXT_LOC HTOG = HTOG_LOC elif evt == EVT_HELP_TRK: HELPTEXT = HELPTEXT_AC HTOG = HTOG_TRK elif evt == EVT_HELP_INTRO: HELPTEXT = HELPTEXT_INTRO HTOG = HTOG_INTRO elif evt == EVT_EXIT: update_RegistryInfo() Draw.Exit() return else: return Draw.Redraw() def TK_Export(filename, onlytrack): global ARG starttime = bsys.time() # Here we check if given dir is TuxKart's one. # If so we save .ac in models/ and .loc and .drv in data/ # else we save all in the given dir. datapath = modelspath = filename basename = bsys.basename(filename) dirname = bsys.dirname(filename) modelsdir = bsys.join(dirname, 'models') datadir = bsys.join(dirname, 'data') if bsys.exists (modelsdir) == 2: if bsys.exists(datadir) == 2: datapath = bsys.join(datadir, basename) modelspath = bsys.join(modelsdir, basename) scene = Blender.Scene.GetCurrent() objs = [] # just the .ac export from the config screen: if onlytrack: if ARG == ('AC_sel'): objs = Blender.Object.GetSelected() else: objs = scene.getChildren() test = Track(objs, filename) endtime = bsys.time() - starttime Blender.Draw.PupMenu("OK|Data exported in %.2f seconds." % endtime) return if ARG == 'sel': objs = Blender.Object.GetSelected() else: objs = scene.getChildren() track = [] thingies = [] list = [o for o in objs if o.getType() == "Mesh" or o.getType() == "Empty"] for o in list: if o.getType() == "Empty": # empties whose names start with GR_ if o.name.find("GR_") < 0: # are exported as AC groups info thingies.append(o) elif o.name == "DRV_LEFT": WriteLeftDriveLine(o, datapath) elif o.name == "DRV_RIGHT": WriteRightDriveLine(o, datapath) else: track.append(o) WriteLocation(thingies, datapath) test = Track(track, modelspath) endtime = bsys.time() - starttime Blender.Draw.PupMenu("OK|Data exported in %.2f seconds." % endtime) # File Selector callbacks: def fs_cbPath(path): global TK_PATH, BUTPATH TK_PATH = bsys.splitext(path)[0] BUTPATH.val = TK_PATH def fs_cbTune(filename): global TK_TUNE, BUTTUNE TK_TUNE = filename BUTTUNE.val = TK_TUNE def fs_callback(filename): global ARG #if hasattr(ARG, find): # if ARG.find('AC_') == 0: onlytrack = 1 # else: onlytrack = 0 onlytrack = 0 TK_Export(filename, onlytrack) # -- End of definitions if not ARG or ARG == 'config': # not ARG if executed as loaded Blender Text Draw.Register(gui, event, b_event) else: fname = Blender.sys.makename() FileSelector(fs_callback, "Export Tuxkart track", fname)