#!BPY

"""
Name: 'SMD Model (.smd)...'
Blender: 237
Group: 'Import'
Tooltip: 'Import SMD Mesh And Armature (.smd)'
"""

__author__ = "Michael Fuller (SlashAndBurn)"
__url__ = ("blender", "3d2toy",
"Author's homepage, http://www.3D2Toy.com")
__version__ = "Beta 1"

# +---------------------------------------------------------+
# | Copyright (c) 2005 Michael Fuller                       |
# | http://www.3D2Toy.com                                   |
# | SlashAndBurn@3D2Toy.com                                 |
# | June 30, 2005                                           |
# +---------------------------------------------------------+

import Blender, meshtools, math, time
from Blender import Armature, Object, Scene, Mathutils, NMesh, Material
from Blender.Mathutils import *

#/////////////////////////////////////////////////////
	
def read(filename):
	startTime = time.clock()
	print '\n\nSMD Import 0.1'
	
	meshName = getNameForMesh(filename)
	
	file = open(filename, "rb")

	# Collect data from SMD format
	
	lines = file.readlines()
	lines.append('');
	
	parseFile(lines,meshName)
	print "Done Parseing"
	file.close()
	
	endTime = time.clock()
	print ("%f seconds\n") % (endTime - startTime)
	
	del lines, file, endTime,startTime
	Blender.Window.RedrawAll()	
	return
	
def parseFile(lines,meshName):
	#{
	bones = []
	vertGroups = []
	armObject = 0
	
	for i in range(0,len(lines)):
		#{
		line = lines[i].split("\r\n",1)[0]
		
		if (line == "nodes"):
			#{
			i = parseNodes(lines,i+1,bones)
			#}
		if (line == "skeleton"):
			#{
			i = parseSkeleton(lines,i+1,bones)
			
			armObject = Object.New("Armature","Armature_Obj")
			armData = Armature.New()
			
			for i in range(0,len(bones)):
				#{
				bone = bones[i][0]
				if (bones[i][1] != -1):
					#{
					parId = (bones[i][1])
					parBone = bones[parId][0]
					bone.setParent(parBone)
					#print bones[parId][0].getName()
					#}
				armData.addBone(bone)
				#}
	
			armObject.link(armData)
			armObject.makeDisplayList()
			Blender.Scene.GetCurrent().link(armObject)
			#}
				
		if (line == "triangles"):
			#{
			blender_mesh=NMesh.New()
			
			#for b in bones:
			#	vertGroups.append([])
				
			parseTriangles(lines,i+1,bones,blender_mesh,vertGroups)
			
			mesh_obj=NMesh.PutRaw(blender_mesh,meshName,1)
			
			blender_Mesh = mesh_obj.getData()
			
			for bi in range(0, len(bones)):
				#{
				#print ("%d/%d") % (bi,len(bones))
				if (len(vertGroups[bi]) > 0):
					#{
					b = bones[bi]
  					blender_mesh.addVertGroup(b[0].getName())
					#blender_mesh.assignVertsToGroup(b[0].getName(), vertGroups[bi], 1, 'replace')
					#blender_mesh.addVertGroup("Bone(%d)" % bi)
					#blender_mesh.assignVertsToGroup(("Bone(%d)" % bi), vertGroups[bi], 1, 'replace')
					#}
				#}
				
			for vg in vertGroups:
				#{
				b = bones[vg[1]]
				blender_mesh.assignVertsToGroup(b[0].getName(), [vg[0]], vg[2], 'add')
				#}
				
			mesh_obj.makeDisplayList()

			del bones, line, vertGroups
			return
			#}
		#}
	#}
	
#Parse Functions

def parseNodes(lines,start,bones):
	print "Node"

	for i in range(start,len(lines)):
		line = lines[i].split("\r\n",1)[0]
		if (line == 'end'):
			#print len(bones)
			return i
		else:
			line = line.split()
			
			name=''
			
			if len(line) == 3:
				name = line[1]
			else:
				for a in range(1,len(line)-1):
					if a == 1:
						name = line[a]
					else:
						name = name + '_' + line[a]
			
			tempName = name.split("\"")
			if (len(tempName) == 3):
				#{
				name =tempName[1]
				#}
			
			print getBoneName(name)
			
			oldName = name
			
			name = getBoneName(oldName)
			
			#name = "Bone(%d)" % hash(fixString(fixString(name,"."),"_"))
			
			tbone = Armature.Bone.New(name)
			
			par = int(line[len(line)-1])
			
			#if par != -1:
				#tbone.setParent(bones[par])
			
			boneInfo = [tbone,par,"data"]
			
			bones.append(boneInfo)

