#!BPY
 
"""
Name: 'morph .tri (.tri)...'
Blender: 249
Group: 'Import'
Tooltip: 'Load a morph TRI file.'
"""

__author__= "Core script by kapaer, modvertice support by deedes"
__version__= "0.2.1"

__bpydoc__= """\
This script imports a morph TRI file to Blender.

Usage:
Run this script from "File->Import" menu and then load the desired TRI file,
and bind morph data to shape key.
Note, this script adds _[number] or (number) after a key's name. The brackets "[]" tell the exporter that the key is a regular morph. The Parentheses "()" are the modifier keys.

deedes:
Please realize that this is a very crude attempt at getting .tri files to work correctly.
While this script works for me I can not guarantee that it is failsave. I'll try to fix bugs if they are found - but do not expect me to.
"""

# ***** BEGIN GPL LICENSE BLOCK *****
#
#	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 3 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, see <http://www.gnu.org/licenses/>
#
# ***** END GPL LICENCE BLOCK *****
# --------------------------------------------------------------------------

import bpy
import Blender
from Blender import Mesh, Draw, Window, Texture, Material, sys
import BPyMessages

try:
	from struct import unpack
except:
	unpack= None

VERSION_STRING = 'FRTRI003'
INT_LEN = 4
FLOAT_LEN = 4
SHORT_LEN = 2

RAISE_WARN = False

# Header
class header:
	str = ''
	vertexNum = 0
	faceNum = 0
	morphNum =0
	uvNum =0
	addMorphNum = 0
	addVertexNum = 0

	def __init__(self):
		self.str = ''
		self.vertexNum = 0
		self.faceNum = 0
		self.uvNum = 0
		self.morphNum = 0
		self.addMorphNum = 0
		self.addVertexNum = 0

	def dump(self):
		print 'Header str: ', self.str
		print 'vertexNum: ', self.vertexNum
		print 'faceNum: ', self.faceNum
		print 'uvNum: ', self.uvNum
		print 'morphNum: ', self.morphNum
		print 'addMorphNum: ', self.addMorphNum
		print 'addVerterxNum: ', self.addVertexNum

# read header
def readHeader(file, header):
	tmp_data = file.read(0x40)
	data = unpack('<8s10I16x', tmp_data)
	header.str = data[0]
	header.vertexNum = data[1]
	header.faceNum = data[2]
	header.uvNum = data[6]
	header.morphNum = data[8]
	header.addMorphNum = data[9]
	header.addVertexNum = data[10]

	
