最高指挥官SCM模型格式导入导出插件代码备份(blender版本)

SupComImp-Exp-0.3.0.0
supcom-importer-0.3.0.0.py

#!BPY

#**************************************************************************************************
# Supreme Commander Importer for Blender3D - www.blender3d.org
# Written by dan - www.sup-com.net
#
# History
#   0.1.0.0   06/06/06   - Initial version. 
#   0.2.0.0   06/06/10   - Added SCA (Animation) support
#   0.3.0.0   06/07/02   - Alpha release
#
# Todo
#   - Material/uv map 2
#   - Bone pos/rot for scm & sca. Not perfect to use gentle words.
#   - Make sca loading independent of the scm_mesh. (e.g get bone pos/loc from armature instead)
#   - GUI for loading
#
#**************************************************************************************************

"""
Name: 'SupCom Model (.scm)...'
Blender: 232
Group: 'Import'
Tooltip: 'Import Supreme Commander Models (*.scm) and Animations (*.sca)'
"""

import Blender
from Blender import NMesh, Scene, Object

import struct
import string
import math
from math import *

import os
from os import path

def pad(size):
	val = 32 - (size % 32)
	
	if (val > 31):
		return 0
		
	return val

class scm_bone :
	
	name = ""
	rest_pose = []
	position = []
	rotation = []
	parent = 0
	parent_index = 0
	absolute_matrix = []
	
	def __init__(self):
		self.name = ""
		self.rest_pose = [[0.0] * 4] * 4
		self.position = [0.0] * 3
		self.rotation = [0.0] * 4
		self.absolute_matrix = []

	def load(self, file):
		bonestruct = '16f3f4f4i'
		buffer = file.read(struct.calcsize(bonestruct))
		bone = struct.unpack(bonestruct, buffer)
		
		for i in range(4):
			self.rest_pose[i] = bone[i*4:i*4+4]
		
		self.rest_pose = Blender.Mathutils.Matrix(self.rest_pose[0], self.rest_pose[1], self.rest_pose[2], self.rest_pose[3])
		
		self.position = bone[16:19]
		self.rotation = bone[19:23]
		self.parent_index = bone[24]
		
		# Read bone name
		#oldpos = file.tell()
		#file.seek(bone[..], 0)
		#self.name = file.readline()
		#file.seek(oldpos, 0)
		
		self.absolute_matrix = Blender.Mathutils.Matrix(self.rest_pose).invert()
		
		return self
	
	def dump(self):
		print 'Bone       ', self.name
		print 'Position   ', self.position
		print 'Rotation   ', self.rotation
		print 'Parent Idx ', self.parent_index		
		if (self.parent != 0): 
			print 'Parent     ', self.parent.name
		else:
			print 'Parent     <NONE>'		
		print 'Rest Pose Inv.'
		for row in range(4):
			print '  ', self.rest_pose[row]		


class scm_vertex :
	
	position = []
	tangent = []
	normal = []
	binormal = []
	uv1 = []
	uv2 = []
	bone_index = []

	def __init__(self):
		self.position = [0.0]*3
		self.tangent = [0.0]*3
		self.normal = [0.0]*3
		self.binormal = [0.0]*3
		self.uv1 = [0.0]*2
		self.uv2 = [0.0]*2
		self.bone_index = [0]*4

	def load(self, file):
		
		vertstruct = '3f3f3f3f2f2f4B'
		vertsize = struct.calcsize(vertstruct)
		
		buffer = file.read(vertsize)
		vertex = struct.unpack(vertstruct, buffer)
		
		self.position = vertex[0:3]
		self.tangent = vertex[3:6]
		self.normal = vertex[6:9]
		self.binormal = vertex[9:12]
		self.uv1 = vertex[12:14]
		self.uv2 = vertex[14:16]
		self.bone_index = vertex[16:20]	
		
		return self
	
	def dump(self):
		print 'position ', self.position
		print 'tangent  ', self.tangent
		print 'normal   ', self.normal
		print 'binormal ', self.binormal
		print 'uv1      ', self.uv1
		print 'uv2      ', self.uv2
		print 'bones    ', self.bone_index


class scm_mesh :
	
	bones = []
	vertices = []
	faces = []
	info = []
	filename = ""
	
	def __init__(self):
		self.bones = []
		self.vertices = []
		self.faces = []
		self.info = []
		self.filename = ""

	def load(self, filename):	
		
		self.filename = filename
		scm = file(filename, 'rb')
		
		# Read header
		headerstruct = '4s11L'
		buffer = scm.read(struct.calcsize(headerstruct))
		header = struct.unpack(headerstruct, buffer)
		
		marker = header[0]
		version = header[1]
		boneoffset = header[2]
		bonecount = header[3]
		vertoffset = header[4]
		extravertoffset = header[5]
		vertcount = header[6]
		indexoffset = header[7]
		indexcount = header[8]
		tricount = indexcount / 3
		infooffset = header[9]
		infocount = header[10]
		totalbonecount = header[11]
		
		if (marker != 'MODL'):
			print 'Not a valid scm'
			return
			
		if (version != 5):
			print 'Unsupported version (%d)' % version
			return
		
		# Read bone names
		scm.seek(pad(scm.tell()), 1)
		length = (boneoffset - 4) - scm.tell()

		# This should probably be handeled by the scm_bone reader as it contains the nameoffset. But I'm lazy
		# and logic tells me it's written in the same order as the bones.
		buffer = scm.read(length)		
		rawnames = struct.unpack(str(length)+'s',buffer)		
		bonenames = string.split(rawnames[0],'\0')[:-1]
		
		# Read bones
		scm.seek(boneoffset, 0)
		for b in range(0, totalbonecount):
			bone = scm_bone()
			bone.load(scm)
			bone.name = bonenames[b]			
			self.bones.append(bone)	
		
		# Set parent (this could probably be done in the other loop since parents are usually written to the file
		# before the children. But you never know..
		for bone in self.bones:
			if (bone.parent_index != -1):
				bone.parent = self.bones[bone.parent_index]
			else:
				bone.parent = 0
		
		# Read vertices
		scm.seek(vertoffset, 0)
		for b in range(0, vertcount):
			vert = scm_vertex()
			vert.load(scm)			
			self.vertices.append(vert)
		
		# Read extra vertex data		
		# Not implemented in Sup Com 1.0!
		
		# Read indices (triangles)
		tristruct = '3h'
		trisize = struct.calcsize(tristruct)
			
		scm.seek(indexoffset, 0)			
		for t in range(tricount):			
			buffer = scm.read(trisize)
			face = struct.unpack(tristruct, buffer)
			self.faces.append(face)
			
		
		# Read info
		if (infocount > 0):
			scm.seek(infooffset)
			buffer = scm.read()
			rawinfo = struct.unpack(str(len(buffer))+'s',buffer)		
			self.info = string.split(rawinfo[0],'\0')[:-1]
		
		scm.close()	
		
		return self

	def dump(self):
		print ''
		print 'Filename: ', self.filename
		print 'Bones ', len(self.bones)
		print 'Verts ', len(self.vertices)
		print 'Faces ', len(self.faces)		
		print ''
		print 'INFO: '
		for info in self.info:
			print '  ', info
		


class sca_bone:

	position = []
	rototation = []
	absolute_matrix = None

	def __init__(self, pos, rot):
		self.position = pos
		self.rotation = rot
		self.absolute_matrix = None
		

	def dump(self):
		print 'Position ', self.position
		print 'Rotation ', self.rotation
		


class sca_frame:

	keytime = 0.0
	keyflags = 0
	bones = []
	anim = None
	
	def __init__(self, anim):
		self.keytime = 0.0
		self.keyflags = 0
		self.bones = []
		self.anim = anim

	def load(self, file):		
		frameheader_fmt = 'fl'
		frameheader_size = struct.calcsize(frameheader_fmt)
		buffer = file.read(frameheader_size)
		
		(self.keytime, self.keyflags) = struct.unpack(frameheader_fmt, buffer)
		
		posrot_fmt = '3f4f'
		posrot_size = struct.calcsize(posrot_fmt)
		
		for b in range (0, self.anim.numbones) :
			buffer = file.read(posrot_size)
			posrot = struct.unpack(posrot_fmt, buffer)			
			bone = sca_bone(posrot[0:3], posrot[3:7])			
			self.bones.append(bone)

	def dump(self):
		print 'Time  ', self.keytime
		print 'Flags ', self.keyflags
		


class sca_anim :
	
	filename = ""
	frames = []
	bones = []
	bonelinks = []
	bonenames = []
	model = None
	numbones = 0
	duration = 0.0
	
	def __init__(self, model):
		self.filename = ""
		self.frames = []
		self.bones = []
		self.numbones = 0
		self.bonelinks = []
		self.bonenames = []
		self.model = model
		self.duration = 0.0
	
	def calcAnimBoneMatrix(self, frame, bone_index):
		
		bone = frame.bones[bone_index];		
		parent_index = self.bonelinks[bone_index]
		
		relRotationMatrix = Blender.Mathutils.Quaternion(bone.rotation[1],bone.rotation[2],bone.rotation[3],bone.rotation[0]).toMatrix().resize4x4()
		relPositionMatrix = Blender.Mathutils.Matrix([1.0, 0.0, 0.0, bone.position[0]],[0.0, 1.0, 0.0, bone.position[1]],[0.0, 0.0, 1.0, bone.position[2]],[0.0, 0.0, 0.0, 1.0]) 
		
		bone.absolute_matrix = relPositionMatrix * relRotationMatrix
		
		if (parent_index >= 0):
			parent = frame.bones[parent_index]
			bone.absolute_matrix = parent.absolute_matrix * bone.absolute_matrix
			
		
		# Call all children
		for b in range(len(self.bonelinks)):
			if (self.bonelinks[b] == bone_index):
				self.calcAnimBoneMatrix(frame, b)
			
		
		bone.absolute_matrix = bone.absolute_matrix * Blender.Mathutils.Matrix(self.model.bones[b].rest_pose).invert()
	
	def load(self, filename):
		self.filename = filename
		sca = file(filename, 'rb')
		
		# Read header
		headerstruct = '4sllflllll'
		buffer = sca.read(struct.calcsize(headerstruct))
		header = struct.unpack(headerstruct, buffer)
		
		(magic,             \
		 version,           \
		 numframes,         \
		 self.duration,     \
		 self.numbones,     \
		 namesoffset,       \
		 linksoffset,       \
		 animoffset,        \
		 framesize) = struct.unpack(headerstruct, buffer)
		
		if (magic != 'ANIM'):
			print 'Not a valid .sca animation file'
			return
		
		if (version != 5):
			print 'Unsupported sca version: %d'  % version
			return
		
		# Read bone names
		sca.seek(namesoffset, 0)
		length = linksoffset - namesoffset
		buffer = sca.read(length)		
		rawnames = struct.unpack(str(length)+'s',buffer)		
		self.bonenames = string.split(rawnames[0], '\0')[:-1]
		
		# Read links
		links_fmt = str(self.numbones)+'l'
		links_size = struct.calcsize(links_fmt)
		
		buffer = sca.read(links_size)
		self.bonelinks = struct.unpack(links_fmt, buffer)
			
		posrot_fmt = '3f4f'
		posrot_size = struct.calcsize(posrot_fmt)
		
		sca.seek(animoffset)
		buffer = sca.read(posrot_size)
		root_posrot = struct.unpack(posrot_fmt, buffer)
		
		for f in range (0, numframes) :
			frame = sca_frame(self)
			frame.load(sca)
			self.frames.append(frame)
		
		sca.close()
		
		return self

	def dump(self):
		print 'SCA:  ', self.filename
		print 'Duration: %fs' % self.duration
		print 'Num loaded frames ', len(self.frames)
		
		print 'Bonelinks'		
		for link in self.bonelinks:
			print ' ', link
		
		print 'Bone names'
		for name in self.bonenames:
			print ' ', name
	

#**************************************************************************************************
# Blender Interface
#**************************************************************************************************

def read(filename) :
	
	print "=== LOADING Sup Com Model ==="
	print ""
	
	mesh = scm_mesh()
	
	if (mesh.load(filename) == None):
		print 'Failed to load %s' %filename
		return
	
	modelpath, modelfile = os.path.split(filename)
	armature_name = string.rstrip(modelfile, ".scm")
	
	armObj = Object.New('Armature', armature_name)
	armData = Blender.Armature.Armature("ARM_" + armature_name) 
	armData.drawAxes = True 
	armObj.link(armData) 
	scene = Blender.Scene.getCurrent() 
	scene.link(armObj) 
	armData.makeEditable() 
	
	for index in range(len(mesh.bones)):
		bone = mesh.bones[index]		
		#lmat = bone.absolute_matrix		
		#lmat = Blender.Mathutils.Matrix(lmat[0],lmat[1],lmat[2],lmat[3])
		
		blender_bone = Blender.Armature.Editbone()		
		#blender_bone.matrix = lmat
		#blender_bone.weight = 1.0
		
		armData.bones[bone.name] = blender_bone

	for index in range(len(mesh.bones)):
		bone = mesh.bones[index]
		blender_bone = armData.bones[bone.name]
		if (bone.parent != 0) :
			blender_bone.parent = armData.bones[bone.parent.name]
			#blender_bone.head = blender_bone.parent.tail
			#blender_bone.tail = blender_bone.parent.head
			
		bone = mesh.bones[index]		
		lmat = bone.absolute_matrix		
		lmat = Blender.Mathutils.Matrix(lmat[0],lmat[1],lmat[2],lmat[3])
				
		blender_bone.matrix = lmat


	armData.update() 
	armObj.makeDisplayList() 

	blender_mesh = NMesh.New()
	
	for vert in mesh.vertices:
		v = NMesh.Vert(vert.position[0], vert.position[1], vert.position[2])
		v.uvco[0]= vert.uv1[0]
		v.uvco[1]= vert.uv1[1]
		blender_mesh.verts.append(v)
		
	
	for tri in mesh.faces:
		face = NMesh.Face()
		
		face.v.append(blender_mesh.verts[tri[0]])
		face.v.append(blender_mesh.verts[tri[1]])
		face.v.append(blender_mesh.verts[tri[2]])
		
		face.uv.append((blender_mesh.verts[tri[0]].uvco[0], blender_mesh.verts[tri[0]].uvco[1]))
		face.uv.append((blender_mesh.verts[tri[1]].uvco[0], blender_mesh.verts[tri[1]].uvco[1]))
		face.uv.append((blender_mesh.verts[tri[2]].uvco[0], blender_mesh.verts[tri[2]].uvco[1]))
		
		blender_mesh.faces.append(face)

	mesh_obj = NMesh.PutRaw(blender_mesh)	
	blender_mesh = mesh_obj.getData()

	for bone in mesh.bones:
		blender_mesh.addVertGroup(bone.name)

	for vertex_index in range(len(mesh.vertices)):
		vertex = mesh.vertices[vertex_index]
		bone_index = vertex.bone_index[0]
		blender_mesh.assignVertsToGroup(mesh.bones[bone_index].name, [vertex_index], 1.0, 'replace')

	armObj.makeParentDeform([mesh_obj], 0, 0)
	armObj.makeDisplayList() 

	scene.update(); 
	Blender.Window.RedrawAll()

	if len(mesh.info):
		print "=== INFO ==="
		for info in mesh.info:
			print "",info

	print "=== COMPLETE ==="

	Blender.Window.DrawProgressBar(1.0, '') 
	
	return mesh


def read_anim(filename, mesh):
	
	print "=== LOADING Sup Com Animation ==="
	print ""
	
	anim = sca_anim(mesh)
	anim.load(filename)
	
	arm_obj = None
	for armobj in Blender.Object.Get():
		data = armobj.getData()
		if type(data) is Blender.Types.ArmatureType:
			arm_obj = armobj
			break
		
	if arm_obj == None:
		print "couldn't apply animation, no armature in the scene" 
		return

	actionpath, actionname = os.path.split(filename)
	
	action = Blender.Armature.NLA.NewAction(actionname)
	action.setActive(arm_obj)
	
	pose = arm_obj.getPose()		
	
	for frame_index in range(len(anim.frames)):

		Blender.Set("curframe", frame_index+1)
		
		frame = anim.frames[frame_index]
		
		anim.calcAnimBoneMatrix(frame, 0)
		
		for b in range(len(frame.bones)):
			
			pose_bone = pose.bones[anim.bonenames[b]]
			
			if (pose_bone == 0):
				print 'Frame %d - Bone \"%s\" not found' % (frame_index, anim.bonenames[b])
				continue
			
			anim_bone = frame.bones[b]				
			model_bone = mesh.bones[b]				
			
			mb_quat = Blender.Mathutils.Quaternion([model_bone.rotation[1], model_bone.rotation[2], model_bone.rotation[3], model_bone.rotation[0]])
			ab_quat = Blender.Mathutils.Quaternion([anim_bone.rotation[1], anim_bone.rotation[2], anim_bone.rotation[3], anim_bone.rotation[0]])
			
			pose_bone.loc = Blender.Mathutils.Vector(anim_bone.position) - Blender.Mathutils.Vector(model_bone.position)
			pose_bone.quat = Blender.Mathutils.DifferenceQuats(ab_quat, mb_quat) 
			
			pose.update()
			pose_bone.insertKey(arm_obj, frame_index+1, [Blender.Object.Pose.ROT, Blender.Object.Pose.LOC])
			pose.update()
		
	
	Blender.Set("curframe", 1)
	
	scene = Blender.Scene.GetCurrent()
	context = scene.getRenderingContext()
	context.endFrame(len(anim.frames)+1)
	
	print "=== COMPLETE ==="
	

