Advertisement
Guest User

Hacked ASE export script for Blender 2.63a

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