#!BPY

"""
Name: 'MD2 (.md2) xx'
Blender: 241
Group: 'Export'
Tooltip: 'Export to Quake file format (.md2).'
"""

__author__ = 'Bob Holcomb'
__version__ = '0.17.5'
__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("tris.md2")
g_frame_filename=Create("default")

g_filename_search=Create("")
g_frame_search=Create("default")

g_texture_path=Create("")

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
	global g_texture_path

	########## Titles
	glClear(GL_COLOR_BUFFER_BIT)
	glRasterPos2d(10, 120)
	Text("MD2 Export")

	######### Parameters GUI Buttons
	######### MD2 Filename text entry
	g_filename = String("MD2 file to save: ", EVENT_NOEVENT, 10, 75, 210, 18,
                            g_filename.val, 255, "MD2 file to save")
	########## MD2 File Search Button
	Button("Search",EVENT_CHOOSE_FILENAME,220,75,80,18)

	##########  MD2 Frame List Text entry
	g_frame_filename = String("Frame List file to load: ", EVENT_NOEVENT, 10, 55, 210, 18,
                                g_frame_filename.val, 255, "Frame List to load-overrides MD2 defaults")
	########## Frame List Search Button
	Button("Search",EVENT_CHOOSE_FRAME,220,55,80,18)

	##########  Texture path to append
	g_texture_path=String("Texture Path: ", EVENT_NOEVENT, 10,35,210,18,
														g_texture_path.val,255, "Texture path to prepend")


	########## Scale slider-default is 1/8 which is a good scale for md2->blender
	g_scale= Slider("Scale Factor: ", EVENT_NOEVENT, 10, 95, 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_NORMALS=((-0.525731, 0.000000, 0.850651),
			(-0.442863, 0.238856, 0.864188),
			(-0.295242, 0.000000, 0.955423),
			(-0.309017, 0.500000, 0.809017),
			(-0.162460, 0.262866, 0.951056),
			(0.000000, 0.000000, 1.000000),
			(0.000000, 0.850651, 0.525731),
			(-0.147621, 0.716567, 0.681718),
			(0.147621, 0.716567, 0.681718),
			(0.000000, 0.525731, 0.850651),
			(0.309017, 0.500000, 0.809017),
			(0.525731, 0.000000, 0.850651),
			(0.295242, 0.000000, 0.955423),
			(0.442863, 0.238856, 0.864188),
			(0.162460, 0.262866, 0.951056),
			(-0.681718, 0.147621, 0.716567),
			(-0.809017, 0.309017, 0.500000),
			(-0.587785, 0.425325, 0.688191),
			(-0.850651, 0.525731, 0.000000),
			(-0.864188, 0.442863, 0.238856),
			(-0.716567, 0.681718, 0.147621),
			(-0.688191, 0.587785, 0.425325),
			(-0.500000, 0.809017, 0.309017),
			(-0.238856, 0.864188, 0.442863),
			(-0.425325, 0.688191, 0.587785),
			(-0.716567, 0.681718, -0.147621),
			(-0.500000, 0.809017, -0.309017),
			(-0.525731, 0.850651, 0.000000),
			(0.000000, 0.850651, -0.525731),
			(-0.238856, 0.864188, -0.442863),
			(0.000000, 0.955423, -0.295242),
			(-0.262866, 0.951056, -0.162460),
			(0.000000, 1.000000, 0.000000),
			(0.000000, 0.955423, 0.295242),
			(-0.262866, 0.951056, 0.162460),
			(0.238856, 0.864188, 0.442863),
			(0.262866, 0.951056, 0.162460),
			(0.500000, 0.809017, 0.309017),
			(0.238856, 0.864188, -0.442863),
			(0.262866, 0.951056, -0.162460),
			(0.500000, 0.809017, -0.309017),
			(0.850651, 0.525731, 0.000000),
			(0.716567, 0.681718, 0.147621),
			(0.716567, 0.681718, -0.147621),
			(0.525731, 0.850651, 0.000000),
			(0.425325, 0.688191, 0.587785),
			(0.864188, 0.442863, 0.238856),
			(0.688191, 0.587785, 0.425325),
			(0.809017, 0.309017, 0.500000),
			(0.681718, 0.147621, 0.716567),
			(0.587785, 0.425325, 0.688191),
			(0.955423, 0.295242, 0.000000),
			(1.000000, 0.000000, 0.000000),
			(0.951056, 0.162460, 0.262866),
			(0.850651, -0.525731, 0.000000),
			(0.955423, -0.295242, 0.000000),
			(0.864188, -0.442863, 0.238856),
			(0.951056, -0.162460, 0.262866),
			(0.809017, -0.309017, 0.500000),
			(0.681718, -0.147621, 0.716567),
			(0.850651, 0.000000, 0.525731),
			(0.864188, 0.442863, -0.238856),
			(0.809017, 0.309017, -0.500000),
			(0.951056, 0.162460, -0.262866),
			(0.525731, 0.000000, -0.850651),
			(0.681718, 0.147621, -0.716567),
			(0.681718, -0.147621, -0.716567),
			(0.850651, 0.000000, -0.525731),
			(0.809017, -0.309017, -0.500000),
			(0.864188, -0.442863, -0.238856),
			(0.951056, -0.162460, -0.262866),
			(0.147621, 0.716567, -0.681718),
			(0.309017, 0.500000, -0.809017),
			(0.425325, 0.688191, -0.587785),
			(0.442863, 0.238856, -0.864188),
			(0.587785, 0.425325, -0.688191),
			(0.688191, 0.587785, -0.425325),
			(-0.147621, 0.716567, -0.681718),
			(-0.309017, 0.500000, -0.809017),
			(0.000000, 0.525731, -0.850651),
			(-0.525731, 0.000000, -0.850651),
			(-0.442863, 0.238856, -0.864188),
			(-0.295242, 0.000000, -0.955423),
			(-0.162460, 0.262866, -0.951056),
			(0.000000, 0.000000, -1.000000),
			(0.295242, 0.000000, -0.955423),
			(0.162460, 0.262866, -0.951056),
			(-0.442863, -0.238856, -0.864188),
			(-0.309017, -0.500000, -0.809017),
			(-0.162460, -0.262866, -0.951056),
			(0.000000, -0.850651, -0.525731),
			(-0.147621, -0.716567, -0.681718),
			(0.147621, -0.716567, -0.681718),
			(0.000000, -0.525731, -0.850651),
			(0.309017, -0.500000, -0.809017),
			(0.442863, -0.238856, -0.864188),
			(0.162460, -0.262866, -0.951056),
			(0.238856, -0.864188, -0.442863),
			(0.500000, -0.809017, -0.309017),
			(0.425325, -0.688191, -0.587785),
			(0.716567, -0.681718, -0.147621),
			(0.688191, -0.587785, -0.425325),
			(0.587785, -0.425325, -0.688191),
			(0.000000, -0.955423, -0.295242),
			(0.000000, -1.000000, 0.000000),
			(0.262866, -0.951056, -0.162460),
			(0.000000, -0.850651, 0.525731),
			(0.000000, -0.955423, 0.295242),
			(0.238856, -0.864188, 0.442863),
			(0.262866, -0.951056, 0.162460),
			(0.500000, -0.809017, 0.309017),
			(0.716567, -0.681718, 0.147621),
			(0.525731, -0.850651, 0.000000),
			(-0.238856, -0.864188, -0.442863),
			(-0.500000, -0.809017, -0.309017),
			(-0.262866, -0.951056, -0.162460),
			(-0.850651, -0.525731, 0.000000),
			(-0.716567, -0.681718, -0.147621),
			(-0.716567, -0.681718, 0.147621),
			(-0.525731, -0.850651, 0.000000),
			(-0.500000, -0.809017, 0.309017),
			(-0.238856, -0.864188, 0.442863),
			(-0.262866, -0.951056, 0.162460),
			(-0.864188, -0.442863, 0.238856),
			(-0.809017, -0.309017, 0.500000),
			(-0.688191, -0.587785, 0.425325),
			(-0.681718, -0.147621, 0.716567),
			(-0.442863, -0.238856, 0.864188),
			(-0.587785, -0.425325, 0.688191),
			(-0.309017, -0.500000, 0.809017),
			(-0.147621, -0.716567, 0.681718),
			(-0.425325, -0.688191, 0.587785),
			(-0.162460, -0.262866, 0.951056),
			(0.442863, -0.238856, 0.864188),
			(0.162460, -0.262866, 0.951056),
			(0.309017, -0.500000, 0.809017),
			(0.147621, -0.716567, 0.681718),
			(0.000000, -0.525731, 0.850651),
			(0.425325, -0.688191, 0.587785),
			(0.587785, -0.425325, 0.688191),
			(0.688191, -0.587785, 0.425325),
			(-0.955423, 0.295242, 0.000000),
			(-0.951056, 0.162460, 0.262866),
			(-1.000000, 0.000000, 0.000000),
			(-0.850651, 0.000000, 0.525731),
			(-0.955423, -0.295242, 0.000000),
			(-0.951056, -0.162460, 0.262866),
			(-0.864188, 0.442863, -0.238856),
			(-0.951056, 0.162460, -0.262866),
			(-0.809017, 0.309017, -0.500000),
			(-0.864188, -0.442863, -0.238856),
			(-0.951056, -0.162460, -0.262866),
			(-0.809017, -0.309017, -0.500000),
			(-0.681718, 0.147621, -0.716567),
			(-0.681718, -0.147621, -0.716567),
			(-0.850651, 0.000000, -0.525731),
			(-0.688191, 0.587785, -0.425325),
			(-0.587785, 0.425325, -0.688191),
			(-0.425325, 0.688191, -0.587785),
			(-0.425325, -0.688191, -0.587785),
			(-0.587785, -0.425325, -0.688191),
			(-0.688191, -0.587785, -0.425325))


######################################################
# 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

	#move the object to the origin if it's not already there
	if object.loc!=(0.0, 0.0, 0.0):
		object.setLocation(0.0,0.0,0.0)
		print "Model not centered at origin-Centering"
		result=Blender.Draw.PupMenu("Model not centered at origin-Centering")

	#resize the object in case it is not the right size
	if object.size!=(1.0,1.0,1.0):
		object.setSize(1.0,1.0,1.0)
		print "Object is scaled-You should scale the mesh verts, not the object"
		result=Blender.Draw.PupMenu("Object is scaled-You should scale the mesh verts, not the object")

	if object.rot!=(0.0,0.0,0.0):
		object.rot=(0.0,0.0,0.0)
		print "Object is rotated-You should rotate the mesh verts, not the object"
		result=Blender.Draw.PupMenu("Object is rotated-You should rotate the mesh verts, not the object")

	#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")

	#triangulate or quit
	if result==1:
		#selecting face mode
		Blender.Mesh.Mode(3)

		editmode = Window.EditMode()    # are we in edit mode?  If so ...
		if editmode: Window.EditMode(0) # leave edit mode before getting the mesh

		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|Quit")
		return False

	elif mesh.faceUV==True:
		for face in mesh.faces:
			if(len(face.uv)==3):
				pass
			else:
				print "Model's vertices do not all have UV"
				result=Blender.Draw.PupMenu("Model's vertices do not all have UV%t|Quit")
				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|Quit")
		return False

	#check it has an 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|Quit")
		return False

	#check if each face uses the same texture map (only one allowed)
	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|Quit")
			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|Quit")
			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: "+str(size[0])+"x"+str(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 defines
	global user_frame_list
	global g_texture_path

	Blender.Window.DrawProgressBar(0.25,"Filling MD2 Data")

	#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=g_texture_path.val+Blender.sys.basename(mesh_image.getFilename())
	if len(md2.skins[0].name)>64:
		print "Texture Path and name is more than 64 characters"
		result=Blender.Draw.PupMenu("Texture path and name is more than 64 characters-Quitting")
		return False

	#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

	Blender.Window.DrawProgressBar(0.5, "Computing GL Commands")

	#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


	progress=0.5
	progressIncrement=0.25/md2.num_frames

	#fill in each frame with frame info and all the vertex data for that frame
	for frame_counter in range(0,md2.num_frames):

		progress+=progressIncrement
		Blender.Window.DrawProgressBar(progress, "Calculating Frame: "+str(frame_counter))

		#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
			maxdot = -999999.0;
			maxdotindex = -1;

			for j in range(0,162):
				#dot = (x[0]*y[0]+x[1]*y[1]+x[2]*y[2])
				#swap y and x for difference in axis orientation
				x1=-mesh.verts[vert_counter].no[1]
				y1=mesh.verts[vert_counter].no[0]
				z1=mesh.verts[vert_counter].no[2]
				dot = (x1*MD2_NORMALS[j][0]+
				       y1*MD2_NORMALS[j][1]+
							 z1*MD2_NORMALS[j][2]);
				if (dot > maxdot):
					maxdot = dot;
					maxdotindex = j;

			md2.frames[frame_counter].vertices[vert_counter].lightnormalindex=maxdotindex

			del maxdot, maxdotindex
			del new_x, new_y, new_z
		del frame_max_x, frame_max_y, frame_max_z, frame_min_x, frame_min_y, frame_min_z
		del frame_scale_x, frame_scale_y, frame_scale_z, frame_trans_x, frame_trans_y, frame_trans_z


	#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].strip()<>"# MD2 Frame Name List":
				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

######################################################
# Globals for GL command list calculations
######################################################
used_tris=[]
strip_verts=[]
strip_st=[]
strip_tris=[]
strip_count=0

######################################################
# Tri-Strip/Tri-Fan functions
######################################################
def find_strip_length(md2, start_tri, start_vert):
	print "Finding strip length"

	global used
	global strip_verts
	global strip_st
	global strip_tris

	m1=m2=0

	used[start_tri]=2

	strip_verts=[0]*0
	strip_tris=[0]*0
	strip_st=[0]*0


	strip_verts.append(md2.faces[start_tri].vertex_index[start_vert%3])
	strip_verts.append(md2.faces[start_tri].vertex_index[(start_vert+2)%3])
	strip_verts.append(md2.faces[start_tri].vertex_index[(start_vert+1)%3])

	strip_st.append(md2.faces[start_tri].texture_index[start_vert%3])
	strip_st.append(md2.faces[start_tri].texture_index[(start_vert+2)%3])
	strip_st.append(md2.faces[start_tri].texture_index[(start_vert+1)%3])

	strip_count=1
	strip_tris.append(start_tri)

	m1=md2.faces[start_tri].vertex_index[(start_vert+2)%3]
	m2=md2.faces[start_tri].vertex_index[(start_vert+1)%3]

	for tri_counter in range(start_tri+1, md2.num_faces):
		for k in range(0,3):
			if(md2.faces[tri_counter].vertex_index[k]!=m1) or (md2.faces[tri_counter].vertex_index[(k+1)%3]!=m2):
				pass
			else:
				#found a matching edge
				print "Found a triangle with a matching edge: ", tri_counter
				if(used[tri_counter]!=0):
					print "Poop! I can't use it!"
				else:
					print "Yeah! I can use it"

					if(strip_count%2==1):  #is this an odd tri
						print "odd triangle"
						m2=md2.faces[tri_counter].vertex_index[(k+2)%3]
					else:
						print "even triangle"
						m1=md2.faces[tri_counter].vertex_index[(k+2)%3]

					strip_verts.append(md2.faces[tri_counter].vertex_index[(k+2)%3])
					strip_st.append(md2.faces[tri_counter].texture_index[(k+2)%3])
					strip_count+=1
					strip_tris.append(tri_counter)

					used[tri_counter]=2
					tri_counter=start_tri+1 # restart looking

	#clear used counter
	for used_counter in range(0, md2.num_faces):
		if used[used_counter]==2:
			used[used_counter]=0

	return strip_count

#***********************************************
def find_fan_length(md2, start_tri, start_vert):
	print "Finding fan length"

	global used
	global strip_verts
	global strip_tris
	global strip_st

	strip_verts=[0]*0
	strip_tris=[0]*0
	strip_st=[0]*0

	m1=m2=0

	used[start_tri]=2

	strip_verts.append(md2.faces[start_tri].vertex_index[start_vert%3])
	strip_verts.append(md2.faces[start_tri].vertex_index[(start_vert+2)%3])
	strip_verts.append(md2.faces[start_tri].vertex_index[(start_vert+1)%3])

	strip_st.append(md2.faces[start_tri].texture_index[start_vert%3])
	strip_st.append(md2.faces[start_tri].texture_index[(start_vert+2)%3])
	strip_st.append(md2.faces[start_tri].texture_index[(start_vert+1)%3])

	strip_count=1
	strip_tris.append(start_tri)

	m1=md2.faces[start_tri].vertex_index[(start_vert)]
	m2=md2.faces[start_tri].vertex_index[(start_vert+1)%3]

	for tri_counter in range(start_tri+1, md2.num_faces):
		for k in range(0,3):
			if (md2.faces[tri_counter].vertex_index[k]!=m1) or (md2.faces[tri_counter].vertex_index[(k+1)%3]!=m2):
				pass
			else:
				#found a matching edge
				print "Found a triangle with a matching edge: ", tri_counter
				if(used[tri_counter]!=0):
					print "Poop! I can't use it!"
				else:
					print "Yeah! I can use it"

					m2=md2.faces[tri_counter].vertex_index[(k+1)%3]

					strip_verts.append(md2.faces[tri_counter].vertex_index[(k+1)%3])
					strip_st.append(md2.faces[tri_counter].texture_index[(k+1)%3])

					strip_count+=1
					strip_tris.append(tri_counter)

					used[tri_counter]=2
					tri_counter=start_tri+1 #restart looking

	#clear used counter
	for used_counter in range(0, md2.num_faces):
		if used[used_counter]==2:
			used[used_counter]=0

	return strip_count

######################################################
# Build GL command List
######################################################
def build_GL_commands(md2):
	print "Building GL Commands"

	global used
	global strip_verts
	global strip_tris
	global strip_st

	#globals initialization
	used=[0]*md2.num_faces
	print "Used: ", used
	num_commands=0

	for tri_counter in range(0,md2.num_faces):
		if used[tri_counter]!=0:
			print "Found a used triangle: ", tri_counter
		else:
			print "Found an unused triangle: ", tri_counter

			#intialization
			best_length=0
			best_type=0
			best_verts=[0]*0
			best_tris=[0]*0
			best_st=[0]*0


			for start_vert in range(0,3):
				fan_length=find_fan_length(md2, tri_counter, start_vert)
				#fan_length=0
				print "Triangle: ", tri_counter, " Vertex: ", start_vert, " Fan Length: ", fan_length

				if (fan_length>best_length):
					best_type=1
					best_length=fan_length
					best_verts=strip_verts
					best_tris=strip_tris
					best_st=strip_st

				#strip_length=find_strip_length(md2, tri_counter, start_vert)
				strip_length=0
				print "Triangle: ", tri_counter, " Vertex: ", start_vert, " Strip Length: ", strip_length

				if (strip_length>best_length):
					best_type=0
					best_length=strip_length
					best_verts=strip_verts
					best_tris=strip_tris
					best_st=strip_st

			print "Best length for this triangle is: ", best_length
			print "Best type is: ", best_type
			print "Best Tris are: ", best_tris
			print "Best Verts are: ", best_verts
			print "Best ST are: ", best_st


			#mark tris as used
			for used_counter in range(0,best_length):
				used[best_tris[used_counter]]=1

			#create command list
			cmd_list=md2_GL_cmd_list()
			if best_type==0:
				cmd_list.num=(-(best_length+2))
			else:
				cmd_list.num=best_length+2

			num_commands+=1
			for command_counter in range(0,best_length+2):
				cmd=md2_GL_command()
				s=md2.tex_coords[best_st[command_counter]].u
				t=md2.tex_coords[best_st[command_counter]].v
				cmd.s=s/float(md2.skin_width)
				cmd.t=t/float(md2.skin_height)
				cmd.vert_index=best_verts[command_counter]
				num_commands+=3
				cmd_list.cmd_list.append(cmd)
			cmd_list.dump()
			md2.GL_commands.append(cmd_list)

		print "Used: ", used

	#add the null command at the end
	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):
	print ""
	print "***********************************"
	print "MD2 Export"
	print "***********************************"
	print ""

	Blender.Window.DrawProgressBar(0.0,"Begining MD2 Export")

	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()

	Blender.Window.DrawProgressBar(1.0, "Writing to Disk")

	#actually write it to disk
	file=open(filename,"wb")
	md2.save(file)
	file.close()

	#cleanup
	md2=0

	print "Closed the file"