#**************************************************************************************************
# Execution stuff..
#**************************************************************************************************
	
anim_filename = ""
model_filename = ""

# File selector callback from blender
def fs_callback(filename):
	global model_filename
	model_filename = filename
	#read(filename)
	Blender.Window.FileSelector(fs_anim_callback, "Import SCA", "*.sca")
	
def fs_anim_callback(filename):
	global anim_filename
	global model_filename
	anim_filename = filename
	#read_anim(filename)
	
	mesh = read(model_filename)
	read_anim(anim_filename, mesh)

#Show the file selector
#Blender.Window.FileSelector(fs_callback, "Import SCM", "*.scm")
#Blender.Window.FileSelector(fs_anim_callback, "Import SCA", "*.sca")

#mesh = read("Z:\\dev\\SupComAnimTest\\Sample\\UXA0099_lod0.scm")
#read_anim("Z:\\dev\\SupComAnimTest\\Sample\\UXA0099_Atest.sca", mesh)

#mesh = read("Z:\\dev\\SupComAnimTest\\Sample\\SupComSCM_lod0.scm")
#read_anim("Z:\\dev\\SupComAnimTest\\Sample\\UXA0100_Atest.sca", mesh)

Blender.Window.FileSelector(fs_callback, "Import SCM", "*.scm")

supcom-exporter-0.3.0.0.py

#!BPY

#**************************************************************************************************
# Supreme Commander Exporter for Blender3D - www.blender3d.org
# Written by dan - www.sup-com.net), Brent (www.scmods.net)
#
# History
#
#   0.1.0.0   2006-07-02   Dan     Initial version.
#
#   0.2.0.0   2007-03-11   Brent   Fixed UV coords, V was inverted.
#				   Support for exporting quads.
#				   Fixed a padding issue.
#
#   0.3.0.0   2007-03-18   Dan     Code refactoring / Clean up.
#				   Fixed 'INFO' section size in header.
#
# Todo
#   - GUI improvements
#   - Support for LOD exporting. Eg. not merging all meshes for an armature into one mech but rather only
#     sub-meshes and export the top level meshes to different files.
#   - Validation, ensure that
#     - Only one root bone
#     - Has UV coords
#     - Prompt before overwriting files & check that directories exists
#   - Tangent/Binormal calculation
#   - Second UV set?
#   - Set animation time per frame
#   - Export LUA script for use in the animation viewer (eg, start anim, set texture etc)..
#   - Set root rot/pos for sca
#
#**************************************************************************************************

"""
Name: 'Supreme Commander'
Blender: 242
Group: 'Export'
Tooltip: 'Model / Animation Exporter for Supreme Commander'
"""

VERSION = '0.3.0.0'

import Blender
import struct

from Blender.BGL import *
from Blender.Draw import *


def pad(size):
	val = 32 - (size % 32)
	if (val < 4):
		val = val + 32
	
	return val

def pad_file(file):
	padding = struct.pack(str(pad(file.tell()))+'x')
	file.write(padding)

class scm_bone :
	
	name = ""
	rest_pose = []
	position = []
	rotation = []
	parent_index = 0
	used = False
	
	def __init__(self):
		self.name = ""
		self.rest_pose = [[0.0] * 4] * 4
		self.position = [0.0] * 3
		self.rotation = [0.0] * 4
		self.used = False

	def save(self, file):
		bonestruct = '16f3f4f4i'
		tmp = [0] * 27		
		tmp[0] = self.rest_pose[0][0]
		tmp[1] = self.rest_pose[0][1]
		tmp[2] = self.rest_pose[0][2]
		tmp[3] = self.rest_pose[0][3]
		tmp[4] = self.rest_pose[1][0]
		tmp[5] = self.rest_pose[1][1]
		tmp[6] = self.rest_pose[1][2]
		tmp[7] = self.rest_pose[1][3]
		#oops
		tmp[9] = self.rest_pose[2][0]
		tmp[10] = self.rest_pose[2][1]
		tmp[11] = self.rest_pose[2][2]
		tmp[12] = self.rest_pose[2][3]
		tmp[13] = self.rest_pose[3][0]
		tmp[14] = self.rest_pose[3][1]
		tmp[15] = self.rest_pose[3][2]
		tmp[16] = self.rest_pose[3][3]
		tmp[17] = self.position[0]
		tmp[18] = self.position[1]
		tmp[19] = self.position[2]
		tmp[20] = self.rotation[0]
		tmp[21] = self.rotation[1]
		tmp[22] = self.rotation[2]
		#ewups
		tmp[8] = self.rotation[3]
		tmp[23] = self.name_offset
		tmp[24] = self.parent_index
		tmp[25] = 0
		tmp[26] = 0
		
		bone = struct.pack(bonestruct,					\
			tmp[0], tmp[1], tmp[2], tmp[3], tmp[4],			\
			tmp[5], tmp[6], tmp[7], tmp[9],				\
			tmp[10],tmp[11],tmp[12],tmp[13],tmp[14],		\
			tmp[15],tmp[16],tmp[17],tmp[18],tmp[19],		\
			tmp[20],tmp[21],tmp[22],tmp[8], tmp[23], tmp[24],	\
			tmp[25],tmp[26])
		
		file.write(bone)
	

class scm_vertex :
	
	position = []
	tangent = []
	normal = []
	binormal = []
	uv1 = []
	uv2 = []
	bone_index = []

	def __init__(self):
		self.position = [0.0]*3
		self.tangent = [0.0]*3
		self.normal = [0.0]*3
		self.binormal = [0.0]*3
		self.uv1 = [0.0]*2
		self.uv2 = [0.0]*2
		self.bone_index = [0]*4

	def save(self, file):
		
		vertstruct = '3f3f3f3f2f2f4B'
		
		vertex = struct.pack(vertstruct, 				\
			self.position[0], self.position[1], self.position[2],	\
			self.tangent[0], self.tangent[1], self.tangent[2],	\
			self.normal[0], self.normal[1], self.normal[2],		\
			self.binormal[0], self.binormal[1], self.binormal[2],	\
			self.uv1[0], self.uv1[1],				\
			self.uv2[0], self.uv2[1],				\
			self.bone_index[0], self.bone_index[1],			\
			self.bone_index[2], self.bone_index[3])
		
		file.write(vertex)
	

class scm_mesh :
	
	bones = []
	vertices = []
	faces = []
	info = []
	
	def __init__(self):
		self.bones = []
		self.vertices = []
		self.faces = []
		self.info = []


	def save(self, filename):	
		scm = file(filename, 'wb')
		
		headerstruct = '4s11L'
		headersize = struct.calcsize(headerstruct)
		
		marker = 'MODL'
		version = 5
		boneoffset = 0
		bonecount = 0  
		vertoffset = 0
		extravertoffset = 0
		vertcount = len(self.vertices)
		indexoffset = 0
		indexcount = len(self.faces) * 3
		infooffset = 0
		infosize = 0
		totalbonecount = len(self.bones)
		
		# Write dummy header
		header = struct.pack(headerstruct + '',				\
			marker, version, boneoffset, bonecount, vertoffset, 	\
			extravertoffset, vertcount, indexoffset, indexcount, 	\
			infooffset, infosize, totalbonecount)
		
		scm.write(header)
		
		pad_file(scm)
	
		# Write bone names
		scm.seek(-4, 1)
		scm.write('NAME')
		for bone in self.bones:
			bone.name_offset = scm.tell()			
			name = bone.name		
			buffer = struct.pack(str(len(name) + 1)+'s', name)
			scm.write(buffer)
			
		
		pad_file(scm)
		
		
		# Write bones
		scm.seek(-4, 1)
		scm.write('SKEL')
		boneoffset = scm.tell()
		
		for bone in self.bones:
			bone.save(scm)
			# if bone.used == True: 
			bonecount += 1
		
		pad_file(scm)
		
		# Write vertices
		scm.seek(-4, 1)
		scm.write('VTXL')
		vertoffset = scm.tell()
		
		for vertex in self.vertices:
			vertex.save(scm)
		
		pad_file(scm)
		
		# Write Faces
		scm.seek(-4, 1)
		scm.write('TRIS')
		indexoffset = scm.tell()
		
		for f in range(len(self.faces)):
			face = struct.pack('3h', self.faces[f][0], self.faces[f][1], self.faces[f][2])
			scm.write(face)
		
		pad_file(scm)
		
		if len(self.info) > 0:
			
			scm.seek(-4, 1)
			scm.write('INFO')
			infooffset = scm.tell()
			
			for i in range(len(self.info)):
				info = self.info[i]
				infolen = len(info) + 1			
				buffer = struct.pack(str(infolen)+'s', info)
				scm.write(buffer)
			
			infosize = scm.tell() - infooffset;
		
		pad_file(scm)
		
		# Now we can update the header
		
		header = struct.pack(headerstruct,				\
			marker, version, boneoffset, bonecount, vertoffset, 	\
			extravertoffset, vertcount, indexoffset, indexcount, 	\
			infooffset, infosize, totalbonecount)		
		
		scm.seek(0, 0)
		scm.write(header)
		
		scm.close()


class sca_bone:

	position = []
	rototation = []

	def __init__(self, pos, rot):
		self.position = pos
		self.rotation = rot

class sca_frame:

	keytime = 0.0
	keyflags = 0
	bones = []
	anim = None
	
	def __init__(self, anim):
		self.keytime = 0.0
		self.keyflags = 0
		self.bones = []
		self.anim = anim

	def save(self, file):		
		frameheader_fmt = 'fl'
		frameheader_size = struct.calcsize(frameheader_fmt)
		
		posrot_fmt = '3f4f'
		posrot_size = struct.calcsize(posrot_fmt)
		
		# Frame header
		buffer = struct.pack(frameheader_fmt, self.keytime, self.keyflags)
		file.write(buffer)
		
		# Bones		
		for bone in self.bones:
			buffer = struct.pack(posrot_fmt, bone.position[0], bone.position[1], bone.position[2], bone.rotation[0], bone.rotation[1], bone.rotation[2], bone.rotation[3])
			file.write(buffer)


