1#!BPY 2 3""" 4Name: 'MS3D ASCII (.txt) v 1.5.2' 5Blender: 242 6Group: 'Export' 7Tooltip: 'MilkShape3d ASCII format for Scorched3d models v1.5.2' 8""" 9 10#Copyright (C) 2004-2007 Paul Vint cbx550f@sourceforge.net 11# 12# This program is free software; you can redistribute it and/or modify 13# it under the terms of the GNU General Public License as published by 14# the Free Software Foundation; either version 2 of the License, or 15# (at your option) any later version. 16# 17# This program is distributed in the hope that it will be useful, 18# but WITHOUT ANY WARRANTY; without even the implied warranty of 19# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20# GNU General Public License for more details. 21# 22# You should have received a copy of the GNU General Public License 23# along with this program; if not, write to the Free Software 24# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 25# 26# 27#------------------------------------------------------------- 28# 29# initial alpha release, 17/08/2004 30# update: Fixed point of view - front, side top etc are now correct 27/08/2004 31# Added error checking for number of verts per face 07/09/2004 32# Reversed vertex winding to fix flipped normals problem & misc speedups 12/03/06 33# Updated for Blender 2.4x - thanks for the help, Berem! 34# TODO: Stop using face nrmals, should be vertex normals! - done. 05/09/06 35# Added proper support for emitted light 05/11/06 36# TODO - Have it export quads as triangles automagically (a la 3ds export) 37# Fix: Smoothing groups are now properly supported 28/10/2007 38 39import Blender 40from Blender import Draw, BGL 41from Blender.BGL import * 42from Blender.Draw import * 43import os, time, sys 44import meshtools 45 46## Events 47EVENT_NOEVENT = 1 48EVENT_DRAW = 2 49EVENT_EXIT = 3 50EVENT_EXPORT = 4 51EVENT_CHOOSE_FILENAME = 5 52EVENT_PROGRESS = 6 53 54def stripPath(path): 55 return path.split('/')[-1].split('\\')[-1] 56 57def fs_callback(filename): 58 global FILENAME 59 if filename.find('.txt', -4) <= 0: filename += '.txt' 60 FILENAME.val = filename 61 #write(filename) 62 63def createS3DPath(): 64 global FILENAME 65 filename = Blender.Get('filename') 66 67 if filename.find('.') != -1: 68 filename = filename.split('.')[0] 69 filename += ".txt" 70 71 #return filename 72 FILENAME.val = filename 73 return filename 74 75def fileSelect(): 76 Blender.Window.FileSelector(fs_callback, "Select", createS3DPath()) 77 78FILENAME = Create('filename') 79createS3DPath() 80 81showProgress = Create(1) 82 83### Basic GUI functions 84def draw(): # Define the draw function (which draws your GUI). 85 #Blender.BGL.glClear(Blender.BGL.GL_COLOR_BUFFER_BIT) # This clears the window 86 # Add here drawing commands to draw your GUI, for example: 87 #Blender.Draw.Toggle("Clear origin",1,10,20,100,20,0,"Tooltip") 88 # The line abive will draw a toggle button. 89 # Note the first number, '1' means this is button number 1 90 global EVENT_NOEVENT,EVENT_DRAW,EVENT_EXIT,EVENT_EXPORT,EVENT_CHOOSE_FILENAME,FILENAME,EVENT_PROGRESS, showProgress 91 92 ypos = 110 93 glClear(GL_COLOR_BUFFER_BIT) 94 glRasterPos2d(8, ypos) 95 Text("MilkShape3d ASCII Export version 1.5") 96 glRasterPos2d(8, ypos - 15) 97 Text("Copyright 2007 - Paul Vint cbx550f@users.sourceforge.net") 98 glRasterPos2d(8, ypos - 30) 99 Text("For updates visit http://sourceforge.net/projects/scorched3d-mods") 100 101 ## Buttons etc 102 glRasterPos2d(8, 83) 103 Button("Export",EVENT_EXPORT, 10, 10, 80, 18) 104 Button("Exit",EVENT_EXIT, 140, 10, 80, 18) 105 showProgress = Toggle("Show Progress",EVENT_PROGRESS,10, 30, 80, 18, showProgress.val, "Deselect to disable the progress bar (faster exporting)") 106 107 glRasterPos2d(8, 83) 108 FILENAME = String("Filename: ", EVENT_NOEVENT, 10, 55, 210, 18, 109 FILENAME.val, 255, ".txt file to export") 110 Button("Browse",EVENT_CHOOSE_FILENAME,220,55,80,18) 111 112 113 114def event(evt,val): # Define mouse and keyboard press events 115 if (evt == Blender.Draw.ESCKEY): # Example if esc key pressed 116 Blender.Draw.Exit() # then exit script 117 return # return from the function 118 119def bevent(evt): #Manage button events 120 global FILENAME,showProgress 121 if (evt == EVENT_EXIT): 122 Blender.Draw.Exit() 123 return 124 elif (evt == EVENT_CHOOSE_FILENAME): 125 fileSelect() 126 Blender.Redraw() 127 return 128 elif (evt == EVENT_EXPORT): 129 #print showProgress 130 #showProgress = showProgress 131 write(FILENAME.val) 132 Blender.Draw.Exit() 133 return 134 135 136#Blender.Draw.Register(draw,event,button) 137 138 139#======= Write ms3d .txt format ========= 140 141dbg = 0 #set to enable debugging 142 143 144def write(filename): 145 global showProgress 146 print showProgress 147 start = time.clock() 148 149 convertToTris = 0; 150 if Blender.sys.exists(filename) and (Blender.Draw.PupMenu("File exists, replace?%t|YES|NO") == 2): 151 fileSelect() 152 return 153 154 155 file = open(filename, "wb") 156 157 # ms3d header 158 file.write("// MilkShape 3D ASCII\n\nFrames: 30\nFrame: 1\n\n") 159 160 file.write("Meshes: " + str(len(Blender.Object.GetSelected())) + '\n') 161 162 objects = Blender.Object.GetSelected() 163 164 for obj in range(len(objects)): 165 meshname = objects[ obj].data.name 166 mesh = Blender.NMesh.GetRaw(meshname) 167 thismesh = meshname 168 169 if dbg: 170 print "\nDEBUG: Mesh", mesh, "Obj: ", obj, "Name: ", objects[obj].data.name 171 if mesh: 172 print "Mesh",objects[obj].data.name, 173 meshname = "\"" + objects[obj].data.name + "\" 0 " + str(obj) + "\n" # Setting flags=0, mat index=object number for now, also note that I'm using object name, not meshname 174 file.write(meshname) 175 176 file.write(str(3 * len(mesh.faces)) + '\n') # should be better way to get # of verts, but this is fast 177 for i in range(len(mesh.faces)): # New in v 0.3 - getting verts from faces directly 178 faceVerts = len(mesh.faces[i].v) # Reversing the winding to fix flipped normals problem 179 for fv in range(faceVerts): # Scan through each vert in face 180 line = '0 ' + str(-round(mesh.faces[i].v[faceVerts-fv-1][0],4)) + ' ' + str(round(mesh.faces[i].v[faceVerts-fv-1][2],4)) + ' ' + str(round(-mesh.faces[i].v[faceVerts-fv-1][1],4)) + ' ' #+ str(round(mesh.faces[i].uv[fv][0],5)) + ' ' + str(round(mesh.faces[i].uv[fv][1],5)) + ' -1\n' 181 if (len(mesh.faces[i].uv)): 182 line += str(round(mesh.faces[i].uv[faceVerts-fv-1][0],5)) + ' ' + str(round(1-mesh.faces[i].uv[faceVerts-fv-1][1],5)) + ' -1\n' 183 else: 184 line += '0.0 0.0 -1\n' 185 186 if not i%50 and showProgress.val: 187 Blender.Window.DrawProgressBar(float(i)/len(mesh.faces), thismesh + "Verts") 188 file.write(line) 189 190 # Normals 191 #print "Norms: ",len(mesh.faces), #FIX! Temp only using # of faces 192 file.write(str(3 * len(mesh.faces))) 193 for i in range(len(mesh.faces)): 194 if not i%50 and showProgress.val: 195 Blender.Window.DrawProgressBar(float(i)/len(mesh.faces), thismesh + "Normals") 196 #file.write('\n' + str(round(mesh.faces[i].no[0],4)) + ' ' + str(round(mesh.faces[i].no[2],4)) + ' ' + str(round(mesh.faces[i].no[1],4))) 197 faceVerts = len(mesh.faces[i].v) 198 for fv in range(faceVerts): # Scan through each vert in face 199 # Check if the face is smoothed or not: 200 if (mesh.faces[i].smooth): 201 file.write('\n' + str(round(-mesh.faces[i].v[faceVerts-fv-1].no[0],4))) 202 file.write(' ' + str(round(mesh.faces[i].v[faceVerts-fv-1].no[2],4))) 203 file.write(' ' + str(round(-mesh.faces[i].v[faceVerts-fv-1].no[1],4))) 204 else: 205 file.write('\n' + str(round(-mesh.faces[i].no[0],4)) + ' ' + str(round(mesh.faces[i].no[2],4)) + ' ' + str(round(-mesh.faces[i].no[1],4))) 206 207 # Faces 208 print "Triangles: " ,len(mesh.faces) 209 file.write('\n' + str(len(mesh.faces)) + '\n') 210 idx = 0 211 line = "" 212 for i in range(len(mesh.faces)): # one face at a time 213 if not i%50 and showProgress.val: 214 Blender.Window.DrawProgressBar(float(i)/len(mesh.faces), thismesh + "Triangles") 215 if (len(mesh.faces[i].v) != 3): #Ensure each face is a triangle! 216 print "\n\n*********** Error! **********\n" 217 print "Object",objects[obj].data.name, "has", len(mesh.faces[i].v), "vertices in face", i, "Must have THREE verts per face - convert to triangles with CTRL-T!" 218 Blender.Window.WaitCursor(0) 219 exitmsg = 'MS3D Export Error:|Mesh \"' + objects[obj].data.name + '\" has ' + str(len(mesh.faces[i].v)) + ' verts in a face, needs to be converted to triangles with CTRL-T!' 220 Blender.Draw.PupMenu(exitmsg) 221 return 222 ######## Keeping depricated (and broken) tri2quad for future reference 223 #if convertToTris==0: #only ask once 224 # print "Object",objects[obj].data.name, "has", len(mesh.faces[i].v), "vertices in face", i, "Must have THREE verts per face" 225 # convertToTris=Blender.Draw.PupMenu("Model not made entirely out of Triangles-Convert?%t|YES|NO") 226 #if convertToTris==1: 227 # for f in mesh.faces: 228 # f.sel = 1 229 # Blender.Mesh.Mode(3) 230 # mesh.quadToTriangle(0) 231 #elif convertToTris==2: 232 # exitmsg = 'MS3D Export Error:|Mesh \"' + objects[obj].data.name + '\" has ' + str(len(mesh.faces[i].v)) + ' verts in a face, needs to be converted to triangles with CTRL-T!' 233 # Blender.Draw.PupMenu(exitmsg) 234 # return 235 236 237 for v in range(len(mesh.faces[i].v)): #Go through each vertex in face - only need triangles for MS3D, but this would allow for quads too for portability 238 line += str(idx) + " " 239 idx += 1 240 #print cnt #debug info 241 #file.write('1 ' + line + str( i) + ' ' + str(i) + ' ' + str(i) + ' 1\n') 242 file.write('1 ' + line + line + ' 1\n') 243 244 line = "" # fresh start for next face 245 246 ######### Materials! ########## 247 numMats = 0 248 for obj in range(len(objects)): #FIXME! Stupid cludge for counting materials 249 meshname = objects[ obj].data.name 250 mesh = Blender.NMesh.GetRaw(meshname) 251 if (len(mesh.materials) == 0): #Ensure the mesh has a material 252 print '*** Error! ', meshname, 'must have a material & texture!' 253 message = 'MS3D Export Error:|Mesh \"' + objects[obj].data.name + '\"needs to have a material!' 254 meshtools.print_boxed(message) 255 Blender.Window.WaitCursor(0) 256 Blender.Draw.PupMenu(message) 257 return 258 259 if mesh.materials[0]: 260 numMats+=1 261 file.write("\nMaterials: " + str(numMats) + "\n") 262 263 for obj in range(len(objects)): 264 meshname = objects[ obj].data.name 265 mesh = Blender.NMesh.GetRaw(meshname) 266 267 for material in mesh.materials: 268 file.write("\"" + material.name + "\"\n") 269 270 #file.write(str(round(material.ref,5))+ " " + str(round(material.ref,5))+ " " + str(round(material.ref,5)) + " " + str(round(material.alpha,5)) + "\n") 271 #TODO Why are these values the same??? TODO 272 # file.write(str("%5f %5f %5f %5f\n" % (material.ref,material.ref,material.ref,material.alpha))) 273 file.write(str("%5f %5f %5f %5f\n" % (material.amb,material.amb,material.amb,material.alpha))) 274 #file.write(str(round(material.rgbCol[0],5)) + " " + str(round(material.rgbCol[1],5)) + " " + str(round(material.rgbCol[2],5)) + " " + str(round(material.alpha,5)) + "\n") 275 file.write(str("%5f %5f %5f %5f\n" % (material.rgbCol[0],material.rgbCol[1],material.rgbCol[2],material.alpha))) 276 file.write(str("%5f %5f %5f %5f\n" % (material.spec,material.spec,material.spec,material.alpha))) 277 278 #file.write(str(round(material.spec,5)) + " " + str(round(material.spec,5)) + " " + str(round(material.spec,5)) + " " + str(round(material.alpha,5))) 279 file.write(str("%5f %5f %5f %5f\n" % (material.emit,material.emit,material.emit,material.alpha))) 280 file.write(str(material.ref) + "\n") 281 file.write(str(material.alpha) + "\n") 282 # get the current texture image 283 imageName="" 284 mytex = material.getTextures() 285 if (dbg): 286 print 'MyTex: ', mytex[0] 287 if ( mytex[0] != None ): 288 image = material.getTextures()[0].tex.getImage() 289 imageName = Blender.sys.basename(image.getFilename()) 290 291 file.write("\".\\" + stripPath(imageName) + "\"") 292 293 else: 294 print 'Warning: Mesh ' + meshname + ' does not have a texture image!' 295 message = 'MS3D Export Warning:|Mesh \"' + meshname + '\" does not have a texture image!' 296 meshtools.print_boxed(message) 297 Blender.Window.WaitCursor(0) 298 Blender.Draw.PupMenu(message) 299 300 file.write("\"\"") 301 302 #file.write("\".\\" + imageName + "\"") 303 file.write("\n\"\"\n") 304 305 306 307 308 file.write('\nBones: 0\n') # we don't need no stinking bones for Scorched! ;) 309 file.write('GroupComments: 0\n') 310 file.write('MaterialComments: 0\n') 311 file.write('BoneComments: 0\n') 312 file.write('ModelComment: 0\n') 313 Blender.Window.DrawProgressBar(1.0, '') #clear progressbar 314 file.close() 315 end = time.clock() 316 seconds = " in %.2f %s" % (end-start, "seconds") 317 message = "Successfully exported " + os.path.basename(filename) + seconds 318 meshtools.print_boxed(message) 319 Blender.Window.WaitCursor(0) 320 Blender.Draw.PupMenu(message) 321 return 322 323 324Blender.Draw.Register(draw,event,bevent) 325 326 327 328