def parseSkeleton(lines,start,bones):
	#{
	print "Skeleton"
	
	matrixData = []
	
	time = 0
	mode = 0
	length = len(bones) + 1
	
	me = NMesh.GetRaw()

	for i in range(start,len(lines)):
		#{
		line = lines[i].split("\r\n",1)[0]
		
		#print line
		
		if (line == 'end'):
			#{
			#del me, matrixData, time, mode, length, line
			return i
			#} End If
		else:
			#{
			if mode % length == 0:
				#{
				line = line.split()
				
				time = int(line[1])
				
				if (time > 1):
					del me, matrixData, time, mode, length, line
					return i
				#}End If
			else:
				#{
				line = line.split()
				
				boneIndex = int(line[0])
				
				bone = bones[boneIndex][0]
				parentIndex = bones[boneIndex][1]
				
				tx = float(line[1])
				ty = float(line[2])
				tz = float(line[3])
				
				rx = math.degrees(float(line[4]))
				ry = math.degrees(float(line[5]))
				rz = math.degrees(float(line[6]))

				if (parentIndex != -1):
					#{
					parentBone = bones[parentIndex][0]
					parentPos = parentBone.getTail()
					
					myMatrix = createMatrix(tx,ty,tz,rx,ry,rz)
					
					parentMatrix = (matrixData[parentIndex])
					
					myMatrix = myMatrix * parentMatrix
					
					myTail = VecMultMat(Vector([3,0,0,1]),myMatrix)
					myHead = VecMultMat(Vector([0,0,0,1]),myMatrix)
					
					myTail.resize3D()
					myHead.resize3D()
					
					#bone.setHead(parentPos)
					#bone.setTail(myTail)
					
					bone.setHead(myHead)
					bone.setTail(myTail)
					
					matrixData.append(myMatrix)
					
					del myMatrix,parentPos,myTail
					#} End If
				else:
					#{
					myMatrix = createMatrix(tx,ty,tz,rx,ry,rz)
					
					myHead = VecMultMat(Vector([0,0,0,1]),myMatrix)
					myTail = VecMultMat(Vector([3,0,0,1]),myMatrix)
					
					myTail.resize3D()
					myHead.resize3D()
					
					bone.setHead(myHead)
					bone.setTail(myTail)
					
					matrixData.append(myMatrix)
					del myTail, myHead, myMatrix
					#} End Else
				#del boneIndex, bone, parentIndex
				#} End Else
			mode = mode + 1
			#} End Else
		#} End For
	#} End Def
					
def parseTriangles(lines,start,bones,blender_mesh,vertGroups):
	#{
	print "Triangle"
	mode=0

	coords = {}
	index = 0
	myMat = 0
	matList = []
	faceData = []
	
	for i in range(start,len(lines)):
		#{
		line = lines[i].split("\r\n",1)[0]
		if (line == 'end'):
			#{
			#print "Found %d Triangles" % len(faces)
			
			return i+1
			#}
		else:
			#{
			if (mode % 4 == 0):
				#{
				myMat = findMaterial(line,blender_mesh,matList)
				#}
			else:
				#{
				if (mode % 4 == 1):
					#{
					faceData = [0,0,0,myMat]
					#}
				
				line = line.split()
				
				aVert = (float(line[1]),float(line[2]),float(line[3]))

				if not coords.has_key(aVert):
					#{
					
					coords[aVert] = index
					index += 1
					
					blender_mesh.verts.append(NMesh.Vert(aVert[0], aVert[1], aVert[2]))
					
					if len(line) == 9:
						vertGroups.append([index-1,int(line[0]),1])
						#vertGroups[int(line[0])].append(index-1)
					else:#Weights
						#{
						weights = int(line[9])
						for w in range(0,weights):
							#{
							vertGroups.append([index-1,int(line[10 + (w * 2)]), float(line[10 + (w * 2) + 1])])
							#}
						#}
					#}

					
				faceData[(mode % 4) -1] = [coords[aVert],float(line[7]),float(line[8])]
				
				if (mode % 4 == 3):
					#{
					face=NMesh.Face()
					face.v.append(blender_mesh.verts[faceData[0][0]])
					face.v.append(blender_mesh.verts[faceData[1][0]])
					face.v.append(blender_mesh.verts[faceData[2][0]])
					face.uv.append((faceData[0][1],faceData[0][2]))
					face.uv.append((faceData[1][1],faceData[1][2]))
					face.uv.append((faceData[2][1],faceData[2][2]))
					face.smooth = 1
					face.materialIndex = myMat
					blender_mesh.faces.append(face)
					mode = -1
					del faceData
					#}
				#}
			mode=mode+1
			#}
		#}
	#}
		
#Helper Functions

def findMaterial(matName,blender_mesh,matList):
	#{
	
	for i in range(0,len(matList)):
		mat = matList[i][0]
		if (mat == matName):
			#{
			return matList[i][1]
			#}
	
	mat = findMaterialLocal(matName)
	blender_mesh.addMaterial(mat)
	
	theNewMat = [matName,len(blender_mesh.getMaterials())-1]
	
	matList.append(theNewMat)
	
	return len(blender_mesh.getMaterials())-1
	#}
	
def findMaterialLocal(matName):
	#{
	matList = Material.Get()
	for i in range(0,len(matList)):
		mat = matList[i]
		if (mat.getName() == matName):
			#{
			return mat
			#}

	return Material.New(matName)
	#}	
	
def findBone (name,bones):
	for i in range(0,len(bones)):
		if bones[i].getName() == name:
			return i

def createMatrix(x,y,z,rx,ry,rz):
	rot = RotationMatrix(rx,4,'x') * RotationMatrix(ry,4,'y') * RotationMatrix(rz,4,'z')
	
	transMatrix = TranslationMatrix(Vector(x,y,z,1))

	myMatrix = rot * transMatrix
	
	del rot
	del transMatrix
	
	return myMatrix

def fixString(source,key):
	#{
	temp = source.split(key)
	dest = ""
	if (len(temp) == 0):
		return source
	
	for s in temp:
		#{
		dest = dest + s
		#]
	return dest
	#}

def getBoneName(source):
	#{
	perCount = source.count(".")
	if (perCount == 0):
		return source
	else:
		temp = source.split(".")
		dest = ""
		for i in range(1,len(temp)):
			dest += temp[i]
		return dest
	#}

def getNameForMesh(name):
	#{
	temp = name.split("\\")
	if (len(temp) == 0):
		#{
		temp = name.split("/")
		#}
	if len(temp) == 0:
		return "BadNameModel"
	else:
		return fixString(temp[len(temp)-1],".")
	#}
	
#/////////////////////////////////////////////////////

def fs_callback(filename):
	read(filename)

Blender.Window.FileSelector(fs_callback, "Import SMD")