Advertisement
Pureon

supcom-exporter-V3

Feb 6th, 2012
562
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 30.31 KB | None | 0 0
  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")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement