#
# Torque Game Engine DTS exporter for blender
#
import Blender
import string
#from Blender import NMesh,Object
from math import *

VERSION = "0.1"

##############################
# Vector class
##############################
class Vector:
	members = [0, 0, 0]
	def __init__(self, x, y, z):
		self.members = [float(x), float(y), float(z)]
	def __getitem__(self, key):
		return(self.members[key])
	def __setitem__(self, key, value):
		self.members[key] = value
	def __add__(self, other):
		# iterate through the members
		for i in range(len(self.members)):
			# add them together
			self[i] = float(self[i]) + float(other[i])
		return(self)
	def __sub__(self, other):
		# iterate through the members
		for i in range(len(self.members)):
			# subtract them
			self[i] = float(self[i]) - float(other[i])
		return(self)
	def __mul__(self, other):
		# iterate through the members
		for i in range(len(self.members)):
			# multiply by the val stored in other
			self[i] *= float(other)
		return(self)
	def __div__(self, other):
		# iterate through the members
		for i in range(len(self.members)):
			# divide by the val stored in other
			self[i] /= float(other)
		return(self)
	#def normalize(self):
##############################
# end of Vector class
##############################
##############################
# Node class - DTS tree node
##############################
class Node:
	name = 0				# index of its name in the DTS string table
	parent = -1			# number of the parent node; -1 if root
	firstObject = -1	# deprecated; set to -1
	child = -1			# deprecated; set to -1
	sibling = -1		# deprecated; set to -1
##############################
# end of Node class
##############################
##############################
# Object class - DTS object
##############################
class Object:
	name = 0				# index of its name in the DTS string table
	numMeshes = 0		# number of meshes (only one for detail level)
	firstMesh = 0		# number of the first mesh (meshes must be consecutive)
	node = 0				# number of the node where the object is stored
	sibling = -1		# deprecated; set to -1
	firstDecal = -1	# deprecated; set to -1
##############################
# end of Object class
##############################
##############################
# Material class - DTS material
##############################
class Material:
	name = 0				# texture name; materials don't use the DTS string table
	flags = 0			# boolean properties
	reflectance = 0	# number of reflectance map?
	bump = 0				# number of bump map? or -1 if none
	detail = 0			# number of detail map? or -1 if none
	detailScale = 0	# ?
	reflection = 0		# ?
	# material flags
	SWrap             = 0x00000001
	TWrap             = 0x00000002
	Translucent       = 0x00000004
	Additive          = 0x00000008
	Subtractive       = 0x00000010
	SelfIlluminating  = 0x00000020
	NeverEnvMap       = 0x00000040
	NoMipMap          = 0x00000080
	MipMapZeroBorder  = 0x00000100
	IFLMaterial       = 0x00000000
	IFLFrame          = 0x10000000
	DetailMap         = 0x20000000
	BumpMap           = 0x40000000
	ReflectanceMap    = 0x80000000
	AuxiliaryMask     = 0xF0000000
##############################
# end of Material class
##############################
##############################
# IFLMaterial class - DTS animated material
##############################
class IFLMaterial:
	name = 0
	slot = 0
	firstFrame = 0
	time = 0
	numFrames = 0
##############################
# end of IFLMaterial class
##############################
##############################
# DetailLevel class - DTS detail level
##############################
class DetailLevel:
	name = 0				# index of the name in the DTS string table
	subshape = 0		# number of the subshape it belongs to
	objectDetail = 0	# number of object mesh to draw for each object
	size = 0				# minimum pixel size
	avgError = -1		# ?
	maxError = -1		# ?
	polyCount = 0		# polygon count of the meshes to draw
##############################
# end of DetailLevel class
##############################
##############################
# Subshape class - DTS subshape (defines a range of nodes and objects)
##############################
class Subshape:
	firstNode = 0
	firstObject = 0
	firstDecal = 0
	numNodes = 0
	numObjects = 0
	numDecals = 0
	firstTranslucent = 0	# not used?
##############################
# end of Subshape class
##############################
##############################
# ObjectState class - related to animated materials?
##############################
class ObjectState:
	vis = 0
	frame = 0
	matFrame = 0
##############################
# end of ObjectState class
##############################
##############################
# Trigger class
##############################
class Trigger:
	state = 0
	pos = 0
##############################
# end of Trigger class
##############################
##############################
# Sequence class
##############################
	nameIndex = 0
	flags = 0
	numKeyFrames = 0
	duration = 0
	priority = 0
	firstGroundFrame = 0
	numGroundFrames = 0
	baseRotation = 0
	baseTranslation = 0
	baseScale = 0
	baseObjectState = 0
	baseDecalState = 0
	firstTrigger = 0
	numTriggers = 0
	toolBegin = 0
	matters = 0
	# flags
	UniformScale    = 0x0001
	AlignedScale    = 0x0002
	ArbitraryScale  = 0x0004
	Blend           = 0x0008
	Cyclic          = 0x0010
	MakePath        = 0x0020
	IFLInit         = 0x0040
	HasTranslucency = 0x0080
