Advertisement
Guest User

Untitled

a guest
Oct 9th, 2019
87
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 46.08 KB | None | 0 0
  1. ## ***** BEGIN GPL LICENSE BLOCK *****
  2. #
  3. # This program is free software; you can redistribute it and/or
  4. # modify it under the terms of the GNU General Public License
  5. # as published by the Free Software Foundation; either version 2
  6. # of the License, or (at your option) any later version.
  7. #
  8. # This program is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with this program; if not, write to the Free Software Foundation,
  15. # Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  16. #
  17. # ***** END GPL LICENCE BLOCK *****
  18. #
  19. #
  20. # REQUIRED OPTIONS -
  21. # - Make Normals Consistent
  22. # - Remove Doubles
  23. # **********************************
  24. bl_info = {
  25.     "name": "ASCII Scene Exporter v2.5.9",
  26.     "author": "Richard Bartlett, MCampagnini, scorpion81( copy/separate option, fix for non-udk materials, material name only specification, ui cleanup)",
  27.     "version": ( 2, 5, 9 ),
  28.     "blender": ( 2, 80, 0 ),
  29.     "location": "File > Export > ASCII Scene Export(.ase)",
  30.     "description": "ASCII Scene Export(.ase) v2.5.9",
  31.     "warning": "",
  32.     "wiki_url": "",
  33.     "tracker_url": "",
  34.     "category": "Import-Export"
  35. }
  36.  
  37. """
  38. --  This script is intended to export in the ASE file format for STATIC MESHES ONLY.
  39. --  This script WILL NOT export skeletal meshes, these should be exported using a
  40. --  different file format.
  41.  
  42. --  More Information at http://code.google.com/p/ase-export-vmc/
  43. --  UDK Thread at http://forums.epicgames.com/threads/776986-Blender-2-57-ASE-export
  44. """
  45.  
  46. import os
  47. import bpy
  48. import math
  49. import time
  50.  
  51. # settings
  52. aseFloat = lambda x: '''{0: 0.4f}'''.format( x )
  53. optionScale = 16.0
  54. optionSubmaterials = False
  55. optionSmoothingGroups = True
  56. optionAllowMultiMats = True
  57.  
  58. # ASE components
  59. aseHeader = ''
  60. aseScene = ''
  61. aseMaterials = ''
  62. aseGeometry = ''
  63.  
  64. # Other
  65. matList = []
  66. numMats = 0
  67. currentMatId = 0
  68.  
  69. #== cError ==================================================================
  70. class cError( Exception ):
  71.  
  72.     def __init__( self, message ):
  73.         self.message = message
  74.         print( '\n\n' + message + '\n\n' )
  75.  
  76. #== Header =================================================================
  77. class cHeader:
  78.     def __init__( self ):
  79.         self.comment = "Ascii Scene Exporter v2.51"
  80.  
  81.     def __repr__( self ):
  82.         return '''*3DSMAX_ASCIIEXPORT\t200\n*COMMENT "{0}"\n'''.format( self.comment )
  83.  
  84. #== Scene ==================================================================
  85. class cScene:
  86.     def __init__( self ):
  87.         self.filename = bpy.data.filepath
  88.         self.firstframe = 0
  89.         self.lastframe = 100
  90.         self.framespeed = 30
  91.         self.ticksperframe = 160
  92.         self.backgroundstatic = ''.join( [aseFloat( x ) for x in [0.0, 0.0, 0.0]] )
  93.         self.ambientstatic = ''.join( [aseFloat( x ) for x in [0.0, 0.0, 0.0]] )
  94.  
  95.     def __repr__( self ):
  96.         return '''*SCENE {{\n\t*SCENE_FILENAME "{0}"\
  97.               \n\t*SCENE_FIRSTFRAME {1}\
  98.               \n\t*SCENE_LASTFRAME {2}\
  99.               \n\t*SCENE_FRAMESPEED {3}\
  100.               \n\t*SCENE_TICKSPERFxRAME {4}\
  101.               \n\t*SCENE_BACKGROUND_STATIC {5}\
  102.               \n\t*SCENE_AMBIENT_STATIC {6}\
  103.               \n}}\n'''.format( self.filename, self.firstframe, self.lastframe, self.framespeed, self.ticksperframe, self.backgroundstatic, self.ambientstatic )
  104.  
  105. #== Materials ==============================================================
  106. class cMaterials:
  107.     def __init__( self ):
  108.         global optionSubmaterials
  109.         global matList
  110.         global numMats
  111.  
  112.         self.material_list = []
  113.  
  114.         # Get all of the materials used by non-collision object meshes  
  115.         for object in bpy.context.selected_objects:
  116.             if collisionObject( object ) == True:
  117.                 continue
  118.             elif object.type != 'MESH':
  119.                 continue
  120.             else:
  121.                 print( object.name + ': Constructing Materials' )
  122.                 for slot in object.material_slots:
  123.                     # if the material is not in the material_list, add it
  124.                     if self.material_list.count( slot.material ) == 0:
  125.                         self.material_list.append( slot.material )
  126.                         matList.append( slot.material.name )
  127.  
  128.         self.material_count = len( self.material_list )
  129.         numMats = self.material_count
  130.  
  131.         # Raise an error if there are no materials found
  132.         if self.material_count == 0:
  133.             raise cError( 'Mesh must have at least one applied material' )
  134.         else:
  135.             if ( optionSubmaterials ):
  136.                 self.dump = cSubMaterials( self.material_list )
  137.             else:
  138.                 self.dump = cMultiMaterials( self.material_list )
  139.  
  140.     def __repr__( self ):
  141.         return str( self.dump )
  142. class cMultiMaterials:
  143.     def __init__( self, material_list ):
  144.         self.numMtls = len( material_list )
  145.         # Initialize material information
  146.         self.dump = '''*MATERIAL_LIST {{\
  147.                    \n\t*MATERIAL_COUNT {0}\
  148.                    '''.format( str( self.numMtls ) )
  149.  
  150.         for index, slot in enumerate( material_list ):
  151.             self.dump += '''\n\t*MATERIAL {0} {{\
  152.                            {1}\
  153.                            \n\t}}'''.format( index, cMaterial( slot ) )
  154.  
  155.         self.dump += '\n}'
  156.  
  157.     def __repr__( self ):
  158.         return self.dump
  159. class cSubMaterials:
  160.     def __init__( self, material_list ):
  161.         slot = material_list[0]
  162.         # Initialize material information
  163.         self.dump = '''*MATERIAL_LIST {\
  164.                    \n\t*MATERIAL_COUNT 1\
  165.                    \n\t*MATERIAL 0 {\
  166.                    '''
  167.         self.matDump = ''
  168.         self.name = material_list[0].name
  169.         self.numSubMtls = len( material_list )
  170.         self.diffusemap = cDiffusemap( slot.texture_slots[0] )
  171.         if ( self.numSubMtls > 1 ):
  172.             self.matClass = 'Multi/Sub-Object'
  173.             self.diffuseDump = ''
  174.         else:
  175.             self.matClass = 'Standard'
  176.             self.numSubMtls = 0
  177.             self.diffuseDump = self.diffdump()
  178.         self.ambient = ''.join( [aseFloat( x ) for x in [0.0, 0.0, 0.0]] )
  179.         self.diffuse = ''.join( [aseFloat( x ) for x in slot.diffuse_color] )
  180.         self.specular = ''.join( [aseFloat( x ) for x in slot.specular_color] )
  181.         #self.shine = aseFloat( slot.specular_hardness / 511 )
  182.         #self.shinestrength = aseFloat( slot.specular_intensity )
  183.         self.transparency = aseFloat( 1.0 )
  184.         self.wiresize = aseFloat( 1.0 )
  185.         self.shading = str( "Phong" ).capitalize()
  186.         self.xpfalloff = aseFloat( 0.0 )
  187.         self.xptype = 'Filter'
  188.         self.falloff = 'In'
  189.         self.soften = False
  190.         self.submtls = []
  191.         self.selfillum = aseFloat( 0.0 )
  192.  
  193.         if ( len( material_list ) > 1 ):
  194.             # Build SubMaterials
  195.             for index, slot in enumerate( material_list ):
  196.                 self.matDump += '''\n\t\t*SUBMATERIAL {0} {{\
  197.                                {1}\
  198.                                \n\t\t}}'''.format( index, cMaterial( slot ) )
  199.  
  200.         self.dump += '''\n\t\t*MATERIAL_NAME "{0}"\
  201.                       \n\t\t*MATERIAL_CLASS "{1}"\
  202.                       \n\t\t*MATERIAL_AMBIENT {2}\
  203.                       \n\t\t*MATERIAL_DIFFUSE {3}\
  204.                       \n\t\t*MATERIAL_SPECULAR {4}\                      
  205.                       \n\t\t*MATERIAL_TRANSPARENCY {5}\
  206.                       \n\t\t*MATERIAL_WIRESIZE {6}\
  207.                       \n\t\t*MATERIAL_SHADING {7}\
  208.                       \n\t\t*MATERIAL_XP_FALLOFF {8}\
  209.                       \n\t\t*MATERIAL_SELFILLUM {9}\
  210.                       \n\t\t*MATERIAL_FALLOFF {10}\
  211.                       \n\t\t*MATERIAL_XP_TYPE {11}\
  212.                       {12}\
  213.                       \n\t\t*NUMSUBMTLS {13}\
  214.                       {14}'''.format( self.name, self.matClass, self.ambient, self.diffuse, self.specular, self.transparency, self.wiresize, self.shading, self.xpfalloff, self.selfillum, self.falloff, self.xptype, self.diffuseDump, self.numSubMtls, self.matDump )
  215.  
  216.  
  217.         self.dump += '\n}'
  218.  
  219.     def diffdump( self ):
  220.         for x in [self.diffusemap]:
  221.             return x
  222.  
  223.     def __repr__( self ):
  224.         return self.dump
  225. class cMaterial:
  226.     def __init__( self, slot ):
  227.         self.dump = ''
  228.         self.name = slot.name
  229.         self.matClass = 'Standard'
  230.         self.ambient = ''.join( [aseFloat( x ) for x in [0.0, 0.0, 0.0]] )
  231.         self.diffuse = ''.join( [aseFloat( x ) for x in slot.diffuse_color] )
  232.         self.specular = ''.join( [aseFloat( x ) for x in slot.specular_color] )
  233.         #self.shine = aseFloat( slot.specular_hardness / 511 )
  234.         #self.shinestrength = aseFloat( slot.specular_intensity )
  235.         self.transparency = aseFloat( 1.0 )
  236.         self.wiresize = aseFloat( 1.0 )
  237.  
  238.         # Material Definition
  239.         self.shading = str( "Phong" ).capitalize()
  240.         self.xpfalloff = aseFloat( 0.0 )
  241.         self.xptype = 'Filter'
  242.         self.falloff = 'In'
  243.         self.soften = False
  244.         self.diffusemap = cDiffusemap(slot) #slot.texture_slots[0] )
  245.         self.submtls = []
  246.         self.selfillum = aseFloat( 0.0 )
  247.         self.dump = '''\n\t\t*MATERIAL_NAME "{0}"\
  248.                       \n\t\t*MATERIAL_CLASS "{1}"\
  249.                       \n\t\t*MATERIAL_AMBIENT {2}\
  250.                       \n\t\t*MATERIAL_DIFFUSE {3}\
  251.                       \n\t\t*MATERIAL_SPECULAR {4}\
  252.                       \n\t\t*MATERIAL_TRANSPARENCY {5}\
  253.                       \n\t\t*MATERIAL_WIRESIZE {6}\
  254.                       \n\t\t*MATERIAL_SHADING {7}\
  255.                       \n\t\t*MATERIAL_XP_FALLOFF {8}\
  256.                       \n\t\t*MATERIAL_SELFILLUM {9}\
  257.                       \n\t\t*MATERIAL_FALLOFF {10}\
  258.                       \n\t\t*MATERIAL_XP_TYPE {11}\
  259.                       {12}\
  260.                       '''.format( self.name, self.matClass, self.ambient, self.diffuse, self.specular, self.transparency, self.wiresize, self.shading, self.xpfalloff, self.selfillum, self.falloff, self.xptype, self.diffdump() )
  261.  
  262.     def diffdump( self ):
  263.         for x in [self.diffusemap]:
  264.             return x
  265.  
  266.     def __repr__( self ):
  267.         return self.dump
  268. class cDiffusemap:
  269.     def __init__( self, slot ):
  270.         import os
  271.         self.dump = ''
  272.         if slot is None:
  273.             self.name = 'default'
  274.             self.mapclass = 'Bitmap'
  275.             self.bitmap = 'None'
  276.         else:
  277.             self.name = slot.name
  278.            
  279.             #texture slot
  280.             if hasattr(slot, 'texture'):
  281.                 if slot.texture.type == 'IMAGE':
  282.                     self.mapclass = 'Bitmap'
  283.                     self.bitmap = slot.texture.image.filepath
  284.                     if slot.texture.image.has_data:
  285.                         pass
  286.                     else:
  287.                         self.bitmap = '\\\\base\\' + self.bitmap.replace( '/', '\\' )
  288.                 else:
  289.                     self.mapclass = 'Bitmap'
  290.                     self.bitmap = 'None'
  291.             #material slot
  292.             else:
  293.                 self.mapclass = 'Bitmap'
  294.                 self.bitmap = '\\\\base\\' + self.name.replace( '/', '\\' )
  295.                
  296.         self.subno = 1
  297.         self.amount = aseFloat( 1.0 )
  298.         self.type = 'Screen'
  299.         self.uoffset = aseFloat( 0.0 )
  300.         self.voffset = aseFloat( 0.0 )
  301.         self.utiling = aseFloat( 1.0 )
  302.         self.vtiling = aseFloat( 1.0 )
  303.         self.angle = aseFloat( 0.0 )
  304.         self.blur = aseFloat( 1.0 )
  305.         self.bluroffset = aseFloat( 0.0 )
  306.         self.noiseamt = aseFloat( 1.0 )
  307.         self.noisesize = aseFloat( 1.0 )
  308.         self.noiselevel = 1
  309.         self.noisephase = aseFloat( 0.0 )
  310.         self.bitmapfilter = 'Pyramidal'
  311.  
  312.         self.dump = '''\n\t\t*MAP_DIFFUSE {{\
  313.                       \n\t\t\t*MAP_NAME "{0}"\
  314.                       \n\t\t\t*MAP_CLASS "{1}"\
  315.                       \n\t\t\t*MAP_SUBNO {2}\
  316.                       \n\t\t\t*MAP_AMOUNT {3}\
  317.                       \n\t\t\t*BITMAP "{4}"\
  318.                       \n\t\t\t*MAP_TYPE {5}\
  319.                       \n\t\t\t*UVW_U_OFFSET {6}\
  320.                       \n\t\t\t*UVW_V_OFFSET {7}\
  321.                       \n\t\t\t*UVW_U_TILING {8}\
  322.                       \n\t\t\t*UVW_V_TILING {9}\
  323.                       \n\t\t\t*UVW_ANGLE {10}\
  324.                       \n\t\t\t*UVW_BLUR {11}\
  325.                       \n\t\t\t*UVW_BLUR_OFFSET {12}\
  326.                       \n\t\t\t*UVW_NOUSE_AMT {13}\
  327.                       \n\t\t\t*UVW_NOISE_SIZE {14}\
  328.                       \n\t\t\t*UVW_NOISE_LEVEL {15}\
  329.                       \n\t\t\t*UVW_NOISE_PHASE {16}\
  330.                       \n\t\t\t*BITMAP_FILTER {17}\
  331.                       \n\t\t}}\
  332.                       '''.format( self.name, self.mapclass, self.subno, self.amount, self.bitmap, self.type, self.uoffset, self.voffset, self.utiling, self.vtiling, self.angle, self.blur, self.bluroffset, self.noiseamt, self.noisesize, self.noiselevel, self.noisephase, self.bitmapfilter )
  333.  
  334.     def __repr__( self ):
  335.         return self.dump
  336.  
  337. #== Geometry ===============================================================
  338. class cGeomObject:
  339.     def __init__( self, object ):
  340.         print( object.name + ": Constructing Geometry" )
  341.         global optionAllowMultiMats
  342.         global currentMatId
  343.  
  344.         self.name = object.name
  345.         self.prop_motionblur = 0
  346.         self.prop_castshadow = 1
  347.         self.prop_recvshadow = 1
  348.  
  349.         if optionAllowMultiMats:
  350.             self.material_ref = 0
  351.         else:
  352.             self.material_ref = matList.index( object.material_slots[object.data.polygons[0].material_index].material.name ) #currentMatId
  353.  
  354.         self.nodetm = cNodeTM( object )
  355.         self.mesh = cMesh( object )
  356.  
  357.         self.dump = '''\n*GEOMOBJECT {{\n\t*NODE_NAME "{0}"\n{1}\n{2}\n\t*PROP_MOTIONBLUR {3}\n\t*PROP_CASTSHADOW {4}\n\t*PROP_RECVSHADOW {5}\n\t*MATERIAL_REF {6}\n}}'''.format( self.name, self.nodetm, self.mesh, self.prop_motionblur, self.prop_castshadow, self.prop_recvshadow, self.material_ref )
  358.  
  359.     def __repr__( self ):
  360.         return self.dump
  361. class cNodeTM:
  362.     def __init__( self, object ):
  363.         self.name = object.name
  364.         self.inherit_pos = '0 0 0'
  365.         self.inherit_rot = '0 0 0'
  366.         self.inherit_scl = '0 0 0'
  367.         self.tm_row0 = '1.0000 0.0000 0.0000'
  368.         self.tm_row1 = '0.0000 1.0000 0.0000'
  369.         self.tm_row2 = '0.0000 0.0000 1.0000'
  370.         self.tm_row3 = '0.0000 0.0000 0.0000'
  371.         self.tm_pos = '0.0000 0.0000 0.0000'
  372.         self.tm_rotaxis = '0.0000 0.0000 0.0000'
  373.         self.tm_rotangle = '0.0000'
  374.         self.tm_scale = '1.0000 1.0000 1.0000'
  375.         self.tm_scaleaxis = '0.0000 0.0000 0.0000'
  376.         self.tm_scaleaxisang = '0.0000'
  377.  
  378.         self.dump = '''\t*NODE_TM {{\
  379.                       \n\t\t*NODE_NAME "{0}"\
  380.                       \n\t\t*INHERIT_POS {1}\
  381.                       \n\t\t*INHERIT_ROT {2}\
  382.                       \n\t\t*INHERIT_SCL {3}\
  383.                       \n\t\t*TM_ROW0 {4}\
  384.                       \n\t\t*TM_ROW1 {5}\
  385.                       \n\t\t*TM_ROW2 {6}\
  386.                       \n\t\t*TM_ROW3 {7}\
  387.                       \n\t\t*TM_POS {8}\
  388.                       \n\t\t*TM_ROTAXIS {9}\
  389.                       \n\t\t*TM_ROTANGLE {10}\
  390.                       \n\t\t*TM_SCALE {11}\
  391.                       \n\t\t*TM_SCALEAXIS {12}\
  392.                       \n\t\t*TM_SCALEAXISANG {13}\
  393.                       \n\t}}'''.format( self.name, self.inherit_pos, self.inherit_rot, self.inherit_scl, self.tm_row0, self.tm_row1, self.tm_row2, self.tm_row3, self.tm_pos, self.tm_rotaxis, self.tm_rotangle, self.tm_scale, self.tm_scaleaxis, self.tm_scaleaxisang )
  394.  
  395.     def __repr__( self ):
  396.         return self.dump
  397. class cMesh:
  398.     def __init__( self, object ):
  399.         bpy.ops.mesh.reveal
  400.  
  401.         if collisionObject( object ) == False:
  402.             object.data.uv_layers.active_index = 0
  403.             object.data.uv_layer_stencil_index = 0
  404.             self.tvertlist = cTVertlist( object )
  405.             self.numtvertex = self.tvertlist.length
  406.             #self.numtvfaces = len( object.data.uv_layer_stencil.data )
  407.             self.numtvfaces = len( object.data.polygons )
  408.             self.tfacelist = cTFacelist( self.numtvfaces )
  409.             self.uvmapchannels = self.uvdump( object )
  410.  
  411.             # OUTPUT
  412.             self.tvertlist_str = '\n\t\t*MESH_TVERTLIST ' + str( self.tvertlist )
  413.             self.numtvertex_str = '\n\t\t*MESH_NUMTVERTEX ' + str( self.numtvertex )
  414.             self.numtvfaces_str = '\n\t\t*MESH_NUMTVFACES ' + str( self.numtvfaces )
  415.             self.tfacelist_str = '\n\t\t*MESH_TFACELIST ' + str( self.tfacelist )
  416.  
  417.         else:
  418.             self.tvertlist_str = ''
  419.             self.numtvertex_str = ''
  420.             self.numtvfaces_str = ''
  421.             self.tfacelist_str = ''
  422.             self.uvmapchannels = ''
  423.  
  424.         self.timevalue = '0'
  425.         self.numvertex = len( object.data.vertices )
  426.         self.numfaces = len( object.data.polygons )
  427.         self.vertlist = cVertlist( object )
  428.         self.facelist = cFacelist( object )
  429.  
  430.         # Vertex Paint
  431.         if len( object.data.vertex_colors ) > 0:
  432.             self.cvertlist = cCVertlist( object )
  433.             self.numcvertex = self.cvertlist.length
  434.             self.numcvfaces = len( object.data.vertex_colors.data.polygons )
  435.             self.cfacelist = cCFacelist( self.numcvfaces )
  436.             # change them into strings now
  437.             self.cvertlist = '\n{0}'.format( self.cvertlist )
  438.             self.numcvertex = '\n\t\t*MESH_NUMCVERTEX {0}'.format( self.numcvertex )
  439.             self.numcvfaces = '\n\t\t*MESH_NUMCVFACES {0}'.format( self.numcvfaces )
  440.             self.cfacelist = '\n{0}'.format( self.cfacelist )
  441.         else:
  442.             self.cvertlist = ''
  443.             self.numcvertex = ''
  444.             self.numcvfaces = ''
  445.             self.cfacelist = ''
  446.  
  447.         self.normals = cNormallist( object )
  448.  
  449.     # get uv layer names for specified object
  450.     def getUVLayerNames( self, object ):
  451.         self.uvLayerNames = []
  452.         obj = object.data
  453.         for uv in obj.uv_layers.keys():
  454.             self.uvLayerNames.append( str( uv ) )
  455.  
  456.     def uvdump( self, object ):
  457.         self.mappingchannels = ''
  458.         # if there is more than 1 uv layer
  459.         if collisionObject( object ) == False:
  460.             self.getUVLayerNames( object )
  461.             if len( self.uvLayerNames ) > 1:
  462.                 # save uv actives
  463.                 active_uv = object.data.uv_layers.active_index
  464.                 obj = object.data
  465.                 activeUV = 0
  466.                 for uvname in self.uvLayerNames:
  467.                     if activeUV == 0:
  468.                         activeUV += 1
  469.                         continue
  470.                     obj.uv_layers.active_index = activeUV
  471.                     obj.uv_layer_stencil_index = activeUV
  472.                     self.uvm_tvertlist = cTVertlist( object )
  473.                     self.uvm_numtvertex = self.uvm_tvertlist.length
  474.                     self.uvm_numtvfaces = len( object.data.uv_layer_stencil.data)
  475.                     self.uvm_tfacelist = cTFacelist( self.uvm_numtvfaces )
  476.  
  477.                     # if len(object.data.vertex_colors) > 0:
  478.                         # self.uvm_cvertlist = cCVertlist(object)
  479.                         # self.uvm_numcvertex = self.uvm_cvertlist.length
  480.                         # self.uvm_numcvfaces = len(object.data.vertex_colors[0].data)
  481.                         # self.uvm_cfacelist = cCFacelist(self.uvm_numcvfaces)
  482.  
  483.                         # self.uvm_cvertlist = '\n{0}'.format(self.uvm_cvertlist)
  484.                         # self.uvm_numcvertex = '\n\t\t*MESH_NUMCVERTEX {0}'.format(self.uvm_numcvertex)
  485.                         # self.uvm_numcvfaces = '\n\t\t*MESH_NUMCVFACES {0}'.format(self.uvm_numcvfaces)
  486.                         # self.uvm_cfacelist = '\n{0}'.format(self.uvm_cfacelist)
  487.                     # else:
  488.                     self.uvm_numcvertex = ''
  489.                     self.uvm_numcvfaces = ''
  490.                     self.uvm_cvertlist = ''
  491.                     self.uvm_cfacelist = ''
  492.  
  493.                     # print extra mapping channels
  494.                     self.mappingchannels += '''\n\t\t*MESH_MAPPINGCHANNEL {0} {{\n\t\t\t*MESH_NUMTVERTEX {1}\n\t\t\t*MESH_TVERTLIST {2}\n\t\t*MESH_NUMTVFACES {3}\n\t\t*MESH_TFACELIST {4}{5}{6}{7}{8}\n\t\t}}'''.format( str( activeUV + 1 ), self.uvm_numtvertex, self.uvm_tvertlist, self.uvm_numtvfaces, self.uvm_tfacelist, self.uvm_numcvertex, self.uvm_cvertlist, self.uvm_numcvfaces, self.uvm_cfacelist )
  495.                     activeUV = activeUV + 1
  496.  
  497.                 # restore uv actives
  498.                 object.data.uv_layers.active_index = active_uv
  499.  
  500.         return self.mappingchannels
  501.  
  502.     # UV textures go AFTER MESH_FACE_LIST
  503.     # MESH_NUMTVERTEX, MESH_TVERTLIST, MESH_NUMTVFACES, MESH_TFACELIST        
  504.     def __repr__( self ):
  505.         temp = '''\t*MESH {{\n\t\t*TIMEVALUE {0}\n\t\t*MESH_NUMVERTEX {1}\n\t\t*MESH_NUMFACES {2}\n\t\t*MESH_VERTEX_LIST {3}\n\t\t*MESH_FACE_LIST {4}{5}{6}{7}{8}{9}{10}{11}{12}{13}\n{14}\n\t}}'''.format( self.timevalue, self.numvertex, self.numfaces, self.vertlist, self.facelist, self.numtvertex_str, self.tvertlist_str, self.numtvfaces_str, self.tfacelist_str, self.numcvertex, self.cvertlist, self.numcvfaces, self.cfacelist, self.uvmapchannels, self.normals )
  506.         return temp
  507. class cVertlist:
  508.     def __init__( self, object ):
  509.         self.vertlist = []
  510.         for data in object.data.vertices:
  511.             temp = cVert( data.index, data.co.to_tuple( 4 ) )
  512.             self.vertlist.append( temp )
  513.  
  514.     def dump( self ):
  515.         temp = ''
  516.         for x in self.vertlist:
  517.             temp += str( x )
  518.         return temp
  519.  
  520.     def __repr__( self ):
  521.         return '''{{\n{0}\t\t}}'''.format( self.dump() )
  522. class cVert:
  523.     def __init__( self, index, coord ):
  524.         global optionScale
  525.  
  526.         self.index = index
  527.         self.x = aseFloat( coord[0] * optionScale )
  528.         self.y = aseFloat( coord[1] * optionScale )
  529.         self.z = aseFloat( coord[2] * optionScale )
  530.  
  531.     def __repr__( self ):
  532.         return '''\t\t\t*MESH_VERTEX {0} {1} {2} {3}\n'''.format( self.index, self.x, self.y, self.z )
  533. class cFacelist:
  534.     def __init__( self, object ):
  535.         global optionAllowMultiMats
  536.         global matList
  537.         global numMats
  538.         global currentMatId
  539.  
  540.         self.facelist = []
  541.         sgID = 0
  542.  
  543.         # Define smoothing groups (if enabled)
  544.         if ( collisionObject( object ) == False ):
  545.             if ( optionSmoothingGroups ):
  546.                 self.smoothing_groups = defineSmoothing( self, object )
  547.             else:
  548.                 self.smoothing_groups = ''
  549.  
  550.         for face in object.data.polygons:
  551.             if optionAllowMultiMats:
  552.                 if ( collisionObject( object ) == False ):
  553.                     self.matid = matList.index( object.material_slots[face.material_index].material.name )
  554.                 else:
  555.                     self.matid = 0
  556.             else:
  557.                 self.matid = currentMatId
  558.             if ( collisionObject( object ) == False ):
  559.                 if ( optionSmoothingGroups ):
  560.                     for group in self.smoothing_groups:
  561.                         if group.count( face.index ) == 0:
  562.                             continue
  563.                         else:
  564.                             #TODO: Compress sg's
  565.                             index = self.smoothing_groups.index( group )
  566.                             sgID = index % 32
  567.  
  568.             temp = '''\t\t\t*MESH_FACE {0}: A: {1} B: {2} C: {3} AB: 0 BC: 0 CA: 0 *MESH_SMOOTHING {4} *MESH_MTLID {5}\n'''.format( face.index, face.vertices[0], face.vertices[1], face.vertices[2], sgID, self.matid )
  569.             self.facelist.append( temp )
  570.  
  571.         #if currentMatId < numMats - 1:
  572.         #    currentMatId += 1
  573.         #else:
  574.         #    currentMatId = 0
  575.         #currentMatId = matList.index( object.material_slots[0].material.name )
  576.  
  577.     def dump( self ):
  578.         temp = ''
  579.         for x in self.facelist:
  580.             temp = temp + str( x )
  581.         return temp
  582.  
  583.     def __repr__( self ):
  584.         return '''{{\n{0}\t\t}}'''.format( self.dump() )
  585. class cTVertlist:
  586.     def __init__( self, object ):
  587.         self.vertlist = []
  588.  
  589.         # update tessface
  590.         mesh = bpy.context.object.data
  591.         mesh.update()
  592.         uv_layers_count = len( mesh.uv_layers )
  593.         mesh.calc_loop_triangles()
  594.  
  595.         for index, face in enumerate( mesh.loop_triangles ):
  596.             if len( mesh.uv_layers ) == 0:
  597.                 raise cError( "cError:  No UV texture data for " + object.name )
  598.             else:
  599.                 temp = cTVert( ( index * 3 ), mesh.uv_layers[mesh.uv_layers.active_index].data[face.index * 3 + 0].uv )
  600.                 self.vertlist.append( temp )
  601.                 temp = cTVert( ( index * 3 ) + 1, mesh.uv_layers[mesh.uv_layers.active_index].data[face.index * 3 + 1].uv )
  602.                 self.vertlist.append( temp )
  603.                 temp = cTVert( ( index * 3 ) + 2, mesh.uv_layers[mesh.uv_layers.active_index].data[face.index * 3 + 2].uv )
  604.                 self.vertlist.append( temp )
  605.  
  606.         self.length = len( self.vertlist )
  607.  
  608.     def dump( self ):
  609.         temp = ''
  610.         for x in self.vertlist:
  611.             temp += str( x )
  612.         return temp
  613.  
  614.     def __repr__( self ):
  615.         return '''{{\n{0}\t\t}}'''.format( self.dump() )
  616. class cTVert:
  617.     def __init__( self, index, coord ):
  618.         self.index = index
  619.         self.u = aseFloat( coord[0] )
  620.         self.v = aseFloat( coord[1] )
  621.  
  622.     def __repr__( self ):
  623.         return '''\t\t\t*MESH_TVERT {0} {1} {2} 0.0000\n'''.format( self.index, self.u, self.v )
  624. class cTFacelist:
  625.     def __init__( self, facecount ):
  626.         self.facelist = []
  627.         for data in range( facecount ):
  628.             temp = cTFace( data )
  629.             self.facelist.append( temp )
  630.  
  631.     def dump( self ):
  632.         temp = ''
  633.         for x in self.facelist:
  634.             temp = temp + str( x )
  635.         return temp
  636.  
  637.     def __repr__( self ):
  638.         return '''{{\n{0}\t\t}}'''.format( self.dump() )
  639. class cTFace:
  640.     def __init__( self, x ):
  641.         self.index = x
  642.         self.vertices = []
  643.         self.vertices.append( x * 3 )
  644.         self.vertices.append( ( x * 3 ) + 1 )
  645.         self.vertices.append( ( x * 3 ) + 2 )
  646.  
  647.     def __repr__( self ):
  648.         return '''\t\t\t*MESH_TFACE {0} {1} {2} {3}\n'''.format( self.index, self.vertices[0], self.vertices[1], self.vertices[2] )
  649. class cCVertlist:
  650.     def __init__( self, object ):
  651.         self.vertlist = []
  652.         self.index = 0
  653.  
  654.         # Blender 2.63+
  655.         bpy.ops.object.mode_set( mode = 'OBJECT' )
  656.         object.data.calc_loop_triangles()
  657.  
  658.         for face in object.data.loop_triangles:
  659.             temp = object.data.vertex_colors[0].data[face.index + 1].color
  660.             self.vertlist.append( cCVert( self.index, temp ) )
  661.             temp = object.data.vertex_colors[0].data[face.index + 2].color
  662.             self.vertlist.append( cCVert( self.index + 1, temp ) )
  663.             temp = object.data.vertex_colors[0].data[face.index + 3].color
  664.             self.vertlist.append( cCVert( self.index + 2, temp ) )
  665.             self.index += 3
  666.  
  667.         self.length = len( self.vertlist )
  668.  
  669.     def dump( self ):
  670.         temp = ''
  671.         for x in self.vertlist:
  672.             temp = temp + str( x )
  673.         return temp
  674.  
  675.     def __repr__( self ):
  676.         return '''\t\t*MESH_CVERTLIST {{\n{0}\t\t}}'''.format( self.dump() )
  677. class cCVert:
  678.     def __init__( self, index, temp ):
  679.         self.index = index
  680.         self.r = aseFloat( float( temp[0] ) )
  681.         self.g = aseFloat( float( temp[1] ) )
  682.         self.b = aseFloat( float( temp[2] ) )
  683.  
  684.     def __repr__( self ):
  685.         return '''\t\t\t*MESH_VERTCOL {0} {1} {2} {3}\n'''.format( self.index, self.r, self.g, self.b )
  686. class cCFacelist:
  687.     def __init__( self, facecount ):
  688.         temp = [0 for x in range( facecount )]
  689.         self.facelist = []
  690.         for index, data in enumerate( temp ):
  691.             self.facelist.append( cCFace( index, data ) )
  692.  
  693.     def dump( self ):
  694.         temp = ''
  695.         for x in self.facelist:
  696.             temp = temp + str( x )
  697.         return temp
  698.  
  699.     def __repr__( self ):
  700.         return '''\t\t*MESH_CFACELIST {{\n{0}\t\t}}'''.format( self.dump() )
  701. class cCFace:
  702.     def __init__( self, index, data ):
  703.         self.index = index
  704.         self.vertices = []
  705.         self.vertices.append( index * 3 )
  706.         self.vertices.append( ( index * 3 ) + 1 )
  707.         self.vertices.append( ( index * 3 ) + 2 )
  708.  
  709.     def __repr__( self ):
  710.         return '''\t\t\t*MESH_CFACE {0} {1} {2} {3}\n'''.format( self.index, self.vertices[0], self.vertices[1], self.vertices[2] )
  711. class cNormallist:
  712.     def __init__( self, object ):
  713.         self.normallist = []
  714.         for face in object.data.polygons:
  715.             self.normallist.append( cNormal( face, object ) )
  716.  
  717.     def dump( self ):
  718.         temp = ''
  719.         for x in self.normallist:
  720.             temp = temp + str( x )
  721.         return temp
  722.  
  723.     def __repr__( self ):
  724.         return '''\t\t*MESH_NORMALS {{\n{0}\t\t}}'''.format( self.dump() )
  725. class cNormal:
  726.     def __init__( self, face, object ):
  727.         self.faceindex = face.index
  728.         self.facenormal = [aseFloat( x ) for x in face.normal.to_tuple( 4 )]
  729.         self.vertnormals = []
  730.         for x in face.vertices:
  731.             self.vertnormals.append( [x, [aseFloat( y ) for y in object.data.vertices[x].normal.to_tuple( 4 )]] )
  732.  
  733.     def __repr__( self ):
  734.         return '''\t\t\t*MESH_FACENORMAL {0} {1} {2} {3}\n\t\t\t\t*MESH_VERTEXNORMAL {4} {5} {6} {7}\n\t\t\t\t*MESH_VERTEXNORMAL {8} {9} {10} {11}\n\t\t\t\t*MESH_VERTEXNORMAL {12} {13} {14} {15}\n'''.format( self.faceindex, self.facenormal[0], self.facenormal[1], self.facenormal[2], self.vertnormals[0][0], self.vertnormals[0][1][0], self.vertnormals[0][1][1], self.vertnormals[0][1][2], self.vertnormals[1][0], self.vertnormals[1][1][0], self.vertnormals[1][1][1], self.vertnormals[1][1][2], self.vertnormals[2][0], self.vertnormals[2][1][0], self.vertnormals[2][1][1], self.vertnormals[2][1][2] )
  735.  
  736. #== Smoothing Groups and Helper Methods =================================
  737. def defineSmoothing( self, object ):
  738.     print( object.name + ": Constructing Smoothing Groups" )
  739.  
  740.     seam_edge_list = []
  741.     sharp_edge_list = []
  742.  
  743.     _mode = object.mode
  744.     #ctx = bpy.context.copy()
  745.     #ctx["object"] = object
  746.     #print("MODE: ", _mode)
  747.     #bpy.context.scene.objects.active = object
  748.     bpy.ops.object.mode_set(mode = 'EDIT')
  749.     bpy.ops.mesh.select_all( action = 'DESELECT' )
  750.     setSelMode( 'EDGE' )
  751.  
  752.     # Get seams and clear them
  753.     #bpy.ops.object.mode_set(ctx, mode = 'OBJECT' )
  754.     for edge in object.data.edges:
  755.         if edge.use_seam:
  756.             seam_edge_list.append( edge.index )
  757.             edge.select = True
  758.  
  759.     #bpy.ops.object.mode_set(ctx, mode = 'EDIT' )
  760.     bpy.ops.mesh.select_all( action = 'SELECT' )
  761.     bpy.ops.mesh.mark_seam( clear = True )
  762.  
  763.     # Get sharp edges, convert them to seams
  764.     bpy.ops.mesh.select_all( action = 'DESELECT' )
  765.     #bpy.ops.object.mode_set(ctx, mode = 'OBJECT' )
  766.     for edge in object.data.edges:
  767.         if edge.use_edge_sharp:
  768.             sharp_edge_list.append( edge )
  769.             edge.select = True
  770.  
  771.    # bpy.ops.object.mode_set( mode = 'EDIT' )
  772.     bpy.ops.mesh.mark_seam()
  773.  
  774.     bpy.ops.mesh.select_all( action = 'DESELECT' )
  775.  
  776.     smoothing_groups = []
  777.     face_list = []
  778.  
  779.     mode = getSelMode( self, False )
  780.     setSelMode( 'FACE' )
  781.  
  782.     for face in object.data.polygons:
  783.         face_list.append( face.index )
  784.  
  785.     while len( face_list ) > 0:
  786.         bpy.ops.object.mode_set( mode = 'OBJECT' )
  787.         object.data.polygons[face_list[0]].select = True
  788.         bpy.ops.object.mode_set( mode = 'EDIT' )
  789.         bpy.ops.mesh.select_linked( limit = True )
  790.  
  791.         # TODO - update when API is updated
  792.         selected_faces = getSelectedFaces( self, True )
  793.         smoothing_groups.append( selected_faces )
  794.         for face_index in selected_faces:
  795.             if face_list.count( face_index ) > 0:
  796.                 face_list.remove( face_index )
  797.         bpy.ops.mesh.select_all( action = 'DESELECT' )
  798.  
  799.     setSelMode( mode, False )
  800.  
  801.     # Clear seams created by sharp edges
  802.     #bpy.ops.object.mode_set( mode = 'OBJECT' )
  803.     for edge in object.data.edges:
  804.         if edge.use_seam:
  805.             edge.select = True
  806.  
  807.     #bpy.ops.object.mode_set( mode = 'EDIT' )
  808.     bpy.ops.mesh.mark_seam( clear = True )
  809.  
  810.     bpy.ops.mesh.select_all( action = 'DESELECT' )
  811.     # Restore original uv seams
  812.    # bpy.ops.object.mode_set( mode = 'OBJECT' )
  813.     for edge_index in seam_edge_list:
  814.         object.data.edges[edge_index].select = True
  815.  
  816.    # bpy.ops.object.mode_set( mode = 'EDIT' )
  817.     bpy.ops.mesh.mark_seam()
  818.  
  819.     bpy.ops.object.mode_set(mode = 'OBJECT' )
  820.     print( '\t' + str( len( smoothing_groups ) ) + ' smoothing groups found.' )
  821.     return smoothing_groups
  822.  
  823. #===========================================================================
  824. # // General Helpers
  825. #===========================================================================
  826.  
  827. # Check if the mesh is a collider
  828. # Return True if collision model, else: false
  829. def collisionObject( object ):
  830.     collisionPrefixes = ['UCX_', 'UBX_', 'USX_']
  831.     for prefix in collisionPrefixes:
  832.         if object.name.find( str( prefix ) ) >= 0:
  833.             return True
  834.     return False
  835.  
  836. # Set the selection mode    
  837. def setSelMode( mode, default = True ):
  838.     if default:
  839.         if mode == 'VERT':
  840.             bpy.context.tool_settings.mesh_select_mode = [True, False, False]
  841.         elif mode == 'EDGE':
  842.             bpy.context.tool_settings.mesh_select_mode = [False, True, False]
  843.         elif mode == 'FACE':
  844.             bpy.context.tool_settings.mesh_select_mode = [False, False, True]
  845.         else:
  846.             return False
  847.     else:
  848.         bpy.context.tool_settings.mesh_select_mode = mode
  849.         return True
  850. def getSelMode( self, default = True ):
  851.     if default:
  852.         if bpy.context.tool_settings.mesh_select_mode[0] == True:
  853.             return 'VERT'
  854.         elif bpy.context.tool_settings.mesh_select_mode[1] == True:
  855.             return 'EDGE'
  856.         elif bpy.context.tool_settings.mesh_select_mode[2] == True:
  857.             return 'FACE'
  858.         return False
  859.     else:
  860.         mode = []
  861.         for value in bpy.context.tool_settings.mesh_select_mode:
  862.             mode.append( value )
  863.  
  864.         return mode
  865. def getSelectedFaces( self, index = False ):
  866.     selected_faces = []
  867.     # Update mesh data
  868.     bpy.ops.object.editmode_toggle()
  869.     bpy.ops.object.editmode_toggle()
  870.  
  871.     _mode = bpy.context.view_layer.objects.active.mode
  872.     bpy.ops.object.mode_set( mode = 'EDIT' )
  873.  
  874.     object = bpy.context.view_layer.objects.active
  875.     for face in object.data.polygons:
  876.         if face.select == True:
  877.             if index == False:
  878.                 selected_faces.append( face )
  879.             else:
  880.                 selected_faces.append( face.index )
  881.  
  882.     bpy.ops.object.mode_set( mode = _mode )
  883.  
  884.     return selected_faces
  885.  
  886. #== Core ===================================================================
  887.  
  888. from bpy_extras.io_utils import ExportHelper
  889. from bpy.props import StringProperty, BoolProperty, FloatProperty
  890.  
  891. class ExportAse( bpy.types.Operator, ExportHelper ):
  892.     '''Load an Ascii Scene Export File'''
  893.     bl_idname = "export.ase"
  894.     bl_label = "Export"
  895.     __doc__ = "Ascii Scene Exporter (.ase)"
  896.     filename_ext = ".ase"
  897.     filter_glob : StringProperty( default = "*.ase", options = {'HIDDEN'} )
  898.  
  899.     filepath : StringProperty(
  900.         name = "File Path",
  901.         description = "File path used for exporting the ASE file",
  902.         maxlen = 1024,
  903.         default = "" )
  904.  
  905.     option_triangulate : BoolProperty(
  906.             name = "Triangulate",
  907.             description = "Triangulates all exportable objects",
  908.             default = True )
  909.  
  910.     option_normals : BoolProperty(
  911.             name = "Recalculate Normals",
  912.             description = "Recalculate normals before exporting",
  913.             default = False )
  914.  
  915.     option_remove_doubles : BoolProperty(
  916.             name = "Remove Doubles",
  917.             description = "Remove any duplicate vertices before exporting",
  918.             default = False )
  919.  
  920.     option_apply_scale : BoolProperty(
  921.             name = "Scale",
  922.             description = "Apply scale transformation",
  923.             default = True )
  924.  
  925.     option_apply_location : BoolProperty(
  926.             name = "Location",
  927.             description = "Apply location transformation",
  928.             default = True )
  929.  
  930.     option_apply_rotation : BoolProperty(
  931.             name = "Rotation",
  932.             description = "Apply rotation transformation",
  933.             default = True )
  934.  
  935.     option_smoothinggroups = False # BoolProperty(
  936.             #name = "Smoothing Groups",
  937.             #description = "Construct hard edge islands as smoothing groups",
  938.             #default = False )
  939.  
  940.     option_separate : BoolProperty(
  941.             name = "Separate",
  942.             description = "A separate ASE file for every selected object",
  943.             default = False )
  944.    
  945.     # motorsep 01-16-2016
  946.     option_split : BoolProperty(
  947.             name = "Split per material",
  948.             description = "Split object into several per material",
  949.             default = False )
  950.            
  951.     option_submaterials = False #BoolProperty(
  952.             #name = "Use Submaterials (UDK)",
  953.             #description = "Export a single material with multiple sub materials",
  954.             #default = False )
  955.  
  956.     option_allowmultimats = False #BoolProperty(
  957.             #name = "Allow Multiple Materials (UDK)",
  958.             #description = "Allow multiple materials per geometry object",
  959.             #default = False )
  960.  
  961.     option_scale : FloatProperty(
  962.             name = "Scale",
  963.             description = "Object scaling factor (default: 1.0)",
  964.             min = 0.01,
  965.             max = 1000.0,
  966.             soft_min = 0.01,
  967.             soft_max = 1000.0,
  968.             default = 1.0 )
  969.    
  970.     option_copy : BoolProperty(
  971.             name = "Export Copy",
  972.             description = "Export a copy of the objects, use e.g. together with triangulate modifier",
  973.             default = True )
  974.  
  975.     def draw( self, context ):
  976.         layout = self.layout
  977.  
  978.         box = layout.box()
  979.         box.label( text = 'Essentials:' )
  980.         box.prop( self, 'option_triangulate' )
  981.         box.prop( self, 'option_copy')
  982.         box.prop( self, 'option_normals' )
  983.         box.prop( self, 'option_remove_doubles' )
  984.         box.label( text = "Transformations:" )
  985.         box.prop( self, 'option_apply_scale' )
  986.         box.prop( self, 'option_apply_rotation' )
  987.         box.prop( self, 'option_apply_location' )
  988.         #box.label( "Materials:" )
  989.        # box.prop( self, 'option_submaterials' )
  990.         #box.prop( self, 'option_allowmultimats' )
  991.         box.label( text = "Advanced:" )
  992.         box.prop( self, 'option_scale' )
  993.         box.prop( self, 'option_separate')
  994.         box.prop( self, 'option_split')                # motorsep 01-16-2016
  995.         #box.prop( self, 'option_smoothinggroups' )
  996.        
  997.     @classmethod
  998.     def poll( cls, context ):
  999.         active = context.active_object
  1000.         selected = context.selected_objects
  1001.         camera = context.view_layer.camera
  1002.         ok = selected or camera
  1003.         return ok
  1004.  
  1005.     def writeASE( self, filename, data ):
  1006.         print( '\nWriting', filename )
  1007.         try:
  1008.             file = open( filename, 'w' )
  1009.         except IOcError:
  1010.             print( 'cError: The file could not be written to. Aborting.' )
  1011.         else:
  1012.             file.write( data )
  1013.             file.close()
  1014.  
  1015.     def execute( self, context ):
  1016.         start = time.clock()
  1017.  
  1018.         global optionScale
  1019.         global optionSubmaterials
  1020.         global optionSmoothingGroups
  1021.         global optionAllowMultiMats
  1022.  
  1023.         global aseHeader
  1024.         global aseScene
  1025.         global aseMaterials
  1026.         global aseGeometry
  1027.  
  1028.         global currentMatId
  1029.         global numMats
  1030.         global matList
  1031.  
  1032.         # Set globals and reinitialize ase components
  1033.         aseHeader = ''
  1034.         aseScene = ''
  1035.         aseMaterials = ''
  1036.         aseGeometry = ''
  1037.  
  1038.         optionScale = self.option_scale
  1039.         optionSubmaterials = self.option_submaterials
  1040.         optionSmoothingGroups = self.option_smoothinggroups
  1041.         optionAllowMultiMats = self.option_allowmultimats
  1042.  
  1043.         matList = []
  1044.         currentMatId = 0
  1045.         numMats = 0
  1046.  
  1047.         # Build ASE Header, Scene
  1048.         print( '\nAscii Scene Export by MCampagnini\n' )
  1049.         print( 'Objects selected: ' + str( len( bpy.context.selected_objects ) ) )
  1050.         aseHeader = str( cHeader() )
  1051.         aseScene = str( cScene() )
  1052.         aseMaterials = str( cMaterials() )
  1053.  
  1054.         # Apply applicable options
  1055.         for object in bpy.context.selected_objects:
  1056.             if object.type == 'MESH':
  1057.                 bpy.context.view_layer.objects.active = object
  1058.                 object.select_set(True)
  1059.  
  1060.                 # Options
  1061.                 bpy.ops.object.mode_set( mode = 'EDIT' )
  1062.                 if self.option_remove_doubles:
  1063.                     bpy.ops.object.mode_set( mode = 'EDIT' )
  1064.                     bpy.ops.mesh.select_all( action = 'SELECT' )
  1065.                     bpy.ops.mesh.remove_doubles()
  1066.                 if self.option_triangulate:
  1067.                     print( object.name + ': Converting to triangles' )
  1068.                     bpy.ops.mesh.select_all( action = 'SELECT' )
  1069.                     bpy.ops.mesh.quads_convert_to_tris()
  1070.                 if self.option_normals:
  1071.                     print( object.name + ': Recalculating normals' )
  1072.                     bpy.ops.object.mode_set( mode = 'EDIT' )
  1073.                     bpy.ops.mesh.select_all( action = 'SELECT' )
  1074.                     bpy.ops.mesh.normals_make_consistent()
  1075.  
  1076.                 # Transformations
  1077.                 bpy.ops.object.mode_set( mode = 'OBJECT' )
  1078.                 bpy.ops.object.transform_apply( location = self.option_apply_location, rotation = self.option_apply_rotation, scale = self.option_apply_scale )
  1079.  
  1080.                 #Construct ASE Geometry Nodes
  1081.                
  1082.                 aseGeom = ''
  1083.                 #Copy object by creating a new mesh from it, applying modifiers
  1084.                
  1085.                 if (self.option_copy):
  1086.                     orig_mesh = object.data
  1087.                     meshes = [bpy.data.meshes.new_from_object(object, preserve_all_data_layers=True)]
  1088.  
  1089.                     if self.option_split:
  1090.                         import bmesh
  1091.  
  1092.                         object.data = meshes[0]
  1093.                         bpy.ops.object.mode_set(mode = 'EDIT')
  1094.                         meshes = []
  1095.  
  1096.                         bm = bmesh.from_edit_mesh(object.data)
  1097.                         for midx, mslot in enumerate(object.material_slots):
  1098.                             mat = mslot.material
  1099.                             if mat:
  1100.                                 bm_new = bm.copy()
  1101.                                 faces = None
  1102.                                 for f in bm_new.faces:
  1103.                                     if f.material_index == midx:
  1104.                                         # type=201 = material
  1105.                                         faces = set(bmesh.ops.similar_faces(bm_new, faces=[f], type=201)['faces'])
  1106.                                         faces = [f for f in bm_new.faces if f not in faces]
  1107.                                         break
  1108.                                 if faces:
  1109.                                     for f in faces:
  1110.                                         bm_new.faces.remove(f)
  1111.                                     new_mesh = orig_mesh.copy()
  1112.                                     bm_new.to_mesh(new_mesh)
  1113.                                     meshes.append(new_mesh)
  1114.  
  1115.                         bpy.ops.object.mode_set(mode = 'OBJECT')
  1116.                         old_mesh = object.data
  1117.                         object.data = orig_mesh
  1118.                         bpy.data.meshes.remove(old_mesh)
  1119.  
  1120.                     for mesh in meshes:
  1121.                         object.data = mesh
  1122.                         aseGeom += str(cGeomObject(object))
  1123.  
  1124.                     object.data = orig_mesh
  1125.                     for mesh in meshes:
  1126.                         bpy.data.meshes.remove(mesh)
  1127.                 else:
  1128.                     aseGeom = str( cGeomObject( object ) )
  1129.                
  1130.                 if (self.option_separate):
  1131.                     aseModel = ''
  1132.                     aseModel += aseHeader
  1133.                     aseModel += aseScene
  1134.                     aseModel += aseMaterials
  1135.                     aseModel += aseGeom
  1136.  
  1137.                     # Write the ASE file
  1138.                     filename = os.path.dirname(self.filepath)
  1139.                     filename += (os.sep + object.name.replace('.', '_'))
  1140.                     filename += ".ase"
  1141.                     self.writeASE(filename, aseModel )
  1142.                 else:
  1143.                     aseGeometry += aseGeom
  1144.                  
  1145.  
  1146.             else:
  1147.                 continue
  1148.  
  1149.         if (self.option_separate == False):
  1150.             aseModel = ''
  1151.             aseModel += aseHeader
  1152.             aseModel += aseScene
  1153.             aseModel += aseMaterials
  1154.             aseModel += aseGeometry
  1155.  
  1156.             # Write the ASE file
  1157.             self.writeASE( self.filepath, aseModel )
  1158.        
  1159.  
  1160.         lapse = ( time.clock() - start )
  1161.         print( 'Completed in ' + str( lapse ) + ' seconds' )
  1162.  
  1163.         return {'FINISHED'}
  1164.  
  1165. def menu_func( self, context ):
  1166.     self.layout.operator( ExportAse.bl_idname, text = "Ascii Scene Exporter (.ase) v2.5.9" )
  1167.  
  1168. #addonClasses = (
  1169. #    ExportAse,
  1170. #)
  1171.  
  1172. #register, unregister = bpy.utils.register_classes_factory(addonClasses)
  1173.  
  1174. def register():
  1175.     bpy.utils.register_class( ExportAse )
  1176.     bpy.types.TOPBAR_MT_file_export.append( menu_func )
  1177.  
  1178. def unregister():
  1179.     bpy.utils.unregister_class( ExportAse )
  1180.     bpy.types.TOPBAR_MT_file_export.remove( menu_func )
  1181.  
  1182. if __name__ == "__main__":
  1183.     register()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement