Scripts:
authorWillian Padovani Germano <wpgermano@gmail.com>
Thu, 15 Dec 2005 01:42:45 +0000 (01:42 +0000)
committerWillian Padovani Germano <wpgermano@gmail.com>
Thu, 15 Dec 2005 01:42:45 +0000 (01:42 +0000)
- added import and export scripts for 3ds and md2 (Quake 2) models
contributed by author Bob Holcomb. Thanks!
- updated ac3d importer to be more forgiving and exporter to get rid of
deprecated call.

More additions and possibly fixes to come.

Note: we're forming a group to take care of importers and exporters and
improve the situation as a whole in Blender. Discussions should happen
at the bf-scripts-dev mailing list:
http://projects.blender.org/mailman/listinfo/bf-scripts-dev

Thanks Tom Musgrove (LetterRip) for helping a lot with scripts.

release/scripts/3ds_export.py [new file with mode: 0644]
release/scripts/3ds_import.py [new file with mode: 0644]
release/scripts/ac3d_export.py
release/scripts/ac3d_import.py
release/scripts/md2_export.py [new file with mode: 0644]
release/scripts/md2_import.py [new file with mode: 0644]

diff --git a/release/scripts/3ds_export.py b/release/scripts/3ds_export.py
new file mode 100644 (file)
index 0000000..ebe06c3
--- /dev/null
@@ -0,0 +1,673 @@
+#!BPY
+
+""" 
+Name: '3D Studio (.3ds)...'
+Blender: 237
+Group: 'Export'
+Tooltip: 'Export to 3DS file format (.3ds).'
+"""
+
+__author__ = ["Campbell Barton", "Bob Holcomb", "Richard Lärkäng", "Damien McGinnes"]
+__url__ = ("blender", "elysiun", "http://www.gametutorials.com")
+__version__ = "0.82"
+__bpydoc__ = """\
+
+3ds Exporter
+
+This script Exports a 3ds file and the materials into blender for editing.
+
+Exporting is based on 3ds loader from www.gametutorials.com(Thanks DigiBen).
+"""
+
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# Script copyright (C) Bob Holcomb 
+#
+# 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 *****
+# --------------------------------------------------------------------------
+
+
+######################################################
+# Importing modules
+######################################################
+
+import Blender
+from Blender import NMesh, Scene, Object, Material
+import struct
+
+
+######################################################
+# Data Structures
+######################################################
+
+#Some of the chunks that we will export
+#----- Primary Chunk, at the beginning of each file
+PRIMARY= long("0x4D4D",16)
+
+#------ Main Chunks
+OBJECTINFO   =      long("0x3D3D",16);      #This gives the version of the mesh and is found right before the material and object information
+VERSION      =      long("0x0002",16);      #This gives the version of the .3ds file
+EDITKEYFRAME=      long("0xB000",16);      #This is the header for all of the key frame info
+
+#------ sub defines of OBJECTINFO
+MATERIAL=45055         #0xAFFF                         // This stored the texture info
+OBJECT=16384           #0x4000                         // This stores the faces, vertices, etc...
+
+#>------ sub defines of MATERIAL
+MATNAME    =      long("0xA000",16);      # This holds the material name
+MATAMBIENT   =      long("0xA010",16);      # Ambient color of the object/material
+MATDIFFUSE   =      long("0xA020",16);      # This holds the color of the object/material
+MATSPECULAR   =      long("0xA030",16);      # SPecular color of the object/material
+MATSHINESS   =      long("0xA040",16);      # ??
+MATMAP       =      long("0xA200",16);      # This is a header for a new material
+MATMAPFILE    =      long("0xA300",16);      # This holds the file name of the texture
+
+RGB1=  long("0x0011",16)
+RGB2=  long("0x0012",16)
+
+#>------ sub defines of OBJECT
+OBJECT_MESH  =      long("0x4100",16);      # This lets us know that we are reading a new object
+OBJECT_LIGHT =      long("0x4600",16);      # This lets un know we are reading a light object
+OBJECT_CAMERA=      long("0x4700",16);      # This lets un know we are reading a camera object
+
+#>------ sub defines of CAMERA
+OBJECT_CAM_RANGES=   long("0x4720",16);      # The camera range values
+
+#>------ sub defines of OBJECT_MESH
+OBJECT_VERTICES =   long("0x4110",16);      # The objects vertices
+OBJECT_FACES    =   long("0x4120",16);      # The objects faces
+OBJECT_MATERIAL =   long("0x4130",16);      # This is found if the object has a material, either texture map or color
+OBJECT_UV       =   long("0x4140",16);      # The UV texture coordinates
+OBJECT_TRANS_MATRIX  =   long("0x4160",16); # The Object Matrix
+
+#==============================================#
+# Strips the slashes from the back of a string #
+#==============================================#
+def stripPath(path):
+       return path.split('/')[-1].split('\\')[-1]
+
+#==================================================#
+# New name based on old with a different extension #
+#==================================================#
+def newFName(ext):
+       return Blender.Get('filename')[: -len(Blender.Get('filename').split('.', -1)[-1]) ] + ext
+
+
+#the chunk class
+class chunk:
+       ID=0
+       size=0
+
+       def __init__(self):
+               self.ID=0
+               self.size=0
+
+       def get_size(self):
+               self.size=6
+
+       def write(self, file):
+               #write header
+               data=struct.pack(\
+               "<HI",\
+               self.ID,\
+               self.size)
+               file.write(data)
+
+       def dump(self):
+               print "ID: ", self.ID
+               print "ID in hex: ", hex(self.ID)
+               print "size: ", self.size
+
+
+
+#may want to add light, camera, keyframe chunks.
+class vert_chunk(chunk):
+       verts=[]
+
+       def __init__(self):
+               self.verts=[]
+               self.ID=OBJECT_VERTICES
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size=2 #for the number of verts short
+               temp_size += 12 * len(self.verts)  #3 floats x 4 bytes each
+               self.size+=temp_size
+               #~ print "vert_chunk size: ", self.size
+               return self.size
+       
+       def write(self, file):
+               chunk.write(self, file)
+               #write header
+               data=struct.pack("<H", len(self.verts))
+               file.write(data)
+               #write verts
+               for vert in self.verts:
+                       data=struct.pack("<3f",vert[0],vert[1], vert[2])
+                       file.write(data)
+
+class obj_material_chunk(chunk):
+       name=""
+       faces=[]
+
+       def __init__(self):
+               self.name=""
+               self.faces=[]
+               self.ID=OBJECT_MATERIAL
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size=(len(self.name)+1)
+               temp_size+=2
+               for face in self.faces:
+                       temp_size+=2
+               self.size+=temp_size
+               #~ print "obj material chunk size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               #write name
+               name_length=len(self.name)+1
+               binary_format="<"+str(name_length)+"s"
+               data=struct.pack(binary_format, self.name)
+               file.write(data)
+               binary_format="<H"
+               #~ print "Nr of faces: ", len(self.faces)
+               data=struct.pack(binary_format, len(self.faces))
+               file.write(data)
+               for face in self.faces:
+                       data=struct.pack(binary_format, face)
+                       file.write(data)
+
+class face_chunk(chunk):
+       faces=[]
+       num_faces=0
+       m_chunks=[]
+
+       def __init__(self):
+               self.faces=[]
+               self.ID=OBJECT_FACES
+               self.num_faces=0
+               self.m_chunks=[]
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size = 2 #num faces info
+               temp_size += 8 * len(self.faces)  #4 short ints x 2 bytes each
+               for m in self.m_chunks:
+                       temp_size+=m.get_size()
+               self.size += temp_size
+               #~ print "face_chunk size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               data=struct.pack("<H", len(self.faces))
+               file.write(data)
+               #write faces
+               for face in self.faces:
+                       data=struct.pack("<4H", face[0],face[1], face[2], 0) # The last zero is only used by 3d studio
+                       file.write(data)
+               #write materials
+               for m in self.m_chunks:
+                       m.write(file)
+
+class uv_chunk(chunk):
+       uv=[]
+       num_uv=0
+
+       def __init__(self):
+               self.uv=[]
+               self.ID=OBJECT_UV
+               self.num_uv=0
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size=2 #for num UV
+               for this_uv in self.uv:
+                       temp_size+=8  #2 floats at 4 bytes each
+               self.size+=temp_size
+               #~ print "uv chunk size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               
+               #write header
+               data=struct.pack("<H", len(self.uv))
+               file.write(data)
+               
+               #write verts
+               for this_uv in self.uv:
+                       data=struct.pack("<2f", this_uv[0], this_uv[1])
+                       file.write(data)
+
+class mesh_chunk(chunk):
+       v_chunk=vert_chunk()
+       f_chunk=face_chunk()
+       uv_chunk=uv_chunk()
+
+       def __init__(self):
+               self.v_chunk=vert_chunk()
+               self.f_chunk=face_chunk()
+               self.uv_chunk=uv_chunk()
+               self.ID=OBJECT_MESH
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size=self.v_chunk.get_size()
+               temp_size+=self.f_chunk.get_size()
+               temp_size+=self.uv_chunk.get_size()
+               self.size+=temp_size
+               #~ print "object mesh chunk size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               #write stuff
+               self.v_chunk.write(file)
+               self.f_chunk.write(file)
+               self.uv_chunk.write(file)
+
+class object_chunk(chunk):
+       name=""
+       mesh_chunks=[]
+
+       def __init__(self):
+               self.name=""
+               self.mesh_chunks=[]
+               self.ID=OBJECT
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size=len(self.name)+1 #+1 for null character
+               for mesh in self.mesh_chunks:
+                       temp_size+=mesh.get_size()
+               self.size+=temp_size
+               #~ print "object chunk size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               #write name
+               
+               binary_format = "<%ds" % (len(self.name)+1)
+               data=struct.pack(binary_format, self.name)
+               file.write(data)
+               #write stuff
+               for mesh in self.mesh_chunks:
+                       mesh.write(file)
+
+class object_info_chunk(chunk):
+       obj_chunks=[]
+       mat_chunks=[]
+
+       def __init__(self):
+               self.obj_chunks=[]
+               self.mat_chunks=[]
+               self.ID=OBJECTINFO
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size=0
+               for mat in self.mat_chunks:
+                       temp_size+=mat.get_size()
+               for obj in self.obj_chunks:
+                       temp_size+=obj.get_size()
+               self.size+=temp_size
+               #~ print "object info size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               #write all the materials
+               for mat in self.mat_chunks:
+                       mat.write(file)
+               #write all the objects
+               for obj in self.obj_chunks:
+                       obj.write(file)
+
+
+
+class version_chunk(chunk):
+       version=3
+
+       def __init__(self):
+               self.ID=VERSION
+               self.version=3 #that the document that I'm using
+
+       def get_size(self):
+               chunk.get_size(self)
+               self.size += 4 #bytes for the version info
+               #~ print "version chunk size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               #write header and version
+               data=struct.pack("<I", self.version)
+               file.write(data)
+
+class rgb_chunk(chunk):
+       col=[]
+
+       def __init__(self):
+               self.col=[]
+
+       def get_size(self):
+               chunk.get_size(self)
+               self.size+=3 #color size
+               #~ print "rgb chunk size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               #write colors
+               for c in self.col:
+                       file.write( struct.pack("<c", chr(int(255*c))) )
+
+
+class rgb1_chunk(rgb_chunk):
+
+       def __init__(self):
+               self.ID=RGB1
+
+class rgb2_chunk(rgb_chunk):
+
+       def __init__(self):
+               self.ID=RGB2
+
+class material_ambient_chunk(chunk):
+       col1=None
+       col2=None
+
+       def __init__(self):
+               self.ID=MATAMBIENT
+               self.col1=rgb1_chunk()
+               self.col2=rgb2_chunk()
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size=self.col1.get_size()
+               temp_size+=self.col2.get_size()
+               self.size+=temp_size
+               #~ print "material ambient size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               #write colors
+               self.col1.write(file)
+               self.col2.write(file)
+
+class material_diffuse_chunk(chunk):
+       col1=None
+       col2=None
+
+       def __init__(self):
+               self.ID=MATDIFFUSE
+               self.col1=rgb1_chunk()
+               self.col2=rgb2_chunk()
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size=self.col1.get_size()
+               temp_size+=self.col2.get_size()
+               self.size+=temp_size
+               #~ print "material diffuse size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               #write colors
+               self.col1.write(file)
+               self.col2.write(file)
+
+class material_specular_chunk(chunk):
+       col1=None
+       col2=None
+
+       def __init__(self):
+               self.ID=MATSPECULAR
+               self.col1=rgb1_chunk()
+               self.col2=rgb2_chunk()
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size=self.col1.get_size()
+               temp_size+=self.col2.get_size()
+               self.size+=temp_size
+               #~ print "material specular size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               #write colors
+               self.col1.write(file)
+               self.col2.write(file)
+
+class material_name_chunk(chunk):
+       name=""
+
+       def __init__(self):
+               self.ID=MATNAME
+               self.name=""
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size=(len(self.name)+1)
+               self.size+=temp_size
+               #~ print "material name size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               #write name
+               name_length=len(self.name)+1
+               binary_format="<"+str(name_length)+"s"
+               data=struct.pack(binary_format, self.name)
+               file.write(data)
+
+class material_chunk(chunk):
+       matname_chunk=None
+       matambient_chunk=None
+       matdiffuse_chunk=None
+       matspecular_chunk=None
+
+       def __init__(self):
+               self.ID=MATERIAL
+               self.matname_chunk=material_name_chunk()
+               self.matambient_chunk=material_ambient_chunk()
+               self.matdiffuse_chunk=material_diffuse_chunk()
+               self.matspecular_chunk=material_specular_chunk()
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size=self.matname_chunk.get_size()
+               temp_size+=self.matambient_chunk.get_size()
+               temp_size+=self.matdiffuse_chunk.get_size()
+               temp_size+=self.matspecular_chunk.get_size()
+               self.size+=temp_size
+               #~ print "material chunk size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               #write name chunk
+               self.matname_chunk.write(file)
+               #write material colors
+               self.matambient_chunk.write(file)
+               self.matdiffuse_chunk.write(file)
+               self.matspecular_chunk.write(file)
+
+class primary_chunk(chunk):
+       version=None
+       obj_info=None
+
+       def __init__(self):
+               self.version=version_chunk()
+               self.obj_info=object_info_chunk()
+               self.ID=PRIMARY
+
+       def get_size(self):
+               chunk.get_size(self)
+               temp_size=self.version.get_size()
+               temp_size+=self.obj_info.get_size()
+               self.size+=temp_size
+               #~ print "primary chunk size: ", self.size
+               return self.size
+
+       def write(self, file):
+               chunk.write(self, file)
+               #write version chunk
+               self.version.write(file)
+               #write object_info chunk
+               self.obj_info.write(file)
+
+def read_chunk(file, chunk):
+               chunk.ID, chunk.size = \
+               struct.unpack(\
+               chunk.binary_format, \
+               file.read(struct.calcsize(chunk.binary_format))  )
+       
+def read_string(file):
+       s=""
+       index=0
+       
+       #read in the characters till we get a null character
+       data=struct.unpack("c", file.read(struct.calcsize("c")))
+       s=s+(data[0])
+       #print "string: ",s
+       while(ord(s[index])!=0):
+               index+=1
+               data=struct.unpack("c", file.read(struct.calcsize("c")))
+               s=s+(data[0])
+               #print "string: ",s
+       return str(s)
+
+######################################################
+# EXPORT
+######################################################
+def save_3ds(filename):
+       # Time the export
+       time1 = Blender.sys.time()
+
+       exported_materials = {}
+
+       #fill the chunks full of data
+       primary=primary_chunk()
+       #get all the objects in this scene
+       object_list = [ ob for ob in Blender.Object.GetSelected() if ob.getType() == 'Mesh' ]
+       #fill up the data structures with objects
+       for obj in object_list:
+               #create a new object chunk
+               primary.obj_info.obj_chunks.append(object_chunk())
+               #get the mesh data
+               blender_mesh = obj.getData()
+               blender_mesh.transform(obj.getMatrix())
+               #set the object name
+               primary.obj_info.obj_chunks[len(primary.obj_info.obj_chunks)-1].name=obj.getName()
+
+               matrix = obj.getMatrix()
+
+               #make a new mesh chunk object
+               mesh=mesh_chunk()
+               
+               mesh.v_chunk.verts = blender_mesh.verts
+               
+               dummy = None # just incase...
+               
+               for m in blender_mesh.materials:
+                       mesh.f_chunk.m_chunks.append(obj_material_chunk())
+                       mesh.f_chunk.m_chunks[len(mesh.f_chunk.m_chunks)-1].name = m.name
+
+                       # materials should only be exported once
+                       try:
+                               dummy = exported_materials[m.name]
+                               
+                               
+                       except KeyError:
+                               material = material_chunk()
+                               material.matname_chunk.name=m.name
+                               material.matambient_chunk.col1.col = m.mirCol
+                               material.matambient_chunk.col2.col = m.mirCol
+                               material.matdiffuse_chunk.col1.col = m.rgbCol
+                               material.matdiffuse_chunk.col2.col = m.rgbCol
+                               material.matspecular_chunk.col1.col = m.specCol
+                               material.matspecular_chunk.col2.col = m.specCol
+                               
+                               primary.obj_info.mat_chunks.append(material)
+                               
+                               exported_materials[m.name] = None
+               
+               del dummy # unpolute the namespace
+               
+               valid_faces = [f for f in blender_mesh.faces if len(f) > 2]
+               facenr=0
+               #fill in faces
+               for face in valid_faces:
+                       
+                       #is this a tri or a quad
+                       num_fv=len(face.v)
+                       
+                       
+                       #it's a tri
+                       if num_fv==3:
+                               mesh.f_chunk.faces.append((face[0].index, face[1].index, face[2].index))
+                               if (face.materialIndex < len(mesh.f_chunk.m_chunks)):
+                                       mesh.f_chunk.m_chunks[face.materialIndex].faces.append(facenr)
+                               facenr+=1
+                       
+                       else: #it's a quad                                      
+                               mesh.f_chunk.faces.append((face[0].index, face[1].index, face[2].index))  # 0,1,2
+                               mesh.f_chunk.faces.append((face[2].index, face[3].index, face[0].index))  # 2,3,0
+                               #first tri
+                               if (face.materialIndex < len(mesh.f_chunk.m_chunks)):
+                                       mesh.f_chunk.m_chunks[face.materialIndex].faces.append(facenr)
+                               facenr+=1
+                               #other tri
+                               if (face.materialIndex < len(mesh.f_chunk.m_chunks)):
+                                       mesh.f_chunk.m_chunks[face.materialIndex].faces.append(facenr)
+                               facenr+=1
+                       
+
+               #fill in the UV info
+               if blender_mesh.hasVertexUV():
+                       for vert in blender_mesh.verts:
+                               mesh.uv_chunk.uv.append((vert.uvco[0], vert.uvco[1]))
+
+               elif blender_mesh.hasFaceUV():
+                       for face in valid_faces:
+                               # Tri or quad.
+                               for uv_coord in face.uv:
+                                       mesh.uv_chunk.uv.append((uv_coord[0], uv_coord[1]))
+
+               #filled in our mesh, lets add it to the file
+               primary.obj_info.obj_chunks[len(primary.obj_info.obj_chunks)-1].mesh_chunks.append(mesh)
+
+       #check the size
+       primary.get_size()
+       #open the files up for writing
+       file = open( filename, "wb" )
+       #recursively write the stuff to file
+       primary.write(file)
+       file.close()
+       print "3ds export time: %.2f" % (Blender.sys.time() - time1)
+       
+
+Blender.Window.FileSelector(save_3ds, "Export 3DS", newFName('3ds'))
diff --git a/release/scripts/3ds_import.py b/release/scripts/3ds_import.py
new file mode 100644 (file)
index 0000000..6bd81c2
--- /dev/null
@@ -0,0 +1,548 @@
+#!BPY
+
+""" 
+Name: '3D Studio (.3ds)...'
+Blender: 237
+Group: 'Import'
+Tooltip: 'Import from 3DS file format (.3ds).'
+"""
+
+__author__ = ["Bob Holcomb", "Richard Lärkäng", "Damien McGinnes", "Campbell Barton"]
+__url__ = ("blender", "elysiun", "http://www.gametutorials.com")
+__version__ = "0.82"
+__bpydoc__ = """\
+
+3ds Importer
+
+This script imports a 3ds file and the materials into blender for editing.
+
+Loader is based on 3ds loader from www.gametutorials.com(Thanks DigiBen).
+
+Changes:<br>
+0.81a (fork- not 0.9) Campbell Barton 2005-06-08<br>
+- Simplified import code<br>
+- Never overwrite data<br>
+- Faster list handling<br>
+- Leaves import selected<br>
+
+0.81 Damien McGinnes 2005-01-09<br>
+- handle missing images better<br>
+    
+0.8 Damien McGinnes 2005-01-08<br>
+- copies sticky UV coords to face ones<br>
+- handles images better<br>
+- Recommend that you run 'RemoveDoubles' on each imported mesh after using this script
+
+"""
+
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# Script copyright (C) Bob Holcomb 
+#
+# 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 *****
+# --------------------------------------------------------------------------
+
+# Importing modules
+
+import Blender
+from Blender import NMesh, Scene, Object, Material, Image
+
+import sys, struct, string
+
+import os
+
+#this script imports uvcoords as sticky vertex coords
+#this parameter enables copying these to face uv coords
+#which shold be more useful.
+
+
+#===========================================================================#
+# Returns unique name of object/mesh (stops overwriting existing meshes)    #
+#===========================================================================#
+def getUniqueName(name):
+       newName = name
+       uniqueInt = 0
+       while 1:
+               try:
+                       ob = Object.Get(newName)
+                       # Okay, this is working, so lets make a new name
+                       newName = '%s.%d' % (name, uniqueInt)
+                       uniqueInt +=1
+               except AttributeError:
+                       if newName not in NMesh.GetNames():
+                               return newName
+                       else:
+                               newName = '%s.%d' % (name, uniqueInt)
+                               uniqueInt +=1
+
+
+######################################################
+# Data Structures
+######################################################
+
+#Some of the chunks that we will see
+#----- Primary Chunk, at the beginning of each file
+PRIMARY= long("0x4D4D",16)
+
+#------ Main Chunks
+OBJECTINFO   =      long("0x3D3D",16);      #This gives the version of the mesh and is found right before the material and object information
+VERSION      =      long("0x0002",16);      #This gives the version of the .3ds file
+EDITKEYFRAME=      long("0xB000",16);      #This is the header for all of the key frame info
+
+#------ sub defines of OBJECTINFO
+MATERIAL=45055         #0xAFFF                         // This stored the texture info
+OBJECT=16384           #0x4000                         // This stores the faces, vertices, etc...
+
+#>------ sub defines of MATERIAL
+MATNAME    =      long("0xA000",16);      # This holds the material name
+MATAMBIENT   =      long("0xA010",16);      # Ambient color of the object/material
+MATDIFFUSE   =      long("0xA020",16);      # This holds the color of the object/material
+MATSPECULAR   =      long("0xA030",16);      # SPecular color of the object/material
+MATSHINESS   =      long("0xA040",16);      # ??
+MATMAP       =      long("0xA200",16);      # This is a header for a new material
+MATMAPFILE    =      long("0xA300",16);      # This holds the file name of the texture
+
+#>------ sub defines of OBJECT
+OBJECT_MESH  =      long("0x4100",16);      # This lets us know that we are reading a new object
+OBJECT_LIGHT =      long("0x4600",16);      # This lets un know we are reading a light object
+OBJECT_CAMERA=      long("0x4700",16);      # This lets un know we are reading a camera object
+
+#>------ sub defines of CAMERA
+OBJECT_CAM_RANGES=   long("0x4720",16);      # The camera range values
+
+#>------ sub defines of OBJECT_MESH
+OBJECT_VERTICES =   long("0x4110",16);      # The objects vertices
+OBJECT_FACES    =   long("0x4120",16);      # The objects faces
+OBJECT_MATERIAL =   long("0x4130",16);      # This is found if the object has a material, either texture map or color
+OBJECT_UV       =   long("0x4140",16);      # The UV texture coordinates
+OBJECT_TRANS_MATRIX  =   long("0x4160",16); # The Object Matrix
+
+#the chunk class
+class chunk:
+       ID=0
+       length=0
+       bytes_read=0
+
+       #we don't read in the bytes_read, we compute that
+       binary_format="<HI"
+
+       def __init__(self):
+               self.ID=0
+               self.length=0
+               self.bytes_read=0
+
+       def dump(self):
+               print "ID: ", self.ID
+               print "ID in hex: ", hex(self.ID)
+               print "length: ", self.length
+               print "bytes_read: ", self.bytes_read
+               
+
+def read_chunk(file, chunk):
+               temp_data=file.read(struct.calcsize(chunk.binary_format))
+               data=struct.unpack(chunk.binary_format, temp_data)
+               chunk.ID=data[0]
+               chunk.length=data[1]
+               #update the bytes read function
+               chunk.bytes_read=6
+
+               #if debugging
+               #chunk.dump()
+
+def read_string(file):
+       s=""
+       index=0
+       #print "reading a string"
+       #read in the characters till we get a null character
+       temp_data=file.read(1)
+       data=struct.unpack("c", temp_data)
+       s=s+(data[0])
+       #print "string: ",s
+       while(ord(s[index])!=0):
+               index+=1
+               temp_data=file.read(1)
+               data=struct.unpack("c", temp_data)
+               s=s+(data[0])
+               #print "string: ",s
+       
+       #remove the null character from the string
+       the_string=s[:-1]
+       return str(the_string)
+
+######################################################
+# IMPORT
+######################################################
+def process_next_object_chunk(file, previous_chunk):
+       new_chunk=chunk()
+       temp_chunk=chunk()
+
+       while (previous_chunk.bytes_read<previous_chunk.length):
+               #read the next chunk
+               read_chunk(file, new_chunk)
+
+
+def process_next_chunk(file, previous_chunk, new_object_list):
+       contextObName = None
+       #contextLamp = None
+       contextMaterial = None
+       contextMatrix = Blender.Mathutils.Matrix(); contextMatrix.identity()
+       contextMesh = None
+       
+       TEXDICT={}
+       MATDICT={}
+       
+       objectList = [] # Keep a list of imported objects.
+       
+       # Localspace variable names, faster.
+       STRUCT_SIZE_1CHAR = struct.calcsize("c")
+       STRUCT_SIZE_2FLOAT = struct.calcsize("2f")
+       STRUCT_SIZE_3FLOAT = struct.calcsize("3f")
+       STRUCT_SIZE_UNSIGNED_SHORT = struct.calcsize("H")
+       STRUCT_SIZE_4UNSIGNED_SHORT = struct.calcsize("4H")
+       STRUCT_SIZE_4x3MAT = struct.calcsize("ffffffffffff")
+       
+       
+       def putContextMesh(myContextMesh):
+               INV_MAT = Blender.Mathutils.CopyMat(contextMatrix)
+               INV_MAT.invert()
+               contextMesh.transform(INV_MAT)
+               objectList.append(NMesh.PutRaw(contextMesh))
+               objectList[-1].name = contextObName
+               objectList[-1].setMatrix(contextMatrix)
+       
+       
+       #a spare chunk
+       new_chunk=chunk()
+       temp_chunk=chunk()
+
+       #loop through all the data for this chunk (previous chunk) and see what it is
+       while (previous_chunk.bytes_read<previous_chunk.length):
+               #read the next chunk
+               #print "reading a chunk"
+               read_chunk(file, new_chunk)
+
+               #is it a Version chunk?
+               if (new_chunk.ID==VERSION):
+                       #print "found a VERSION chunk"
+                       #read in the version of the file
+                       #it's an unsigned short (H)
+                       temp_data=file.read(struct.calcsize("I"))
+                       data=struct.unpack("I", temp_data)
+                       version=data[0]
+                       new_chunk.bytes_read+=4 #read the 4 bytes for the version number
+                       #this loader works with version 3 and below, but may not with 4 and above
+                       if (version>3):
+                               print "\tNon-Fatal Error:  Version greater than 3, may not load correctly: ", version
+
+               #is it an object info chunk?
+               elif (new_chunk.ID==OBJECTINFO):
+                       # print "found an OBJECTINFO chunk"
+                       process_next_chunk(file, new_chunk, new_object_list)
+                       
+                       #keep track of how much we read in the main chunk
+                       new_chunk.bytes_read+=temp_chunk.bytes_read
+
+               #is it an object chunk?
+               elif (new_chunk.ID==OBJECT):
+                       # print "found an OBJECT chunk"
+                       tempName = str(read_string(file))
+                       contextObName = getUniqueName( tempName )
+                       new_chunk.bytes_read += (len(tempName)+1)
+               
+               #is it a material chunk?
+               elif (new_chunk.ID==MATERIAL):
+                       # print "found a MATERIAL chunk"
+                       contextMaterial = Material.New()
+               
+               elif (new_chunk.ID==MATNAME):
+                       # print "Found a MATNAME chunk"
+                       material_name=""
+                       material_name=str(read_string(file))
+                       
+                       #plus one for the null character that ended the string
+                       new_chunk.bytes_read+=(len(material_name)+1)
+                       
+                       contextMaterial.setName(material_name)
+                       MATDICT[material_name] = contextMaterial.name
+               
+               elif (new_chunk.ID==MATAMBIENT):
+                       # print "Found a MATAMBIENT chunk"
+
+                       read_chunk(file, temp_chunk)
+                       temp_data=file.read(struct.calcsize("3B"))
+                       data=struct.unpack("3B", temp_data)
+                       temp_chunk.bytes_read+=3
+                       contextMaterial.mirCol = [float(col)/255 for col in data] # data [0,1,2] == rgb
+                       new_chunk.bytes_read+=temp_chunk.bytes_read
+
+               elif (new_chunk.ID==MATDIFFUSE):
+                       # print "Found a MATDIFFUSE chunk"
+
+                       read_chunk(file, temp_chunk)
+                       temp_data=file.read(struct.calcsize("3B"))
+                       data=struct.unpack("3B", temp_data)
+                       temp_chunk.bytes_read+=3
+                       contextMaterial.rgbCol = [float(col)/255 for col in data] # data [0,1,2] == rgb
+                       new_chunk.bytes_read+=temp_chunk.bytes_read
+
+               elif (new_chunk.ID==MATSPECULAR):
+                       # print "Found a MATSPECULAR chunk"
+
+                       read_chunk(file, temp_chunk)
+                       temp_data=file.read(struct.calcsize("3B"))
+                       data=struct.unpack("3B", temp_data)
+                       temp_chunk.bytes_read+=3
+                       
+                       contextMaterial.specCol = [float(col)/255 for col in data] # data [0,1,2] == rgb
+                       new_chunk.bytes_read+=temp_chunk.bytes_read
+
+               elif (new_chunk.ID==MATMAP):
+                       # print "Found a MATMAP chunk"
+                       pass # This chunk has no data
+
+               elif (new_chunk.ID==MATMAPFILE):
+                       # print "Found a MATMAPFILE chunk"
+                       texture_name=""
+                       texture_name=str(read_string(file))
+                       try:
+                               img = Image.Load(texture_name)
+                               TEXDICT[contextMaterial.name]=img
+                       except IOError:
+                               fname = os.path.join( os.path.dirname(FILENAME), texture_name)
+                               try:
+                                       img = Image.Load(fname)
+                                       TEXDICT[contextMaterial.name]=img
+                               except IOError:
+                                       print "\tERROR: failed to load image ",texture_name
+                                       TEXDICT[contextMaterial.name] = None # Dummy
+                                       
+                       #plus one for the null character that gets removed
+                       new_chunk.bytes_read += (len(texture_name)+1)
+
+
+               elif (new_chunk.ID==OBJECT_MESH):
+                       # print "Found an OBJECT_MESH chunk"
+                       if contextMesh != None: # Write context mesh if we have one.
+                               putContextMesh(contextMesh)
+                       
+                       contextMesh = NMesh.New()
+                       
+                       # Reset matrix
+                       contextMatrix = Blender.Mathutils.Matrix(); contextMatrix.identity()
+                       
+               elif (new_chunk.ID==OBJECT_VERTICES):
+                       # print "Found an OBJECT_VERTICES chunk"
+                       #print "object_verts: length: ", new_chunk.length
+                       temp_data=file.read(STRUCT_SIZE_UNSIGNED_SHORT)
+                       data=struct.unpack("H", temp_data)
+                       new_chunk.bytes_read+=2
+                       num_verts=data[0]
+                       # print "number of verts: ", num_verts
+                       for counter in range (num_verts):
+                               temp_data=file.read(STRUCT_SIZE_3FLOAT)
+                               new_chunk.bytes_read += STRUCT_SIZE_3FLOAT #12: 3 floats x 4 bytes each
+                               data=struct.unpack("3f", temp_data)
+                               v=NMesh.Vert(data[0],data[1],data[2])
+                               contextMesh.verts.append(v)
+                       #print "object verts: bytes read: ", new_chunk.bytes_read
+
+               elif (new_chunk.ID==OBJECT_FACES):
+                       # print "Found an OBJECT_FACES chunk"
+                       #print "object faces: length: ", new_chunk.length
+                       temp_data=file.read(STRUCT_SIZE_UNSIGNED_SHORT)
+                       data=struct.unpack("H", temp_data)
+                       new_chunk.bytes_read+=2
+                       num_faces=data[0]
+                       #print "number of faces: ", num_faces
+
+                       for counter in range(num_faces):
+                               temp_data=file.read(STRUCT_SIZE_4UNSIGNED_SHORT)
+                               new_chunk.bytes_read += STRUCT_SIZE_4UNSIGNED_SHORT #4 short ints x 2 bytes each
+                               data=struct.unpack("4H", temp_data)
+                               
+                               #insert the mesh info into the faces, don't worry about data[3] it is a 3D studio thing
+                               f = NMesh.Face( [contextMesh.verts[data[i]] for i in xrange(3) ] )
+                               f.uv = [ tuple(contextMesh.verts[data[i]].uvco[:2]) for  i in xrange(3) ]
+                               contextMesh.faces.append(f)
+                       #print "object faces: bytes read: ", new_chunk.bytes_read
+
+               elif (new_chunk.ID==OBJECT_MATERIAL):
+                       # print "Found an OBJECT_MATERIAL chunk"
+                       material_name=""
+                       material_name=str(read_string(file))
+                       new_chunk.bytes_read += len(material_name)+1 # remove 1 null character.
+
+                       #look up the material in all the materials
+                       material_found=0
+                       for mat in Material.Get():
+                               
+                               #found it, add it to the mesh
+                               if(mat.name==material_name):
+                                       if len(contextMesh.materials) >= 15:
+                                               print "\tCant assign more than 16 materials per mesh, keep going..."
+                                               break
+                                       else:
+                                               meshHasMat = 0
+                                               for myMat in contextMesh.materials:
+                                                       if myMat.name == mat.name:
+                                                               meshHasMat = 1
+                                               
+                                               if meshHasMat == 0:
+                                                       contextMesh.addMaterial(mat)
+                                                       material_found=1
+                                                       
+                                                       #figure out what material index this is for the mesh
+                                                       for mat_counter in range(len(contextMesh.materials)):
+                                                               if contextMesh.materials[mat_counter].name == material_name:
+                                                                       mat_index=mat_counter
+                                                                       #print "material index: ",mat_index
+                                                       
+                                               
+                                               break # get out of this for loop so we don't accidentally set material_found back to 0
+                               else:
+                                       material_found=0
+                                       # print "Not matching: ", mat.name, " and ", material_name
+
+                       if material_found == 1:
+                               contextMaterial = mat
+                               #read the number of faces using this material
+                               temp_data=file.read(STRUCT_SIZE_UNSIGNED_SHORT)
+                               data=struct.unpack("H", temp_data)
+                               new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT
+                               num_faces_using_mat=data[0]
+
+                               #list of faces using mat
+                               for face_counter in range(num_faces_using_mat):
+                                       temp_data=file.read(STRUCT_SIZE_UNSIGNED_SHORT)
+                                       new_chunk.bytes_read += STRUCT_SIZE_UNSIGNED_SHORT
+                                       data=struct.unpack("H", temp_data)
+                                       contextMesh.faces[data[0]].materialIndex = mat_index
+                                       
+                                       try:
+                                               mname = MATDICT[contextMaterial.name]
+                                               contextMesh.faces[data[0]].image = TEXDICT[mname]
+                                       except:
+                                               continue
+                       else:
+                               #read past the information about the material you couldn't find
+                               #print "Couldn't find material.  Reading past face material info"
+                               buffer_size=new_chunk.length-new_chunk.bytes_read
+                               binary_format=str(buffer_size)+"c"
+                               temp_data=file.read(struct.calcsize(binary_format))
+                               new_chunk.bytes_read+=buffer_size
+                       
+                       #print "object mat: bytes read: ", new_chunk.bytes_read
+
+               elif (new_chunk.ID == OBJECT_UV):
+                       # print "Found an OBJECT_UV chunk"
+                       temp_data=file.read(STRUCT_SIZE_UNSIGNED_SHORT)
+                       data=struct.unpack("H", temp_data)
+                       new_chunk.bytes_read+=2
+                       num_uv=data[0]
+
+                       for counter in range(num_uv):
+                               temp_data=file.read(STRUCT_SIZE_2FLOAT)
+                               new_chunk.bytes_read += STRUCT_SIZE_2FLOAT #2 float x 4 bytes each
+                               data=struct.unpack("2f", temp_data)
+                               
+                               #insert the insert the UV coords in the vertex data
+                               contextMesh.verts[counter].uvco = data
+               
+               elif (new_chunk.ID == OBJECT_TRANS_MATRIX):
+                       # print "Found an OBJECT_TRANS_MATRIX chunk"
+                       
+                       temp_data=file.read(STRUCT_SIZE_4x3MAT)
+                       data = list( struct.unpack("ffffffffffff", temp_data) )
+                       new_chunk.bytes_read += STRUCT_SIZE_4x3MAT 
+                       
+                       contextMatrix = Blender.Mathutils.Matrix(\
+                        data[:3] + [0],\
+                        data[3:6] + [0],\
+                        data[6:9] + [0],\
+                        data[9:] + [1])
+               
+               
+               
+               else: #(new_chunk.ID!=VERSION or new_chunk.ID!=OBJECTINFO or new_chunk.ID!=OBJECT or new_chunk.ID!=MATERIAL):
+                       # print "skipping to end of this chunk"
+                       buffer_size=new_chunk.length-new_chunk.bytes_read
+                       binary_format=str(buffer_size)+"c"
+                       temp_data=file.read(struct.calcsize(binary_format))
+                       new_chunk.bytes_read+=buffer_size
+
+
+               #update the previous chunk bytes read
+               previous_chunk.bytes_read += new_chunk.bytes_read
+               #print "Bytes left in this chunk: ", previous_chunk.length-previous_chunk.bytes_read
+       
+       # FINISHED LOOP
+       # There will be a number of objects still not added
+       if contextMesh != None:
+               putContextMesh(contextMesh)
+       
+       for ob in objectList:
+               ob.sel = 1
+
+def load_3ds (filename):
+       print 'Importing "%s"' % filename
+       
+       time1 = Blender.sys.time()
+       
+       global FILENAME
+       FILENAME=filename
+       current_chunk=chunk()
+       
+       file=open(filename,"rb")
+       
+       #here we go!
+       # print "reading the first chunk"
+       new_object_list = []
+       read_chunk(file, current_chunk)
+       if (current_chunk.ID!=PRIMARY):
+               print "\tFatal Error:  Not a valid 3ds file: ", filename
+               file.close()
+               return
+
+       process_next_chunk(file, current_chunk, new_object_list)
+       
+       # Select all new objects.
+       for ob in new_object_list: ob.sel = 1
+       
+       print 'finished importing: "%s" in %.4f sec.' % (filename, (Blender.sys.time()-time1))
+       file.close()
+
+#***********************************************
+# MAIN
+#***********************************************
+def my_callback(filename):
+       load_3ds(filename)
+
+Blender.Window.FileSelector(my_callback, "Import 3DS", '*.3ds')
+
+# For testing compatibility
+'''
+TIME = Blender.sys.time()
+import os
+for _3ds in os.listdir('/3ds/'):
+       if _3ds.lower().endswith('3ds'):
+               print _3ds
+               newScn = Scene.New(_3ds)
+               newScn.makeCurrent()
+               my_callback('/3ds/' + _3ds)
+
+print "TOTAL TIME: ", Blender.sys.time() - TIME
+'''
index b9b7b8e5ae6081bc1a12fb516a3f2bfd77a7a936..ea9ba239003614ee022fe14f5ceea054aad7ce42 100644 (file)
@@ -202,7 +202,8 @@ def transform_verts(verts, m):
        vecs = []
        for v in verts:
                vec = Mathutils.Vector([v[0],v[1],v[2], 1])