class sca_anim :
	
	frames = []
	bonelinks = []
	bonenames = []
	duration = 0.0
	
	def __init__(self):
		self.frames = []
		self.bonelinks = []
		self.bonenames = []
		self.duration = 0.0
	
	def save(self, filename):
		
		self.filename = filename
		sca = file(filename, 'wb')
		
		headerstruct = '4sllflllll'
		
		# Write temp header
		
		magic = 'ANIM'
		version = 5
		numframes = len(self.frames)
		numbones = len(self.bonenames)
		
		namesoffset = 0
		linksoffset = 0
		animoffset = 0
		framesize = 0
		
		header = struct.pack(headerstruct, 
			magic, version, numframes, self.duration, numbones,
			namesoffset, linksoffset, animoffset, framesize)
		
		sca.write(header)
		
		pad_file(sca)
		
		# Write bone names
		sca.seek(-4, 1)
		sca.write('NAME')
		namesoffset = sca.tell()
		for bone_name in self.bonenames:
			buffer = struct.pack(str(len(bone_name) + 1)+'s', bone_name)
			sca.write(buffer)
		
		pad_file(sca)
		
		# Write bone links
		sca.seek(-4, 1)
		sca.write('LINK')
		linksoffset = sca.tell()
		for link in self.bonelinks:
			buffer = struct.pack('l', link)
			sca.write(buffer)
		
		pad_file(sca)
		
		# Write data
		sca.seek(-4, 1)
		sca.write('DATA')
		
		animoffset = sca.tell()
		
		posrot_fmt = '3f4f'
		posrot_size = struct.calcsize(posrot_fmt)
		
		#TODO Root position / rotation
		buffer = struct.pack(posrot_fmt, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
		sca.write(buffer)
		
		for frame in self.frames:
			framesize = sca.tell()
			frame.save(sca)
			framesize = sca.tell() - framesize		
		
		# Update header
		sca.seek(0, 0)
		
		header = struct.pack(headerstruct, 
			magic, version, numframes, self.duration, numbones,
			namesoffset, linksoffset, animoffset, framesize)
		
		sca.write(header)
		
		sca.close()


######################################################
# Blender Interface
######################################################

# Helper methods

def apply_transform(verts, matrix):
	x, y, z = verts
	xloc, yloc, zloc = matrix[3][0], matrix[3][1], matrix[3][2]
	
	return Blender.Mathutils.Vector(
		x*matrix[0][0] + y*matrix[1][0] + z*matrix[2][0] + xloc,
		x*matrix[0][1] + y*matrix[1][1] + z*matrix[2][1] + yloc,
		x*matrix[0][2] + y*matrix[1][2] + z*matrix[2][2] + zloc)

log = []
log_max_lines = 10

def Log(message):
	global log, log_max_lines
	
	log.append(message)
	print message
	
	if len(log) > log_max_lines:
		log.delete(0)

# Armature world matrix
world_matrix = []
BONES = []


# Helper method for itterating through the bone tree
def itterate_bones(mesh, bone, parent = None, scm_parent_index = -1):
	
	global world_matrix
	global BONES

	if (parent != None and bone.parent.name != parent.name):
		return
	
	# Create supcom bone
	sc_bone = scm_bone()
	sc_bone.name = bone.name
	sc_bone.bone_index = len(mesh.bones)
	sc_bone.parent_index = scm_parent_index
	
	bone_matrix = Blender.Mathutils.Matrix(bone.matrix['ARMATURESPACE'])
	
	# Calculate the inverse rest pose for the bone
	sc_bone.rest_pose = Blender.Mathutils.Matrix(bone_matrix * world_matrix).invert()

	if (parent == None):
		rel_mat = bone_matrix * world_matrix
	else:
		rel_mat = bone_matrix * Blender.Mathutils.Matrix(parent.matrix['ARMATURESPACE']).invert()
	
	rotation = rel_mat.toQuat() #.normalize()
	
	# Rotation & Position relative to parent
	sc_bone.rotation = [ rotation.w, 
			     rotation.x, 
			     rotation.y, 
			     rotation.z ]
			     
	sc_bone.position = [ rel_mat[3][0], 
			     rel_mat[3][1], 
			     rel_mat[3][2] ]
	
	BONES.append(sc_bone)
	mesh.bones.append(sc_bone)
	
	# recursive call for all children
	if (bone.children != None):
		for child in bone.children: 
			itterate_bones(mesh, child, bone, sc_bone.bone_index)


def make_scm(arm_obj):
	
	global world_matrix
	
	arm = arm_obj.getData()
	
	world_matrix = Blender.Mathutils.Matrix(arm_obj.getMatrix('worldspace'))

	# Get all mesh objects for the selected armature
	mesh_objs = []
	for obj in Blender.Scene.GetCurrent().getChildren():
		if obj.parent == arm_obj and obj.getType() == 'Mesh':
			blender_mesh = obj.getData(False, True)
			mesh_objs.append(obj)

	# Create SCM Mesh
	supcom_mesh = scm_mesh()

	# Traverse the bone tree
	for bone in arm.bones.values():
		if (bone.parent == None):
			itterate_bones(supcom_mesh, bone)			

	# Keep a counter for the number of vertices added by the previous
	# meshes so that we can recalculate the vert indices for faces.
	vert_counter = 0
	
	# Process all the meshes
	for mesh_obj in mesh_objs:
		
		blender_mesh = mesh_obj.getData(False, True)	
		mesh_world_matrix = Blender.Mathutils.Matrix(mesh_obj.getMatrix('worldspace'));
		
		for face in blender_mesh.faces:
			vert_count = len(supcom_mesh.vertices)
			for i in range(len(face.verts)):
				vert = face.verts[i]
				sc_vertex = scm_vertex()
				sc_vertex.position = apply_transform(vert.co, mesh_world_matrix)
				sc_vertex.normal = apply_transform(vert.no, mesh_world_matrix)
				
				#TODO, Calculate these.
				sc_vertex.tangent = [0.0, 0.0, 0.0]
				sc_vertex.binormal = [0.0, 0.0, 0.0]
				
				sc_vertex.uv1 = [face.uv[i][0], 1.0 - face.uv[i][1]]
				sc_vertex.uv2 = [face.uv[i][0], 1.0 - face.uv[i][1]]
				
				inf = blender_mesh.getVertexInfluences(vert.index)
				if (len(inf)) > 0:
					bonename = inf[0][0]
					for b in range(len(supcom_mesh.bones)):
						bone = supcom_mesh.bones[b]
						if bone.name == bonename:
							bone.used = True
							sc_vertex.bone_index[0] = b
							break
				
				supcom_mesh.vertices.append(sc_vertex)
			
			# Triangle
			if len(face.verts) == 3:
				supcom_mesh.faces.append([vert_count + 0, vert_count + 1, vert_count + 2])
				
			#Quad, split it to two triangles
			elif len(face.verts) == 4:
				supcom_mesh.faces.append([vert_count + 0, vert_count + 1, vert_count + 3])
				supcom_mesh.faces.append([vert_count + 1, vert_count + 2, vert_count + 3])
	

	return supcom_mesh


def make_sca(arm_obj, action):

	global BONES
	global world_matrix
	
	action.setActive(arm_obj)
	
	scene = Blender.Scene.GetCurrent()
	render_context = scene.getRenderingContext()
	endframe = render_context.endFrame()
	animation = sca_anim()	
	animation.duration = 1.5
	
	# Add bone names & links
	for bone in BONES:
		animation.bonenames.append(bone.name)
		animation.bonelinks.append(bone.parent_index)

	# Add frames
	frame_counter = 1
	while frame_counter <= endframe:
		frame = sca_frame(animation)
		
		render_context.currentFrame(frame_counter)
		Blender.Window.RedrawAll()
		arm_obj.evaluatePose(frame_counter)
		frame_counter += 1
		
		POSED_BONES = {}
		for posebone in arm_obj.getPose().bones.values():
			POSED_BONES[posebone.name] = posebone.poseMatrix
		
		for bone in BONES:
			pose_bone_matrix = POSED_BONES[bone.name]
			
			if (bone.parent_index == -1):
				rel_mat = pose_bone_matrix * world_matrix
			else:
				rel_mat = pose_bone_matrix * Blender.Mathutils.Matrix(POSED_BONES[BONES[bone.parent_index].name]).invert()
			
			rotation = rel_mat.toQuat().normalize()
			rot = [ rotation.w, rotation.x, rotation.y, rotation.z ]					     
			pos = [ rel_mat[3][0], rel_mat[3][1], rel_mat[3][2] ]
			
			anim_bone = sca_bone(pos, rot)
			frame.bones.append(anim_bone)
		
		animation.frames.append(frame)

	return animation

def export(outdir):
	global VERSION
	
	# Get Selected object(s)
	selected_objects = Blender.Object.GetSelected()
	
	# Look for an armature
	arm = None
	for obj in selected_objects:
		data = obj.getData()
		if type(data) is Blender.Types.ArmatureType:
			arm = data
			arm_obj = obj
			break
	
	if arm == None:
		Log('No armature found')
		return
	
	# SCM
	Log('exporting model: ' + arm_obj.name)
	
	mesh = make_scm(arm_obj)
	mesh.info.append('Exporter:Blender supcom-exporter.py ' + VERSION)
	mesh.save(outdir + arm.name + '.scm')
	mesh = None

	# SCA
	actions = Blender.Armature.NLA.GetActions().iteritems()
	for action in actions:
		Log('exporting animation: ' + action[0])
		animation = make_sca(arm_obj, action[1])
		animation.save(outdir + action[0] + '.sca')		

	Log('')
	Log('Done!')

######################################################
# GUI
######################################################

# Events
EVENT_NOEVENT = 1
EVENT_DRAW = 2
EVENT_EXIT = 3
EVENT_CLOSE_LOG = 4
EVENT_EXPORTDIR_TEXT = 5
EVENT_EXPORTDIR = 6

export_directory = Blender.Draw.Create("sds")

show_log = 0

def fileselector_callback(filename):
	export_directory.val = filename

def draw():
	global EVENT_NOEVENT, EVENT_DRAW, EVENT_EXIT, EVENT_CLOSE_LOG
	global export_directory, show_log
	global log, log_max_lines
	global VERSION

	# Titles
	glClear(GL_COLOR_BUFFER_BIT)
	
	top = 60 + log_max_lines * 12 + 8
	top_x = 304
	
	glColor3f(0.8, 0.8, 1)
	glRecti(top_x, top, 4, 4)
	glBegin(GL_LINES)
	glColor3f(0.8, 0.8, 0.8)
	glVertex2d(4, top)
	glVertex2d(4, 4)
	glVertex2d(4, 4)
	glVertex2d(top_x, 4)
	glColor3f(0.5, 0.5, 0.5)
	glVertex2d(top_x, top)
	glVertex2d(4, top)
	glVertex2d(top_x, top)
	glVertex2d(top_x, top-1)
	glEnd()
	
	glColor3f(0, 0, 0)
	glRasterPos2d(10, top-16)
	Text("Supreme Commander Exporter " + VERSION)	
	
	# Show exporting log
	if show_log:

		for index in range(0, len(log)):
			i = (len(log) - 1) - index
			glRasterPos2i(10, 40 + i*12)
			Text(log[index]) #, 'tiny')
		
		Button("Close", EVENT_EXIT , 10, 10, 80, 18)

	# Exporter GUI
	else:
		Blender.Draw.Button("Browse...", EVENT_EXPORTDIR, 10, 40, 80, 18, "")
		export_directory = Blender.Draw.String("", EVENT_EXPORTDIR_TEXT, 100, 40, 200, 18, export_directory.val, 255, "Where to save the exported files")
		
		# Draw and Exit Buttons
		Button("Export", EVENT_DRAW , 10, 10, 80, 18)
		Button("Exit", EVENT_EXIT , 100, 10, 80, 18)

def event(evt, val):	
	if (evt == QKEY and not val): 
		Exit()

def bevent(evt):
	global EVENT_NOEVENT,EVENT_DRAW,EVENT_EXIT
	global export_directory
	global show_log

	if (evt == EVENT_EXIT): 
		show_log = 0
		Exit()
	
	elif (evt == EVENT_EXPORTDIR):
		Blender.Window.FileSelector (fileselector_callback, "Select output DIR")
		Blender.Redraw()
	
	elif (evt== EVENT_DRAW):
		
		if (export_directory.val == ""):
			return
		
		show_log = 1
		export(export_directory.val)
		Blender.Redraw()


Register(draw, event, bevent)

SupComImp-Exp-0.3.5.0
supcom-importer-0.3.5.0.py

#!BPY

#**************************************************************************************************
# Supreme Commander Importer for Blender3D - www.blender3d.org
#
# Written by dan - www.sup-com.net
#
# History
#   0.1.0   06/06/06   - Initial version. 
#   0.2.0   06/06/10   - Added SCA (Animation) support
#   0.3.0   06/07/02   - Alpha release
#
# Todo
#   - Material/uv map 2
#   - Bone pos/rot for scm & sca. Not perfect to use gentle words.
#   - Make sca loading independent of the scm_mesh. (e.g get bone pos/loc from armature instead)
#   - GUI for loading
#
#**************************************************************************************************

"""
Name: 'SupCom Model (.scm) 3.5'
Blender: 232
Group: 'Import'
Tooltip: 'Import Supreme Commander Models (*.scm) and Animations (*.sca)'
"""

import Blender
from Blender import NMesh, Scene, Object

from Blender import Mathutils
from Blender.Mathutils import *

from Blender.BGL import *
from Blender import Draw
from Blender.Draw import *

import os
from os import path


import struct
import string
import math
from math import *


VERSION = '3.5'

######################################################
# User defined behaviour, Select as you need
######################################################

#Enable Progress Bar ( 0 = faster )
PROG_BAR_ENABLE = 1
#how many steps a progress bar has (the lesser the faster)
PROG_BAR_STEP = 25

#LOG File for debuging
#Enable LOG File (0 = Disabled , 1 = Enabled )
LOG_ENABLE = 0
#Filename / Path. Default is blender directory Filename SC-E_LOG.txt
LOG_FILENAME = "SC-E_LOG.txt"

######################################################
# Init Supreme Commander SCM( _bone, _vertex, _mesh), SCA(_bone, _frame, _anim) Layout
######################################################
#xy_to_xz_transform = Matrix([1, 0, 0], [ 0, 0, 1], [ 0, 1, 0])
xy_to_xz_transform = Matrix([1, 0, 0], [ 0, 0, 1], [ 0, -1, 0])


	#	-1	0	0
	#	0	0	1
	#	0	1	0


class scm_bone :
	
	name = ""
	#rest_pose_inv = []
	rel_mat = []
	rel_mat_inv = []
	position = []
	rotation = []
	#abs_pos = []
	parent = 0
	parent_index = 0
	
	rel_matrix_inv = []
	#children = []
	#numchildren = 0
	#global xy_to_xz_transform
	
	def __init__(self, name):
		self.name = name
		#self.rest_pose_inv = [[0.0] * 4] * 4
		#self.position = [0.0] * 3
		#self.rotation = [0.0] * 4
		self.rel_mat_inv = Matrix([0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0])
		self.rel_mat = Matrix([0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0])
		self.rotation = Quaternion(0,0,0,0)
		self.position = Vector(0,0,0)

	def load(self, file):
		#global xy_to_xz_transform
		bonestruct = '16f3f4f4i'
		buffer = file.read(struct.calcsize(bonestruct))
		readout = struct.unpack(bonestruct, buffer)

		#supcom information:
		readRPI = Matrix([0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0])
		#// Inverse transform of the bone relative to the local origin of the mesh
		#// 4x4 Matrix with row major (i.e. D3D default ordering)
		for i in range(4):
			readRPI[i] = readout[i*4:i*4+4]
		
		self.rel_mat_inv = Matrix(readRPI[0], readRPI[1], readRPI[2], readRPI[3])#*xy_to_xz_transform #note rot here changes pointing direction of spikie 
		self.rel_mat = self.rel_mat_inv.invert()

		#self.rel_mat = (Matrix(rest_pose_inv).invert()) 

		#// Position relative to the parent bone.
		pos = readout[16:19]
		self.position = Vector(pos[0], pos[1], pos[2])
		
		#// Rotation relative to the parent bone.
		rot = readout[19:23]
		self.rotation = Quaternion( rot[0], rot[1], rot[2], rot[3] )

		#// Index of the bone's parent in the SCM_BoneData array
		self.parent_index = readout[24]
		
		# Read bone name
		#oldpos = file.tell()
		#file.seek(bone[..], 0)
		#self.name = file.readline()
		#file.seek(oldpos, 0)

		
		return self
	
	def dump(self):
		print 'Bone       ', self.name
		print 'Position   ', self.position
		print 'Rotation   ', self.rotation
		print 'Parent Idx ', self.parent_index		
		if (self.parent != 0): 
			print 'Parent     ', self.parent.name
		else:
			print 'Parent     <NONE>'		
		print 'Rest Pose Inv.'
		#for row in range(4):
			#print '  ', self.rest_pose_inv[row]		


class scm_vertex :
	
	position = []
	tangent = []
	normal = []
	binormal = []
	uv1 = []
	uv2 = []
	bone_index = []

	def __init__(self):
		self.position 	= Vector(0,0,0)
		self.tangent 	= Vector(0,0,0)
		self.normal 	= Vector(0,0,0)
		self.binormal 	= Vector(0,0,0)
		self.uv1 	= Vector(0,0)
		self.uv2 	= Vector(0,0)
		self.bone_index = [0]*4

	def load(self, file):
		
		vertstruct = '3f3f3f3f2f2f4B'
		vertsize = struct.calcsize(vertstruct)
		
		buffer = file.read(vertsize)
		vertex = struct.unpack(vertstruct, buffer)
		
		self.position = vertex[0:3]
		self.tangent = vertex[3:6]
		self.normal = vertex[6:9]
		self.binormal = vertex[9:12]
		self.uv1 = vertex[12:14]
		self.uv2 = vertex[14:16]
		self.bone_index = vertex[16:20]	
		
		return self
	
	def dump(self):
		print 'position ', self.position
		print 'tangent  ', self.tangent
		print 'normal   ', self.normal
		print 'binormal ', self.binormal
		print 'uv1      ', self.uv1
		print 'uv2      ', self.uv2
		print 'bones    ', self.bone_index


class scm_mesh :
	
	bones = []
	vertices = []
	faces = []
	info = []
	filename = ""
	
	def __init__(self):
		self.bones = []
		self.vertices = []
		self.faces = []
		self.info = []
		self.filename = ""

	def load(self, filename):	
		global xy_to_xz_transform
		self.filename = filename
		scm = file(filename, 'rb')
		
		# Read header
		headerstruct = '4s11L'
		buffer = scm.read(struct.calcsize(headerstruct))
		header = struct.unpack(headerstruct, buffer)
		
		marker = header[0]
		version = header[1]
		boneoffset = header[2]
		bonecount = header[3]
		vertoffset = header[4]
		extravertoffset = header[5]
		vertcount = header[6]
		indexoffset = header[7]
		indexcount = header[8]
		tricount = indexcount / 3 #?
		infooffset = header[9]
		infocount = header[10]
		totalbonecount = header[11]
		
		if (marker != 'MODL'):
			print 'Not a valid scm'
			return
			
		if (version != 5):
			print 'Unsupported version (%d)' % version
			return
		
		# Read bone names
		scm.seek(pad(scm.tell()), 1)
		length = (boneoffset - 4) - scm.tell()

		# This should probably be handeled by the scm_bone reader as it contains the nameoffset. But I'm lazy
		# and logic tells me it's written in the same order as the bones.
		buffer = scm.read(length)		
		rawnames = struct.unpack(str(length)+'s',buffer)		
		bonenames = string.split(rawnames[0],'\0')[:-1]
		
		# Read bones
		scm.seek(boneoffset, 0)
		for b in range(0, totalbonecount):
			bone = scm_bone(bonenames[b])
			bone.load(scm) 			
			self.bones.append(bone)
				
		#show them (for debug)				
		#for b in range(0, totalbonecount):
			#print "bone %d has %d children = " %(b, self.bones[b].numchildren)
		
		# Set parent (this could probably be done in the other loop since parents are usually written to the file
		# before the children. But you never know..
		for bone in self.bones:
			if (bone.parent_index != -1):
				bone.parent = self.bones[bone.parent_index]
			else:
				bone.parent = 0
		
			# the bone matrix relative to the parent.
			if (bone.parent != 0):
				mrel = (bone.rel_mat) * Matrix(bone.parent.rel_mat).invert() #* xy_to_xz_transform
				bone.rel_matrix_inv = Matrix(mrel).invert()
			else:
				mrel = bone.rel_mat * xy_to_xz_transform  #there is no parent
				bone.rel_matrix_inv = Matrix(mrel).invert()
				
				
		#self.rest_pose = Matrix(self.rest_pose[0], self.rest_pose[1], self.rest_pose[2], self.rest_pose[3])    	in sc =  rest pose inv
		#self.rel_mat = (Matrix(self.rest_pose).invert()) * xy_to_xz_transform  				in sc = rest pose yz
			
		
		# Read vertices
		scm.seek(vertoffset, 0)
		for b in range(0, vertcount):
			vert = scm_vertex()
			vert.load(scm)			
			self.vertices.append(vert)
		
		# Read extra vertex data		
		# Not implemented in Sup Com 1.0!
		
		# Read indices (triangles)
		tristruct = '3h'
		trisize = struct.calcsize(tristruct)
			
		scm.seek(indexoffset, 0)			
		for t in range(tricount):			
			buffer = scm.read(trisize)
			face = struct.unpack(tristruct, buffer)
			self.faces.append(list(face))
			
		
		# Read info
		if (infocount > 0):
			scm.seek(infooffset)
			buffer = scm.read()
			rawinfo = struct.unpack(str(len(buffer))+'s',buffer)		
			self.info = string.split(rawinfo[0],'\0')[:-1]
		
		scm.close()	
		
		return self

	def dump(self):
		print ''
		print 'Filename: ', self.filename
		print 'Bones ', len(self.bones)
		print 'Verts ', len(self.vertices)
		print 'Faces ', len(self.faces)		
		print ''
		print 'INFO: '
		for info in self.info:
			print '  ', info
		


class sca_bone:

	position = []
	rotation = []
	#changed: rototation -> rotation
	pose_pos = []
	pose_rot = []
	rel_matrix = []
	pose_matrix = []
	#rel_mat = None

	def __init__(self, pos, rot):
		self.position = pos
		self.rotation = rot
		self.rel_matrix = None
		self.pose_matrix = None
		#self.rel_mat = None
		

	def dump(self):
		print 'Position ', self.position
		print 'Rotation ', self.rotation
		


class sca_frame:

	keytime = 0.0
	keyflags = 0
	bones = []
	anim = None
	
	def __init__(self, anim):
		self.keytime = 0.0
		self.keyflags = 0
		self.bones = []
		self.anim = anim

	def load(self, file):		
		frameheader_fmt = 'fl'
		frameheader_size = struct.calcsize(frameheader_fmt)
		buffer = file.read(frameheader_size)
		
		(self.keytime, self.keyflags) = struct.unpack(frameheader_fmt, buffer)
		
		posrot_fmt = '3f4f'
		posrot_size = struct.calcsize(posrot_fmt)
		
		for b in range (0, self.anim.numbones) :
			buffer = file.read(posrot_size)
			posrot = struct.unpack(posrot_fmt, buffer)			
			bone = sca_bone(posrot[0:3], posrot[3:7])			
			self.bones.append(bone)

	def dump(self):
		print 'Time  ', self.keytime
		print 'Flags ', self.keyflags
		


class sca_anim :
	
	filename = ""
	frames = []
	bones = []
	bonelinks = []
	bonenames = []
	numbones = 0
	duration = 0.0
	
	def __init__(self):
		self.filename = ""
		self.frames = []
		self.bones = []
		self.numbones = 0
		self.bonelinks = []
		self.bonenames = []
		self.duration = 0.0




	def calcAnimBoneMatrix(self, frame, bone_index, armature_bones, frame_index):
		global xy_to_xz_transform
		
		bone = frame.bones[bone_index];	
		parent_index = self.bonelinks[bone_index]

		# note that the pos/rot of the armature_bones are still in relative supcom coordinates.
		# so we can correct the relative pos-increase by the relative armature-increase
		pose_rel_pos = Vector(bone.position) 
		pose_rel_rot = Quaternion(bone.rotation) 

		# the matrix representation... it's easier to work with matrix notation I think.
		# the rotation:
		pose_rel_matrix = (pose_rel_rot.toMatrix()).resize4x4() 
		
		
		# the translation:
		for i in xrange(3):
			pose_rel_matrix[3][i] = pose_rel_pos[i]

		#pose_rel_matrix = pose_rel_matrix * xy_to_xz_transform
		#rel_pos = pose_rel_pos - armature_bones[bone_index].position
		#rel_rot = DifferenceQuats(pose_rel_rot, armature_bones[bone_index].rotation)
		
		if (parent_index == -1) :
			# for the root bone, this is already the absolution pos/rot, but,
			# the root bone should be rotated into the blender coordinates
			bone.rel_matrix = pose_rel_matrix * xy_to_xz_transform
			
			#testmat =(bone.rel_mat) * Matrix(bone.parent.rel_mat).invert()
			
		if (parent_index >= 0):
			# for all the children, they are seen relative to the parents.
			bone.rel_matrix = pose_rel_matrix 
		
		# Call all children
		for b in range(len(self.bonelinks)):
			if (self.bonelinks[b] == bone_index):
				self.calcAnimBoneMatrix(frame, b, armature_bones, frame_index)

		# the (rendered) animation positions are relative to
		# both the parent, and to the relative rest position of the bone.
		bone.pose_matrix = Matrix(bone.rel_matrix * armature_bones[bone_index].rel_matrix_inv)#* xy_to_xz_transform)

		#bone.pose_matrix = bone.pose_matrix * xy_to_xz_transform
		# pose position relative to the armature
		bone.pose_pos = Vector(bone.pose_matrix.translationPart())
		
		bone.pose_rot = bone.pose_matrix.toQuat()
		
		#test = bone.pose_matrix.toQuat()
		#bone.pose_rot = Quaternion( test.x, test.y, test.z, test.w)


	def load(self, filename):
		self.filename = filename
		sca = file(filename, 'rb')
		
		# Read header
		headerstruct = '4sllflllll'
		buffer = sca.read(struct.calcsize(headerstruct))
		header = struct.unpack(headerstruct, buffer)
		
		(magic,             \
		 version,           \
		 numframes,         \
		 self.duration,     \
		 self.numbones,     \
		 namesoffset,       \
		 linksoffset,       \
		 animoffset,        \
		 framesize) = struct.unpack(headerstruct, buffer)
		
		if (magic != 'ANIM'):
			print 'Not a valid .sca animation file'
			return
		
		if (version != 5):
			print 'Unsupported sca version: %d'  % version
			return
		
		# Read bone names
		sca.seek(namesoffset, 0)
		length = linksoffset - namesoffset
		buffer = sca.read(length)		
		rawnames = struct.unpack(str(length)+'s',buffer)		
		self.bonenames = string.split(rawnames[0], '\0')[:-1]
		
		# Read links
		links_fmt = str(self.numbones)+'l'
		links_size = struct.calcsize(links_fmt)
		
		buffer = sca.read(links_size)
		self.bonelinks = struct.unpack(links_fmt, buffer)
			
		posrot_fmt = '3f4f'
		posrot_size = struct.calcsize(posrot_fmt)
		
		sca.seek(animoffset)
		buffer = sca.read(posrot_size)
		root_posrot = struct.unpack(posrot_fmt, buffer)
		
		for f in range (0, numframes) :
			frame = sca_frame(self)
			frame.load(sca)
			self.frames.append(frame)
		
		sca.close()
		
		return self

	def dump(self):
		print 'SCA:  ', self.filename
		print 'Duration: %fs' % self.duration
		print 'Num loaded frames ', len(self.frames)
		
		print 'Bonelinks'		
		for link in self.bonelinks:
			print ' ', link
		
		print 'Bone names'
		for name in self.bonenames:
			print ' ', name
	
				
def pad(size):
	val = 32 - (size % 32)
	
	if (val > 31):
		return 0
		
	return val
			
			
#**************************************************************************************************
# Blender Interface
#**************************************************************************************************

def read_scm() :
	global xy_to_xz_transform
	global scm_filepath # [0] both [1] path [2] name
	global sca_filepath # [0] both [1] path [2] name
	
	print "=== LOADING Sup Com Model ==="
	print ""
	
	xy_to_xz_transform.resize4x4()
	
	scene = Blender.Scene.GetCurrent() 	
	
	mesh = scm_mesh()
	
	if (mesh.load(scm_filepath[0]) == None):
		print 'Failed to load %s' %scm_filepath[2]
		return
	
	ProgBarLSCM = ProgressBar( "Imp: load SCM", (2*len(mesh.vertices) + len(mesh.faces)))	
	
	armature_name = string.rstrip(scm_filepath[2], ".scm")
	print "armature ", armature_name
	
	###		CREATE ARMATURE 
	armObj = Object.New('Armature', armature_name)	#bad 
	
	armData = Blender.Armature.Armature(armature_name) 
	armData.drawAxes = True 
	armData.makeEditable()	
	 
	for index in range(len(mesh.bones)):
		bone = mesh.bones[index]	
		blender_bone = Blender.Armature.Editbone()		
		#not nice parent may not exist,  but usualy should exist (depends on storing in scm)
		if (bone.parent != 0) :
			blender_bone.parent = armData.bones[bone.parent.name]
		

		#blender_bone.options(Armature.CONNECTED)
		blender_bone.matrix = Matrix(bone.rel_mat * xy_to_xz_transform)		
				
		armData.bones[bone.name] = blender_bone

	
	#save changes (after linking!)
	armObj.link(armData)
	scene.link(armObj)
	#scene.objects.new(armData)
	###		CREATE MESH
	meshData = Blender.Mesh.New('Mesh')
	
	ProgBarLSCM.text = "IMP: Verticles"
	#add verts
	vertlist = []
	for vert in mesh.vertices:
		ProgBarLSCM.do()
		vertlist.append(Vector(vert.position)*xy_to_xz_transform)
	
	meshData.verts.extend(vertlist)

	meshData.vertexUV = True
	for i_v in xrange(len(meshData.verts)):
		uv1 = mesh.vertices[i_v].uv1
		meshData.verts[i_v].uvco = Vector(uv1[0], 1.0-uv1[1])
	
	ProgBarLSCM.text = "IMP: Faces"
	
	#reverse faces
	#for face in mesh.faces:
	#	face.reverse()
		
	#add faces
	meshData.faces.extend(mesh.faces)	
	meshData.faceUV = True	
	for face in meshData.faces :
		ProgBarLSCM.do()
		face.uv = [face.verts[i].uvco for i in range(3)]
	
	#put in scene
	mesh_obj = Object.New('Mesh','Mesh')
	mesh_obj.link(meshData)
	
	#scene.objects.new(meshData)

	#assigns vertex groups #mesh must be in object
	for bone in mesh.bones:
		meshData.addVertGroup(bone.name)

	for vertex_index in range(len(mesh.vertices)):
		ProgBarLSCM.do()
		vertex = mesh.vertices[vertex_index]
		bone_index = vertex.bone_index[0]
		meshData.assignVertsToGroup(mesh.bones[bone_index].name, [vertex_index], 1.0, Blender.Mesh.AssignModes.REPLACE) #'REPLACE'
	
	# it works... but you've to select/change to edit mode for the object to show up.
	# dunno why that is, maybe because Nmesh is depracated.
	meshData.update()
		
	#bones disapear wen update ?
	#armData.update()

	scene.link(mesh_obj)
	
	#must be in scene
	#armObj = None 
	#meshObj = None
	#for obj in Object.Get():
	#	if obj.getType() == 'Armature':
	#		armObj = obj
	#	if obj.getType() == 'Mesh':
	#		meshObj = obj
			
	#mesh_obj = Blender.Object.Get('Mesh')
	armObj.makeParentDeform([mesh_obj], 0, 0)
	#makeParentVertex(objects, indices, noninverse=0, fast=0)
	
	meshData.update()
	armData.update() 

	if len(mesh.info):
		print "=== INFO ==="
		for info in mesh.info:
			print "",info

	print "=== COMPLETE ==="
	
	if sca_filepath[0] != "" :
		read_anim(mesh)

	


def read_anim(mesh):
	global xy_to_xz_transform
	global sca_filepath # [0] both [1] path [2] name
	
	#xy_to_xz_quat = xy_to_xz_transform.toQuat()
	
	print "=== LOADING Sup Com Animation ==="
	print ""
	
	anim = sca_anim()
	anim.load(sca_filepath[0])
	
	ProgBarLSCA = ProgressBar( "Imp: Frames", len(anim.frames))
	
	scene = Blender.Scene.GetCurrent()
	
	arm_obj = None
	for obj in scene.objects:
		data = obj.getData()
		if type(data) is Blender.Types.ArmatureType:
			arm_obj = obj
			break
		
	if arm_obj == None:
		print "couldn't apply animation, no armature in the scene" 
		return
	#arm_obj = armObj

	print arm_obj.name
	#actionpath, actionname = os.path.split(filename)
	
	action = Blender.Armature.NLA.NewAction(sca_filepath[2])
	action.setActive(arm_obj)
	
	pose = arm_obj.getPose()
	
	for frame_index in xrange(len(anim.frames)):
		ProgBarLSCA.do()

		Blender.Set("curframe", frame_index+1)
		frame = anim.frames[frame_index]

		# this changes the relative orientation (supcom) to absolute orientation (blender)
		anim.calcAnimBoneMatrix(frame, 0, mesh.bones, frame_index)

		# this inserts the bones information into blender.
		for b in range(len(frame.bones)):
			pose_bone = pose.bones[anim.bonenames[b]]
			
			if (pose_bone == None):
				print 'Frame %d - Bone \"%s\" not found' % (frame_index, anim.bonenames[b])
				continue	
				
			anim_bone = frame.bones[b]
			
			pose_bone.loc =  anim_bone.pose_pos 
			pose_bone.quat = anim_bone.pose_rot
			pose_bone.size = Vector(1,1,1)
					
			pose.update()
			pose_bone.insertKey(arm_obj, frame_index+1, [Blender.Object.Pose.LOC, Blender.Object.Pose.ROT, Blender.Object.Pose.SIZE])
			pose.update()
	
	Blender.Set("curframe", 1)
	
	#scene = Blender.Scene.GetCurrent()
	context = scene.getRenderingContext()

	context.endFrame(len(anim.frames)+1)
	
	print "=== COMPLETE ==="



######################################################
# GUI
######################################################
	
log = []
log_max_lines = 14
LOG_FILE = None

#log to file
def LOGn(message):
	global LOG_FILE
	global LOG_ENABLE
	global LOG_FILENAME
	
	if LOG_ENABLE :
		if LOG_FILE == None :			
			LOG_FILE = open( LOG_FILENAME, 'w')
			LOG_FILE.write('SupCom Exporter LOG File:\n\n')
			
			LOGp( "LOG enabled: %s" % (LOG_FILENAME))
			Log(message + '\n')
			
		else :		
			LOG_FILE.write(message + '\n')
			
#Log to file, to console and exp window					
def LOGp(message):
	global log, log_max_lines
	
	LOGn(message)
	print message	
	log.append(message)
	
	if len(log) > log_max_lines:
		del log[0]
		
		
counter = []
cLog = []
#log for a amount of errors like vertex errors
def countLOG(message):
	global cLog
	global counter
	
	cont = False
	
	for i in xrange(len(cLog)):
		if cLog[i] == message:
			cont = True
			counter[i] +=1
			break
			
	if not cont :
		
		cLog.append( message)
		counter.append( 1)

def closeLog():
	global cLog, LOG_FILE
	global counter
	
	for i in xrange(len(cLog)):
		LOGp("%s (Times:%d)" % (cLog[i], counter[i]))

	if LOG_FILE != None :
		LOG_FILE.close()	
		
	Blender.Window.RedrawAll()
		

class ProgressBar :

	global PROG_BAR_STEP
	global PROG_BAR_ENABLE
	
	progress = 0
	progressold = 0	
	current = 0	
	end = 0
	div = 0
	text = "None"	
	
	def __init__(self, text, end):
		self.progress = 0
		self.progressold = 0
		self.current = 0
		self.end = end
		self.text = text
		self.div = PROG_BAR_STEP
		
		#it looks like blender needs to init this progress bar with 0.0
		if PROG_BAR_ENABLE :
			Blender.Window.DrawProgressBar ( 0.0 , text)
		
	def do(self):	
		
		if PROG_BAR_ENABLE :
			self.current += 1
			self.progress = (self.current*self.div)/self.end
			
			if self.progress != self.progressold :
				self.progressold = self.progress
				Blender.Window.DrawProgressBar ( float(self.progress)/self.div , self.text)
				



# Events
EVENT_DRAW = 1
EVENT_EXIT = 2
EVENT_SCMDIR = 3
EVENT_SCADIR = 4


show_log = 0

sca_filepath = [ "", "", "None"]
scm_filepath = [ "", "", "None"]

def anim_fileselector_callback(filename):
	global sca_filepath
	
	sca_filepath[0] = filename
	
	#print "Filename%sI\n" % filename
	#if ( filename != "" ) :
		#sca_filepath = [ "" , "None"]
		#return
	#else :
	
	length = len(filename)
	if filename[length-4:length] == ".sca" :		
		sca_filepath[1], sca_filepath[2]  = os.path.split(filename)
	else :
		sca_filepath[0] = ""
		sca_filepath[1] = ""
		sca_filepath[2] = "Non Supported"
	#sca_filepath[1] , sca_filepath[2] = os.path.split(filename)


def fileselector_callback(filename):
	global scm_filepath
	#sadly you cant check whether the cancel button is pressed
	scm_filepath[0] = filename
	#print "Filename%sI\n" % filename
	#if ( filename != "" ) :
	length = len(filename)
	if filename[length-4:length] == ".scm" :		
		scm_filepath[1], scm_filepath[2]  = os.path.split(filename)
	else :
		scm_filepath[0] = ""
		scm_filepath[1] = ""
		scm_filepath[2] = "Non Supported"
	#	Exit()
	

#def forcequit()
#	Exit()

	
def draw():
	global EVENT_DRAW, EVENT_EXIT,EVENT_SCADIR,EVENT_SCADIR
	global scm_filepath, sca_filepath, show_log
	global log_max_lines, log
	global VERSION

	# Titles
	glClear(GL_COLOR_BUFFER_BIT)
	
	top = 60 + log_max_lines * 12 + 8
	#top = 500
	top_x = 304
	
	glColor3f(0.8, 0.8, 1)
	glRecti(top_x, top, 4, 4)
	glBegin(GL_LINES)
	glColor3f(0.8, 0.8, 0.8)
	glVertex2d(4, top)
	glVertex2d(4, 4)
	glVertex2d(4, 4)
	glVertex2d(top_x, 4)
	glColor3f(0.5, 0.5, 0.5)
	glVertex2d(top_x, top)
	glVertex2d(4, top)
	glVertex2d(top_x, top)
	glVertex2d(top_x, top-1)
	glEnd()
	
	glColor3f(0, 0, 0)
	glRasterPos2d(10, top-16)
	Text("Supreme Commander Importer " + VERSION)	

	# Show exporting log
	if show_log:

		for index in range(0, len(log)):
			i = (len(log) - 1) - index
			glRasterPos2i(10, 40 + i*12)
			Text(log[index]) #, 'tiny')
		
		
		Button("Close", EVENT_EXIT , 10, 10, 80, 18)

		# Exporter GUI
	else:
		
		

		#if scm_directory.val == 'scm':
		#if scm_filepath[1] == 'None' :
			#automatically launch the browse window once
		#	Blender.Window.FileSelector (fileselector_callback, "Select SCM File", "*.scm")
			#Exit() # why dont you go to exit?, never
		#	forcequit()

		
		Blender.Draw.Button("Browse SCM", EVENT_SCMDIR, 10, 45, 80, 18, "Select scm")
		Blender.Draw.Button("Browse SCA", EVENT_SCADIR, 10, 80, 80, 18, "Select sca")	
		
		glRasterPos2d(100, 47)
		if scm_filepath[0] == "" :
			Text("SCM: " + scm_filepath[2])
		else:
			Text(":" + scm_filepath[2])
			
		glRasterPos2d(100, 82)
		if sca_filepath[0] == "" :
			Text("SCA: " + sca_filepath[2])
		else : 
			Text(":" + sca_filepath[2])

		# Draw and Exit Buttons
		Button("Import", EVENT_DRAW , 10, 10, 80, 18)
		Button("Exit", EVENT_EXIT , 100, 10, 80, 18)

def event(evt, val):	
	if (evt == QKEY and not val): 
		Exit()

def bevent(evt):
	global EVENT_DRAW,EVENT_EXIT
	global scm_filepath
	global show_log

	if (evt == EVENT_EXIT): 
		#show_log = 1
		Exit()
	
	elif (evt == EVENT_SCMDIR):
		Blender.Window.FileSelector (fileselector_callback, "Select SCM File")
		Blender.Redraw()
		
	elif (evt == EVENT_SCADIR):
		if scm_filepath[0] != "" :			
			Blender.Window.FileSelector (anim_fileselector_callback, "Select SCA File", scm_filepath[1]+"\\")			
		else :
			Blender.Window.FileSelector (anim_fileselector_callback, "Select SCA File")			
		Blender.Redraw()
	
	elif (evt == EVENT_DRAW):
		
		if (scm_filepath[0] != ''):		
			show_log = 1			
			read_scm() 
			
		#if (sca_filepath[0] != ''):
		#	show_log = 1
		#	read_sca()
		
		Blender.Redraw()

		

Register(draw, event, bevent)

#open fileselector automaticly
Blender.Window.FileSelector (fileselector_callback, "Select SCM File")

supcom-exporter-0.3.5.0.py

#!BPY

#**************************************************************************************************
# Supreme Commander Exporter for Blender3D - www.blender3d.org
#
# Written by dan - www.sup-com.net), Brent (www.scmods.net)
#
# further improvements by GeomanNL and Darius 
#
# History
#
# 0.1.0  2006-07-02	Dan Initial version.
#
# 0.2.0   2007-03-11	Brent Fixed UV coords, V was inverted.
#				Support for exporting quads.
#				Fixed a padding issue.
#
# 0.3.0   2007-03-18	Dan Code refactoring / Clean up.
#				Fixed 'INFO' section size in header.
#
# 0.3.3  2007-09-25	GeomanNL  fixed a file-write bug 
#				orientation fix, changed to matrix rotation
#				other excellent stuff 
#				(me darius took the freedom to write your entry:D)
#									
# 0.3.5  2009-03-20	Darius_  tangent and binormal calc
#				vertex optimation
#				blender front to supcom front
#				some more fixes and reorganizing/cleanup code
#				  
#								
# Todo
#   - GUI improvements
#   - Support for LOD exporting. Eg. not merging all meshes for an armature into one mech but rather only
#     sub-meshes and export the top level meshes to different files.
#   - Validation, ensure that
#     - Prompt before overwriting files & check that directories exists
#   - Second UV set?
#   - Set animation time per frame
#   - Export LUA script for use in the animation viewer (eg, start anim, set texture etc)..
#   - Set root rot/pos for sca
#
#**************************************************************************************************

"""
Name: 'Supreme Commander 3.5'
Blender: 242
Group: 'Export'
Tooltip: 'Model / Animation Exporter for Supreme Commander'
"""
import Blender
import struct

from Blender.BGL import *
from Blender import Draw
from Blender.Draw import *

from Blender import Mathutils
from Blender.Mathutils import *

from Blender import Window



import os
from os import path

VERSION = '3.5'

######################################################
# User defined behaviour, Select as you need
######################################################
#if you want to leave an info in your scm file put it in there
USER_INFO = ""


#Enable Progress Bar ( 0 = faster )
PROG_BAR_ENABLE = 1
#how many steps a progress bar has (the lesser the faster)
PROG_BAR_STEP = 25

#slower - reduce vertex amount 
VERTEX_OPTIMIZE = 1	

#LOG File for debuging
#Enable LOG File (0 = Disabled , 1 = Enabled )
LOG_ENABLE = 0
#Filename / Path. Default is blender directory Filename SC-E_LOG.txt
LOG_FILENAME = "SC-E_LOG.txt"

LOG_BONE = 1
LOG_VERT = 0


######################################################
# Init Supreme Commander SCM( _bone, _vertex, _mesh), SCA(_bone, _frame, _anim) Layout
######################################################

#Transform matrix	    z -> yx -> xy -> z	
xy_to_xz_transform = Matrix([ 1, 0, 0], 
							[ 0, 0, -1], 
							[ 0, 1, 0])
						
#xy_to_xz_transform = Matrix([ 1, 0, 0], [ 0, 1, 0], [ 0, 0, 1])
							
# Armature world matrix
MArmatureWorld = Matrix()
BONES = []

ANIMATION_DURATION = 1.5

class scm_bone :
	
	rest_pose = []		
	rest_pose_inv = []							
	rotation = []
	position = []	
	parent_index = 0
	used = False
	name = ""
	
	def __init__(self, name, rest_pose_inv, rotation, position, parent_index):
									
		self.rest_pose_inv = rest_pose_inv									
		self.rotation = rotation		
		self.position = position		
		self.parent_index = parent_index
		self.used = False
		self.name = name

	def save(self, file):
		bonestruct = '16f3f4f4i'
		#bonestruct = '16f3f4f4L' #Deprecation warning L and mistyrious binary output
		rp_inv = [0] * 16

		icount = 0
		for irow in xrange(4):
			#rest pose_inv
			for icol in xrange(4):
				rp_inv[icount] = self.rest_pose_inv[irow][icol]
				icount = icount + 1
		
		bonedata = struct.pack(bonestruct,									
			rp_inv[0], rp_inv[1], rp_inv[2], rp_inv[3],							
			rp_inv[4], rp_inv[5], rp_inv[6], rp_inv[7],							
			rp_inv[8], rp_inv[9], rp_inv[10],rp_inv[11],						
			rp_inv[12],rp_inv[13],rp_inv[14],rp_inv[15],						
			self.position[0],self.position[1],self.position[2],					
			self.rotation.w,self.rotation.x,self.rotation.y,self.rotation.z, #Quaternion (w,x,y,z)#w,x,y,z
			self.name_offset, self.parent_index,								
			0,0)
		
		
		#print self.name
		#print self.rest_pose_inv
			
		
		if LOG_BONE :	
			LOGn(" %s rp_inv: [%.3f, %.3f, %.3f, %.3f],\t [%.3f, %.3f, %.3f, %.3f],\t [%.3f, %.3f, %.3f, %.3f],\t [%.3f, %.3f, %.3f, %.3f] \tpos: [%.3f, %.3f, %.3f] \trot: [%.3f, %.3f, %.3f, %.3f] %d" 
			% (	self.name, rp_inv[0], rp_inv[1], rp_inv[2], rp_inv[3],							
				rp_inv[4], rp_inv[5], rp_inv[6], rp_inv[7],							
				rp_inv[8], rp_inv[9], rp_inv[10],rp_inv[11],						
				rp_inv[12],rp_inv[13],rp_inv[14],rp_inv[15],						
				self.position[0],self.position[1],self.position[2],					
				self.rotation[0],self.rotation[1],self.rotation[2],self.rotation[3], self.parent_index))
		
		
		file.write(bonedata)
	

class scm_vertex :

	global xy_to_xz_transform
	position = []
	tangent  = []
	normal   = []
	binormal = []
	uvc = 0
	uv1 = []
	uv2 = []
	bone_index = []
			
	def __init__(self, pos , no , uv1, bone_index):
			
		self.position = pos
		self.normal   = no
		
		#tangent and binormal wil be calculated by face
		self.tangent  = Vector( 0, 0, 0)
		self.binormal = Vector( 0, 0, 0)
		
		self.uvc = 1
		self.uv1 = uv1 
		self.uv2 = uv1# Vector(0,0) #uv1 #better results with copy ... strange, where is the use of that?
		
		self.bone_index = bone_index
			

	def save(self, file):
		
		vertstruct = '3f3f3f3f2f2f4B'
		
		#so finaly we can norm because here it is sure that no tang norm will be added
		#self.normal = CrossVecs(self.tangent, self.binormal).normalize()
		self.tangent.normalize()
		self.binormal.normalize()
		self.normal.normalize()
		
		if False :
			self.tangent = Vector(0,0,0)
			self.binormal= Vector(0,0,0)
			#self.normal  = Vector(0,0,0)

		if LOG_VERT :		
			LOGn( " pos: [%.3f, %.3f, %.3f] \tn: [%.3f, %.3f, %.3f] \tt: [%.3f, %.3f, %.3f] \tb: [%.3f, %.3f, %.3f] \tuv [ %.3f, %.3f | %.3f, %.3f ] \tbi: [%d, %d, %d, %d]" 
			
			% (
				self.position[0], self.position[1], self.position[2],	
				self.normal[0],   self.normal[1],   self.normal[2],		
				self.tangent[0],  self.tangent[1],  self.tangent[2],	
				self.binormal[0], self.binormal[1], self.binormal[2],	
				self.uv1[0], self.uv1[1],								
				self.uv2[0], self.uv2[1],								
				self.bone_index[0], self.bone_index[1],					
				self.bone_index[2], self.bone_index[3]) )
					
   # so you store in this order:
   # pos, normal, tangent, binormal, uv1, uv2, ibone
		vertex = struct.pack(vertstruct, 							
			self.position[0], self.position[1], self.position[2],	
			self.normal[0],   self.normal[1],   self.normal[2],		
			self.tangent[0],  self.tangent[1],  self.tangent[2],	
			self.binormal[0], self.binormal[1], self.binormal[2],	
			self.uv1[0], self.uv1[1],								
			self.uv2[0], self.uv2[1],								
			self.bone_index[0], self.bone_index[1],					
			self.bone_index[2], self.bone_index[3])
		

		file.write(vertex)
		
#helper the real scm face 'tupel is stored in mesh
#quad face
class qFace : 

		vertex_cont = []
		
		def __init__(self):
			self.vertex_cont = []
		
		def addVert(self, vertex):
			self.vertex_cont.extend( vertex )
			
		def addToMesh(self, mesh):
		
			face1 = Face()
			face1.addVert([ self.vertex_cont[0], self.vertex_cont[1], self.vertex_cont[2] ])
			face1.CalcTB()
			
			face2 = Face()
			face2.addVert([ self.vertex_cont[2], self.vertex_cont[3], self.vertex_cont[0] ])
			face2.CalcTB()
			
			mesh.addQFace(face1, face2)
			
		
#helper the real scm face 'tupel is stored in mesh
#tri face
class Face :
	
	vertex_cont = []
	
	def __init__(self):	
		self.vertex_cont = []
		
	def addVert(self, vertex):
		self.vertex_cont.extend(vertex)
		
	#now contains 3 vertexes calculate bi and ta and add to mesh
			
	def CalcTB( self ) :
		vert1 = self.vertex_cont[0]
		vert2 = self.vertex_cont[1]
		vert3 = self.vertex_cont[2]
		
		uv = [ vert1.uv1, vert2.uv1, vert3.uv1]

		# Calculate Tangent and Binormal
		#		(v3 - v1).(p2 - p1) - (v2 - v1).(p3 - p1)
		#	T  =  ------------------------------------------------
		#		(u2 - u1).(v3 - v1) - (v2 - v1).(u3 - u1)
		#		(u3 - u1).(p2 - p1) - (u2 - u1).(p3 - p1)
		#	B  =  -------------------------------------------------
		#		(v2 - v1).(u3 - u1) - (u2 - u1).(v3 - v1)		
		
		P2P1 = vert2.position - vert1.position
		P3P1 = vert3.position - vert1.position
		
		#UV2UV1 = [ uv[1][0]-uv[0][0], uv[1][1]-uv[0][1] ]
		#UV3UV1 = [ uv[2][0]-uv[0][0], uv[2][1]-uv[0][1] ]
	
		UV2UV1 = uv[1] - uv[0]
		UV3UV1 = uv[2] - uv[0]
		
		divide = (UV2UV1[1]*UV3UV1[0] - UV2UV1[0]*UV3UV1[1])
		
		if ( divide != 0.0 ) :		
			tangent = Vector((UV3UV1[1]*P2P1 - UV2UV1[1]*P3P1)/(divide))
			binormal = Vector((UV3UV1[0]*P2P1 - UV2UV1[0]*P3P1)/(-divide))
		else :			
			countLOG("<Warning: Vertex-T-B divided through zero")	
			tangent = Vector(0,0,0)
			binormal = Vector(0,0,0)	

		
		#add calculated tangent and binormal to vertices
		for ind in xrange(3):
			self.vertex_cont[ind].tangent = tangent
			self.vertex_cont[ind].binormal =  binormal	
			
	def addToMesh( self, mesh ) :
		self.CalcTB()
		mesh.addFace( self )
		
 
class scm_mesh :
	
	bones = []
	vertices = []
	vertcounter = 0
	faces = []
	info = []
	
	def __init__(self):
		self.bones = []
		self.vertices = []
		self.faces = []
		self.info = []
		self.vertcounter = 0
		
	def addVert( self, nvert ):
		if VERTEX_OPTIMIZE :		
			#search for vertex already in list
			vertind = 0
			for vert in self.vertices :								
				if nvert.uv1 == vert.uv1 and nvert.position == vert.position :
					break	#found vert in list keep that index				
				vertind += 1 #hmm not that one
				
			if vertind == len(self.vertices)  :
				self.vertices.append( nvert )
			else:
				vert = self.vertices[vertind]
				
				vert.tangent = Vector( (vert.tangent + nvert.tangent) )					
				vert.binormal = Vector( (vert.binormal + nvert.binormal) )					
				vert.normal = Vector( (vert.normal + nvert.normal) )
				
				self.vertices[vertind] = vert					
			
			return vertind 			
		else:
			self.vertices.append(nvert)
			return len(self.vertices)-1
			
	def addFace( self, face ):
		
		facein = [ self.addVert(nvert) for nvert in face.vertex_cont]
		self.faces.append(facein)
	
	def addQFace( self, face1, face2):
		
		facein = [ self.addVert(nvert) for nvert in face1.vertex_cont]
		self.faces.append(facein)
		
		facein = [ facein[2], self.addVert(face2.vertex_cont[1]), facein[0]]
		self.faces.append(facein)
		

	def save(self, filename):	
		
		LOGp('Writing Mesh...')
		
		scm = file(filename, 'wb')
		
		
		#headerstruct = '12L' #Deprecation warning L and mistyrious binary output
	
		headerstruct = '4s11L'
		headersize = struct.calcsize(headerstruct)
		
		#marker = 'MODL'
		marker = 'MODL'
		version = 5
		boneoffset = 0
		bonecount = 0  
		vertoffset = 0
		extravertoffset = 0
		vertcount = len(self.vertices)
		indexoffset = 0
		indexcount = len(self.faces) * 3
		infooffset = 0
		infosize = 0
		totalbonecount = len(self.bones)
		
		
		
		# Write dummy header
		header = struct.pack(headerstruct + '',						
			marker, version, boneoffset, bonecount, vertoffset, 	
			extravertoffset, vertcount, indexoffset, indexcount, 	
			infooffset, infosize, totalbonecount)
		
		scm.write(header)
		
		
		# Write bone names
		pad_file(scm, 'NAME')
		
		for bone in self.bones:
			bone.name_offset = scm.tell()			
			name = bone.name		
			buffer = struct.pack(str(len(name) + 1)+'s', name)
			scm.write(buffer)
			#Log(buffer)
		
		LOGn("[/bone struckt]")
		
		# Write bones
		boneoffset = pad_file(scm, 'SKEL')
			
		for bone in self.bones:
			bone.save(scm)
			# if bone.used == True: 
			bonecount = bonecount + 1
		

		
		
		# Write vertices		
		vertoffset = pad_file(scm, 'VTXL')
		
		for vertex in self.vertices:
			vertex.save(scm)
			
		
				
		# Write Faces
		indexoffset = pad_file(scm, 'TRIS')
		
		for f in range(len(self.faces)):
			face = struct.pack('3H', self.faces[f][0], self.faces[f][1], self.faces[f][2])
			#face = struct.pack('3h', self.faces[f][0], self.faces[f][1], self.faces[f][2])
			scm.write(face)
		
		
		LOGp( "Bones: %d, Vertices: %d, Faces: %d; \n" % (bonecount, len(self.vertices), len(self.faces)))			
		
		#Write Info
		if len(self.info) > 0:
			
			infooffset = pad_file(scm, 'INFO')
						
			for i in range(len(self.info)):
				info = self.info[i]
				infolen = len(info) + 1			
				buffer = struct.pack(str(infolen)+'s', info)
				scm.write(buffer)
			
			infosize = scm.tell() - infooffset;
		
		# Now we can update the header	
		scm.seek(0, 0)	
		
		header = struct.pack(headerstruct,							
			marker, version, boneoffset, bonecount, vertoffset, 	
			extravertoffset, vertcount, indexoffset, indexcount, 	
			infooffset, infosize, totalbonecount)		
			
		scm.write(header)
		
		scm.close()


class sca_bone:

	position = Vector( 0, 0, 0)
	rotation = Quaternion( 0, 0, 0, 0 )

	def __init__(self, pos, rot):
		self.position = pos
		self.rotation = rot

		
class sca_frame:

	keytime = 0.0
	keyflags = 0
	bones = []
	anim = None
	
	def __init__(self, anim):
		self.keytime = 0.0
		self.keyflags = 0
		self.anim = anim
		self.bones = []

	def save(self, file):
		frameheader_fmt = 'fl'
		frameheader_size = struct.calcsize(frameheader_fmt)
		
		posrot_fmt = '3f4f'
		posrot_size = struct.calcsize(posrot_fmt)
		
		# Frame header
		buffer = struct.pack(frameheader_fmt, self.keytime, self.keyflags)
		file.write(buffer)
		
		#Log(":%d:" % (len(self.bones)))
		
		# Bones		
		for bone in self.bones:
			
			buffer = struct.pack(	posrot_fmt, 										
									bone.position.x, bone.position.y, bone.position.z,					
									bone.rotation.w, bone.rotation.x, bone.rotation.y, bone.rotation.z)
									
			file.write(buffer)


class sca_anim :
	frames = []
	bonelinks = []
	bonenames = []
	duration = 0.0
	
	def __init__(self):
		global ANIMATION_DURATION
		self.frames = []
		self.bonelinks = []
		self.bonenames = []
		self.duration = ANIMATION_DURATION
	
	def save(self, filename):
	
		LOGp('Writing SCA...')
		
		#self.filename = filename
		sca = file(filename, 'wb')
		
		headerstruct = '4sllflllll'
		
		# Write temp header		
		magic = 'ANIM'
		version = 5
		numframes = len(self.frames)
		numbones = len(self.bonenames)		
		
		namesoffset = 0
		linksoffset = 0
		animoffset = 0
		framesize = 0
		
		header = struct.pack(headerstruct, 
			magic, version, numframes, self.duration, numbones,
			namesoffset, linksoffset, animoffset, framesize)
		#note: this header is seen correctly by the GPG dumpsca.py
		
		sca.write(header)		
		
		# Write bone names
		namesoffset = pad_file(sca, 'NAME')

		for bone_name in self.bonenames:
			buffer = struct.pack(str(len(bone_name) + 1)+'s', bone_name)
			sca.write(buffer)
		

		# Write bone links
		linksoffset = pad_file(sca, 'LINK')
		
		for link in self.bonelinks:
			buffer = struct.pack('l', link)
			sca.write(buffer)
		
		
		# Write data
		animoffset = pad_file(sca, 'DATA')
		
		#the space occupied by postion and rotation info on the bones.
		posrot_fmt = '3f4f'
		posrot_size = struct.calcsize(posrot_fmt)
		
		#this writes the position/rotation of the root bone in the first animation, as if it is at position 0, and no rotation
		#note: it looks like rot=1,0,0,0 is needed to indicate no rotation.
		buffer = struct.pack(posrot_fmt, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0)
		sca.write(buffer)
		
		for frame in self.frames:
			framesize = sca.tell()
			frame.save(sca)
			framesize = sca.tell() - framesize		
		
		
		# Update header
		sca.seek(0, 0)
		
		header = struct.pack(headerstruct, 
			magic, version, numframes, self.duration, numbones,
			namesoffset, linksoffset, animoffset, framesize)
		
		sca.write(header)
		
		sca.close()
		
		LOGp( "Bones: %d, Frames: %d;\n" % (numbones, numframes) )			
		
		#Log('OFFSETS: names = %d  links = %d  anim = %d  framesize = %d' % (namesoffset, linksoffset, animoffset, framesize))


######################################################
# Exporter Functions
######################################################

# Helper methods
######################################################

def pad(size):
	val = 32 - (size % 32)
	if (val < 4):
		val = val + 32
	
	return val

def pad_file(file, s4comment):
	N = pad(file.tell()) - 4
	filldata = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
	padding = struct.pack(str(N)+'s4s', filldata[0:N], s4comment)
	
	file.write(padding)

	return file.tell()
	

			
#
######################################################



# Helper method for itterating through the bone tree
def itterate_bones(mesh, bone, parent = None, scm_parent_index = -1):
	
	global MArmatureWorld
	global BONES
	global xy_to_xz_transform
	


	
	if (parent != None and bone.parent.name != parent.name):
		PupMenu("Error: Invalid parenting in bone ... multiple parents?!%t|OK")
		Exit()
		#print "Invalid parenting in bone", bone.name," and parent ", parent.name
		return

	b_rest_pose 	= Matrix([0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0])
	b_rest_pose_inv = Matrix([0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0])
	b_rotation		= Quaternion( 0,0,0,0 )
	b_position		= Vector( 0,0,0 )
	b_index = len(mesh.bones)
	
	
	#MArmatureWorld = Matrix(arm_obj.matrixWorld)
	bone_matrix = Matrix(bone.matrix['ARMATURESPACE'])
	
	# Calculate the inverse rest pose for the bone #instead bonearmmat*worldmat = Matrix['BONESPACE']
	b_rest_pose 	= Matrix( bone_matrix * MArmatureWorld  )	
	b_rest_pose_inv = Matrix( b_rest_pose * xy_to_xz_transform ).invert()
	
	if (parent == None):
		rel_mat = b_rest_pose * xy_to_xz_transform 
		#root pos is the same as the rest-pose		
	else:
		parent_matrix_inv = Matrix( parent.matrix['ARMATURESPACE'] ).invert()
		rel_mat = Matrix(bone_matrix * parent_matrix_inv)
		# must be BM * PMI in that order
		# do not use an extra (absolute) extra rotation here, cause this is only relative
	
	#  Position & Rotation   relative to parent (if there is a parent)		
	b_rotation = 	rel_mat.toQuat()#.normalize()
	
	#row 3, cols 0,1,2 indicate position	
	b_position = Vector( rel_mat[3] )
	
	#def __init__(self, name, rest_pose_inv, rotation, position, parent_index):
	sc_bone = scm_bone( bone.name, b_rest_pose_inv, b_rotation, b_position, scm_parent_index )
	
	BONES.append(sc_bone)
	mesh.bones.append(sc_bone)
	
	# recursive call for all children
	if (bone.children != None):
		for child in bone.children: 
			itterate_bones( mesh, child, bone, b_index )


def make_scm(arm_obj):
	
	global MArmatureWorld
	global xy_to_xz_transform
	
	arm = arm_obj.getData()
	
	scn = Blender.Scene.GetCurrent()
	
	# Get all mesh objects for the selected armature & calculate progbar length
	pb_length = 0
	mesh_objs = []
	for obj in scn.objects:
		if obj.parent == arm_obj and obj.getType() == 'Mesh':
			#calculate progbar length
			bmesh_data = obj.getData(False, True)
			pb_length += len(bmesh_data.faces)
			
			mesh_objs.append(obj)	
		
	ProgBarFaces = ProgressBar( "Exp: Verts", pb_length )
			
	# Create SCM Mesh
	supcom_mesh = scm_mesh()
	
	# Traverse the bone tree	and  check if there is one root bone 
	numroots = 0
	for bone in arm.bones.values():			
		if (bone.parent == None):
			numroots += 1
			itterate_bones(supcom_mesh, bone)			
			
	if numroots > 1:
		PupMenu("Error: there are multiple root bones -> check you bone relations!%t|OK")
		Exit()
		return

	#this inserts a converted armature back into the existing model, to see
	#if the bone locations are correct (was used for debugging)
	#test_the_armature = 1
	#if test_the_armature:
	#	#create an extra test armature
	#	testarm_data= Blender.Armature.New('testArmature')
	#	testarm_ob = scn.objects.new(testarm_data)
	#	testarm_data.makeEditable()
	#	prev_eb = Non
	#	for bone in supcom_mesh.bones:
	#		print "mesh bone: ", bone.name
	#		eb = Blender.Armature.Editbone()
	#		eb.name = bone.name
	#		rest_pose = bone.rest_pose[3]
	#		eb.head = Vector(rest_pose[0], rest_pose[1], rest_pose[2])
	#		eb.tail = eb.head + Vector(1,0,0)
	#		if bone.parent_index != -1:
	#			eb.parent = prev_eb
	#		prev_eb = eb
	#		testarm_data.bones[eb.name]= eb
	#	testarm_data.update()
	

	
	# Process all the meshes
	for mesh_obj in mesh_objs:
		
		bmesh_data = mesh_obj.getData(False, True)	

		if not bmesh_data.faceUV :
			PupMenu("Error: Mesh has no texture values -> Please set your UV!%t|OK")
			Exit()
			return
			
		MatrixMesh = Matrix(mesh_obj.matrixWorld)
		mesh_name = mesh_obj.name
		
		for face in bmesh_data.faces:
		
			ProgBarFaces.do()									
			
			vertList = []
						
			for i in xrange(len(face.verts)):
				vert = face.verts[i]
				
				v_nor = Vector( 0, 0, 0 )
				v_pos = Vector( 0, 0, 0 )
				v_uv1 = Vector( 0, 0) #SC allows 2 uv's				
				v_boneIndex = [0]*4 #  SC supports up to 4 bones we will use only one
				#v_boneIndex = [-1,0,0,0]
				
				#Find controling bone
				v_boneIndex[0] = -1
				inf = bmesh_data.getVertexInfluences(vert.index)
				for j in xrange(len(inf)) :
					bonename = inf[j][0]
					for b in range(len(supcom_mesh.bones)):
						bone = supcom_mesh.bones[b]
						if bone.name == bonename:
							bone.used = True
							v_boneIndex[0] = b
							break
							
				if (v_boneIndex[0] == -1):
					v_boneIndex[0] = 0
					
					Blender.Window.EditMode(0)
					vert.sel = 1
					
					countLOG("Warning: Verticle without Bone Influence in %s. Selected " % (mesh_name))
				
				v_pos = Vector( vert.co * (MatrixMesh * xy_to_xz_transform))			
				
				v_nor = vert.no * (MatrixMesh * xy_to_xz_transform)
				
				#needed cause supcom scans an image in the opposite vertical direction or something?.
				v_uv1 = Vector(face.uv[i][0], 1.0 - face.uv[i][1])					
			
				vertList.append( scm_vertex( v_pos, v_nor, v_uv1 , v_boneIndex) )				
			
			if len(vertList) > 3:
				newFace = qFace()				
			else:
				newFace = Face()				
				
			newFace.addVert(vertList)
			newFace.addToMesh(supcom_mesh)	

	return supcom_mesh


def make_sca(arm_obj, action):

	global BONES
	global MArmatureWorld
	global xy_to_xz_transform
	
	action.setActive(arm_obj)
	
	scene = Blender.Scene.GetCurrent()
	render_context = scene.getRenderingContext()
	endframe = render_context.endFrame()
	animation = sca_anim()	
	#animation.duration = 1.5
	
	# Add bone names & links
	for bone in BONES:
		animation.bonenames.append(bone.name)
		animation.bonelinks.append(bone.parent_index)
		LOGn('adding bone: %s with parent %d ' % (bone.name, bone.parent_index))
	
	ProgBarAnimation = ProgressBar( "Exp: Anim", endframe) 
	
	# Add frames
	frame_counter = 1
	while frame_counter <= endframe:
		LOGn('adding frame %d of %d' % (frame_counter, endframe))		
		ProgBarAnimation.do()

		frame = sca_frame(animation)
			
		arm_obj.evaluatePose(frame_counter)
		frame_counter += 1
		
		POSED_BONES = {}
		for posebone in arm_obj.getPose().bones.values():
			POSED_BONES[posebone.name] = posebone.poseMatrix
		
		for bone in BONES:
			pose_bone_matrix = POSED_BONES[bone.name]
			
			if (bone.parent_index == -1):
				rel_mat = (Matrix(pose_bone_matrix) * MArmatureWorld) * xy_to_xz_transform
			else:
				rel_mat = Matrix(pose_bone_matrix) * Matrix(POSED_BONES[BONES[bone.parent_index].name]).invert()
			
			rotation = rel_mat.toQuat().normalize()
			#rot = rotation #[ rotation.w, rotation.x, rotation.y, rotation.z ]		
			pos = Vector( rel_mat[3][0], rel_mat[3][1], rel_mat[3][2] )
			
			anim_bone = sca_bone(pos, rotation)
			frame.bones.append(anim_bone)
		
		animation.frames.append(frame)		
	
	return animation

def export(outdir):
	global VERSION, USER_INFO
	global MArmatureWorld
	global xy_to_xz_transform
	
	w_edit_mode = Blender.Window.EditMode()
		
	#No animation export in editmode
	if w_edit_mode :
		Blender.Window.EditMode(0)
		
	Blender.Window.WaitCursor(1)
	
	xy_to_xz_transform.resize4x4()	
	
	scn = Blender.Scene.GetCurrent()
	# Get Selected object(s)
	selected_objects = Blender.Object.GetSelected()
	
	# Look for an armature
	arm = None
	arm_obj = None
	for obj in selected_objects:
		data = obj.getData()
		if type(data) is Blender.Types.ArmatureType:
			arm = data
			arm_obj = obj
			break
	
	# Is there one armature? Take this one
	if arm == None :	
		for obj in scn.objects:
			data = obj.getData()
			if obj.getType() == 'Armature' :
				if arm == None:
					arm = data
					arm_obj = obj
				else :
					arm = None
					break	
						
				
	if arm == None:
		PupMenu("Error: Please select your armature.%t|OK")
		Exit()
		return
		
	# this defines the ARMATURE_SPACE.
	# all bones in the armature are positioned relative to this space.
	MArmatureWorld = Matrix(arm_obj.matrixWorld)	
		
	# SCM
	LOGp(' ')
	LOGp(' ')
	LOGp('Exporting model: ' + arm_obj.name )
	LOGp('----------------------------------')
	mesh = make_scm(arm_obj)
	if mesh == None :
		LOGp('Aborted!')
		return
		
	mesh.info.append('Exported with Blender SupCom-Exporter ' + VERSION)
	if USER_INFO != "" :
		mesh.info.append( USER_INFO )
	mesh.save(outdir + arm_obj.name + '.scm')
	mesh = None

	# SCA
	actions = Blender.Armature.NLA.GetActions().iteritems()
	for action in actions:
		#action[0] = the key,  action[1] = the dictionary
		####maybe this could help?
		LOGp(' ')
		LOGp('Animation: ' + action[0])
		animation = make_sca(arm_obj, action[1])
		animation.save(outdir + action[0] + '.sca')		

	Blender.Window.EditMode(w_edit_mode)	
	Blender.Window.WaitCursor(0)

	LOGp('----------------------------------')
	
	closeLog()

	
	
	
	
	
######################################################
# GUI
######################################################

	
log = []
log_max_lines = 14
LOG_FILE = None

#log to file
def LOGn(message):
	global LOG_FILE
	global LOG_ENABLE
	global LOG_FILENAME
	
	if LOG_ENABLE :
		if LOG_FILE == None :			
			LOG_FILE = open( LOG_FILENAME, 'w')
			LOG_FILE.write('SupCom Exporter LOG File:\n\n')
			
			LOGp( "LOG enabled: %s" % (LOG_FILENAME))
			Log(message + '\n')
			
		else :		
			LOG_FILE.write(message + '\n')
			
#Log to file, to console and exp window					
def LOGp(message):
	global log, log_max_lines
	
	LOGn(message)
	print message	
	log.append(message)
	
	if len(log) > log_max_lines:
		del log[0]
		
		
counter = []
cLog = []
#log for a amount of errors like vertex errors
def countLOG(message):
	global cLog
	global counter
	
	cont = False
	
	for i in xrange(len(cLog)):
		if cLog[i] == message:
			cont = True
			counter[i] +=1
			break
			
	if not cont :
		
		cLog.append( message)
		counter.append( 1)

def closeLog():
	global cLog, LOG_FILE
	global counter
	
	for i in xrange(len(cLog)):
		LOGp("%s (Times:%d)" % (cLog[i], counter[i]))

	if LOG_FILE != None :
		LOG_FILE.close()	
		
	Blender.Window.RedrawAll()
		

class ProgressBar :

	global PROG_BAR_STEP
	global PROG_BAR_ENABLE
	
	progress = 0
	progressold = 0	
	current = 0	
	end = 0
	div = 0
	text = "None"	
	
	def __init__(self, text, end):
		self.progress = 0
		self.progressold = 0
		self.current = 0
		self.end = end
		self.text = text
		self.div = PROG_BAR_STEP
		
		#it looks like blender needs to init this progress bar with 0.0
		if PROG_BAR_ENABLE :
			Blender.Window.DrawProgressBar ( 0.0 , text)
		
	def do(self):	
		
		if PROG_BAR_ENABLE :
			self.current += 1
			self.progress = (self.current*self.div)/self.end
			
			if self.progress != self.progressold :
				self.progressold = self.progress
				Blender.Window.DrawProgressBar ( float(self.progress)/self.div , self.text)
				





# Events
EVENT_NOEVENT = 1
EVENT_DRAW = 2
EVENT_EXIT = 3
EVENT_CLOSE_LOG = 4
EVENT_EXPORTDIR_TEXT = 5
EVENT_EXPORTDIR = 6

export_directory = Blender.Draw.Create("sds")

show_log = 0

def fileselector_callback(filename):
	modelpath, modelfile = os.path.split(filename)
	export_directory.val = modelpath + '/'

def draw():
	global EVENT_NOEVENT, EVENT_DRAW, EVENT_EXIT, EVENT_CLOSE_LOG
	global export_directory, show_log
	global log_max_lines, log
	global VERSION

	# Titles
	glClear(GL_COLOR_BUFFER_BIT)
	
	top = 60 + log_max_lines * 12 + 8
	#top = 500
	top_x = 304
	
	glColor3f(0.8, 0.8, 1)
	glRecti(top_x, top, 4, 4)
	glBegin(GL_LINES)
	glColor3f(0.8, 0.8, 0.8)
	glVertex2d(4, top)
	glVertex2d(4, 4)
	glVertex2d(4, 4)
	glVertex2d(top_x, 4)
	glColor3f(0.5, 0.5, 0.5)
	glVertex2d(top_x, top)
	glVertex2d(4, top)
	glVertex2d(top_x, top)
	glVertex2d(top_x, top-1)
	glEnd()
	
	glColor3f(0, 0, 0)
	glRasterPos2d(10, top-16)
	Text("Supreme Commander Exporter " + VERSION)	
	
	# Show exporting log
	if show_log:

		for index in range(0, len(log)):
			i = (len(log) - 1) - index
			glRasterPos2i(10, 40 + i*12)
			Text(log[index]) #, 'tiny')
		
		
		Button("Close", EVENT_EXIT , 10, 10, 80, 18)
		#Blender.Window.RedrawAll()

		# Exporter GUI
	else:
		Blender.Draw.Button("Browse...", EVENT_EXPORTDIR, 10, 40, 80, 18, "")

		#if export_directory.val == 'sds':
			#automatically launch the browse window once
		#	Blender.Window.FileSelector (fileselector_callback, "Select output DIR")
		#	Blender.Redraw()
			
		export_directory = Blender.Draw.String("", EVENT_EXPORTDIR_TEXT, 100, 40, 200, 18, export_directory.val, 255, "Where to save the exported files")
			
		# Draw and Exit Buttons
		Button("Export", EVENT_DRAW , 10, 10, 80, 18)
		Button("Exit", EVENT_EXIT , 100, 10, 80, 18)

def event(evt, val):	
	if (evt == QKEY and not val): 
		Exit()

def bevent(evt):
	global EVENT_NOEVENT,EVENT_DRAW,EVENT_EXIT
	global export_directory
	global show_log

	if (evt == EVENT_EXIT): 
		show_log = 1
		Exit()
	
	elif (evt == EVENT_EXPORTDIR):
		Blender.Window.FileSelector (fileselector_callback, "Select output DIR")
		#Blender.Redraw()
	
	elif (evt == EVENT_DRAW):
		
		if (export_directory.val == ""):
			return
		
		show_log = 1
		modelpath, modelfile = os.path.split(export_directory.val)
		export(modelpath + '/')
		#changed cause filename was included in the dir selection		 
		#Blender.Redraw()


Register(draw, event, bevent)

#automatically launch the browse window once
Blender.Window.FileSelector (fileselector_callback, "Select output DIR")

最高指挥官2SCM模型格式导入插件(blender版本)
说明:此二代模型导入插件存在问题,需修改才能使用.
SupCom2 Import.py

#!BPY

#**************************************************************************************************
# Supreme Commander Importer for Blender3D - www.blender3d.org
#
# Written by dan - www.sup-com.net
# modified by Gniarf (2010/08)
#
# History
#   0.1.0   06/06/06   - Initial version. 
#   0.2.0   06/06/10   - Added SCA (Animation) support
#   0.3.0   06/07/02   - Alpha release
#   0.4.0   10/08/12   - modification to load Supcom2 sca won't load Supcom 1 sca anymore
#
# Todo
#   - Material/uv map 2
#   - Bone pos/rot for scm & sca. Not perfect to use gentle words.
#   - Make sca loading independent of the scm_mesh. (e.g get bone pos/loc from armature instead)
#   - GUI for loading
#
#**************************************************************************************************

"""
Name: 'SupCom2 Model (.scm)'
Blender: 232
Group: 'Import'
Tooltip: 'Imports Supreme Commander 2 Models (*.scm) and Animations (*.sca)'
"""

import Blender
from Blender import NMesh, Scene, Object

from Blender import Mathutils
from Blender.Mathutils import *

from Blender.BGL import *
from Blender import Draw
from Blender.Draw import *

import os
from os import path


import struct
import string
import math
from math import *


VERSION = '4.5'

######################################################
# User defined behaviour, Select as you need
######################################################

#Enable Progress Bar ( 0 = faster )
PROG_BAR_ENABLE = 1
#how many steps a progress bar has (the lesser the faster)
PROG_BAR_STEP = 25

#LOG File for debuging
#Enable LOG File (0 = Disabled , 1 = Enabled )
LOG_ENABLE = 0
#Filename / Path. Default is blender directory Filename SC-E_LOG.txt
LOG_FILENAME = "SC-E_LOG.txt"

######################################################
# Init Supreme Commander SCM( _bone, _vertex, _mesh), SCA(_bone, _frame, _anim) Layout
######################################################
#xy_to_xz_transform = Matrix([1, 0, 0], [ 0, 0, 1], [ 0, 1, 0])
xy_to_xz_transform = Matrix([1, 0, 0], [ 0, 0, 1], [ 0, -1, 0])


	#	-1	0	0
	#	0	0	1
	#	0	1	0


class scm_bone :
	
	name = ""
	rel_mat = []
	rel_mat_inv = []
	position = []
	rotation = []
	parent = 0
	parent_index = 0
	
	rel_matrix_inv = []
	
	def __init__(self, name):
		self.name = name
		self.rel_mat_inv = Matrix([0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0])
		self.rel_mat = Matrix([0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0])
		self.rotation = Quaternion(0,0,0,0)
		self.position = Vector(0,0,0)

	def load(self, file):
		bonestruct = '16f3f4f4i'
		buffer = file.read(struct.calcsize(bonestruct))
		readout = struct.unpack(bonestruct, buffer)

		#supcom information:
		readRPI = Matrix([0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0])
		#// Inverse transform of the bone relative to the local origin of the mesh
		#// 4x4 Matrix with row major (i.e. D3D default ordering)
		for i in range(4):
			readRPI[i] = readout[i*4:i*4+4]
		
		self.rel_mat_inv = Matrix(readRPI[0], readRPI[1], readRPI[2], readRPI[3])#*xy_to_xz_transform #note rot here changes pointing direction of spikie 
		self.rel_mat = self.rel_mat_inv.invert()

		#self.rel_mat = (Matrix(rest_pose_inv).invert()) 

		#// Position relative to the parent bone.
		pos = readout[16:19]
		self.position = Vector(pos[0], pos[1], pos[2])
		
		#// Rotation relative to the parent bone.
		rot = readout[19:23]
		self.rotation = Quaternion( rot[0], rot[1], rot[2], rot[3] )

		#// Index of the bone's parent in the SCM_BoneData array
		self.parent_index = readout[24]

		
		return self
	
	def dump(self):
		print 'Bone       ', self.name
		print 'Position   ', self.position
		print 'Rotation   ', self.rotation
		print 'Parent Idx ', self.parent_index		
		if (self.parent != 0): 
			print 'Parent     ', self.parent.name
		else:
			print 'Parent     <NONE>'		
		print 'Rest Pose Inv.'
		#for row in range(4):
			#print '  ', self.rest_pose_inv[row]		


class scm_vertex :
	
	position = []
	tangent = []
	normal = []
	binormal = []
	uv1 = []
	uv2 = []
	bone_index = []

	def __init__(self):
		self.position 	= Vector(0,0,0)
		self.tangent 	= Vector(0,0,0)
		self.normal 	= Vector(0,0,0)
		self.binormal 	= Vector(0,0,0)
		self.uv1 	= Vector(0,0)
		self.uv2 	= Vector(0,0)
		self.bone_index = [0]*4

	def load(self, file):
		
		vertstruct = '3f3f3f3f2f2f4B'
		vertsize = struct.calcsize(vertstruct)
		
		
		buffer = file.read(vertsize)
		vertex = struct.unpack(vertstruct, buffer)
		
		self.position = vertex[0:3]
		self.tangent = vertex[3:6]
		self.normal = vertex[6:9]
		self.binormal = vertex[9:12]
		self.uv1 = vertex[12:14]
		self.uv2 = vertex[14:16]
		self.bone_index = vertex[16:20]
		return self
	
	def dump(self):
		print 'position ', self.position
		print 'tangent  ', self.tangent
		print 'normal   ', self.normal
		print 'binormal ', self.binormal
		print 'uv1      ', self.uv1
		print 'uv2      ', self.uv2
		print 'bones    ', self.bone_index


class scm_mesh :
	
	bones = []
	vertices = []
	faces = []
	info = []
	filename = ""
	
	def __init__(self):
		self.bones = []
		self.vertices = []
		self.faces = []
		self.info = []
		self.filename = ""

	def load(self, filename):	
		global xy_to_xz_transform
		self.filename = filename
		scm = file(filename, 'rb')
		
		# Read header
		headerstruct = '4s11L'
		buffer = scm.read(struct.calcsize(headerstruct))
		header = struct.unpack(headerstruct, buffer)
		
		marker = header[0]
		version = header[1]
		boneoffset = header[2]
		bonecount = header[3]
		vertoffset = header[4]
		extravertoffset = header[5]
		vertcount = header[6]
		indexoffset = header[7]
		indexcount = header[8]
		tricount = indexcount / 3 #?
		infooffset = header[9]
		infocount = header[10]
		totalbonecount = header[11]
		
		if (marker != 'MODL'):
			print 'Not a valid scm'
			return
			
		#if (version != 5):
		#	print 'Unsupported version (%d)' % version
		#	return
		
		# Read bone names
		scm.seek(pad(scm.tell()), 1)
		length = (boneoffset - 4) - scm.tell()

		# This should probably be handeled by the scm_bone reader as it contains the nameoffset. But I'm lazy
		# and logic tells me it's written in the same order as the bones.
		buffer = scm.read(length)		
		rawnames = struct.unpack(str(length)+'s',buffer)		
		bonenames = string.split(rawnames[0],'\0')[:-1]
		
		# Read bones
		scm.seek(boneoffset, 0)
		for b in range(0, totalbonecount):
			bone = scm_bone(bonenames[b])
			bone.load(scm) 			
			self.bones.append(bone)
				
		#show them (for debug)				
		#for b in range(0, totalbonecount):
			#print "bone %d has %d children = " %(b, self.bones[b].numchildren)
		
		# Set parent (this could probably be done in the other loop since parents are usually written to the file
		# before the children. But you never know..
		for bone in self.bones:
			if (bone.parent_index != -1):
				bone.parent = self.bones[bone.parent_index]
			else:
				bone.parent = 0
		
			# the bone matrix relative to the parent.
			if (bone.parent != 0):
				mrel = (bone.rel_mat) * Matrix(bone.parent.rel_mat).invert() #* xy_to_xz_transform
				bone.rel_matrix_inv = Matrix(mrel).invert()
			else:
				mrel = bone.rel_mat * xy_to_xz_transform  #there is no parent
				bone.rel_matrix_inv = Matrix(mrel).invert()
						
		
		# Read vertices
		scm.seek(vertoffset, 0)
		for b in range(0, vertcount):
			vert = scm_vertex()
			vert.load(scm)			
			self.vertices.append(vert)
		
		# Read extra vertex data		
		# Not implemented in Sup Com 1.0!
		
		# Read indices (triangles)
		tristruct = '3h'
		trisize = struct.calcsize(tristruct)
			
		scm.seek(indexoffset, 0)			
		for t in range(tricount):			
			buffer = scm.read(trisize)
			face = struct.unpack(tristruct, buffer)
			self.faces.append(list(face))
			
		
		# Read info
		if (infocount > 0):
			scm.seek(infooffset)
			buffer = scm.read()
			rawinfo = struct.unpack(str(len(buffer))+'s',buffer)		
			self.info = string.split(rawinfo[0],'\0')[:-1]
		
		scm.close()	
		
		return self

	def dump(self):
		print ''
		print 'Filename: ', self.filename
		print 'Bones ', len(self.bones)
		print 'Verts ', len(self.vertices)
		print 'Faces ', len(self.faces)		
		print ''
		print 'INFO: '
		for info in self.info:
			print '  ', info
		


class sca_bone:

	position = []
	rotation = []
	#changed: rototation -> rotation
	pose_pos = []
	pose_rot = []

	def __init__(self, pos, rot):
		self.position = pos
		self.rotation = rot


	def dump(self):
		print 'Position ', self.position
		print 'Rotation ', self.rotation
		


class sca_frame:

	keytime = 0.0
	bones = []
	anim = None
	
	def __init__(self, anim):
		self.keytime = 0.0
		self.bones = []
		self.anim = anim

	def load(self, file):		
		frameheader_fmt = 'f'
		frameheader_size = struct.calcsize(frameheader_fmt)
		buffer = file.read(frameheader_size)
		
		(self.keytime,) = struct.unpack(frameheader_fmt, buffer)
		
		posrot_fmt = '3f4f'
		posrot_size = struct.calcsize(posrot_fmt)
		
		buffer = file.read(posrot_size)
		posrot = struct.unpack(posrot_fmt, buffer)			
		bone = sca_bone([posrot[4],posrot[5],posrot[6]], [posrot[0],posrot[1],posrot[2],posrot[3]])
		self.bones.append(bone)

	def dump(self):
		print 'Time  ', self.keytime
		


class sca_anim :
	
	filename = ""
	frames = [[]]
	bones = []
	bonelinks = []
	bonenames = []
	numbones = 0
	duration = 0.0
	numframes = 0
	
	def __init__(self):
		self.filename = ""
		self.frames=list()
		self.bones = []
		self.numbones = 0
		self.bonelinks = []
		self.bonenames = list()
		self.duration = 0.0

	def read_bone_name(self,sca):
		bone_name=""
		letter=""
		while letter!='\0':
			bone_name=bone_name+letter
			letter=sca.read(1)
		return bone_name

	def load(self, filename):
		self.filename = filename
		sca = file(filename, 'rb')
		
		# Read header
		headerstruct = '4sllflllll'
		buffer = sca.read(struct.calcsize(headerstruct))
		header = struct.unpack(headerstruct, buffer)
		
		
		(magic,             \
		 version,           \
		 self.numframes,         \
		 self.duration,     \
		 self.numbones,     \
		 namesoffset,       \
		 linksoffset,       \
		 animoffset,        \
		 framesize) = struct.unpack(headerstruct, buffer)
		 
		#namesoffset,linksoffset,animoffset are NOT always
		#reliable, so use data alignment instead
		
		if (magic != 'ANIM'):
			print 'Not a valid .sca animation file'
			return
		
		#if (version != 5):
		#	print 'Unsupported sca version: %d'  % version
		#	return
		
		#Read bone names
		# go to next section (names)
		token_found=0
		while (token_found==0):
			sca.seek(28-(sca.tell()%32), os.SEEK_CUR )
			magic = sca.read(4)
			if (magic=='NAME'):
				token_found=1
			else:
				sca.seek(8, os.SEEK_CUR )
				
		
		for i in range(0,self.numbones):
			self.bonenames.append(self.read_bone_name(sca))
			print self.bonenames[i]
		
		# Read links
		token_found=0
		while (token_found==0):
			sca.seek(28-(sca.tell()%32), os.SEEK_CUR )
			magic = sca.read(4)
			if (magic=='LINK'):
				token_found=1
			else:
				sca.seek(8, os.SEEK_CUR )
		links_fmt = str(self.numbones)+'l'
		links_size = struct.calcsize(links_fmt)
		buffer = sca.read(links_size)
		self.bonelinks = struct.unpack(links_fmt, buffer)
			
		posrot_fmt = '3f4f'
		posrot_size = struct.calcsize(posrot_fmt)
		
		# go to next section (data)
		#sounds like data is 0x40 aligned
		token_found=0
		while (token_found==0):
			sca.seek(28-(sca.tell()%32), os.SEEK_CUR )
			magic = sca.read(4)
			if (magic=='DATA'):
				token_found=1
			else:
				sca.seek(8, os.SEEK_CUR )
		buffer = sca.read(posrot_size)
		root_posrot = struct.unpack(posrot_fmt, buffer)
		for f in range(0, self.numbones) :
			buffer = sca.read(4)
			(frames_for_this_bone,) = struct.unpack('l', buffer)
			frame_list_for_this_bone=list()
			for i in range(0, frames_for_this_bone):
				frame_list_for_this_bone.append(sca_frame(self))
				frame_list_for_this_bone[i].load(sca)
			self.frames.append(frame_list_for_this_bone)
		sca.close()
		
		return self

	def dump(self):
		print 'SCA:  ', self.filename
		print 'Duration: %fs' % self.duration
		print 'Num loaded frames ', len(self.frames)
		
		print 'Bonelinks'		
		for link in self.bonelinks:
			print ' ', link
		
		print 'Bone names'
		for name in self.bonenames:
			print ' ', name
	
				
def pad(size):
	val = 32 - (size % 32)
	
	if (val > 31):
		return 0
		
	return val
			
			
#**************************************************************************************************
# Blender Interface
#**************************************************************************************************
def GetLeftRightBone(BoneNames,bone):
	if BoneNames[bone].find("Left") != -1:
		return BoneNames[bone].replace("Left","Right",1)
	elif BoneNames[bone].find("Right") != -1:
		return BoneNames[bone].replace("Right","Left",1)
	return BoneNames[bone]


def read_scm() :
	global xy_to_xz_transform
	global scm_filepath # [0] both [1] path [2] name
	global sca_filepath # [0] both [1] path [2] name
	
	print "=== LOADING Sup Com Model ==="
	print ""
	
	xy_to_xz_transform.resize4x4()
	
	scene = Blender.Scene.GetCurrent() 	
	
	mesh = scm_mesh()
	
	if (mesh.load(scm_filepath[0]) == None):
		print 'Failed to load %s' %scm_filepath[2]
		return
	
	ProgBarLSCM = ProgressBar( "Imp: load SCM", (2*len(mesh.vertices) + len(mesh.faces)))	
	
	armature_name = string.rstrip(scm_filepath[2], ".scm")
	print "armature ", armature_name
	
	###		CREATE ARMATURE 
	armObj = Object.New('Armature', armature_name)	#bad 
	
	armData = Blender.Armature.Armature(armature_name) 
	armData.drawAxes = True 
	armData.makeEditable()	
	 
	for index in range(len(mesh.bones)):
		bone = mesh.bones[index]	
		blender_bone = Blender.Armature.Editbone()		
		#not nice parent may not exist,  but usualy should exist (depends on storing in scm)
		if (bone.parent != 0) :
			blender_bone.parent = armData.bones[bone.parent.name]
		

		#blender_bone.options(Armature.CONNECTED)
		blender_bone.matrix = Matrix(bone.rel_mat * xy_to_xz_transform)		
				
		armData.bones[bone.name] = blender_bone

	
	#save changes (after linking!)
	armObj.link(armData)
	scene.link(armObj)
	#scene.objects.new(armData)
	###		CREATE MESH
	meshData = Blender.Mesh.New('Mesh')
	
	ProgBarLSCM.text = "IMP: Verticles"
	#add verts
	vertlist = []
	for vert in mesh.vertices:
		ProgBarLSCM.do()
		vertlist.append(Vector(vert.position)*xy_to_xz_transform)
	
	meshData.verts.extend(vertlist)

	meshData.vertexUV = True
	for i_v in xrange(len(meshData.verts)):
		uv1 = mesh.vertices[i_v].uv1
		meshData.verts[i_v].uvco = Vector(uv1[0], 1.0-uv1[1])
	
	ProgBarLSCM.text = "IMP: Faces"
	
	#reverse faces
	#for face in mesh.faces:
	#	face.reverse()
		
	#add faces
	meshData.faces.extend(mesh.faces)	
	meshData.faceUV = True	
	for face in meshData.faces :
		ProgBarLSCM.do()
		face.uv = [face.verts[i].uvco for i in range(3)]
	
	#put in scene
	mesh_obj = Object.New('Mesh','Mesh')
	mesh_obj.link(meshData)
	
	#scene.objects.new(meshData)

	#assigns vertex groups #mesh must be in object
	for bone in mesh.bones:
		meshData.addVertGroup(bone.name)

	for vertex_index in range(len(mesh.vertices)):
		ProgBarLSCM.do()
		vertex = mesh.vertices[vertex_index]
		bone_index = vertex.bone_index[0]
		meshData.assignVertsToGroup(mesh.bones[bone_index].name, [vertex_index], 1.0, Blender.Mesh.AssignModes.REPLACE) #'REPLACE'
	
	# it works... but you've to select/change to edit mode for the object to show up.
	# dunno why that is, maybe because Nmesh is depracated.
	meshData.update()
		
	#bones disapear wen update ?
	#armData.update()

	scene.link(mesh_obj)
	
	#must be in scene
	#armObj = None 
	#meshObj = None
	#for obj in Object.Get():
	#	if obj.getType() == 'Armature':
	#		armObj = obj
	#	if obj.getType() == 'Mesh':
	#		meshObj = obj
			
	#mesh_obj = Blender.Object.Get('Mesh')
	armObj.makeParentDeform([mesh_obj], 0, 0)
	#makeParentVertex(objects, indices, noninverse=0, fast=0)
	
	meshData.update()
	armData.update() 

	if len(mesh.info):
		print "=== INFO ==="
		for info in mesh.info:
			print "",info

	print "=== COMPLETE ==="
	
	if sca_filepath[0] != "" :
		read_anim(mesh)

	


def read_anim(mesh):
	global sca_filepath # [0] both [1] path [2] name

	
	print "=== LOADING Sup Com Animation ==="
	print ""
	
	anim = sca_anim()
	anim.load(sca_filepath[0])
	
	ProgBarLSCA = ProgressBar( "Imp: Frames", len(anim.frames))
	
	scene = Blender.Scene.GetCurrent()
	
	arm_obj = None
	for obj in scene.objects:
		data = obj.getData()
		if type(data) is Blender.Types.ArmatureType:
			arm_obj = obj
			break
		
	if arm_obj == None:
		print "couldn't apply animation, no armature in the scene" 
		return

	print arm_obj.name
	
	action = Blender.Armature.NLA.NewAction(sca_filepath[2])
	action.setActive(arm_obj)
	
	pose = arm_obj.getPose()
	
	for b in range(0,anim.numbones):
		ProgBarLSCA.do()
		for frame_index in range(0,len(anim.frames[b]) ):
			
			frame = anim.frames[b][frame_index]
			#Blender.Set("curframe",1+frame_index)
			
			pose_bone = pose.bones[anim.bonenames[b]]
			
			if (pose_bone == None):
				print 'Frame %d - Bone \"%s\" not found' % (frame_index, anim.bonenames[b])
				continue	

			anim_bone = frame.bones[0]
			pose_bone.loc = Vector(anim_bone.position)-Vector(mesh.bones[b].position)
			pose_bone.quat = DifferenceQuats(Quaternion(mesh.bones[b].rotation), Quaternion(anim_bone.rotation))
			#switch x and -y
			temp=pose_bone.loc.x
			pose_bone.loc.x=pose_bone.loc.y
			pose_bone.loc.y=temp
			# print anim.bonenames[b]
			# print " loc x=" +str(pose_bone.loc.x) +" y="+str(pose_bone.loc.y)+" z="+str(pose_bone.loc.z)
			# print " rot x=" +str(pose_bone.quat.x) +" y="+str(pose_bone.quat.y)+" z="+str(pose_bone.quat.z)+" w="+str(pose_bone.quat.w)
			


			pose_bone.size = Vector(1,1,1)
					
			pose.update()
			pose_bone.insertKey(arm_obj, 1+int(anim.numframes*frame.keytime/anim.duration), [Blender.Object.Pose.LOC, Blender.Object.Pose.ROT, Blender.Object.Pose.SIZE])
			pose.update()
	
	
	Blender.Set("curframe", 1)
	
	#scene = Blender.Scene.GetCurrent()
	context = scene.getRenderingContext()

	context.endFrame(anim.numframes)
	
	print "=== COMPLETE ==="



######################################################
# GUI
######################################################
	
log = []
log_max_lines = 14
LOG_FILE = None

#log to file
def LOGn(message):
	global LOG_FILE
	global LOG_ENABLE
	global LOG_FILENAME
	
	if LOG_ENABLE :
		if LOG_FILE == None :			
			LOG_FILE = open( LOG_FILENAME, 'w')
			LOG_FILE.write('SupCom Exporter LOG File:\n\n')
			
			LOGp( "LOG enabled: %s" % (LOG_FILENAME))
			Log(message + '\n')
			
		else :		
			LOG_FILE.write(message + '\n')
			
#Log to file, to console and exp window					
def LOGp(message):
	global log, log_max_lines
	
	LOGn(message)
	print message	
	log.append(message)
	
	if len(log) > log_max_lines:
		del log[0]
		
		
counter = []
cLog = []
#log for a amount of errors like vertex errors
def countLOG(message):
	global cLog
	global counter
	
	cont = False
	
	for i in xrange(len(cLog)):
		if cLog[i] == message:
			cont = True
			counter[i] +=1
			break
			
	if not cont :
		
		cLog.append( message)
		counter.append( 1)

def closeLog():
	global cLog, LOG_FILE
	global counter
	
	for i in xrange(len(cLog)):
		LOGp("%s (Times:%d)" % (cLog[i], counter[i]))

	if LOG_FILE != None :
		LOG_FILE.close()	
		
	Blender.Window.RedrawAll()
		

class ProgressBar :

	global PROG_BAR_STEP
	global PROG_BAR_ENABLE
	
	progress = 0
	progressold = 0	
	current = 0	
	end = 0
	div = 0
	text = "None"	
	
	def __init__(self, text, end):
		self.progress = 0
		self.progressold = 0
		self.current = 0
		self.end = end
		self.text = text
		self.div = PROG_BAR_STEP
		
		#it looks like blender needs to init this progress bar with 0.0
		if PROG_BAR_ENABLE :
			Blender.Window.DrawProgressBar ( 0.0 , text)
		
	def do(self):	
		
		if PROG_BAR_ENABLE :
			self.current += 1
			self.progress = (self.current*self.div)/self.end
			
			if self.progress != self.progressold :
				self.progressold = self.progress
				Blender.Window.DrawProgressBar ( float(self.progress)/self.div , self.text)
				



# Events
EVENT_DRAW = 1
EVENT_EXIT = 2
EVENT_SCMDIR = 3
EVENT_SCADIR = 4


show_log = 0

sca_filepath = [ "", "", "None"]
scm_filepath = [ "", "", "None"]

def anim_fileselector_callback(filename):
	global sca_filepath
	
	sca_filepath[0] = filename
	
	#print "Filename%sI\n" % filename
	#if ( filename != "" ) :
		#sca_filepath = [ "" , "None"]
		#return
	#else :
	
	length = len(filename)
	if filename[length-4:length] == ".sca" :		
		sca_filepath[1], sca_filepath[2]  = os.path.split(filename)
	else :
		sca_filepath[0] = ""
		sca_filepath[1] = ""
		sca_filepath[2] = "Non Supported"


def fileselector_callback(filename):
	global scm_filepath
	#sadly you cant check whether the cancel button is pressed
	scm_filepath[0] = filename
	length = len(filename)
	if filename[length-4:length] == ".scm" :		
		scm_filepath[1], scm_filepath[2]  = os.path.split(filename)
	else :
		scm_filepath[0] = ""
		scm_filepath[1] = ""
		scm_filepath[2] = "Non Supported"

	
def draw():
	global EVENT_DRAW, EVENT_EXIT,EVENT_SCADIR,EVENT_SCADIR
	global scm_filepath, sca_filepath, show_log
	global log_max_lines, log
	global VERSION

	# Titles
	glClear(GL_COLOR_BUFFER_BIT)
	
	top = 60 + log_max_lines * 12 + 8
	#top = 500
	top_x = 304
	
	glColor3f(0.8, 0.8, 1)
	glRecti(top_x, top, 4, 4)
	glBegin(GL_LINES)
	glColor3f(0.8, 0.8, 0.8)
	glVertex2d(4, top)
	glVertex2d(4, 4)
	glVertex2d(4, 4)
	glVertex2d(top_x, 4)
	glColor3f(0.5, 0.5, 0.5)
	glVertex2d(top_x, top)
	glVertex2d(4, top)
	glVertex2d(top_x, top)
	glVertex2d(top_x, top-1)
	glEnd()
	
	glColor3f(0, 0, 0)
	glRasterPos2d(10, top-16)
	Text("Supreme Commander2 Importer " + VERSION)	

	# Show exporting log
	if show_log:

		for index in range(0, len(log)):
			i = (len(log) - 1) - index
			glRasterPos2i(10, 40 + i*12)
			Text(log[index]) #, 'tiny')
		
		
		Button("Close", EVENT_EXIT , 10, 10, 80, 18)

		# Exporter GUI
	else:
		
		Blender.Draw.Button("Browse SCM", EVENT_SCMDIR, 10, 45, 80, 18, "Select scm")
		Blender.Draw.Button("Browse SCA", EVENT_SCADIR, 10, 80, 80, 18, "Select sca")	
		
		glRasterPos2d(100, 47)
		if scm_filepath[0] == "" :
			Text("SCM: " + scm_filepath[2])
		else:
			Text(":" + scm_filepath[2])
			
		glRasterPos2d(100, 82)
		if sca_filepath[0] == "" :
			Text("SCA: " + sca_filepath[2])
		else : 
			Text(":" + sca_filepath[2])

		# Draw and Exit Buttons
		Button("Import", EVENT_DRAW , 10, 10, 80, 18)
		Button("Exit", EVENT_EXIT , 100, 10, 80, 18)

def event(evt, val):	
	if (evt == QKEY and not val): 
		Exit()

def bevent(evt):
	global EVENT_DRAW,EVENT_EXIT
	global scm_filepath
	global show_log

	if (evt == EVENT_EXIT): 
		#show_log = 1
		Exit()
	
	elif (evt == EVENT_SCMDIR):
		Blender.Window.FileSelector (fileselector_callback, "Select SCM File")
		Blender.Redraw()
		
	elif (evt == EVENT_SCADIR):
		if scm_filepath[0] != "" :			
			Blender.Window.FileSelector (anim_fileselector_callback, "Select SCA File", scm_filepath[1]+"\\")			
		else :
			Blender.Window.FileSelector (anim_fileselector_callback, "Select SCA File")			
		Blender.Redraw()
	
	elif (evt == EVENT_DRAW):
		
		if (scm_filepath[0] != ''):		
			show_log = 1			
			read_scm() 
		
		Blender.Redraw()

		

Register(draw, event, bevent)

#open fileselector automaticly
Blender.Window.FileSelector (fileselector_callback, "Select SCM File")