# read objects
def readMesh(file, mesh, header):
	global RAISE_WARN

	# read primary mesh
	print 'Loading primary mesh...'

	# load vertex data
	verts_list = []
	for i in range(header.vertexNum):
		tmp_data = file.read(FLOAT_LEN*3)
		data = unpack('<3f', tmp_data)
		verts_list.append((data[0], data[1], data[2]))
	if ROTATE_X90:
		verts_list[:] = [(v[0], -v[2], v[1]) for v in verts_list]
	mesh.verts.extend(verts_list)

	# load vertex and modvertice data
	vertsAdd_list = []
	
	print "Loading "+str(header.addVertexNum)+" modvertices."
		
	for i in range(header.addVertexNum):	# loads modvertices
		tmp_data = file.read(FLOAT_LEN*3)	# mod vertices are 3 float values (3 x 32 bit)
		data = unpack('<3f', tmp_data)
		vertsAdd_list.append((data[0], data[1], data[2]))

	if ROTATE_X90:	#if rotation was checked, this rotates the mesh
		vertsAdd_list[:] = [(v[0], -v[2], v[1]) for v in vertsAdd_list]

	# loading faces
	face_list = []

	for i in range(header.faceNum):			# loads the faces connection data
		tmp_data = file.read(INT_LEN*3)		# 3 integers (IDs for the vertices they connect
		data = unpack('<3I', tmp_data)
		face_list.append((data[0], data[1], data[2]))
	mesh.faces.extend(face_list)
	del face_list

	# UV
	mesh.faceUV = True
	uv_pos = []

	for i in range(header.uvNum):			#grabs the U&V coordinates for the vertices
		tmp_data = file.read(FLOAT_LEN*2)
		data = unpack('<2f', tmp_data)
		uv_pos.append((data[0], data[1]))

	for i in mesh.faces:
		iuv = i.uv
		for ii, j in enumerate(i):
			tmp_data = file.read(INT_LEN)
			data = unpack('<I', tmp_data)
			iuv[ii].x = uv_pos[data[0]][0]
			iuv[ii].y = uv_pos[data[0]][1]

	del uv_pos

	# read morph data

	if header.morphNum > 0:
		print 'Loading morph data...'
		mesh_verts = mesh.verts
		mesh.insertKey(1, 'relative')
		for i in range(header.morphNum):
			tmp_data = file.read(INT_LEN)
			data = unpack('<I', tmp_data)
			tmp_data = file.read(data[0])
			data = unpack('<'+str(data[0]-1)+'sx', tmp_data)
			morphName = data[0] + ' ['+str(i+1)+']'
			tmp_data = file.read(FLOAT_LEN)
			data = unpack('<f', tmp_data)
			baseDiff = data[0]

			for ii, nv in enumerate(mesh_verts):
				tmp_data = file.read(SHORT_LEN*3)
				if len(tmp_data) != SHORT_LEN*3:
					raise ValueError("read error.(tri file is too short?)")
				data = unpack('<3h', tmp_data)

				if ROTATE_X90:
					data = [data[0], -data[2], data[1]]

				nv.co[0] = verts_list[ii][0] + data[0] * baseDiff
				nv.co[1] = verts_list[ii][1] + data[1] * baseDiff
				nv.co[2] = verts_list[ii][2] + data[2] * baseDiff
			mesh.insertKey(1, 'relative')
			mesh.key.blocks[-1].name = morphName
			print "  Loaded "+morphName

		# set vertex to base position
		for ii, nv in enumerate(mesh_verts):
			nv.co[0] = verts_list[ii][0]
			nv.co[1] = verts_list[ii][1]
			nv.co[2] = verts_list[ii][2]

	# read additional morph data

	if header.addMorphNum > 0:
		print 'Loading extended morph data...'
		mesh_verts = mesh.verts
		vertsAdd_Index = 0
		for i in range(header.addMorphNum):
			tmp_data = file.read(INT_LEN)
			data = unpack('<I', tmp_data)
			tmp_data = file.read(data[0])
			data = unpack('<'+str(data[0]-1)+'sx', tmp_data)
			morphName = data[0] + ' ('+str(i+1)+')'
			tmp_data = file.read(INT_LEN)
			data = unpack('<I', tmp_data)
			blockLength = data[0] + 1
			
			if blockLength > 1:
				tmp_data = file.read(INT_LEN)
				data = unpack('<I', tmp_data)
				
				for ii, nv in enumerate(mesh_verts):
					if (data[0] == ii) & (blockLength > 0) & (vertsAdd_Index < len(vertsAdd_list)):
						blockLength = blockLength - 1
						nv.co[0] = vertsAdd_list[vertsAdd_Index][0]
						nv.co[1] = vertsAdd_list[vertsAdd_Index][1]
						nv.co[2] = vertsAdd_list[vertsAdd_Index][2]
						vertsAdd_Index = vertsAdd_Index + 1
						if blockLength > 1:
							tmp_data = file.read(INT_LEN)
							data = unpack('<I', tmp_data)
					else:
						nv.co[0] = verts_list[ii][0]
						nv.co[1] = verts_list[ii][1]
						nv.co[2] = verts_list[ii][2]
						
			else:
				for ii, nv in enumerate(mesh_verts):
					nv.co[0] = verts_list[ii][0]
					nv.co[1] = verts_list[ii][1]
					nv.co[2] = verts_list[ii][2]
			mesh.insertKey(1, 'relative')
			mesh.key.blocks[-1].name = morphName
			print "  Loaded "+morphName
			#break;

		# set vertex to base position
		for ii, nv in enumerate(mesh_verts):
			nv.co[0] = verts_list[ii][0]
			nv.co[1] = verts_list[ii][1]
			nv.co[2] = verts_list[ii][2]

	del verts_list
	del	vertsAdd_list

# menu
def importMenu():
	# IMPORT_AS_INSTANCE= Blender.Draw.Create(0)
	global ROTATE_X90
	ROTATE_X90 = Blender.Draw.Create(1)
	ROTATE_X90.val = 0
	
	# Get USER Options
	pup_block= [\
	('Rotate -X90', ROTATE_X90, 'Rotate X -90')\
	]
	
	if not Blender.Draw.PupBlock('Import Tri...', pup_block):
		return
	
	Blender.Window.WaitCursor(1)
	
	ROTATE_X90 = ROTATE_X90.val


# import
def loadTri(filename):

	if BPyMessages.Error_NoFile(filename):
		return
	
	print '\n\nImporting tri: "%s"' % (Blender.sys.expandpath(filename))
	file = open(filename,'rb')
	fileHeader = header()

	# read header
	readHeader(file, fileHeader)

	# version check
	if fileHeader.str != VERSION_STRING:
		file.close()
		raise TypeError('\tFatal Error:  Not a valid tri file: ', filename)

	# options
	importMenu()

	# import object
	name= filename.split('\\')[-1].split('/')[-1]
	scn= bpy.data.scenes.active
	mesh = Mesh.New()

	readMesh(file, mesh, fileHeader)

	ob = scn.objects.new(mesh, name)
	file.close()
	print "... finished"

# main
if __name__=='__main__':
	if unpack == None:
		Blender.Draw.PupMenu('Error%t|a full python installation not found')
	else:
		Blender.Window.FileSelector(loadTri, 'Import tri', '*.tri')