-               vecs.append(Mathutils.VecMultMat(vec, m))
+               #vecs.append(Mathutils.VecMultMat(vec, m))
+               vecs.append(vec*m)
        return vecs
 
 # ---
index d2505022adf88a44af67b28e834cf1f042ff7e9f..4dcde65fb4af78a9b17c3abe18d78b89385edba4 100644 (file)
@@ -10,7 +10,7 @@ Tip: 'Import an AC3D (.ac) file.'
 __author__ = "Willian P. Germano"
 __url__ = ("blender", "elysiun", "AC3D's homepage, http://www.ac3d.org",
        "PLib 3d gaming lib, http://plib.sf.net")
-__version__ = "2.36 2005-04-14"
+__version__ = "2.36a 2005-12-04"
 
 __bpydoc__ = """\
 This script imports AC3D models into Blender.
@@ -43,9 +43,9 @@ users can configure (see config options above).
 # $Id$
 #
 # --------------------------------------------------------------------------
-# AC3DImport version 2.36 Apr 14, 2005
+# AC3DImport version 2.36a Dec 04, 2005
 # Program versions: Blender 2.36+ and AC3Db files (means version 0xb)
-# changed: updated to use the Scripts Config Editor facilities
+# changed: fixed a bug: error on 1 vertex "closed" polylines
 # --------------------------------------------------------------------------
 # ***** BEGIN GPL LICENSE BLOCK *****
 #
@@ -366,7 +366,7 @@ class AC3DImport:
                                        faces.append(cut)
                                        face = face[1:]
 
-                               if flaglow == 1:
+                               if flaglow == 1 and faces:
                                        face = [faces[-1][-1], faces[0][0]]
                                        faces.append(face)
 
@@ -498,7 +498,9 @@ class AC3DImport:
                                for vi in range(len(f)):
                                        bface.v.append(mesh.verts[f[vi][0]])
                                        bface.uv.append((f[vi][1][0], f[vi][1][1]))
-                               mesh.faces.append(bface)
+                               #mesh.faces.append(bface)
+                               # quick hack, will switch from NMesh to Mesh later:
+                               if len(bface.v) > 1: mesh.addFace(bface)
 
                        mesh.mode = 0
                        object = Blender.NMesh.PutRaw(mesh)
diff --git a/release/scripts/md2_export.py b/release/scripts/md2_export.py
new file mode 100644 (file)
index 0000000..d02f110
--- /dev/null
@@ -0,0 +1,1016 @@
+#!BPY
+
+"""
+Name: 'MD2 (.md2)'
+Blender: 239
+Group: 'Export'
+Tooltip: 'Export to Quake file format (.md2).'
+"""
+
+__author__ = 'Bob Holcomb'
+__version__ = '0.16'
+__url__ = ["Bob's site, http://bane.servebeer.com",
+     "Support forum, http://scourage.servebeer.com/phpbb/", "blender", "elysiun"]
+__email__ = ["Bob Holcomb, bob_holcomb:hotmail*com", "scripts"]
+__bpydoc__ = """\
+This script Exports a Quake 2 file (MD2).
+
+ Additional help from: Shadwolf, Skandal, Rojo, Cambo<br>
+ Thanks Guys!
+"""
+
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# Script copyright (C): Bob Holcomb
+#
+# 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 *****
+# --------------------------------------------------------------------------
+
+
+import Blender
+from Blender import *
+from Blender.Draw import *
+from Blender.BGL import *
+from Blender.Window import *
+
+import struct, string
+from types import *
+
+
+
+######################################################
+# GUI Loader
+######################################################
+
+# Export globals
+g_filename=Create("/home/bob/work/blender_scripts/md2/test-export.md2")
+g_frame_filename=Create("default")
+
+g_filename_search=Create("model")
+g_frame_search=Create("default")
+
+user_frame_list=[]
+
+#Globals
+g_scale=Create(1.0)
+
+# Events
+EVENT_NOEVENT=1
+EVENT_SAVE_MD2=2
+EVENT_CHOOSE_FILENAME=3
+EVENT_CHOOSE_FRAME=4
+EVENT_EXIT=100
+
+######################################################
+# Callbacks for Window functions
+######################################################
+def filename_callback(input_filename):
+       global g_filename
+       g_filename.val=input_filename
+
+def frame_callback(input_frame):
+       global g_frame_filename
+       g_frame_filename.val=input_frame
+
+def draw_gui():
+       global g_scale
+       global g_filename
+       global g_frame_filename
+       global EVENT_NOEVENT,EVENT_SAVE_MD2,EVENT_CHOOSE_FILENAME,EVENT_CHOOSE_FRAME,EVENT_EXIT
+
+       ########## Titles
+       glClear(GL_COLOR_BUFFER_BIT)
+       glRasterPos2d(8, 103)
+       Text("MD2 Export")
+
+       ######### Parameters GUI Buttons
+       g_filename = String("MD2 file to save: ", EVENT_NOEVENT, 10, 55, 210, 18,
+                            g_filename.val, 255, "MD2 file to save")
+       ########## MD2 File Search Button
+       Button("Search",EVENT_CHOOSE_FILENAME,220,55,80,18)
+
+       g_frame_filename = String("Frame List file to load: ", EVENT_NOEVENT, 10, 35, 210, 18,
+                                g_frame_filename.val, 255, "Frame List to load-overrides MD2 defaults")
+       ########## Texture Search Button
+       Button("Search",EVENT_CHOOSE_FRAME,220,35,80,18)
+
+       ########## Scale slider-default is 1/8 which is a good scale for md2->blender
+       g_scale= Slider("Scale Factor: ", EVENT_NOEVENT, 10, 75, 210, 18,
+                    1.0, 0.001, 10.0, 1, "Scale factor for obj Model");
+
+       ######### Draw and Exit Buttons
+       Button("Export",EVENT_SAVE_MD2 , 10, 10, 80, 18)
+       Button("Exit",EVENT_EXIT , 170, 10, 80, 18)
+
+def event(evt, val):   
+       if (evt == QKEY and not val):
+               Exit()
+
+def bevent(evt):
+       global g_filename
+       global g_frame_filename
+       global EVENT_NOEVENT,EVENT_SAVE_MD2,EVENT_EXIT
+
+       ######### Manages GUI events
+       if (evt==EVENT_EXIT):
+               Blender.Draw.Exit()
+       elif (evt==EVENT_CHOOSE_FILENAME):
+               FileSelector(filename_callback, "MD2 File Selection")
+       elif (evt==EVENT_CHOOSE_FRAME):
+               FileSelector(frame_callback, "Frame Selection")
+       elif (evt==EVENT_SAVE_MD2):
+               if (g_filename.val == "model"):
+                       save_md2("blender.md2")
+                       Blender.Draw.Exit()
+                       return
+               else:
+                       save_md2(g_filename.val)
+                       Blender.Draw.Exit()
+                       return
+
+Register(draw_gui, event, bevent)
+
+######################################################
+# MD2 Model Constants
+######################################################
+MD2_MAX_TRIANGLES=4096
+MD2_MAX_VERTICES=2048
+MD2_MAX_TEXCOORDS=2048
+MD2_MAX_FRAMES=512
+MD2_MAX_SKINS=32
+MD2_MAX_FRAMESIZE=(MD2_MAX_VERTICES * 4 + 128)
+
+MD2_FRAME_NAME_LIST=(("stand",1,40),
+                                       ("run",41,46),
+                                       ("attack",47,54),
+                                       ("pain1",55,58),
+                                       ("pain2",59,62),
+                                       ("pain3",63,66),
+                                       ("jump",67,72),
+                                       ("flip",73,84),
+                                       ("salute", 85,95),
+                                       ("taunt",96,112),
+                                       ("wave",113,123),
+                                       ("point",124,135),
+                                       ("crstnd",136,154),
+                                       ("crwalk",155,160),
+                                       ("crattack",161,169),
+                                       ("crpain",170,173),
+                                       ("crdeath",174,178),
+                                       ("death1",179,184),
+                                       ("death2",185,190),
+                                       ("death3",191,198))
+                                       #198 frames
+
+######################################################
+# MD2 data structures
+######################################################
+class md2_point:
+       vertices=[]
+       lightnormalindex=0
+       binary_format="<3BB"
+       def __init__(self):
+               self.vertices=[0]*3
+               self.lightnormalindex=0
+       def save(self, file):
+               temp_data=[0]*4
+               temp_data[0]=self.vertices[0]
+               temp_data[1]=self.vertices[1]
+               temp_data[2]=self.vertices[2]
+               temp_data[3]=self.lightnormalindex
+               data=struct.pack(self.binary_format, temp_data[0], temp_data[1], temp_data[2], temp_data[3])
+               file.write(data)
+       def dump(self):
+               print "MD2 Point Structure"
+               print "vertex X: ", self.vertices[0]
+               print "vertex Y: ", self.vertices[1]
+               print "vertex Z: ", self.vertices[2]
+               print "lightnormalindex: ",self.lightnormalindex
+               print ""
+               
+class md2_face:
+       vertex_index=[]
+       texture_index=[]
+       binary_format="<3h3h"
+       def __init__(self):
+               self.vertex_index = [ 0, 0, 0 ]
+               self.texture_index = [ 0, 0, 0]
+       def save(self, file):
+               temp_data=[0]*6
+               #swap vertices around so they draw right
+               temp_data[0]=self.vertex_index[0]
+               temp_data[1]=self.vertex_index[2]
+               temp_data[2]=self.vertex_index[1]
+               #swap texture vertices around so they draw right
+               temp_data[3]=self.texture_index[0]
+               temp_data[4]=self.texture_index[2]
+               temp_data[5]=self.texture_index[1]
+               data=struct.pack(self.binary_format,temp_data[0],temp_data[1],temp_data[2],temp_data[3],temp_data[4],temp_data[5])
+               file.write(data)
+       def dump (self):
+               print "MD2 Face Structure"
+               print "vertex 1 index: ", self.vertex_index[0]
+               print "vertex 2 index: ", self.vertex_index[1]
+               print "vertex 3 index: ", self.vertex_index[2]
+               print "texture 1 index: ", self.texture_index[0]
+               print "texture 2 index: ", self.texture_index[1]
+               print "texture 3 index: ", self.texture_index[2]
+               print ""
+               
+class md2_tex_coord:
+       u=0
+       v=0
+       binary_format="<2h"
+       def __init__(self):
+               self.u=0
+               self.v=0
+       def save(self, file):
+               temp_data=[0]*2
+               temp_data[0]=self.u
+               temp_data[1]=self.v
+               data=struct.pack(self.binary_format, temp_data[0], temp_data[1])
+               file.write(data)
+       def dump (self):
+               print "MD2 Texture Coordinate Structure"
+               print "texture coordinate u: ",self.u
+               print "texture coordinate v: ",self.v
+               print ""
+               
+class md2_GL_command:
+       s=0.0
+       t=0.0
+       vert_index=0
+       binary_format="<2fi"
+       
+       def __init__(self):
+               self.s=0.0
+               self.t=0.0
+               vert_index=0
+       def save(self,file):
+               temp_data=[0]*3
+               temp_data[0]=float(self.s)
+               temp_data[1]=float(self.t)
+               temp_data[2]=self.vert_index
+               data=struct.pack(self.binary_format, temp_data[0],temp_data[1],temp_data[2])
+               file.write(data)
+       def dump (self):
+               print "MD2 OpenGL Command"
+               print "s: ", self.s
+               print "t: ", self.t
+               print "Vertex Index: ", self.vert_index
+               print ""
+
+class md2_GL_cmd_list:
+       num=0
+       cmd_list=[]
+       binary_format="<i"
+       
+       def __init__(self):
+               self.num=0
+               self.cmd_list=[]
+       
+       def save(self,file):
+               data=struct.pack(self.binary_format, self.num)
+               file.write(data)
+               for cmd in self.cmd_list:
+                       cmd.save(file)
+       def dump(self):
+               print "MD2 OpenGL Command List"
+               print "number: ", self.num
+               for cmd in self.cmd_list:
+                       cmd.dump()
+               print ""
+
+class md2_skin:
+       name=""
+       binary_format="<64s"
+       def __init__(self):
+               self.name=""
+       def save(self, file):
+               temp_data=self.name
+               data=struct.pack(self.binary_format, temp_data)
+               file.write(data)
+       def dump (self):
+               print "MD2 Skin"
+               print "skin name: ",self.name
+               print ""
+               
+class md2_frame:
+       scale=[]
+       translate=[]
+       name=[]
+       vertices=[]
+       binary_format="<3f3f16s"
+
+       def __init__(self):
+               self.scale=[0.0]*3
+               self.translate=[0.0]*3
+               self.name=""
+               self.vertices=[]
+       def save(self, file):
+               temp_data=[0]*7
+               temp_data[0]=float(self.scale[0])
+               temp_data[1]=float(self.scale[1])
+               temp_data[2]=float(self.scale[2])
+               temp_data[3]=float(self.translate[0])
+               temp_data[4]=float(self.translate[1])
+               temp_data[5]=float(self.translate[2])
+               temp_data[6]=self.name
+               data=struct.pack(self.binary_format, temp_data[0],temp_data[1],temp_data[2],temp_data[3],temp_data[4],temp_data[5],temp_data[6])
+               file.write(data)
+       def dump (self):
+               print "MD2 Frame"
+               print "scale x: ",self.scale[0]
+               print "scale y: ",self.scale[1]
+               print "scale z: ",self.scale[2]
+               print "translate x: ",self.translate[0]
+               print "translate y: ",self.translate[1]
+               print "translate z: ",self.translate[2]
+               print "name: ",self.name
+               print ""
+               
+class md2_obj:
+       #Header Structure
+       ident=0                         #int 0  This is used to identify the file
+       version=0                       #int 1  The version number of the file (Must be 8)
+       skin_width=0            #int 2  The skin width in pixels
+       skin_height=0           #int 3  The skin height in pixels
+       frame_size=0            #int 4  The size in bytes the frames are
+       num_skins=0                     #int 5  The number of skins associated with the model
+       num_vertices=0          #int 6  The number of vertices (constant for each frame)
+       num_tex_coords=0        #int 7  The number of texture coordinates
+       num_faces=0                     #int 8  The number of faces (polygons)
+       num_GL_commands=0       #int 9  The number of gl commands
+       num_frames=0            #int 10 The number of animation frames
+       offset_skins=0          #int 11 The offset in the file for the skin data
+       offset_tex_coords=0     #int 12 The offset in the file for the texture data
+       offset_faces=0          #int 13 The offset in the file for the face data
+       offset_frames=0         #int 14 The offset in the file for the frames data
+       offset_GL_commands=0#int 15     The offset in the file for the gl commands data
+       offset_end=0            #int 16 The end of the file offset
+       binary_format="<17i"  #little-endian (<), 17 integers (17i)
+       #md2 data objects
+       tex_coords=[]
+       faces=[]
+       frames=[]
+       skins=[]
+       GL_commands=[]
+       
+       def __init__ (self):
+               self.tex_coords=[]
+               self.faces=[]
+               self.frames=[]
+               self.skins=[]
+       def save(self, file):
+               temp_data=[0]*17
+               temp_data[0]=self.ident
+               temp_data[1]=self.version
+               temp_data[2]=self.skin_width
+               temp_data[3]=self.skin_height
+               temp_data[4]=self.frame_size
+               temp_data[5]=self.num_skins
+               temp_data[6]=self.num_vertices
+               temp_data[7]=self.num_tex_coords
+               temp_data[8]=self.num_faces
+               temp_data[9]=self.num_GL_commands
+               temp_data[10]=self.num_frames
+               temp_data[11]=self.offset_skins
+               temp_data[12]=self.offset_tex_coords
+               temp_data[13]=self.offset_faces
+               temp_data[14]=self.offset_frames
+               temp_data[15]=self.offset_GL_commands
+               temp_data[16]=self.offset_end
+               data=struct.pack(self.binary_format, temp_data[0],temp_data[1],temp_data[2],temp_data[3],temp_data[4],temp_data[5],temp_data[6],temp_data[7],temp_data[8],temp_data[9],temp_data[10],temp_data[11],temp_data[12],temp_data[13],temp_data[14],temp_data[15],temp_data[16])
+               file.write(data)
+               #write the skin data
+               for skin in self.skins:
+                       skin.save(file)
+               #save the texture coordinates
+               for tex_coord in self.tex_coords:
+                       tex_coord.save(file)
+               #save the face info
+               for face in self.faces:
+                       face.save(file)
+               #save the frames
+               for frame in self.frames:
+                       frame.save(file)
+                       for vert in frame.vertices:
+                               vert.save(file)
+               #save the GL command List
+               for cmd in self.GL_commands:
+                       cmd.save(file)
+       def dump (self):
+               print "Header Information"
+               print "ident: ", self.ident
+               print "version: ", self.version
+               print "skin width: ", self.skin_width
+               print "skin height: ", self.skin_height
+               print "frame size: ", self.frame_size
+               print "number of skins: ", self.num_skins
+               print "number of texture coordinates: ", self.num_tex_coords
+               print "number of faces: ", self.num_faces
+               print "number of frames: ", self.num_frames
+               print "number of vertices: ", self.num_vertices
+               print "number of GL commands: ",self.num_GL_commands
+               print "offset skins: ", self.offset_skins
+               print "offset texture coordinates: ", self.offset_tex_coords
+               print "offset faces: ", self.offset_faces
+               print "offset frames: ",self.offset_frames
+               print "offset GL Commands: ",self.offset_GL_commands
+               print "offset end: ",self.offset_end
+               print ""
+
+######################################################
+# Validation
+######################################################
+def validation(object):
+       global user_frame_list
+       
+       #get access to the mesh data
+       mesh=object.getData(False, True) #get the object (not just name) and the Mesh, not NMesh
+
+       #check it's composed of only tri's      
+       result=0
+       for face in mesh.faces:
+               if len(face.verts)!=3:
+                       #select the face for future triangulation
+                       face.sel=1
+                       if result==0:  #first time we have this problem, don't pop-up a window every time it finds a quad
+                               print "Model not made entirely of triangles"
+                               result=Blender.Draw.PupMenu("Model not made entirely out of Triangles-Convert?%t|YES|NO")
+       if result==1:
+               mesh.quadToTriangle(0) #use closest verticies in breaking a quad
+       elif result==2:
+               return False #user will fix (I guess)
+
+       #check it has UV coordinates
+       if mesh.vertexUV==True:
+               print "Vertex UV not supported"
+               result=Blender.Draw.PupMenu("Vertex UV not suppored-Use Sticky UV%t|OK")
+               return False
+                       
+       elif mesh.faceUV==True:
+               for face in mesh.faces:
+                       if(len(face.uv)==3):
+                               pass
+                       else:
+                               print "Models vertices do not all have UV"
+                               result=Blender.Draw.PupMenu("Models vertices do not all have UV%t|OK")
+                               return False
+       
+       else:
+               print "Model does not have UV (face or vertex)"
+               result=Blender.Draw.PupMenu("Model does not have UV (face or vertex)%t|OK")
+               return False
+
+       #check it has only 1 associated texture map
+       last_face=""
+       last_face=mesh.faces[0].image
+       if last_face=="":
+               print "Model does not have a texture Map"
+               result=Blender.Draw.PupMenu("Model does not have a texture Map%t|OK")
+               return False
+
+       for face in mesh.faces:
+               mesh_image=face.image
+               if not mesh_image:
+                       print "Model has a face without a texture Map"
+                       result=Blender.Draw.PupMenu("Model has a face without a texture Map%t|OK")
+                       return False
+               if mesh_image!=last_face:
+                       print "Model has more than 1 texture map assigned"
+                       result=Blender.Draw.PupMenu("Model has more than 1 texture map assigned%t|OK")
+                       return False
+               
+       size=mesh_image.getSize()
+       #is this really what the user wants
+       if (size[0]!=256 or size[1]!=256):
+               print "Texture map size is non-standard (not 256x256), it is: ",size[0],"x",size[1]
+               result=Blender.Draw.PupMenu("Texture map size is non-standard (not 256x256), it is: "+size[0]+"x"+size[1]+": Continue?%t|YES|NO")
+               if(result==2):
+                       return False
+
+       #verify frame list data
+       user_frame_list=get_frame_list()        
+       temp=user_frame_list[len(user_frame_list)-1]
+       temp_num_frames=temp[2]
+       
+       #verify tri/vert/frame counts are within MD2 standard
+       face_count=len(mesh.faces)
+       vert_count=len(mesh.verts)      
+       frame_count=temp_num_frames
+       
+       if face_count>MD2_MAX_TRIANGLES:
+               print "Number of triangles exceeds MD2 standard: ", face_count,">",MD2_MAX_TRIANGLES
+               result=Blender.Draw.PupMenu("Number of triangles exceeds MD2 standard: Continue?%t|YES|NO")
+               if(result==2):
+                       return False
+       if vert_count>MD2_MAX_VERTICES:
+               print "Number of verticies exceeds MD2 standard",vert_count,">",MD2_MAX_VERTICES
+               result=Blender.Draw.PupMenu("Number of verticies exceeds MD2 standard: Continue?%t|YES|NO")
+               if(result==2):
+                       return False
+       if frame_count>MD2_MAX_FRAMES:
+               print "Number of frames exceeds MD2 standard of",frame_count,">",MD2_MAX_FRAMES
+               result=Blender.Draw.PupMenu("Number of frames exceeds MD2 standard: Continue?%t|YES|NO")
+               if(result==2):
+                       return False
+       #model is OK
+       return True
+
+######################################################
+# Fill MD2 data structure
+######################################################
+def fill_md2(md2, object):
+       global user_frame_list
+       #get a Mesh, not NMesh
+       mesh=object.getData(False, True)        
+       
+       #load up some intermediate data structures
+       tex_list={}
+       tex_count=0
+       #create the vertex list from the first frame
+       Blender.Set("curframe", 1)
+       
+       #header information
+       md2.ident=844121161
+       md2.version=8   
+       md2.num_vertices=len(mesh.verts)
+       md2.num_faces=len(mesh.faces)
+
+       #get the skin information
+       #use the first faces' image for the texture information
+       mesh_image=mesh.faces[0].image
+       size=mesh_image.getSize()
+       md2.skin_width=size[0]
+       md2.skin_height=size[1]
+       md2.num_skins=1
+       #add a skin node to the md2 data structure
+       md2.skins.append(md2_skin())
+       md2.skins[0].name=Blender.sys.basename(mesh_image.getFilename())
+
+       #put texture information in the md2 structure
+       #build UV coord dictionary (prevents double entries-saves space)
+       for face in mesh.faces:
+               for i in range(0,3):
+                       t=(face.uv[i])
+                       tex_key=(t[0],t[1])
+                       if not tex_list.has_key(tex_key):
+                               tex_list[tex_key]=tex_count
+                               tex_count+=1
+       md2.num_tex_coords=tex_count #each vert has its own UV coord
+
+       for this_tex in range (0, md2.num_tex_coords):
+               md2.tex_coords.append(md2_tex_coord())
+       for coord, index in tex_list.iteritems():
+               #md2.tex_coords.append(md2_tex_coord())
+               md2.tex_coords[index].u=int(coord[0]*md2.skin_width)
+               md2.tex_coords[index].v=int((1-coord[1])*md2.skin_height)
+
+       #put faces in the md2 structure
+       #for each face in the model
+       for this_face in range(0, md2.num_faces):
+               md2.faces.append(md2_face())
+               for i in range(0,3):
+                       #blender uses indexed vertexes so this works very well
+                       md2.faces[this_face].vertex_index[i]=mesh.faces[this_face].verts[i].index
+                       #lookup texture index in dictionary
+                       uv_coord=(mesh.faces[this_face].uv[i])
+                       tex_key=(uv_coord[0],uv_coord[1])
+                       tex_index=tex_list[tex_key]
+                       md2.faces[this_face].texture_index[i]=tex_index
+
+       #compute GL commands
+       md2.num_GL_commands=build_GL_commands(md2)
+
+       #get the frame data
+       #calculate 1 frame size  + (1 vert size*num_verts)
+       md2.frame_size=40+(md2.num_vertices*4) #in bytes
+       
+       #get the frame list
+       user_frame_list=get_frame_list()
+       if user_frame_list=="default":
+               md2.num_frames=198
+       else:
+               temp=user_frame_list[len(user_frame_list)-1]  #last item
+               md2.num_frames=temp[2] #last frame number
+       
+       #fill in each frame with frame info and all the vertex data for that frame
+       for frame_counter in range(0,md2.num_frames):
+               #add a frame
+               md2.frames.append(md2_frame())
+               #update the mesh objects vertex positions for the animation
+               Blender.Set("curframe", frame_counter)  #set blender to the correct frame
+               mesh.getFromObject(object.name)  #update the mesh to make verts current
+               
+#each frame has a scale and transform value that gets the vertex value between 0-255
+#since the scale and transform are the same for the all the verts in the frame, we only need
+#to figure this out once per frame
+               
+               #we need to start with the bounding box
+               bounding_box=object.getBoundBox() #uses the object, not the mesh data
+               #initialize with the first vertex for both min and max.  X and Y are swapped for MD2 format
+               point=bounding_box[0]
+               frame_min_x=point[1]
+               frame_max_x=point[1]
+               frame_min_y=point[0]
+               frame_max_y=point[0]
+               frame_min_z=point[2]
+               frame_max_z=point[2]
+               #find min/max values
+               for point in bounding_box:
+                       if frame_min_x>point[1]: frame_min_x=point[1]
+                       if frame_max_x<point[1]: frame_max_x=point[1]
+                       if frame_min_y>point[0]: frame_min_y=point[0]
+                       if frame_max_y<point[0]: frame_max_y=point[0]
+                       if frame_min_z>point[2]: frame_min_z=point[2]
+                       if frame_max_z<point[2]: frame_max_z=point[2]
+
+               #the scale is the difference between the min and max (on that axis) / 255
+               frame_scale_x=(frame_max_x-frame_min_x)/255
+               frame_scale_y=(frame_max_y-frame_min_y)/255
+               frame_scale_z=(frame_max_z-frame_min_z)/255
+               
+               #translate value of the mesh to center it on the origin
+               frame_trans_x=frame_min_x
+               frame_trans_y=frame_min_y
+               frame_trans_z=frame_min_z
+               
+               #fill in the data
+               md2.frames[frame_counter].scale=(-frame_scale_x, frame_scale_y, frame_scale_z)
+               md2.frames[frame_counter].translate=(-frame_trans_x, frame_trans_y, frame_trans_z)
+               
+               #now for the vertices
+               for vert_counter in range(0, md2.num_vertices):
+                       #add a vertex to the md2 structure
+                       md2.frames[frame_counter].vertices.append(md2_point())
+                       #figure out the new coords based on scale and transform
+                       #then translates the point so it's not less than 0
+                       #then scale it so it's between 0..255
+                       new_x=int((mesh.verts[vert_counter].co[1]-frame_trans_x)/frame_scale_x)
+                       new_y=int((mesh.verts[vert_counter].co[0]-frame_trans_y)/frame_scale_y)
+                       new_z=int((mesh.verts[vert_counter].co[2]-frame_trans_z)/frame_scale_z)
+                       #put them in the structure
+                       md2.frames[frame_counter].vertices[vert_counter].vertices=(new_x, new_y, new_z)
+
+                       #need to add the lookup table check here
+                       md2.frames[frame_counter].vertices[vert_counter].lightnormalindex=0
+                       
+       #output all the frame names-user_frame_list is loaded during the validation
+       for frame_set in user_frame_list:
+               for counter in range(frame_set[1]-1, frame_set[2]):
+                       md2.frames[counter].name=frame_set[0]+"_"+str(counter-frame_set[1]+2)
+
+       #compute these after everthing is loaded into a md2 structure
+       header_size=17*4 #17 integers, and each integer is 4 bytes
+       skin_size=64*md2.num_skins #64 char per skin * number of skins
+       tex_coord_size=4*md2.num_tex_coords #2 short * number of texture coords
+       face_size=12*md2.num_faces #3 shorts for vertex index, 3 shorts for tex index
+       frames_size=(((12+12+16)+(4*md2.num_vertices)) * md2.num_frames) #frame info+verts per frame*num frames
+       GL_command_size=md2.num_GL_commands*4 #each is an int or float, so 4 bytes per
+       
+       #fill in the info about offsets
+       md2.offset_skins=0+header_size
+       md2.offset_tex_coords=md2.offset_skins+skin_size
+       md2.offset_faces=md2.offset_tex_coords+tex_coord_size
+       md2.offset_frames=md2.offset_faces+face_size
+       md2.offset_GL_commands=md2.offset_frames+frames_size
+       md2.offset_end=md2.offset_GL_commands+GL_command_size
+
+######################################################
+# Get Frame List
+######################################################
+def get_frame_list():
+       global g_frame_filename
+       frame_list=[]
+
+       if g_frame_filename.val=="default":
+               return MD2_FRAME_NAME_LIST
+
+       else:
+       #check for file
+               if (Blender.sys.exists(g_frame_filename.val)==1):
+                       #open file and read it in
+                       file=open(g_frame_filename.val,"r")
+                       lines=file.readlines()
+                       file.close()
+
+                       #check header (first line)
+                       if lines[0]<>"# MD2 Frame Name List\n":
+                               print "its not a valid file"
+                               result=Blender.Draw.PupMenu("This is not a valid frame definition file-using default%t|OK")
+                               return MD2_FRAME_NAME_LIST
+                       else:
+                               #read in the data
+                               num_frames=0
+                               for counter in range(1, len(lines)):
+                                       current_line=lines[counter]
+                                       if current_line[0]=="#":
+                                               #found a comment
+                                               pass
+                                       else:
+                                               data=current_line.split()
+                                               frame_list.append([data[0],num_frames+1, num_frames+int(data[1])])
+                                               num_frames+=int(data[1])
+                               return frame_list
+               else:
+                       print "Cannot find file"
+                       result=Blender.Draw.PupMenu("Cannot find frame definion file-using default%t|OK")
+                       return MD2_FRAME_NAME_LIST
+
+######################################################
+# Tri-Strip/Tri-Fan functions
+######################################################
+def find_strip_length(md2, start_tri, start_vert):
+       #variables shared between fan and strip functions
+       global used
+       global strip_vert
+       global strip_st
+       global strip_tris
+       global strip_count
+
+       m1=m2=0
+       st1=st2=0
+       
+       used[start_tri]=2
+
+       last=start_tri
+
+       strip_vert[0]=md2.faces[last].vertex_index[start_vert%3]
+       strip_vert[1]=md2.faces[last].vertex_index[(start_vert+1)%3]
+       strip_vert[2]=md2.faces[last].vertex_index[(start_vert+2)%3]
+
+       strip_st[0]=md2.faces[last].texture_index[start_vert%3]
+       strip_st[1]=md2.faces[last].texture_index[(start_vert+1)%3]
+       strip_st[2]=md2.faces[last].texture_index[(start_vert+2)%3]
+
+       strip_tris[0]=start_tri
+       strip_count=1
+
+       m1=md2.faces[last].vertex_index[(start_vert+2)%3]
+       st1=md2.faces[last].texture_index[(start_vert+2)%3]
+       m2=md2.faces[last].vertex_index[(start_vert+1)%3]
+       st2=md2.faces[last].texture_index[(start_vert+1)%3]
+       
+       #look for matching triangle
+       check=start_tri+1
+       
+       for tri_counter in range(start_tri+1, md2.num_faces):
+               
+               for k in range(0,3):
+                       if md2.faces[check].vertex_index[k]!=m1:
+                               continue
+                       if md2.faces[check].texture_index[k]!=st1:
+                               continue
+                       if md2.faces[check].vertex_index[(k+1)%3]!=m2:
+                               continue
+                       if md2.faces[check].texture_index[(k+1)%3]!=st2:
+                               continue
+                       
+                       #if we can't use this triangle, this tri_strip is done
+                       if (used[tri_counter]!=0):
+                               for clear_counter in range(start_tri+1, md2.num_faces):
+                                       if used[clear_counter]==2:
+                                               used[clear_counter]=0
+                               return strip_count
+
+                       #new edge
+                       if (strip_count & 1):
+                               m2=md2.faces[check].vertex_index[(k+2)%3]
+                               st2=md2.faces[check].texture_index[(k+2)%3]
+                       else:
+                               m1=md2.faces[check].vertex_index[(k+2)%3]
+                               st1=md2.faces[check].texture_index[(k+2)%3]
+                       
+                       strip_vert[strip_count+2]=md2.faces[tri_counter].vertex_index[(k+2)%3]
+                       strip_st[strip_count+2]=md2.faces[tri_counter].texture_index[(k+2)%3]
+                       strip_tris[strip_count]=tri_counter
+                       strip_count+=1
+       
+                       used[tri_counter]=2
+               check+=1
+       return strip_count
+
+def find_fan_length(md2, start_tri, start_vert):
+       #variables shared between fan and strip functions
+       global used
+       global strip_vert
+       global strip_st
+       global strip_tris
+       global strip_count
+
+       m1=m2=0
+       st1=st2=0
+       
+       used[start_tri]=2
+
+       last=start_tri
+
+       strip_vert[0]=md2.faces[last].vertex_index[start_vert%3]
+       strip_vert[1]=md2.faces[last].vertex_index[(start_vert+1)%3]
+       strip_vert[2]=md2.faces[last].vertex_index[(start_vert+2)%3]
+       
+       strip_st[0]=md2.faces[last].texture_index[start_vert%3]
+       strip_st[1]=md2.faces[last].texture_index[(start_vert+1)%3]
+       strip_st[2]=md2.faces[last].texture_index[(start_vert+2)%3]
+
+       strip_tris[0]=start_tri
+       strip_count=1
+
+       m1=md2.faces[last].vertex_index[(start_vert+0)%3]
+       st1=md2.faces[last].texture_index[(start_vert+0)%3]
+       m2=md2.faces[last].vertex_index[(start_vert+2)%3]
+       st2=md2.faces[last].texture_index[(start_vert+2)%3]
+
+       #look for matching triangle     
+       check=start_tri+1
+       for tri_counter in range(start_tri+1, md2.num_faces):
+               for k in range(0,3):
+                       if md2.faces[check].vertex_index[k]!=m1:
+                               continue
+                       if md2.faces[check].texture_index[k]!=st1:
+                               continue
+                       if md2.faces[check].vertex_index[(k+1)%3]!=m2:
+                               continue
+                       if md2.faces[check].texture_index[(k+1)%3]!=st2:
+                               continue
+                       
+                       #if we can't use this triangle, this tri_strip is done
+                       if (used[tri_counter]!=0):
+                               for clear_counter in range(start_tri+1, md2.num_faces):
+                                       if used[clear_counter]==2:
+                                               used[clear_counter]=0
+                               return strip_count
+
+                       #new edge
+                       m2=md2.faces[check].vertex_index[(k+2)%3]
+                       st2=md2.faces[check].texture_index[(k+2)%3]
+                       
+                       strip_vert[strip_count+2]=m2
+                       strip_st[strip_count+2]=st2
+                       strip_tris[strip_count]=tri_counter
+                       strip_count+=1
+       
+                       used[tri_counter]=2
+               check+=1
+       return strip_count
+
+
+######################################################
+# Globals for GL command list calculations
+######################################################
+used=[]
+strip_vert=0
+strip_st=0
+strip_tris=0
+strip_count=0
+
+######################################################
+# Build GL command List
+######################################################
+def build_GL_commands(md2):
+       #variables shared between fan and strip functions
+       global used
+       used=[0]*md2.num_faces
+       global strip_vert
+       strip_vert=[0]*128
+       global strip_st
+       strip_st=[0]*128
+       global strip_tris
+       strip_tris=[0]*128
+       global strip_count
+       strip_count=0
+       
+       #variables
+       num_commands=0
+       start_vert=0
+       fan_length=strip_length=0
+       length=best_length=0
+       best_type=0
+       best_vert=[0]*1024
+       best_st=[0]*1024
+       best_tris=[0]*1024
+       s=0.0
+       t=0.0
+       
+       for face_counter in range(0,md2.num_faces):
+               if used[face_counter]!=0: #don't evaluate a tri that's been used
+                       #print "found a used triangle: ", face_counter
+                       pass
+               else:
+                       best_length=0 #restart the counter
+                       #for each vertex index in this face
+                       for start_vert in range(0,3):
+                               fan_length=find_fan_length(md2, face_counter, start_vert)
+                               if (fan_length>best_length):
+                                       best_type=1
+                                       best_length=fan_length
+                                       for index in range (0, best_length+2):
+                                               best_st[index]=strip_st[index]
+                                               best_vert[index]=strip_vert[index]
+                                       for index in range(0, best_length):
+                                               best_tris[index]=strip_tris[index]
+                               
+                               strip_length=find_strip_length(md2, face_counter, start_vert)
+                               if (strip_length>best_length): 
+                                       best_type=0
+                                       best_length=strip_length
+                                       for index in range (0, best_length+2):
+                                               best_st[index]=strip_st[index]
+                                               best_vert[index]=strip_vert[index]
+                                       for index in range(0, best_length):
+                                               best_tris[index]=strip_tris[index]
+                       
+                       #mark the tris on the best strip/fan as used
+                       for used_counter in range (0, best_length):
+                               used[best_tris[used_counter]]=1
+       
+                       temp_cmdlist=md2_GL_cmd_list()
+                       #push the number of commands into the command stream
+                       if best_type==1:
+                               temp_cmdlist.num=best_length+2
+                               num_commands+=1
+                       else:   
+                               temp_cmdlist.num=(-(best_length+2))
+                               num_commands+=1
+                       for command_counter in range (0, best_length+2):
+                               #emit a vertex into the reorder buffer
+                               cmd=md2_GL_command()
+                               index=best_st[command_counter]
+                               #calc and put S/T coords in the structure
+                               s=md2.tex_coords[index].u
+                               t=md2.tex_coords[index].v
+                               s=(s+0.5)/md2.skin_width
+                               t=(t+0.5)/md2.skin_height
+                               cmd.s=s
+                               cmd.t=t
+                               cmd.vert_index=best_vert[command_counter]
+                               temp_cmdlist.cmd_list.append(cmd)
+                               num_commands+=3
+                       md2.GL_commands.append(temp_cmdlist)
+       
+       #end of list
+       temp_cmdlist=md2_GL_cmd_list()  
+       temp_cmdlist.num=0
+       md2.GL_commands.append(temp_cmdlist)  
+       num_commands+=1
+       
+       #cleanup and return
+       used=best_vert=best_st=best_tris=strip_vert=strip_st=strip_tris=0
+       return num_commands
+
+######################################################
+# Save MD2 Format
+######################################################
+def save_md2(filename):
+       md2=md2_obj()  #blank md2 object to save
+
+       #get the object
+       mesh_objs = Blender.Object.GetSelected()
+
+       #check there is a blender object selected
+       if len(mesh_objs)==0:
+               print "Fatal Error: Must select a mesh to output as MD2"
+               print "Found nothing"
+               result=Blender.Draw.PupMenu("Must select an object to export%t|OK")
+               return
+
+       mesh_obj=mesh_objs[0] #this gets the first object (should be only one)
+
+       #check if it's a mesh object
+       if mesh_obj.getType()!="Mesh":
+               print "Fatal Error: Must select a mesh to output as MD2"
+               print "Found: ", mesh_obj.getType()
+               result=Blender.Draw.PupMenu("Selected Object must be a mesh to output as MD2%t|OK")
+               return
+
+       ok=validation(mesh_obj)
+       if ok==False:
+               return
+       
+       fill_md2(md2, mesh_obj)
+       md2.dump()
+       
+       #actually write it to disk
+       file=open(filename,"wb")
+       md2.save(file)
+       file.close()
+       
+       #cleanup
+       md2=0
+       
+       print "Closed the file"
diff --git a/release/scripts/md2_import.py b/release/scripts/md2_import.py
new file mode 100644 (file)
index 0000000..82b76d4
--- /dev/null
@@ -0,0 +1,571 @@
+#!BPY
+
+"""
+Name: 'MD2 (.md2)'
+Blender: 239
+Group: 'Import'
+Tooltip: 'Import from Quake file format (.md2).'
+"""
+
+__author__ = 'Bob Holcomb'
+__version__ = '0.15'
+__url__ = ["Bob's site, http://bane.servebeer.com",
+     "Support forum, http://scourage.servebeer.com/phpbb/", "blender", "elysiun"]
+__email__ = ["Bob Holcomb, bob_holcomb:hotmail*com", "scripts"]
+__bpydoc__ = """\
+This script imports a Quake 2 file (MD2), textures, 
+and animations into blender for editing.  Loader is based on MD2 loader from www.gametutorials.com-Thanks DigiBen! and the md3 blender loader by PhaethonH <phaethon@linux.ucla.edu><br>
+
+ Additional help from: Shadwolf, Skandal, Rojo, Cambo<br>
+ Thanks Guys!
+"""
+
+# ***** BEGIN GPL LICENSE BLOCK *****
+#
+# Script copyright (C) Bob Holcomb
+#
+# 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 *****
+# --------------------------------------------------------------------------
+
+
+import Blender
+from Blender import NMesh, Object, sys
+from Blender.BGL import *
+from Blender.Draw import *
+from Blender.Window import *
+from Blender.Image import *
+
+import struct, string
+from types import *
+
+
+
+
+######################################################
+# Main Body
+######################################################
+
+#returns the string from a null terminated string
+def asciiz (s):
+  n = 0
+  while (ord(s[n]) != 0):
+    n = n + 1
+  return s[0:n]
+
+
+######################################################
+# MD2 Model Constants
+######################################################
+MD2_MAX_TRIANGLES=4096
+MD2_MAX_VERTICES=2048
+MD2_MAX_TEXCOORDS=2048
+MD2_MAX_FRAMES=512
+MD2_MAX_SKINS=32
+MD2_MAX_FRAMESIZE=(MD2_MAX_VERTICES * 4 + 128)
+
+######################################################
+# MD2 data structures
+######################################################
+class md2_alias_triangle:
+       vertices=[]
+       lightnormalindex=0
+
+       binary_format="<3BB" #little-endian (<), 3 Unsigned char
+       
+       def __init__(self):
+               self.vertices=[0]*3
+               self.lightnormalindex=0
+
+       def load(self, file):
+               temp_data = file.read(struct.calcsize(self.binary_format))
+               data = struct.unpack(self.binary_format, temp_data)
+               self.vertices[0]=data[0]
+               self.vertices[1]=data[1]
+               self.vertices[2]=data[2]
+               self.lightnormalindex=data[3]
+               return self
+
+       def dump(self):
+               print "MD2 Alias_Triangle Structure"
+               print "vertex: ", self.vertices[0]
+               print "vertex: ", self.vertices[1]
+               print "vertex: ", self.vertices[2]
+               print "lightnormalindex: ",self.lightnormalindex
+               print ""
+
+class md2_face:
+       vertex_index=[]
+       texture_index=[]
+
+       binary_format="<3h3h" #little-endian (<), 3 short, 3 short
+       
+       def __init__(self):
+               self.vertex_index = [ 0, 0, 0 ]
+               self.texture_index = [ 0, 0, 0]
+
+       def load (self, file):
+               temp_data=file.read(struct.calcsize(self.binary_format))
+               data=struct.unpack(self.binary_format, temp_data)
+               self.vertex_index[0]=data[0]
+               self.vertex_index[1]=data[1]
+               self.vertex_index[2]=data[2]
+               self.texture_index[0]=data[3]
+               self.texture_index[1]=data[4]
+               self.texture_index[2]=data[5]
+               return self
+
+       def dump (self):
+               print "MD2 Face Structure"
+               print "vertex index: ", self.vertex_index[0]
+               print "vertex index: ", self.vertex_index[1]
+               print "vertex index: ", self.vertex_index[2]
+               print "texture index: ", self.texture_index[0]
+               print "texture index: ", self.texture_index[1]
+               print "texture index: ", self.texture_index[2]
+               print ""
+
+class md2_tex_coord:
+       u=0
+       v=0
+
+       binary_format="<2h" #little-endian (<), 2 unsigned short
+
+       def __init__(self):
+               self.u=0
+               self.v=0
+
+       def load (self, file):
+               temp_data=file.read(struct.calcsize(self.binary_format))
+               data=struct.unpack(self.binary_format, temp_data)
+               self.u=data[0]
+               self.v=data[1]
+               return self
+
+       def dump (self):
+               print "MD2 Texture Coordinate Structure"
+               print "texture coordinate u: ",self.u
+               print "texture coordinate v: ",self.v
+               print ""
+
+
+class md2_skin:
+       name=""
+
+       binary_format="<64s" #little-endian (<), char[64]
+
+       def __init__(self):
+               self.name=""
+
+       def load (self, file):
+               temp_data=file.read(struct.calcsize(self.binary_format))
+               data=struct.unpack(self.binary_format, temp_data)
+               self.name=asciiz(data[0])
+               return self
+
+       def dump (self):
+               print "MD2 Skin"
+               print "skin name: ",self.name
+               print ""
+
+class md2_alias_frame:
+       scale=[]
+       translate=[]
+       name=[]
+       vertices=[]
+
+       binary_format="<3f3f16s" #little-endian (<), 3 float, 3 float char[16]
+       #did not add the "3bb" to the end of the binary format
+       #because the alias_vertices will be read in through
+       #thier own loader
+
+       def __init__(self):
+               self.scale=[0.0]*3
+               self.translate=[0.0]*3
+               self.name=""
+               self.vertices=[]
+
+
+       def load (self, file):
+               temp_data=file.read(struct.calcsize(self.binary_format))
+               data=struct.unpack(self.binary_format, temp_data)
+               self.scale[0]=data[0]
+               self.scale[1]=data[1]
+               self.scale[2]=data[2]
+               self.translate[0]=data[3]
+               self.translate[1]=data[4]
+               self.translate[2]=data[5]
+               self.name=asciiz(data[6])
+               return self
+
+       def dump (self):
+               print "MD2 Alias Frame"
+               print "scale x: ",self.scale[0]
+               print "scale y: ",self.scale[1]
+               print "scale z: ",self.scale[2]
+               print "translate x: ",self.translate[0]
+               print "translate y: ",self.translate[1]
+               print "translate z: ",self.translate[2]
+               print "name: ",self.name
+               print ""
+
+class md2_obj:
+       #Header Structure
+       ident=0                         #int 0  This is used to identify the file
+       version=0                       #int 1  The version number of the file (Must be 8)
+       skin_width=0            #int 2  The skin width in pixels
+       skin_height=0           #int 3  The skin height in pixels
+       frame_size=0            #int 4  The size in bytes the frames are
+       num_skins=0                     #int 5  The number of skins associated with the model
+       num_vertices=0          #int 6  The number of vertices (constant for each frame)
+       num_tex_coords=0        #int 7  The number of texture coordinates
+       num_faces=0                     #int 8  The number of faces (polygons)
+       num_GL_commands=0       #int 9  The number of gl commands
+       num_frames=0            #int 10 The number of animation frames
+       offset_skins=0          #int 11 The offset in the file for the skin data
+       offset_tex_coords=0     #int 12 The offset in the file for the texture data
+       offset_faces=0          #int 13 The offset in the file for the face data
+       offset_frames=0         #int 14 The offset in the file for the frames data
+       offset_GL_commands=0#int 15     The offset in the file for the gl commands data
+       offset_end=0            #int 16 The end of the file offset
+
+       binary_format="<17i"  #little-endian (<), 17 integers (17i)
+
+       #md2 data objects
+       tex_coords=[]
+       faces=[]
+       frames=[]
+       skins=[]
+
+       def __init__ (self):
+               self.tex_coords=[]
+               self.faces=[]
+               self.frames=[]
+               self.skins=[]
+
+
+       def load (self, file):
+               temp_data = file.read(struct.calcsize(self.binary_format))
+               data = struct.unpack(self.binary_format, temp_data)
+
+               self.ident=data[0]
+               self.version=data[1]
+
+               if (self.ident!=844121161 or self.version!=8):
+                       print "Not a valid MD2 file"
+                       Exit()
+
+               self.skin_width=data[2]
+               self.skin_height=data[3]
+               self.frame_size=data[4]
+
+               #make the # of skin objects for model
+               self.num_skins=data[5]
+               for i in xrange(0,self.num_skins):
+                       self.skins.append(md2_skin())
+
+               self.num_vertices=data[6]
+
+               #make the # of texture coordinates for model
+               self.num_tex_coords=data[7]
+               for i in xrange(0,self.num_tex_coords):
+                       self.tex_coords.append(md2_tex_coord())
+
+               #make the # of triangle faces for model
+               self.num_faces=data[8]
+               for i in xrange(0,self.num_faces):
+                       self.faces.append(md2_face())
+
+               self.num_GL_commands=data[9]
+
+               #make the # of frames for the model
+               self.num_frames=data[10]
+               for i in xrange(0,self.num_frames):
+                       self.frames.append(md2_alias_frame())
+                       #make the # of vertices for each frame
+                       for j in xrange(0,self.num_vertices):
+                               self.frames[i].vertices.append(md2_alias_triangle())
+
+               self.offset_skins=data[11]
+               self.offset_tex_coords=data[12]
+               self.offset_faces=data[13]
+               self.offset_frames=data[14]
+               self.offset_GL_commands=data[15]
+
+               #load the skin info
+               file.seek(self.offset_skins,0)
+               for i in xrange(0, self.num_skins):
+                       self.skins[i].load(file)
+                       #self.skins[i].dump()
+
+               #load the texture coordinates
+               file.seek(self.offset_tex_coords,0)
+               for i in xrange(0, self.num_tex_coords):
+                       self.tex_coords[i].load(file)
+                       #self.tex_coords[i].dump()
+
+               #load the face info
+               file.seek(self.offset_faces,0)
+               for i in xrange(0, self.num_faces):
+                       self.faces[i].load(file)
+                       #self.faces[i].dump()
+
+               #load the frames
+               file.seek(self.offset_frames,0)
+               for i in xrange(0, self.num_frames):
+                       self.frames[i].load(file)
+                       #self.frames[i].dump()
+                       for j in xrange(0,self.num_vertices):
+                               self.frames[i].vertices[j].load(file)
+                               #self.frames[i].vertices[j].dump()
+               return self
+
+       def dump (self):
+               print "Header Information"
+               print "ident: ", self.ident
+               print "version: ", self.version
+               print "skin width: ", self.skin_width
+               print "skin height: ", self.skin_height
+               print "frame size: ", self.frame_size
+               print "number of skins: ", self.num_skins
+               print "number of texture coordinates: ", self.num_tex_coords
+               print "number of faces: ", self.num_faces
+               print "number of frames: ", self.num_frames
+               print "number of vertices: ", self.num_vertices
+               print "offset skins: ", self.offset_skins
+               print "offset texture coordinates: ", self.offset_tex_coords
+               print "offset faces: ", self.offset_faces
+               print "offset frames: ",self.offset_frames
+               print ""
+
+######################################################
+# Import functions
+######################################################
+def load_textures(md2, texture_filename):
+       #did the user specify a texture they wanted to use?
+       if (texture_filename!="texture"):
+               if (Blender.sys.exists(texture_filename)):
+                       mesh_image=Blender.Image.Load(texture_filename)
+                       return mesh_image
+               else:
+                       result=Blender.Draw.PupMenu("Cannot find texture: "+texture_filename+"-Continue?%t|OK")
+                       if(result==1):
+                               return -1
+       #does the model have textures specified with it?
+       if int(md2.num_skins) > 0:
+               for i in xrange(0,md2.num_skins):
+                       #md2.skins[i].dump()
+                       if (Blender.sys.exists(md2.skins[i].name)):
+                               mesh_image=Blender.Image.Load(md2.skins[i].name)
+                       else:
+                               result=Blender.Draw.PupMenu("Cannot find texture: "+md2.skins[i].name+"-Continue?%t|OK")
+                               if(result==1):
+                                       return -1
+               return mesh_image 
+       else:
+               result=Blender.Draw.PupMenu("There will be no Texutre"+"-Continue?%t|OK")
+               if(result==1):
+                       return -1
+       
+
+def animate_md2(md2, mesh_obj):
+       ######### Animate the verts through keyframe animation
+       mesh=mesh_obj.getData()
+       for i in xrange(1, md2.num_frames):
+               #update the vertices
+               for j in xrange(0,md2.num_vertices):
+                       x=(md2.frames[i].scale[0]*md2.frames[i].vertices[j].vertices[0]+md2.frames[i].translate[0])*g_scale.val
+                       y=(md2.frames[i].scale[1]*md2.frames[i].vertices[j].vertices[1]+md2.frames[i].translate[1])*g_scale.val
+                       z=(md2.frames[i].scale[2]*md2.frames[i].vertices[j].vertices[2]+md2.frames[i].translate[2])*g_scale.val
+
+                       #put the vertex in the right spot
+                       mesh.verts[j].co[0]=y
+                       mesh.verts[j].co[1]=-x
+                       mesh.verts[j].co[2]=z
+
+               mesh.update()
+               NMesh.PutRaw(mesh, mesh_obj.name)
+               #absolute keys, need to figure out how to get them working around the 100 frame limitation
+               mesh.insertKey(i,"absolute")
+               
+               #not really necissary, but I like playing with the frame counter
+               Blender.Set("curframe", i)
+
+
+def load_md2 (md2_filename, texture_filename):
+       #read the file in
+       file=open(md2_filename,"rb")
+       md2=md2_obj()
+       md2.load(file)
+       #md2.dump()
+       file.close()
+
+       ######### Creates a new mesh
+       mesh = NMesh.New()
+
+       uv_coord=[]
+       uv_list=[]
+
+       #load the textures to use later
+       #-1 if there is no texture to load
+       mesh_image=load_textures(md2, texture_filename)
+
+       ######### Make the verts
+       DrawProgressBar(0.25,"Loading Vertex Data")
+       for i in xrange(0,md2.num_vertices):
+               #use the first frame for the mesh vertices
+               x=(md2.frames[0].scale[0]*md2.frames[0].vertices[i].vertices[0]+md2.frames[0].translate[0])*g_scale.val
+               y=(md2.frames[0].scale[1]*md2.frames[0].vertices[i].vertices[1]+md2.frames[0].translate[1])*g_scale.val
+               z=(md2.frames[0].scale[2]*md2.frames[0].vertices[i].vertices[2]+md2.frames[0].translate[2])*g_scale.val
+               vertex=NMesh.Vert(y,-x,z)
+               mesh.verts.append(vertex)
+
+       ######## Make the UV list
+       DrawProgressBar(0.50,"Loading UV Data")
+       mesh.hasFaceUV(1)  #turn on face UV coordinates for this mesh
+       for i in xrange(0, md2.num_tex_coords):
+               u=(float(md2.tex_coords[i].u)/float(md2.skin_width))
+               v=(float(md2.tex_coords[i].v)/float(md2.skin_height))
+               #for some reason quake2 texture maps are upside down, flip that
+               uv_coord=(u,1-v)
+               uv_list.append(uv_coord)
+
+       ######### Make the faces
+       DrawProgressBar(0.75,"Loading Face Data")
+       for i in xrange(0,md2.num_faces):
+               face = NMesh.Face()
+               #draw the triangles in reverse order so they show up
+               face.v.append(mesh.verts[md2.faces[i].vertex_index[0]])
+               face.v.append(mesh.verts[md2.faces[i].vertex_index[2]])
+               face.v.append(mesh.verts[md2.faces[i].vertex_index[1]])
+               #append the list of UV
+               #ditto in reverse order with the texture verts
+               face.uv.append(uv_list[md2.faces[i].texture_index[0]])
+               face.uv.append(uv_list[md2.faces[i].texture_index[2]])
+               face.uv.append(uv_list[md2.faces[i].texture_index[1]])
+
+               #set the texture that this face uses if it has one
+               if (mesh_image!=-1):
+                       face.image=mesh_image
+               
+               #add the face
+               mesh.faces.append(face)
+
+       mesh_obj=NMesh.PutRaw(mesh)
+       animate_md2(md2, mesh_obj)
+       DrawProgressBar(0.999,"Loading Animation Data")
+       
+       #locate the Object containing the mesh at the cursor location
+       cursor_pos=Blender.Window.GetCursorPos()
+       mesh_obj.setLocation(float(cursor_pos[0]),float(cursor_pos[1]),float(cursor_pos[2]))
+       DrawProgressBar (1.0, "Finished") 
+
+#***********************************************
+# MAIN
+#***********************************************
+
+# Import globals
+g_md2_filename=Create("model")
+g_texture_filename=Create("texture")
+
+g_filename_search=Create("model")
+g_texture_search=Create("texture")
+
+#Globals
+g_scale=Create(1.0)
+
+# Events
+EVENT_NOEVENT=1
+EVENT_LOAD_MD2=2
+EVENT_CHOOSE_FILENAME=3
+EVENT_CHOOSE_TEXTURE=4
+EVENT_SAVE_MD2=5
+EVENT_EXIT=100
+
+######################################################
+# Callbacks for Window functions
+######################################################
+def filename_callback(input_filename):
+       global g_md2_filename
+       g_md2_filename.val=input_filename
+
+def texture_callback(input_texture):
+       global g_texture_filename
+       g_texture_filename.val=input_texture
+
+######################################################
+# GUI Loader
+######################################################
+
+
+def draw_gui():
+       global g_scale
+       global g_md2_filename
+       global g_texture_filename
+       global EVENT_NOEVENT,EVENT_LOAD_MD2,EVENT_CHOOSE_FILENAME,EVENT_CHOOSE_TEXTURE,EVENT_EXIT
+
+       ########## Titles
+       glClear(GL_COLOR_BUFFER_BIT)
+       glRasterPos2d(8, 125)
+       Text("MD2 loader")
+
+       ######### Parameters GUI Buttons
+       g_md2_filename = String("MD2 file to load: ", EVENT_NOEVENT, 10, 55, 210, 18,
+                            g_md2_filename.val, 255, "MD2 file to load")
+       ########## MD2 File Search Button
+       Button("Search",EVENT_CHOOSE_FILENAME,220,55,80,18)
+
+       g_texture_filename = String("Texture file to load: ", EVENT_NOEVENT, 10, 35, 210, 18,
+                                g_texture_filename.val, 255, "Texture file to load-overrides MD2 file")
+       ########## Texture Search Button
+       Button("Search",EVENT_CHOOSE_TEXTURE,220,35,80,18)
+
+       ########## Scale slider-default is 1/8 which is a good scale for md2->blender
+       g_scale= Slider("Scale Factor: ", EVENT_NOEVENT, 10, 75, 210, 18,
+                    1.0, 0.001, 10.0, 1, "Scale factor for obj Model");
+
+       ######### Draw and Exit Buttons
+       Button("Load",EVENT_LOAD_MD2 , 10, 10, 80, 18)
+       Button("Exit",EVENT_EXIT , 170, 10, 80, 18)
+
+def event(evt, val):   
+       if (evt == QKEY and not val):
+               Blender.Draw.Exit()
+
+def bevent(evt):
+       global g_md2_filename
+       global g_texture_filename
+       global EVENT_NOEVENT,EVENT_LOAD_MD2,EVENT_SAVE_MD2,EVENT_EXIT
+
+       ######### Manages GUI events
+       if (evt==EVENT_EXIT):
+               Blender.Draw.Exit()
+       elif (evt==EVENT_CHOOSE_FILENAME):
+               FileSelector(filename_callback, "MD2 File Selection")
+       elif (evt==EVENT_CHOOSE_TEXTURE):
+               FileSelector(texture_callback, "Texture Selection")
+       elif (evt==EVENT_LOAD_MD2):
+               if (g_md2_filename.val == "model"):
+                       Blender.Draw.Exit()
+                       return
+               else:
+                       load_md2(g_md2_filename.val, g_texture_filename.val)
+                       Blender.Redraw()
+                       Blender.Draw.Exit()
+                       return
+
+
+Register(draw_gui, event, bevent)