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