This week only. Pastebin PRO Accounts Christmas Special! Don't miss out!Want more features on Pastebin? Sign Up, it's FREE!
Guest

supcom-exporter-V3

By: Pureon on Feb 6th, 2012  |  syntax: Python  |  size: 30.31 KB  |  views: 396  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. #!BPY
  2.  
  3. #**************************************************************************************************
  4. # Supreme Commander Exporter for Blender3D - www.blender3d.org
  5. #
  6. # Written by dan - www.sup-com.net), Brent (www.scmods.net)
  7. #
  8. # further improvements by GeomanNL and Darius
  9. #
  10. # History
  11. #
  12. # 0.1.0  2006-07-02     Dan Initial version.
  13. #
  14. # 0.2.0   2007-03-11    Brent Fixed UV coords, V was inverted.
  15. #                               Support for exporting quads.
  16. #                               Fixed a padding issue.
  17. #
  18. # 0.3.0   2007-03-18    Dan Code refactoring / Clean up.
  19. #                               Fixed 'INFO' section size in header.
  20. #
  21. # 0.3.3  2007-09-25     GeomanNL  fixed a file-write bug
  22. #                               orientation fix, changed to matrix rotation
  23. #                               other excellent stuff
  24. #                               (me darius took the freedom to write your entry:D)
  25. #                                                                      
  26. # 0.3.5  2009-03-20     Darius_  tangent and binormal calc
  27. #                               vertex optimation
  28. #                               blender front to supcom front
  29. #                               some more fixes and reorganizing/cleanup code
  30. #                                
  31. #                                                              
  32. # Todo
  33. #   - GUI improvements
  34. #   - Support for LOD exporting. Eg. not merging all meshes for an armature into one mech but rather only
  35. #     sub-meshes and export the top level meshes to different files.
  36. #   - Validation, ensure that
  37. #     - Prompt before overwriting files & check that directories exists
  38. #   - Second UV set?
  39. #   - Set animation time per frame
  40. #   - Export LUA script for use in the animation viewer (eg, start anim, set texture etc)..
  41. #   - Set root rot/pos for sca
  42. #
  43. #**************************************************************************************************
  44.  
  45. """
  46. Name: 'Supreme Commander 3.5'
  47. Blender: 242
  48. Group: 'Export'
  49. Tooltip: 'Model / Animation Exporter for Supreme Commander'
  50. """
  51. import Blender
  52. import struct
  53.  
  54. from Blender.BGL import *
  55. from Blender import Draw
  56. from Blender.Draw import *
  57.  
  58. from Blender import Mathutils
  59. from Blender.Mathutils import *
  60.  
  61. from Blender import Window
  62.  
  63.  
  64.  
  65. import os
  66. from os import path
  67.  
  68. VERSION = '3.5'
  69.  
  70. ######################################################
  71. # User defined behaviour, Select as you need
  72. ######################################################
  73. #if you want to leave an info in your scm file put it in there
  74. USER_INFO = ""
  75.  
  76.  
  77. #Enable Progress Bar ( 0 = faster )
  78. PROG_BAR_ENABLE = 1
  79. #how many steps a progress bar has (the lesser the faster)
  80. PROG_BAR_STEP = 25
  81.  
  82. #slower - reduce vertex amount
  83. VERTEX_OPTIMIZE = 1    
  84.  
  85. #LOG File for debuging
  86. #Enable LOG File (0 = Disabled , 1 = Enabled )
  87. LOG_ENABLE = 0
  88. #Filename / Path. Default is blender directory Filename SC-E_LOG.txt
  89. LOG_FILENAME = "SC-E_LOG.txt"
  90.  
  91. LOG_BONE = 1
  92. LOG_VERT = 0
  93.  
  94.  
  95. ######################################################
  96. # Init Supreme Commander SCM( _bone, _vertex, _mesh), SCA(_bone, _frame, _anim) Layout
  97. ######################################################
  98.  
  99. #Transform matrix           z -> yx -> xy -> z 
  100. xy_to_xz_transform = Matrix([ 1, 0, 0],
  101.                                                         [ 0, 0, -1],
  102.                                                         [ 0, 1, 0])
  103.                                                
  104. #xy_to_xz_transform = Matrix([ 1, 0, 0], [ 0, 1, 0], [ 0, 0, 1])
  105.                                                        
  106. # Armature world matrix
  107. MArmatureWorld = Matrix()
  108. BONES = []
  109.  
  110. ANIMATION_DURATION = 1.5
  111.  
  112. class scm_bone :
  113.        
  114.         rest_pose = []         
  115.         rest_pose_inv = []                                                     
  116.         rotation = []
  117.         position = []  
  118.         parent_index = 0
  119.         used = False
  120.         name = ""
  121.        
  122.         def __init__(self, name, rest_pose_inv, rotation, position, parent_index):
  123.                                                                        
  124.                 self.rest_pose_inv = rest_pose_inv                                                                     
  125.                 self.rotation = rotation               
  126.                 self.position = position               
  127.                 self.parent_index = parent_index
  128.                 self.used = False
  129.                 self.name = name
  130.  
  131.         def save(self, file):
  132.                 bonestruct = '16f3f4f4i'
  133.                 #bonestruct = '16f3f4f4L' #Deprecation warning L and mistyrious binary output
  134.                 rp_inv = [0] * 16
  135.  
  136.                 icount = 0
  137.                 for irow in xrange(4):
  138.                         #rest pose_inv
  139.                         for icol in xrange(4):
  140.                                 rp_inv[icount] = self.rest_pose_inv[irow][icol]
  141.                                 icount = icount + 1
  142.                
  143.                 bonedata = struct.pack(bonestruct,                                                                     
  144.                         rp_inv[0], rp_inv[1], rp_inv[2], rp_inv[3],                                                    
  145.                         rp_inv[4], rp_inv[5], rp_inv[6], rp_inv[7],                                                    
  146.                         rp_inv[8], rp_inv[9], rp_inv[10],rp_inv[11],                                           
  147.                         rp_inv[12],rp_inv[13],rp_inv[14],rp_inv[15],                                           
  148.                         self.position[0],self.position[1],self.position[2],                                    
  149.                         self.rotation.w,self.rotation.x,self.rotation.y,self.rotation.z, #Quaternion (w,x,y,z)#w,x,y,z
  150.                         self.name_offset, self.parent_index,                                                           
  151.                         0,0)
  152.                
  153.                
  154.                 #print self.name
  155.                 #print self.rest_pose_inv
  156.                        
  157.                
  158.                 if LOG_BONE :  
  159.                         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"
  160.                         % (     self.name, rp_inv[0], rp_inv[1], rp_inv[2], rp_inv[3],                                                 
  161.                                 rp_inv[4], rp_inv[5], rp_inv[6], rp_inv[7],                                                    
  162.                                 rp_inv[8], rp_inv[9], rp_inv[10],rp_inv[11],                                           
  163.                                 rp_inv[12],rp_inv[13],rp_inv[14],rp_inv[15],                                           
  164.                                 self.position[0],self.position[1],self.position[2],                                    
  165.                                 self.rotation[0],self.rotation[1],self.rotation[2],self.rotation[3], self.parent_index))
  166.                
  167.                
  168.                 file.write(bonedata)
  169.        
  170.  
  171. class scm_vertex :
  172.  
  173.         global xy_to_xz_transform
  174.         position = []
  175.         tangent  = []
  176.         normal   = []
  177.         binormal = []
  178.         uvc = 0
  179.         uv1 = []
  180.         uv2 = []
  181.         bone_index = []
  182.                        
  183.         def __init__(self, pos , no , uv1, bone_index):
  184.                        
  185.                 self.position = pos
  186.                 self.normal   = no
  187.                
  188.                 #tangent and binormal wil be calculated by face
  189.                 self.tangent  = Vector( 0, 0, 0)
  190.                 self.binormal = Vector( 0, 0, 0)
  191.                
  192.                 self.uvc = 1
  193.                 self.uv1 = uv1
  194.                 self.uv2 = uv1# Vector(0,0) #uv1 #better results with copy ... strange, where is the use of that?
  195.                
  196.                 self.bone_index = bone_index
  197.                        
  198.  
  199.         def save(self, file):
  200.                
  201.                 vertstruct = '3f3f3f3f2f2f4B'
  202.                
  203.                 #so finaly we can norm because here it is sure that no tang norm will be added
  204.                 #self.normal = CrossVecs(self.tangent, self.binormal).normalize()
  205.                 self.tangent.normalize()
  206.                 self.binormal.normalize()
  207.                 self.normal.normalize()
  208.                
  209.                 if False :
  210.                         self.tangent = Vector(0,0,0)
  211.                         self.binormal= Vector(0,0,0)
  212.                         #self.normal  = Vector(0,0,0)
  213.  
  214.                 if LOG_VERT :          
  215.                         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]"
  216.                        
  217.                         % (
  218.                                 self.position[0], self.position[1], self.position[2],  
  219.                                 self.normal[0],   self.normal[1],   self.normal[2],            
  220.                                 self.tangent[0],  self.tangent[1],  self.tangent[2],   
  221.                                 self.binormal[0], self.binormal[1], self.binormal[2],  
  222.                                 self.uv1[0], self.uv1[1],                                                              
  223.                                 self.uv2[0], self.uv2[1],                                                              
  224.                                 self.bone_index[0], self.bone_index[1],                                
  225.                                 self.bone_index[2], self.bone_index[3]) )
  226.                                        
  227.         # so you store in this order:
  228.         # pos, normal, tangent, binormal, uv1, uv2, ibone
  229.                 vertex = struct.pack(vertstruct,                                                       
  230.                         self.position[0], self.position[1], self.position[2],  
  231.                         self.normal[0],   self.normal[1],   self.normal[2],            
  232.                         self.tangent[0],  self.tangent[1],  self.tangent[2],   
  233.                         self.binormal[0], self.binormal[1], self.binormal[2],  
  234.                         self.uv1[0], self.uv1[1],                                                              
  235.                         self.uv2[0], self.uv2[1],                                                              
  236.                         self.bone_index[0], self.bone_index[1],                                
  237.                         self.bone_index[2], self.bone_index[3])
  238.                
  239.  
  240.                 file.write(vertex)
  241.                
  242. #helper the real scm face 'tupel is stored in mesh
  243. #quad face
  244. class qFace :
  245.  
  246.                 vertex_cont = []
  247.                
  248.                 def __init__(self):
  249.                         self.vertex_cont = []
  250.                
  251.                 def addVert(self, vertex):
  252.                         self.vertex_cont.extend( vertex )
  253.                        
  254.                 def addToMesh(self, mesh):
  255.                
  256.                         face1 = Face()
  257.                         face1.addVert([ self.vertex_cont[0], self.vertex_cont[1], self.vertex_cont[2] ])
  258.                         face1.CalcTB()
  259.                        
  260.                         face2 = Face()
  261.                         face2.addVert([ self.vertex_cont[2], self.vertex_cont[3], self.vertex_cont[0] ])
  262.                         face2.CalcTB()
  263.                        
  264.                         mesh.addQFace(face1, face2)
  265.                        
  266.                
  267. #helper the real scm face 'tupel is stored in mesh
  268. #tri face
  269. class Face :
  270.        
  271.         vertex_cont = []
  272.        
  273.         def __init__(self):    
  274.                 self.vertex_cont = []
  275.                
  276.         def addVert(self, vertex):
  277.                 self.vertex_cont.extend(vertex)
  278.                
  279.         #now contains 3 vertexes calculate bi and ta and add to mesh
  280.                        
  281.         def CalcTB( self ) :
  282.                 vert1 = self.vertex_cont[0]
  283.                 vert2 = self.vertex_cont[1]
  284.                 vert3 = self.vertex_cont[2]
  285.                
  286.                 uv = [ vert1.uv1, vert2.uv1, vert3.uv1]
  287.  
  288.                 # Calculate Tangent and Binormal
  289.                 #               (v3 - v1).(p2 - p1) - (v2 - v1).(p3 - p1)
  290.                 #       T  =  ------------------------------------------------
  291.                 #               (u2 - u1).(v3 - v1) - (v2 - v1).(u3 - u1)
  292.                 #               (u3 - u1).(p2 - p1) - (u2 - u1).(p3 - p1)
  293.                 #       B  =  -------------------------------------------------
  294.                 #               (v2 - v1).(u3 - u1) - (u2 - u1).(v3 - v1)              
  295.                
  296.                 P2P1 = vert2.position - vert1.position
  297.                 P3P1 = vert3.position - vert1.position
  298.                
  299.                 #UV2UV1 = [ uv[1][0]-uv[0][0], uv[1][1]-uv[0][1] ]
  300.                 #UV3UV1 = [ uv[2][0]-uv[0][0], uv[2][1]-uv[0][1] ]
  301.        
  302.                 UV2UV1 = uv[1] - uv[0]
  303.                 UV3UV1 = uv[2] - uv[0]
  304.                
  305.                 divide = (UV2UV1[1]*UV3UV1[0] - UV2UV1[0]*UV3UV1[1])
  306.                
  307.                 if ( divide != 0.0 ) :         
  308.                         tangent = Vector((UV3UV1[1]*P2P1 - UV2UV1[1]*P3P1)/(divide))
  309.                         binormal = Vector((UV3UV1[0]*P2P1 - UV2UV1[0]*P3P1)/(-divide))
  310.                 else :                 
  311.                         countLOG("<Warning: Vertex-T-B divided through zero")  
  312.                         tangent = Vector(0,0,0)
  313.                         binormal = Vector(0,0,0)       
  314.  
  315.                
  316.                 #add calculated tangent and binormal to vertices
  317.                 for ind in xrange(3):
  318.                         self.vertex_cont[ind].tangent = tangent
  319.                         self.vertex_cont[ind].binormal =  binormal     
  320.                        
  321.         def addToMesh( self, mesh ) :
  322.                 self.CalcTB()
  323.                 mesh.addFace( self )
  324.                
  325.  
  326. class scm_mesh :
  327.        
  328.         bones = []
  329.         vertices = []
  330.         vertcounter = 0
  331.         faces = []
  332.         info = []
  333.        
  334.         def __init__(self):
  335.                 self.bones = []
  336.                 self.vertices = []
  337.                 self.faces = []
  338.                 self.info = []
  339.                 self.vertcounter = 0
  340.                
  341.         def addVert( self, nvert ):
  342.                 if VERTEX_OPTIMIZE :           
  343.                         #search for vertex already in list
  344.                         vertind = 0
  345.                         for vert in self.vertices :                                                            
  346.                                 if nvert.uv1 == vert.uv1 and nvert.position == vert.position :
  347.                                         break   #found vert in list keep that index                            
  348.                                 vertind += 1 #hmm not that one
  349.                                
  350.                         if vertind == len(self.vertices)  :
  351.                                 self.vertices.append( nvert )
  352.                         else:
  353.                                 vert = self.vertices[vertind]
  354.                                
  355.                                 vert.tangent = Vector( (vert.tangent + nvert.tangent) )                                
  356.                                 vert.binormal = Vector( (vert.binormal + nvert.binormal) )                                     
  357.                                 vert.normal = Vector( (vert.normal + nvert.normal) )
  358.                                
  359.                                 self.vertices[vertind] = vert                                  
  360.                        
  361.                         return vertind                 
  362.                 else:
  363.                         self.vertices.append(nvert)
  364.                         return len(self.vertices)-1
  365.                        
  366.         def addFace( self, face ):
  367.                
  368.                 facein = [ self.addVert(nvert) for nvert in face.vertex_cont]
  369.                 self.faces.append(facein)
  370.        
  371.         def addQFace( self, face1, face2):
  372.                
  373.                 facein = [ self.addVert(nvert) for nvert in face1.vertex_cont]
  374.                 self.faces.append(facein)
  375.                
  376.                 facein = [ facein[2], self.addVert(face2.vertex_cont[1]), facein[0]]
  377.                 self.faces.append(facein)
  378.                
  379.  
  380.         def save(self, filename):      
  381.                
  382.                 LOGp('Writing Mesh...')
  383.                
  384.                 scm = file(filename, 'wb')
  385.                
  386.                
  387.                 #headerstruct = '12L' #Deprecation warning L and mistyrious binary output
  388.        
  389.                 headerstruct = '4s11L'
  390.                 headersize = struct.calcsize(headerstruct)
  391.                
  392.                 #marker = 'MODL'
  393.                 marker = 'MODL'
  394.                 version = 5
  395.                 boneoffset = 0
  396.                 bonecount = 0  
  397.                 vertoffset = 0
  398.                 extravertoffset = 0
  399.                 vertcount = len(self.vertices)
  400.                 indexoffset = 0
  401.                 indexcount = len(self.faces) * 3
  402.                 infooffset = 0
  403.                 infosize = 0
  404.                 totalbonecount = len(self.bones)
  405.                
  406.                
  407.                
  408.                 # Write dummy header
  409.                 header = struct.pack(headerstruct + '',                                        
  410.                         marker, version, boneoffset, bonecount, vertoffset,    
  411.                         extravertoffset, vertcount, indexoffset, indexcount,    
  412.                         infooffset, infosize, totalbonecount)
  413.                
  414.                 scm.write(header)
  415.                
  416.                
  417.                 # Write bone names
  418.                 pad_file(scm, 'NAME')
  419.                
  420.                 for bone in self.bones:
  421.                         bone.name_offset = scm.tell()                  
  422.                         name = bone.name               
  423.                         buffer = struct.pack(str(len(name) + 1)+'s', name)
  424.                         scm.write(buffer)
  425.                         #Log(buffer)
  426.                
  427.                 LOGn("[/bone struckt]")
  428.                
  429.                 # Write bones
  430.                 boneoffset = pad_file(scm, 'SKEL')
  431.                        
  432.                 for bone in self.bones:
  433.                         bone.save(scm)
  434.                         # if bone.used == True:
  435.                         bonecount = bonecount + 1
  436.                
  437.  
  438.                
  439.                
  440.                 # Write vertices               
  441.                 vertoffset = pad_file(scm, 'VTXL')
  442.                
  443.                 for vertex in self.vertices:
  444.                         vertex.save(scm)
  445.                        
  446.                
  447.                                
  448.                 # Write Faces
  449.                 indexoffset = pad_file(scm, 'TRIS')
  450.                
  451.                 for f in range(len(self.faces)):
  452.                         face = struct.pack('3H', self.faces[f][0], self.faces[f][1], self.faces[f][2])
  453.                         #face = struct.pack('3h', self.faces[f][0], self.faces[f][1], self.faces[f][2])
  454.                         scm.write(face)
  455.                
  456.                
  457.                 LOGp( "Bones: %d, Vertices: %d, Faces: %d; \n" % (bonecount, len(self.vertices), len(self.faces)))                     
  458.                
  459.                 #Write Info
  460.                 if len(self.info) > 0:
  461.                        
  462.                         infooffset = pad_file(scm, 'INFO')
  463.                                                
  464.                         for i in range(len(self.info)):
  465.                                 info = self.info[i]
  466.                                 infolen = len(info) + 1                
  467.                                 buffer = struct.pack(str(infolen)+'s', info)
  468.                                 scm.write(buffer)
  469.                        
  470.                         infosize = scm.tell() - infooffset;
  471.                
  472.                 # Now we can update the header 
  473.                 scm.seek(0, 0) 
  474.                
  475.                 header = struct.pack(headerstruct,                                                     
  476.                         marker, version, boneoffset, bonecount, vertoffset,    
  477.                         extravertoffset, vertcount, indexoffset, indexcount,    
  478.                         infooffset, infosize, totalbonecount)          
  479.                        
  480.                 scm.write(header)
  481.                
  482.                 scm.close()
  483.  
  484.  
  485. class sca_bone:
  486.  
  487.         position = Vector( 0, 0, 0)
  488.         rotation = Quaternion( 0, 0, 0, 0 )
  489.  
  490.         def __init__(self, pos, rot):
  491.                 self.position = pos
  492.                 self.rotation = rot
  493.  
  494.                
  495. class sca_frame:
  496.  
  497.         keytime = 0.0
  498.         keyflags = 0
  499.         bones = []
  500.         anim = None
  501.        
  502.         def __init__(self, anim):
  503.                 self.keytime = 0.0
  504.                 self.keyflags = 0
  505.                 self.anim = anim
  506.                 self.bones = []
  507.  
  508.         def save(self, file):
  509.                 frameheader_fmt = 'fl'
  510.                 frameheader_size = struct.calcsize(frameheader_fmt)
  511.                
  512.                 posrot_fmt = '3f4f'
  513.                 posrot_size = struct.calcsize(posrot_fmt)
  514.                
  515.                 # Frame header
  516.                 buffer = struct.pack(frameheader_fmt, self.keytime, self.keyflags)
  517.                 file.write(buffer)
  518.                
  519.                 #Log(":%d:" % (len(self.bones)))
  520.                
  521.                 # Bones        
  522.                 for bone in self.bones:
  523.                        
  524.                         buffer = struct.pack(   posrot_fmt,                                                                            
  525.                                                                         bone.position.x, bone.position.y, bone.position.z,                                     
  526.                                                                         bone.rotation.w, bone.rotation.x, bone.rotation.y, bone.rotation.z)
  527.                                                                        
  528.                         file.write(buffer)
  529.  
  530.  
  531. class sca_anim :
  532.         frames = []
  533.         bonelinks = []
  534.         bonenames = []
  535.         duration = 0.0
  536.        
  537.         def __init__(self):
  538.                 global ANIMATION_DURATION
  539.                 self.frames = []
  540.                 self.bonelinks = []
  541.                 self.bonenames = []
  542.                 self.duration = ANIMATION_DURATION
  543.        
  544.         def save(self, filename):
  545.        
  546.                 LOGp('Writing SCA...')
  547.                
  548.                 #self.filename = filename
  549.                 sca = file(filename, 'wb')
  550.                
  551.                 headerstruct = '4sllflllll'
  552.                
  553.                 # Write temp header            
  554.                 magic = 'ANIM'
  555.                 version = 5
  556.                 numframes = len(self.frames)
  557.                 numbones = len(self.bonenames)         
  558.                
  559.                 namesoffset = 0
  560.                 linksoffset = 0
  561.                 animoffset = 0
  562.                 framesize = 0
  563.                
  564.                 header = struct.pack(headerstruct,
  565.                         magic, version, numframes, self.duration, numbones,
  566.                         namesoffset, linksoffset, animoffset, framesize)
  567.                 #note: this header is seen correctly by the GPG dumpsca.py
  568.                
  569.                 sca.write(header)              
  570.                
  571.                 # Write bone names
  572.                 namesoffset = pad_file(sca, 'NAME')
  573.  
  574.                 for bone_name in self.bonenames:
  575.                         buffer = struct.pack(str(len(bone_name) + 1)+'s', bone_name)
  576.                         sca.write(buffer)
  577.                
  578.  
  579.                 # Write bone links
  580.                 linksoffset = pad_file(sca, 'LINK')
  581.                
  582.                 for link in self.bonelinks:
  583.                         buffer = struct.pack('l', link)
  584.                         sca.write(buffer)
  585.                
  586.                
  587.                 # Write data
  588.                 animoffset = pad_file(sca, 'DATA')
  589.                
  590.                 #the space occupied by postion and rotation info on the bones.
  591.                 posrot_fmt = '3f4f'
  592.                 posrot_size = struct.calcsize(posrot_fmt)
  593.                
  594.                 #this writes the position/rotation of the root bone in the first animation, as if it is at position 0, and no rotation
  595.                 #note: it looks like rot=1,0,0,0 is needed to indicate no rotation.
  596.                 buffer = struct.pack(posrot_fmt, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0)
  597.                 sca.write(buffer)
  598.                
  599.                 for frame in self.frames:
  600.                         framesize = sca.tell()
  601.                         frame.save(sca)
  602.                         framesize = sca.tell() - framesize             
  603.                
  604.                
  605.                 # Update header
  606.                 sca.seek(0, 0)
  607.                
  608.                 header = struct.pack(headerstruct,
  609.                         magic, version, numframes, self.duration, numbones,
  610.                         namesoffset, linksoffset, animoffset, framesize)
  611.                
  612.                 sca.write(header)
  613.                
  614.                 sca.close()
  615.                
  616.                 LOGp( "Bones: %d, Frames: %d;\n" % (numbones, numframes) )                     
  617.                
  618.                 #Log('OFFSETS: names = %d  links = %d  anim = %d  framesize = %d' % (namesoffset, linksoffset, animoffset, framesize))
  619.  
  620.  
  621. ######################################################
  622. # Exporter Functions
  623. ######################################################
  624.  
  625. # Helper methods
  626. ######################################################
  627.  
  628. def pad(size):
  629.         val = 32 - (size % 32)
  630.         if (val < 4):
  631.                 val = val + 32
  632.        
  633.         return val
  634.  
  635. def pad_file(file, s4comment):
  636.         N = pad(file.tell()) - 4
  637.         filldata = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
  638.         padding = struct.pack(str(N)+'s4s', filldata[0:N], s4comment)
  639.        
  640.         file.write(padding)
  641.  
  642.         return file.tell()
  643.        
  644.  
  645.                        
  646. #
  647. ######################################################
  648.  
  649.  
  650.  
  651. # Helper method for itterating through the bone tree
  652. def itterate_bones(mesh, bone, parent = None, scm_parent_index = -1):
  653.        
  654.         global MArmatureWorld
  655.         global BONES
  656.         global xy_to_xz_transform
  657.        
  658.  
  659.  
  660.        
  661.         if (parent != None and bone.parent.name != parent.name):
  662.                 PupMenu("Error: Invalid parenting in bone ... multiple parents?!%t|OK")
  663.                 Exit()
  664.                 #print "Invalid parenting in bone", bone.name," and parent ", parent.name
  665.                 return
  666.  
  667.         b_rest_pose     = Matrix([0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0])
  668.         b_rest_pose_inv = Matrix([0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0])
  669.         b_rotation              = Quaternion( 0,0,0,0 )
  670.         b_position              = Vector( 0,0,0 )
  671.         b_index = len(mesh.bones)
  672.        
  673.        
  674.         #MArmatureWorld = Matrix(arm_obj.matrixWorld)
  675.         bone_matrix = Matrix(bone.matrix['ARMATURESPACE'])
  676.        
  677.         # Calculate the inverse rest pose for the bone #instead bonearmmat*worldmat = Matrix['BONESPACE']
  678.         b_rest_pose     = Matrix( bone_matrix * MArmatureWorld  )      
  679.         b_rest_pose_inv = Matrix( b_rest_pose * xy_to_xz_transform ).invert()
  680.        
  681.         if (parent == None):
  682.                 rel_mat = b_rest_pose * xy_to_xz_transform
  683.                 #root pos is the same as the rest-pose         
  684.         else:
  685.                 parent_matrix_inv = Matrix( parent.matrix['ARMATURESPACE'] ).invert()
  686.                 rel_mat = Matrix(bone_matrix * parent_matrix_inv)
  687.                 # must be BM * PMI in that order
  688.                 # do not use an extra (absolute) extra rotation here, cause this is only relative
  689.        
  690.         #  Position & Rotation   relative to parent (if there is a parent)             
  691.         b_rotation =    rel_mat.toQuat()#.normalize()
  692.        
  693.         #row 3, cols 0,1,2 indicate position   
  694.         b_position = Vector( rel_mat[3] )
  695.        
  696.         #def __init__(self, name, rest_pose_inv, rotation, position, parent_index):
  697.         sc_bone = scm_bone( bone.name, b_rest_pose_inv, b_rotation, b_position, scm_parent_index )
  698.        
  699.         BONES.append(sc_bone)
  700.         mesh.bones.append(sc_bone)
  701.        
  702.         # recursive call for all children
  703.         if (bone.children != None):
  704.                 for child in bone.children:
  705.                         itterate_bones( mesh, child, bone, b_index )
  706.  
  707.  
  708. def make_scm(arm_obj):
  709.        
  710.         global MArmatureWorld
  711.         global xy_to_xz_transform
  712.        
  713.         arm = arm_obj.getData()
  714.        
  715.         scn = Blender.Scene.GetCurrent()
  716.        
  717.         # Get all mesh objects for the selected armature & calculate progbar length
  718.         pb_length = 0
  719.         mesh_objs = []
  720.         for obj in scn.objects:
  721.                 if obj.parent == arm_obj and obj.getType() == 'Mesh':
  722.                         #calculate progbar length
  723.                         bmesh_data = obj.getData(False, True)
  724.                         pb_length += len(bmesh_data.faces)
  725.                        
  726.                         mesh_objs.append(obj)  
  727.                
  728.         ProgBarFaces = ProgressBar( "Exp: Verts", pb_length )
  729.                        
  730.         # Create SCM Mesh
  731.         supcom_mesh = scm_mesh()
  732.        
  733.         # Traverse the bone tree        and  check if there is one root bone
  734.         numroots = 0
  735.         for bone in arm.bones.values():                
  736.                 if (bone.parent == None):
  737.                         numroots += 1
  738.                         itterate_bones(supcom_mesh, bone)                      
  739.                        
  740.         if numroots > 1:
  741.                 PupMenu("Error: there are multiple root bones -> check you bone relations!%t|OK")
  742.                 Exit()
  743.                 return
  744.  
  745.         #this inserts a converted armature back into the existing model, to see
  746.         #if the bone locations are correct (was used for debugging)
  747.         #test_the_armature = 1
  748.         #if test_the_armature:
  749.         #       #create an extra test armature
  750.         #       testarm_data= Blender.Armature.New('testArmature')
  751.         #       testarm_ob = scn.objects.new(testarm_data)
  752.         #       testarm_data.makeEditable()
  753.         #       prev_eb = Non
  754.         #       for bone in supcom_mesh.bones:
  755.         #               print "mesh bone: ", bone.name
  756.         #               eb = Blender.Armature.Editbone()
  757.         #               eb.name = bone.name
  758.         #               rest_pose = bone.rest_pose[3]
  759.         #               eb.head = Vector(rest_pose[0], rest_pose[1], rest_pose[2])
  760.         #               eb.tail = eb.head + Vector(1,0,0)
  761.         #               if bone.parent_index != -1:
  762.         #                       eb.parent = prev_eb
  763.         #               prev_eb = eb
  764.         #               testarm_data.bones[eb.name]= eb
  765.         #       testarm_data.update()
  766.        
  767.  
  768.        
  769.         # Process all the meshes
  770.         for mesh_obj in mesh_objs:
  771.                
  772.                 bmesh_data = mesh_obj.getData(False, True)     
  773.  
  774.                 if not bmesh_data.faceUV :
  775.                         PupMenu("Error: Mesh has no texture values -> Please set your UV!%t|OK")
  776.                         Exit()
  777.                         return
  778.                        
  779.                 MatrixMesh = Matrix(mesh_obj.matrixWorld)
  780.                 mesh_name = mesh_obj.name
  781.                
  782.                 for face in bmesh_data.faces:
  783.                
  784.                         ProgBarFaces.do()                                                                      
  785.                        
  786.                         vertList = []
  787.                                                
  788.                         for i in xrange(len(face.verts)):
  789.                                 vert = face.verts[i]
  790.                                
  791.                                 v_nor = Vector( 0, 0, 0 )
  792.                                 v_pos = Vector( 0, 0, 0 )
  793.                                 v_uv1 = Vector( 0, 0) #SC allows 2 uv's                        
  794.                                 v_boneIndex = [0]*4 #  SC supports up to 4 bones we will use only one
  795.                                 #v_boneIndex = [-1,0,0,0]
  796.                                
  797.                                 #Find controling bone
  798.                                 v_boneIndex[0] = -1
  799.                                 inf = bmesh_data.getVertexInfluences(vert.index)
  800.                                 for j in xrange(len(inf)) :
  801.                                         bonename = inf[j][0]
  802.                                         for b in range(len(supcom_mesh.bones)):
  803.                                                 bone = supcom_mesh.bones[b]
  804.                                                 if bone.name == bonename:
  805.                                                         bone.used = True
  806.                                                         v_boneIndex[0] = b
  807.                                                         break
  808.                                                        
  809.                                 if (v_boneIndex[0] == -1):
  810.                                         v_boneIndex[0] = 0
  811.                                        
  812.                                         Blender.Window.EditMode(0)
  813.                                         vert.sel = 1
  814.                                        
  815.                                         countLOG("Warning: Verticle without Bone Influence in %s. Selected " % (mesh_name))
  816.                                
  817.                                 v_pos = Vector( vert.co * (MatrixMesh * xy_to_xz_transform))                   
  818.                                
  819.                                 v_nor = vert.no * (MatrixMesh * xy_to_xz_transform)
  820.                                
  821.                                 #needed cause supcom scans an image in the opposite vertical direction or something?.
  822.                                 v_uv1 = Vector(face.uv[i][0], 1.0 - face.uv[i][1])                                     
  823.                        
  824.                                 vertList.append( scm_vertex( v_pos, v_nor, v_uv1 , v_boneIndex) )                              
  825.                        
  826.                         if len(vertList) > 3:
  827.                                 newFace = qFace()                              
  828.                         else:
  829.                                 newFace = Face()                               
  830.                                
  831.                         newFace.addVert(vertList)
  832.                         newFace.addToMesh(supcom_mesh) 
  833.  
  834.         return supcom_mesh
  835.  
  836.  
  837. def make_sca(arm_obj, action):
  838.  
  839.         global BONES
  840.         global MArmatureWorld
  841.         global xy_to_xz_transform
  842.        
  843.         action.setActive(arm_obj)
  844.        
  845.         scene = Blender.Scene.GetCurrent()
  846.         render_context = scene.getRenderingContext()
  847.         endframe = render_context.endFrame()
  848.         animation = sca_anim() 
  849.         #animation.duration = 1.5
  850.        
  851.         # Add bone names & links
  852.         for bone in BONES:
  853.                 animation.bonenames.append(bone.name)
  854.                 animation.bonelinks.append(bone.parent_index)
  855.                 LOGn('adding bone: %s with parent %d ' % (bone.name, bone.parent_index))
  856.        
  857.         ProgBarAnimation = ProgressBar( "Exp: Anim", endframe)
  858.        
  859.         # Add frames
  860.         frame_counter = 1
  861.         while frame_counter <= endframe:
  862.                 LOGn('adding frame %d of %d' % (frame_counter, endframe))              
  863.                 ProgBarAnimation.do()
  864.  
  865.                 frame = sca_frame(animation)
  866.                        
  867.                 arm_obj.evaluatePose(frame_counter)
  868.                 frame_counter += 1
  869.                
  870.                 POSED_BONES = {}
  871.                 for posebone in arm_obj.getPose().bones.values():
  872.                         POSED_BONES[posebone.name] = posebone.poseMatrix
  873.                
  874.                 for bone in BONES:
  875.                         pose_bone_matrix = POSED_BONES[bone.name]
  876.                        
  877.                         if (bone.parent_index == -1):
  878.                                 rel_mat = (Matrix(pose_bone_matrix) * MArmatureWorld) * xy_to_xz_transform
  879.                         else:
  880.                                 rel_mat = Matrix(pose_bone_matrix) * Matrix(POSED_BONES[BONES[bone.parent_index].name]).invert()
  881.                        
  882.                         rotation = rel_mat.toQuat().normalize()
  883.                         #rot = rotation #[ rotation.w, rotation.x, rotation.y, rotation.z ]            
  884.                         pos = Vector( rel_mat[3][0], rel_mat[3][1], rel_mat[3][2] )
  885.                        
  886.                         anim_bone = sca_bone(pos, rotation)
  887.                         frame.bones.append(anim_bone)
  888.                
  889.                 animation.frames.append(frame)         
  890.        
  891.         return animation
  892.  
  893. def export(outdir):
  894.         global VERSION, USER_INFO
  895.         global MArmatureWorld
  896.         global xy_to_xz_transform
  897.        
  898.         w_edit_mode = Blender.Window.EditMode()
  899.                
  900.         #No animation export in editmode
  901.         if w_edit_mode :
  902.                 Blender.Window.EditMode(0)
  903.                
  904.         Blender.Window.WaitCursor(1)
  905.        
  906.         xy_to_xz_transform.resize4x4() 
  907.        
  908.         scn = Blender.Scene.GetCurrent()
  909.         # Get Selected object(s)
  910.         selected_objects = Blender.Object.GetSelected()
  911.        
  912.         # Look for an armature
  913.         arm = None
  914.         arm_obj = None
  915.         for obj in selected_objects:
  916.                 data = obj.getData()
  917.                 if type(data) is Blender.Types.ArmatureType:
  918.                         arm = data
  919.                         arm_obj = obj
  920.                         break
  921.        
  922.         # Is there one armature? Take this one
  923.         if arm == None :       
  924.                 for obj in scn.objects:
  925.                         data = obj.getData()
  926.                         if obj.getType() == 'Armature' :
  927.                                 if arm == None:
  928.                                         arm = data
  929.                                         arm_obj = obj
  930.                                 else :
  931.                                         arm = None
  932.                                         break  
  933.                                                
  934.                                
  935.         if arm == None:
  936.                 PupMenu("Error: Please select your armature.%t|OK")
  937.                 Exit()
  938.                 return
  939.                
  940.         # this defines the ARMATURE_SPACE.
  941.         # all bones in the armature are positioned relative to this space.
  942.         MArmatureWorld = Matrix(arm_obj.matrixWorld)   
  943.                
  944.         # SCM
  945.         LOGp(' ')
  946.         LOGp(' ')
  947.         LOGp('Exporting model: ' + arm_obj.name )
  948.         LOGp('----------------------------------')
  949.         mesh = make_scm(arm_obj)
  950.         if mesh == None :
  951.                 LOGp('Aborted!')
  952.                 return
  953.                
  954.         mesh.info.append('Exported with Blender SupCom-Exporter ' + VERSION)
  955.         if USER_INFO != "" :
  956.                 mesh.info.append( USER_INFO )
  957.         mesh.save(outdir + arm_obj.name + '.scm')
  958.         mesh = None
  959.  
  960.         # SCA
  961.         actions = Blender.Armature.NLA.GetActions().iteritems()
  962.         for action in actions:
  963.                 #action[0] = the key,  action[1] = the dictionary
  964.                 ####maybe this could help?
  965.                 LOGp(' ')
  966.                 LOGp('Animation: ' + action[0])
  967.                 animation = make_sca(arm_obj, action[1])
  968.                 animation.save(outdir + action[0] + '.sca')            
  969.  
  970.         Blender.Window.EditMode(w_edit_mode)   
  971.         Blender.Window.WaitCursor(0)
  972.  
  973.         LOGp('----------------------------------')
  974.        
  975.         closeLog()
  976.  
  977.        
  978.        
  979.        
  980.        
  981.        
  982. ######################################################
  983. # GUI
  984. ######################################################
  985.  
  986.        
  987. log = []
  988. log_max_lines = 14
  989. LOG_FILE = None
  990.  
  991. #log to file
  992. def LOGn(message):
  993.         global LOG_FILE
  994.         global LOG_ENABLE
  995.         global LOG_FILENAME
  996.        
  997.         if LOG_ENABLE :
  998.                 if LOG_FILE == None :                  
  999.                         LOG_FILE = open( LOG_FILENAME, 'w')
  1000.                         LOG_FILE.write('SupCom Exporter LOG File:\n\n')
  1001.                        
  1002.                         LOGp( "LOG enabled: %s" % (LOG_FILENAME))
  1003.                         Log(message + '\n')
  1004.                        
  1005.                 else :         
  1006.                         LOG_FILE.write(message + '\n')
  1007.                        
  1008. #Log to file, to console and exp window                                
  1009. def LOGp(message):
  1010.         global log, log_max_lines
  1011.        
  1012.         LOGn(message)
  1013.         print message  
  1014.         log.append(message)
  1015.        
  1016.         if len(log) > log_max_lines:
  1017.                 del log[0]
  1018.                
  1019.                
  1020. counter = []
  1021. cLog = []
  1022. #log for a amount of errors like vertex errors
  1023. def countLOG(message):
  1024.         global cLog
  1025.         global counter
  1026.        
  1027.         cont = False
  1028.        
  1029.         for i in xrange(len(cLog)):
  1030.                 if cLog[i] == message:
  1031.                         cont = True
  1032.                         counter[i] +=1
  1033.                         break
  1034.                        
  1035.         if not cont :
  1036.                
  1037.                 cLog.append( message)
  1038.                 counter.append( 1)
  1039.  
  1040. def closeLog():
  1041.         global cLog, LOG_FILE
  1042.         global counter
  1043.        
  1044.         for i in xrange(len(cLog)):
  1045.                 LOGp("%s (Times:%d)" % (cLog[i], counter[i]))
  1046.  
  1047.         if LOG_FILE != None :
  1048.                 LOG_FILE.close()       
  1049.                
  1050.         Blender.Window.RedrawAll()
  1051.                
  1052.  
  1053. class ProgressBar :
  1054.  
  1055.         global PROG_BAR_STEP
  1056.         global PROG_BAR_ENABLE
  1057.        
  1058.         progress = 0
  1059.         progressold = 0
  1060.         current = 0    
  1061.         end = 0
  1062.         div = 0
  1063.         text = "None"  
  1064.        
  1065.         def __init__(self, text, end):
  1066.                 self.progress = 0
  1067.                 self.progressold = 0
  1068.                 self.current = 0
  1069.                 self.end = end
  1070.                 self.text = text
  1071.                 self.div = PROG_BAR_STEP
  1072.                
  1073.                 #it looks like blender needs to init this progress bar with 0.0
  1074.                 if PROG_BAR_ENABLE :
  1075.                         Blender.Window.DrawProgressBar ( 0.0 , text)
  1076.                
  1077.         def do(self):  
  1078.                
  1079.                 if PROG_BAR_ENABLE :
  1080.                         self.current += 1
  1081.                         self.progress = (self.current*self.div)/self.end
  1082.                        
  1083.                         if self.progress != self.progressold :
  1084.                                 self.progressold = self.progress
  1085.                                 Blender.Window.DrawProgressBar ( float(self.progress)/self.div , self.text)
  1086.                                
  1087.  
  1088.  
  1089.  
  1090.  
  1091.  
  1092. # Events
  1093. EVENT_NOEVENT = 1
  1094. EVENT_DRAW = 2
  1095. EVENT_EXIT = 3
  1096. EVENT_CLOSE_LOG = 4
  1097. EVENT_EXPORTDIR_TEXT = 5
  1098. EVENT_EXPORTDIR = 6
  1099.  
  1100. export_directory = Blender.Draw.Create("sds")
  1101.  
  1102. show_log = 0
  1103.  
  1104. def fileselector_callback(filename):
  1105.         modelpath, modelfile = os.path.split(filename)
  1106.         export_directory.val = modelpath + '/'
  1107.  
  1108. def draw():
  1109.         global EVENT_NOEVENT, EVENT_DRAW, EVENT_EXIT, EVENT_CLOSE_LOG
  1110.         global export_directory, show_log
  1111.         global log_max_lines, log
  1112.         global VERSION
  1113.  
  1114.         # Titles
  1115.         glClear(GL_COLOR_BUFFER_BIT)
  1116.        
  1117.         top = 60 + log_max_lines * 12 + 8
  1118.         #top = 500
  1119.         top_x = 304
  1120.        
  1121.         glColor3f(0.8, 0.8, 1)
  1122.         glRecti(top_x, top, 4, 4)
  1123.         glBegin(GL_LINES)
  1124.         glColor3f(0.8, 0.8, 0.8)
  1125.         glVertex2d(4, top)
  1126.         glVertex2d(4, 4)
  1127.         glVertex2d(4, 4)
  1128.         glVertex2d(top_x, 4)
  1129.         glColor3f(0.5, 0.5, 0.5)
  1130.         glVertex2d(top_x, top)
  1131.         glVertex2d(4, top)
  1132.         glVertex2d(top_x, top)
  1133.         glVertex2d(top_x, top-1)
  1134.         glEnd()
  1135.        
  1136.         glColor3f(0, 0, 0)
  1137.         glRasterPos2d(10, top-16)
  1138.         Text("Supreme Commander Exporter " + VERSION)  
  1139.        
  1140.         # Show exporting log
  1141.         if show_log:
  1142.  
  1143.                 for index in range(0, len(log)):
  1144.                         i = (len(log) - 1) - index
  1145.                         glRasterPos2i(10, 40 + i*12)
  1146.                         Text(log[index]) #, 'tiny')
  1147.                
  1148.                
  1149.                 Button("Close", EVENT_EXIT , 10, 10, 80, 18)
  1150.                 #Blender.Window.RedrawAll()
  1151.  
  1152.                 # Exporter GUI
  1153.         else:
  1154.                 Blender.Draw.Button("Browse...", EVENT_EXPORTDIR, 10, 40, 80, 18, "")
  1155.  
  1156.                 #if export_directory.val == 'sds':
  1157.                         #automatically launch the browse window once
  1158.                 #       Blender.Window.FileSelector (fileselector_callback, "Select output DIR")
  1159.                 #       Blender.Redraw()
  1160.                        
  1161.                 export_directory = Blender.Draw.String("", EVENT_EXPORTDIR_TEXT, 100, 40, 200, 18, export_directory.val, 255, "Where to save the exported files")
  1162.                        
  1163.                 # Draw and Exit Buttons
  1164.                 Button("Export", EVENT_DRAW , 10, 10, 80, 18)
  1165.                 Button("Exit", EVENT_EXIT , 100, 10, 80, 18)
  1166.  
  1167. def event(evt, val):   
  1168.         if (evt == QKEY and not val):
  1169.                 Exit()
  1170.  
  1171. def bevent(evt):
  1172.         global EVENT_NOEVENT,EVENT_DRAW,EVENT_EXIT
  1173.         global export_directory
  1174.         global show_log
  1175.  
  1176.         if (evt == EVENT_EXIT):
  1177.                 show_log = 1
  1178.                 Exit()
  1179.        
  1180.         elif (evt == EVENT_EXPORTDIR):
  1181.                 Blender.Window.FileSelector (fileselector_callback, "Select output DIR")
  1182.                 #Blender.Redraw()
  1183.        
  1184.         elif (evt == EVENT_DRAW):
  1185.                
  1186.                 if (export_directory.val == ""):
  1187.                         return
  1188.                
  1189.                 show_log = 1
  1190.                 modelpath, modelfile = os.path.split(export_directory.val)
  1191.                 export(modelpath + '/')
  1192.                 #changed cause filename was included in the dir selection                
  1193.                 #Blender.Redraw()
  1194.  
  1195.  
  1196. Register(draw, event, bevent)
  1197.  
  1198. #automatically launch the browse window once
  1199. Blender.Window.FileSelector (fileselector_callback, "Select output DIR")
clone this paste RAW Paste Data