##############################
# end of Sequence class
##############################
##############################
# Matters class
##############################
class Matters:
	rotation = 0
	translation = 0
	scale = 0
	decal = 0
	ifl = 0
	vis = 0
	frame = 0
	matframe = 0
##############################
# end of Matters class
##############################
##############################
# Box class
##############################
class Box:
	min = Vector
	max = Vector
##############################
# end of Box class
##############################
##############################
# Primitive class
##############################
class Primitive:
	firstElement = 0
	numElements = 0
	type = 0
	# types
	Triangles    = 0x00000000
	Strip        = 0x40000000
	Fan          = 0x80000000		# may not be supported in the engine?
	TypeMask     = 0xC0000000
	Indexed      = 0x20000000		# not supported in the engine?
	NoMaterial   = 0x10000000
	MaterialMask = 0x0FFFFFFF
##############################
# end of Primitive class
##############################
##############################
# Primitive class
##############################
class Mesh:
	# members
	type = 0
	numFrames = 0
	matFRames = 0
	parent = 0

	verts = []
	tverts = []
	normals = []
	enormals = []
	primitives = []
	indices = []
	mindices = []
	### BOX ###
	bounds = Box
	### BOX ###
	center = Vector(0, 0, 0)
	radius = 0
	vertsPerFrame = 0
	flags = 0
	# data used by skin meshes
	vindex = 0
	vbone = 0
	vweight = 0
	nodeIndex = 0
	nodeTransform = 0

	# types
	T_Standard  = 0
	T_Skin      = 1
	T_Decal     = 2
	T_Sorted    = 3
	T_Null      = 4
	# flags
	Billboard      = 0x80000000
	HasDetail      = 0x40000000
	BillboardZ     = 0x20000000
	EncodedNormals = 0x10000000

	def __init__(self, t):
		self.bounds.min = Vector(0, 0, 0)
		self.bounds.max = Vector(0, 0, 0)
		self.center = Vector(0, 0, 0)
		self.radius = float(0.0)
		self.numFrames = 1
		self.matFrames = 1
		self.vertsPerFrame = 0
		self.parent = -1
		self.flags = 0
		self.type = t
	def getType(self):
		return(type)
	def setType(self, t):
		type = t
	def setFlag(self, f):
		flags |= f
	def getPolyCount(self):
		count = 0
		for p in range(len(primitives)):
			if (primitives[p].type & Primitive.Stripe):
				count += primitives[p].numElements - 2
			else:
				count += primitives[p].numElements / 3
		return(count)
	def getRadius(self):
		return(radius)
	def getCenter(self):
		return(center)
	def getBounds(self):
		return(bounds)
	def setMaterial(self, n):
		for p in range(len(primitives)):
			p.type = (p.type & ~Primitive.MaterialMask) | (n & Primitive.MaterialMask)
	def getVertexBone(self, node):
		b = 0
		for b in range(len(nodeIndex)):
			if (nodeIndex[b] == node):
				return(b)
	def getNodeIndexCount(self):
		return(len(nodeIndex))
	def getNodeIndex(self, node):
		if node >= 0:
			if node < len((nodeIndex)):
				return(nodeIndex[node])
		return(-1)
	#def setNodeTransform(self, node, t, q):
	def setCenter(self, c):
		center = c
	def setBounds(self, b):
		bounds = b
	def setRadius(self, r):
		radius = r
	def setFrames(self, n):
		self.numFrames = n
		self.vertsPerFrame = len(self.verts)/n
	def setParent(self, n):
		parent = n
	def calculateBounds(self):
		self.bounds.max = Vector(-10e30, -10e30, -10e30)
		self.bounds.min = Vector(10e30, 10e30, 10e30)
		for vertex in self.verts:
			if vertex[0] < self.bounds.min[0]:
				self.bounds.min[0] = vertex[0]
			if vertex[1] < self.bounds.min[1]:
				self.bounds.min[1] = vertex[1]
			if vertex[2] < self.bounds.min[2]:
				self.bounds.min[2] = vertex[2]
			if vertex[0] > self.bounds.max[0]:
				self.bounds.max[0] = vertex[0]
			if vertex[1] > self.bounds.max[1]:
				self.bounds.max[1] = vertex[1]
			if vertex[2] > self.bounds.max[2]:
				self.bounds.max[2] = vertex[2]
	def calculateCenter(self):
		for v in range(len(self.bounds.max.members)):
			self.center[v] = ((self.bounds.min.members[v] - self.bounds.max.members[v])/2) + self.bounds.max.members[v]
	def calculateRadius(self):
		for vertex in self.verts:
			tV = vertex - self.center
			result = 0
			for n in range(len(tV.members)):
				result += tV.members[n] * tV.members[n]
			distance = sqrt(result)
			if distance > self.radius:
				self.radius = distance
	def encodeNormal(self, p):
		bestIndex = 0
	#def save(self):
##############################
# end of Mesh class
##############################
##############################
# Shape class
##############################
class Shape:
	nodes = 0
	objects = 0
	decals = 0
	subshapes = 0
	IFLmaterials = 0
	materials = 0
	nodeDefRotations = 0
	nodeDefTranslations = 0

	#def save(self):
	#def read(self):
	#def getBounds(self):
	#def getRadius(self):
	#def getTubeRadius(self):
	#def addName(self, s):
	#def calculateBounds(self):
	#def calculateRadius(self):
	#def calculateTubeRadius(self):
	#def calculateCenter(self):
	#def setSmallestSize(self, i):
	#def setCenter(self, p):
	#def getNodeWorldPosRot(self, n, trans, rot):
	#def write(self):
##############################
# end of Shape class
##############################

# functions
# strips the path off of a filepath specified.
def basename(filepath):
	if "\\" in filepath:
		words = string.split(filepath, "\\")
	else:
		words = string.split(filepath, "/")
	words = string.split(words[-1], ".")
	return string.join(words[:-1], ".")

def process_objects(objnames):
	# objects
	dtsobjs = []
	numdtsobjs = 0
	print "Objects:"
	for objname in objnames:
		if not type(objname.data) == Blender.Types.NMeshType:
			continue
		print "--> ", objname.data.name
		print "location = ", objname.getLocation()
		meshobj = Blender.NMesh.GetRaw(objname.data.name)
		dtsobj = Mesh(Mesh.T_Standard)
		dtsobj.numFrames = 1
		dtsobj.matFrames = 1
		dtsobj.verts = []
		dtsobj.tverts = []
		dtsobj.normals = []
		dtsobj.enormals = []
		# get verts, uv coords, normals, and enormals
		for v in range(len(meshobj.verts)):
			###print "v = ", v, "x = ", meshobj.verts[v].co[0], ", y = ", meshobj.verts[v].co[1], ", z = ", meshobj.verts[v].co[2]
			# verts
			dtsobj.verts.append(Vector(meshobj.verts[v].co[0], meshobj.verts[v].co[1], meshobj.verts[v].co[2]))

			# uv coords
			dtsobj.tverts.append(meshobj.verts[v].uvco[0])
			dtsobj.tverts.append(meshobj.verts[v].uvco[1])

			# normals
			dtsobj.normals.append(meshobj.verts[v].no[0])
			dtsobj.normals.append(meshobj.verts[v].no[1])
			dtsobj.normals.append(meshobj.verts[v].no[2])

			# enormals
			dtsobj.enormals.append(meshobj.verts[v].no[0])
			dtsobj.enormals.append(meshobj.verts[v].no[1])
			dtsobj.enormals.append(meshobj.verts[v].no[2])
		for f in meshobj.faces:
			print "vIx = ", meshobj.verts.index(f.v[0]), "vIy = ", meshobj.verts.index(f.v[1]), "vIz = ", meshobj.verts.index(f.v[2])
			dtsobj.indices.append(meshobj.verts.index(f.v[2]))
			dtsobj.indices.append(meshobj.verts.index(f.v[1]))
			dtsobj.indices.append(meshobj.verts.index(f.v[0]))

		# create our primitive
		p = Primitive
		p.firstElement = len(dtsobj.indices)
		p.numElements = 3
		p.type = Primitive.Strip | Primitive.Indexed
		# no materials for now
		p.type |= Primitive.NoMaterial

		dtsobj.setFrames(1)
		dtsobj.setParent(-1)
		dtsobj.calculateBounds()
		dtsobj.calculateCenter()
		dtsobj.calculateRadius()
		dtsobj.vertsPerFrame = len(dtsobj.verts)

		dtsobj.primitives.append(p)
		dtsobjs.insert(numdtsobjs, dtsobj)
		numdtsobjs += 1

##############################
# main logic
##############################
if __name__ == "__main__":
	print "DTS Exporter running..."

	# filename to write out
	filename = basename(Blender.Get("filename")) + ".dts"
	print "Writing file: \"%s\"" % filename

	# get all of the objects 
	objs = Blender.Object.get()
	# count the objects
	numdtsobjs = 0
	for objname in objs:
		if not type(objname.data) == Blender.Types.NMeshType:
			continue
		numdtsobjs += 1
	if numdtsobjs <= 0:
		print "Nothing to export"
		exit

   # process our objects
	process_objects(objs)

# fun times
	t = Vector(1, 2, 3)
	s = Vector(3, 3, 4)
	v = s / 2
	print "x = ", v[0], ", y = ", v[1], ", z = ", v[2]
	print "Primitive.Strip = 0x%x" % Primitive.Strip
# end of the fun times

	print "DTS Exporter complete."
