B3DExport.py 60 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612
  1. #!BPY
  2. """
  3. Name: 'B3D Exporter (.b3d)...'
  4. Blender: 259
  5. Group: 'Export'
  6. Tooltip: 'Export to Blitz3D file format (.b3d)'
  7. """
  8. __author__ = ["iego 'GaNDaLDF' Parisi, MTLZ (is06), Joerg Henrichs, Marianne Gagnon"]
  9. __url__ = ["www.gandaldf.com"]
  10. __version__ = "3.0"
  11. __bpydoc__ = """\
  12. """
  13. # BLITZ3D EXPORTER 3.0
  14. # Copyright (C) 2009 by Diego "GaNDaLDF" Parisi - www.gandaldf.com
  15. # Lightmap issue fixed by Capricorn 76 Pty. Ltd. - www.capricorn76.com
  16. # Blender 2.63 compatiblity based on work by MTLZ, www.is06.com
  17. # With changes by Marianne Gagnon, Joerg Henrichs and Vincent Lejeune, supertuxkart.sf.net (Copyright (C) 2011-2012)
  18. #
  19. # LICENSE:
  20. # This program is free software; you can redistribute it and/or modify
  21. # it under the terms of the GNU General Public License as published by
  22. # the Free Software Foundation; either version 2 of the License, or
  23. # (at your option) any later version.
  24. #
  25. # This program is distributed in the hope that it will be useful,
  26. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  27. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  28. # GNU General Public License for more details.
  29. #
  30. # You should have received a copy of the GNU General Public License
  31. # along with this program; if not, write to the Free Software
  32. # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  33. bl_info = {
  34. "name": "B3D (BLITZ3D) Model Exporter",
  35. "description": "Exports a blender scene or object to the B3D (BLITZ3D) format",
  36. "author": "Diego 'GaNDaLDF' Parisi, MTLZ (is06), Joerg Henrichs, Marianne Gagnon, Vincent Lejeune",
  37. "version": (3,1),
  38. "blender": (2, 5, 9),
  39. "api": 31236,
  40. "location": "File > Export",
  41. "warning": '', # used for warning icon and text in addons panel
  42. "wiki_url": "http://supertuxkart.sourceforge.net/Get_involved",
  43. "tracker_url": "https://sourceforge.net/apps/trac/supertuxkart/",
  44. "category": "Import-Export"}
  45. import bpy
  46. import sys,os,os.path,struct,math,string
  47. import mathutils
  48. import math
  49. if not hasattr(sys,"argv"): sys.argv = ["???"]
  50. #Global Stacks
  51. b3d_parameters = {}
  52. texture_flags = []
  53. texs_stack = {}
  54. brus_stack = []
  55. vertex_groups = []
  56. bone_stack = {}
  57. keys_stack = []
  58. texture_count = 0
  59. # bone_stack indices constants
  60. BONE_PARENT_MATRIX = 0
  61. BONE_PARENT = 1
  62. BONE_ITSELF = 2
  63. # texture stack indices constants
  64. TEXTURE_ID = 0
  65. TEXTURE_FLAGS = 1
  66. per_face_vertices = {}
  67. the_scene = None
  68. #Transformation Matrix
  69. TRANS_MATRIX = mathutils.Matrix([[1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]])
  70. BONE_TRANS_MATRIX = mathutils.Matrix([[-1,0,0,0],[0,0,-1,0],[0,-1,0,0],[0,0,0,1]])
  71. DEBUG = False
  72. PROGRESS = True
  73. PROGRESS_VERBOSE = False
  74. tesselated_objects = {}
  75. #Support Functions
  76. def write_int(value):
  77. return struct.pack("<i",value)
  78. def write_float(value):
  79. return struct.pack("<f",value)
  80. def write_float_couple(value1, value2):
  81. return struct.pack("<ff", value1, value2)
  82. def write_float_triplet(value1, value2, value3):
  83. return struct.pack("<fff", value1, value2, value3)
  84. def write_float_quad(value1, value2, value3, value4):
  85. return struct.pack("<ffff", value1, value2, value3, value4)
  86. def write_string(value):
  87. encoded = str.encode(value)
  88. if len(encoded) > 48:
  89. value = encoded[0:48]
  90. binary_format = "<%ds"%(len(encoded)+1)
  91. return struct.pack(binary_format, encoded)
  92. def write_chunk(name,value):
  93. dummy = bytearray()
  94. return dummy + name + write_int(len(value)) + value
  95. trimmed_paths = {}
  96. def getArmatureAnimationEnd(armature):
  97. end_frame = 1
  98. if armature.animation_data.action:
  99. ipo = armature.animation_data.action.fcurves
  100. for curve in ipo:
  101. if "pose" in curve.data_path:
  102. end_frame = max(end_frame, curve.keyframe_points[-1].co[0])
  103. for nla_track in armature.animation_data.nla_tracks:
  104. if len(nla_track.strips) > 0:
  105. end_frame = max(end_frame, nla_track.strips[-1].frame_end)
  106. return end_frame
  107. # ==== Write B3D File ====
  108. # (main exporter function)
  109. def write_b3d_file(filename, objects=[]):
  110. global texture_flags, texs_stack, trimmed_paths, tesselated_objects
  111. global brus_stack, vertex_groups, bone_stack, keys_stack
  112. #Global Stacks
  113. texture_flags = []
  114. texs_stack = {}
  115. brus_stack = []
  116. vertex_groups = []
  117. bone_stack = []
  118. keys_stack = []
  119. trimmed_paths = {}
  120. file_buf = bytearray()
  121. temp_buf = bytearray()
  122. tesselated_objects = {}
  123. import time
  124. start = time.time()
  125. temp_buf += write_int(1) #Version
  126. temp_buf += write_texs(objects) #TEXS
  127. temp_buf += write_brus(objects) #BRUS
  128. temp_buf += write_node(objects) #NODE
  129. if len(temp_buf) > 0:
  130. file_buf += write_chunk(b"BB3D",temp_buf)
  131. temp_buf = ""
  132. file = open(filename,'wb')
  133. file.write(file_buf)
  134. file.close()
  135. # free memory
  136. trimmed_paths = {}
  137. end = time.time()
  138. print("Exported in", (end - start))
  139. def tesselate_if_needed(objdata):
  140. if objdata not in tesselated_objects:
  141. objdata.calc_tessface()
  142. tesselated_objects[objdata] = True
  143. return objdata
  144. def getUVTextures(obj_data):
  145. # BMesh in blender 2.63 broke this
  146. if bpy.app.version[1] >= 63:
  147. return tesselate_if_needed(obj_data).tessface_uv_textures
  148. else:
  149. return obj_data.uv_textures
  150. def getFaces(obj_data):
  151. # BMesh in blender 2.63 broke this
  152. if bpy.app.version[1] >= 63:
  153. return tesselate_if_needed(obj_data).tessfaces
  154. else:
  155. return obj_data.faces
  156. def getVertexColors(obj_data):
  157. # BMesh in blender 2.63 broke this
  158. if bpy.app.version[1] >= 63:
  159. return tesselate_if_needed(obj_data).tessface_vertex_colors
  160. else:
  161. return obj_data.vertex_colors
  162. # ==== Write TEXS Chunk ====
  163. def write_texs(objects=[]):
  164. global b3d_parameters
  165. global trimmed_paths
  166. global texture_count
  167. texs_buf = bytearray()
  168. temp_buf = bytearray()
  169. layer_max = 0
  170. obj_count = 0
  171. set_wrote = 0
  172. if objects:
  173. exp_obj = objects
  174. else:
  175. if b3d_parameters.get("export-selected"):
  176. exp_obj = [ob for ob in bpy.data.objects if ob.select]
  177. else:
  178. exp_obj = bpy.data.objects
  179. if PROGRESS: print(len(exp_obj),"TEXS")
  180. if PROGRESS_VERBOSE: progress = 0
  181. for obj in exp_obj:
  182. if PROGRESS_VERBOSE:
  183. progress = progress + 1
  184. if (progress % 10 == 0): print("TEXS",progress,"/",len(exp_obj))
  185. if obj.type == "MESH":
  186. set_count = 0
  187. set_wrote = 0
  188. #data = obj.getData(mesh = True)
  189. data = obj.data
  190. # FIXME?
  191. #orig_uvlayer = data.activeUVLayer
  192. layer_set = [[],[],[],[],[],[],[],[]]
  193. # 8 UV layers are supported
  194. texture_flags.append([None,None,None,None,None,None,None,None])
  195. #if len(data.getUVLayerNames()) <= 8:
  196. uv_textures = getUVTextures(data)
  197. if len(uv_textures) <= 8:
  198. if len(uv_textures) > layer_max:
  199. layer_max = len(uv_textures)
  200. else:
  201. layer_max = 8
  202. for face in getFaces(data):
  203. for iuvlayer,uvlayer in enumerate(uv_textures):
  204. if iuvlayer < 8:
  205. # FIXME?
  206. #data.activeUVLayer = uvlayer
  207. #layer_set[iuvlayer].append(face.uv)
  208. new_data = None
  209. try:
  210. new_data = uvlayer.data[face.index].uv
  211. except:
  212. pass
  213. layer_set[iuvlayer].append( new_data )
  214. for i in range(len(uv_textures)):
  215. if set_wrote:
  216. set_count += 1
  217. set_wrote = 0
  218. for iuvlayer in range(i,len(uv_textures)):
  219. if layer_set[i] == layer_set[iuvlayer]:
  220. if texture_flags[obj_count][iuvlayer] is None:
  221. if set_count == 0:
  222. tex_flag = 1
  223. elif set_count == 1:
  224. tex_flag = 65536
  225. elif set_count > 1:
  226. tex_flag = 1
  227. if b3d_parameters.get("mipmap"):
  228. enable_mipmaps=8
  229. else:
  230. enable_mipmaps=0
  231. texture_flags[obj_count][iuvlayer] = tex_flag | enable_mipmaps
  232. set_wrote = 1
  233. for face in getFaces(data):
  234. for iuvlayer,uvlayer in enumerate(uv_textures):
  235. if iuvlayer < 8:
  236. if not (iuvlayer < len(uv_textures)):
  237. continue
  238. # FIXME?
  239. #data.activeUVLayer = uvlayer
  240. #if DEBUG: print("<uv face=", face.index, ">")
  241. img = getUVTextures(data)[iuvlayer].data[face.index].image
  242. if img:
  243. if img.filepath in trimmed_paths:
  244. img_name = trimmed_paths[img.filepath]
  245. else:
  246. img_name = bpy.path.basename(img.filepath)
  247. trimmed_paths[img.filepath] = img_name
  248. if not img_name in texs_stack:
  249. texs_stack[img_name] = [len(texs_stack), texture_flags[obj_count][iuvlayer]]
  250. temp_buf += write_string(img_name) #Texture File Name
  251. temp_buf += write_int(texture_flags[obj_count][iuvlayer]) #Flags
  252. temp_buf += write_int(2) #Blend
  253. temp_buf += write_float(0) #X_Pos
  254. temp_buf += write_float(0) #Y_Pos
  255. temp_buf += write_float(1) #X_Scale
  256. temp_buf += write_float(1) #Y_Scale
  257. temp_buf += write_float(0) #Rotation
  258. #else:
  259. # if DEBUG: print(" <image id=(previous)","name=","'"+img_name+"'","/>")
  260. #if DEBUG: print("</uv>")
  261. obj_count += 1
  262. #FIXME?
  263. #if orig_uvlayer:
  264. # data.activeUVLayer = orig_uvlayer
  265. texture_count = layer_max
  266. if len(temp_buf) > 0:
  267. texs_buf += write_chunk(b"TEXS",temp_buf)
  268. temp_buf = ""
  269. return texs_buf
  270. # ==== Write BRUS Chunk ====
  271. def write_brus(objects=[]):
  272. global b3d_parameters
  273. global trimmed_paths
  274. global texture_count
  275. brus_buf = bytearray()
  276. temp_buf = bytearray()
  277. mat_count = 0
  278. obj_count = 0
  279. if DEBUG: print("<!-- BRUS chunk -->")
  280. if objects:
  281. exp_obj = objects
  282. else:
  283. if b3d_parameters.get("export-selected"):
  284. exp_obj = [ob for ob in bpy.data.objects if ob.select]
  285. else:
  286. exp_obj = bpy.data.objects
  287. if PROGRESS: print(len(exp_obj),"BRUS")
  288. if PROGRESS_VERBOSE: progress = 0
  289. for obj in exp_obj:
  290. if PROGRESS_VERBOSE:
  291. progress += 1
  292. if (progress % 10 == 0): print("BRUS",progress,"/",len(exp_obj))
  293. if obj.type == "MESH":
  294. data = obj.data
  295. uv_textures = getUVTextures(data)
  296. if len(uv_textures) <= 0:
  297. continue
  298. if DEBUG: print("<obj name=",obj.name,">")
  299. img_found = 0
  300. for face in getFaces(data):
  301. face_stack = []
  302. for iuvlayer,uvlayer in enumerate(uv_textures):
  303. if iuvlayer < 8:
  304. img_id = -1
  305. if face.index >= len(uv_textures[iuvlayer].data):
  306. continue
  307. img = uv_textures[iuvlayer].data[face.index].image
  308. if not img:
  309. continue
  310. img_found = 1
  311. if img.filepath in trimmed_paths:
  312. img_name = trimmed_paths[img.filepath]
  313. else:
  314. img_name = os.path.basename(img.filepath)
  315. trimmed_paths[img.filepath] = img_name
  316. if DEBUG: print(" <!-- Building FACE 'stack' -->")
  317. if img_name in texs_stack:
  318. img_id = texs_stack[img_name][TEXTURE_ID]
  319. face_stack.insert(iuvlayer,img_id)
  320. if DEBUG: print(" <uv face=",face.index,"layer=", iuvlayer, " imgid=", img_id, "/>")
  321. for i in range(len(face_stack),texture_count):
  322. face_stack.append(-1)
  323. if DEBUG: print(" <!-- Writing chunk -->")
  324. if not img_found:
  325. if data.materials:
  326. if data.materials[face.material_index]:
  327. mat_data = data.materials[face.material_index]
  328. mat_colr = mat_data.diffuse_color[0]
  329. mat_colg = mat_data.diffuse_color[1]
  330. mat_colb = mat_data.diffuse_color[2]
  331. mat_alpha = mat_data.alpha
  332. mat_name = mat_data.name
  333. if not mat_name in brus_stack:
  334. brus_stack.append(mat_name)
  335. temp_buf += write_string(mat_name) #Brush Name
  336. temp_buf += write_float(mat_colr) #Red
  337. temp_buf += write_float(mat_colg) #Green
  338. temp_buf += write_float(mat_colb) #Blue
  339. temp_buf += write_float(mat_alpha) #Alpha
  340. temp_buf += write_float(0) #Shininess
  341. temp_buf += write_int(1) #Blend
  342. if b3d_parameters.get("vertex-colors") and len(getVertexColors(data)):
  343. temp_buf += write_int(2) #Fx
  344. else:
  345. temp_buf += write_int(0) #Fx
  346. for i in face_stack:
  347. temp_buf += write_int(i) #Texture ID
  348. else:
  349. if b3d_parameters.get("vertex-colors") and len(getVertexColors(data)) > 0:
  350. if not face_stack in brus_stack:
  351. brus_stack.append(face_stack)
  352. mat_count += 1
  353. temp_buf += write_string("Brush.%.3i"%mat_count) #Brush Name
  354. temp_buf += write_float(1) #Red
  355. temp_buf += write_float(1) #Green
  356. temp_buf += write_float(1) #Blue
  357. temp_buf += write_float(1) #Alpha
  358. temp_buf += write_float(0) #Shininess
  359. temp_buf += write_int(1) #Blend
  360. temp_buf += write_int(2) #Fx
  361. for i in face_stack:
  362. temp_buf += write_int(i) #Texture ID
  363. else: # img_found
  364. if not face_stack in brus_stack:
  365. brus_stack.append(face_stack)
  366. mat_count += 1
  367. temp_buf += write_string("Brush.%.3i"%mat_count) #Brush Name
  368. temp_buf += write_float(1) #Red
  369. temp_buf += write_float(1) #Green
  370. temp_buf += write_float(1) #Blue
  371. temp_buf += write_float(1) #Alpha
  372. temp_buf += write_float(0) #Shininess
  373. temp_buf += write_int(1) #Blend
  374. if DEBUG: print(" <brush id=",len(brus_stack),">")
  375. if b3d_parameters.get("vertex-colors") and len(getVertexColors(data)) > 0:
  376. temp_buf += write_int(2) #Fx
  377. else:
  378. temp_buf += write_int(0) #Fx
  379. for i in face_stack:
  380. temp_buf += write_int(i) #Texture ID
  381. if DEBUG: print(" <texture id=",i,">")
  382. if DEBUG: print(" </brush>")
  383. if DEBUG: print("")
  384. if DEBUG: print("</obj>")
  385. obj_count += 1
  386. #FIXME?
  387. #if orig_uvlayer:
  388. # data.activeUVLayer = orig_uvlayer
  389. if len(temp_buf) > 0:
  390. brus_buf += write_chunk(b"BRUS",write_int(texture_count) + temp_buf) #N Texs
  391. temp_buf = ""
  392. return brus_buf
  393. # ==== Write NODE Chunk ====
  394. def write_node(objects=[]):
  395. global bone_stack
  396. global keys_stack
  397. global b3d_parameters
  398. global the_scene
  399. root_buf = []
  400. node_buf = []
  401. main_buf = bytearray()
  402. temp_buf = []
  403. obj_count = 0
  404. amb_light = 0
  405. num_mesh = 0
  406. num_ligs = 0
  407. num_cams = 0
  408. num_lorc = 0
  409. #exp_scn = Blender.Scene.GetCurrent()
  410. #exp_scn = the_scene
  411. #exp_con = exp_scn.getRenderingContext()
  412. #first_frame = Blender.Draw.Create(exp_con.startFrame())
  413. #last_frame = Blender.Draw.Create(exp_con.endFrame())
  414. #num_frames = last_frame.val - first_frame.val
  415. first_frame = the_scene.frame_start
  416. if DEBUG: print("<node first_frame=", first_frame, ">")
  417. if objects:
  418. exp_obj = objects
  419. else:
  420. if b3d_parameters.get("export-selected"):
  421. exp_obj = [ob for ob in bpy.data.objects if ob.select]
  422. else:
  423. exp_obj = bpy.data.objects
  424. for obj in exp_obj:
  425. if obj.type == "MESH":
  426. num_mesh += 1
  427. if obj.type == "CAMERA":
  428. num_cams += 1
  429. if obj.type == "LAMP":
  430. num_ligs += 1
  431. if b3d_parameters.get("cameras"):
  432. num_lorc += num_cams
  433. if b3d_parameters.get("lights"):
  434. num_lorc += 1
  435. num_lorc += num_ligs
  436. if num_mesh + num_lorc > 1:
  437. exp_root = 1
  438. else:
  439. exp_root = 0
  440. if exp_root:
  441. root_buf.append(write_string("ROOT")) #Node Name
  442. root_buf.append(write_float_triplet(0, 0, 0)) #Position X,Y,Z
  443. root_buf.append(write_float_triplet(1, 1, 1)) #Scale X, Y, Z
  444. root_buf.append(write_float_quad(1, 0, 0, 0)) #Rotation W, X, Y, Z
  445. if PROGRESS: progress = 0
  446. for obj in exp_obj:
  447. if PROGRESS:
  448. progress += 1
  449. print("NODE:",progress,"/",len(exp_obj),obj.name)
  450. if obj.type == "MESH":
  451. if DEBUG: print(" <mesh name=",obj.name,">")
  452. bone_stack = {}
  453. keys_stack = []
  454. anim_data = None
  455. # check if this object has an armature modifier
  456. for curr_mod in obj.modifiers:
  457. if curr_mod.type == 'ARMATURE':
  458. arm = curr_mod.object
  459. if arm is not None:
  460. anim_data = arm.animation_data
  461. # check if this object has an armature parent (second way to do armature animations in blender)
  462. if anim_data is None:
  463. if obj.parent:
  464. if obj.parent.type == "ARMATURE":
  465. arm = obj.parent
  466. if arm.animation_data:
  467. anim_data = arm.animation_data
  468. if anim_data:
  469. matrix = mathutils.Matrix()
  470. temp_buf.append(write_string(obj.name)) #Node Name
  471. position = matrix.to_translation()
  472. temp_buf.append(write_float_triplet(position[0], position[1], position[2])) #Position X, Y, Z
  473. scale = matrix.to_scale()
  474. temp_buf.append(write_float_triplet(scale[0], scale[2], scale[1])) #Scale X, Y, Z
  475. if DEBUG: print(" <arm name=", obj.name, " loc=", -position[0], position[1], position[2], " scale=", scale[0], scale[1], scale[2], "/>")
  476. quat = matrix.to_quaternion()
  477. quat.normalize()
  478. temp_buf.append(write_float_quad(quat.w, quat.x, quat.z, quat.y))
  479. else:
  480. if b3d_parameters.get("local-space"):
  481. matrix = TRANS_MATRIX.copy()
  482. scale_matrix = mathutils.Matrix()
  483. else:
  484. matrix = obj.matrix_world*TRANS_MATRIX
  485. scale_matrix = obj.matrix_world.copy()
  486. if bpy.app.version[1] >= 62:
  487. # blender 2.62 broke the API : Column-major access was changed to row-major access
  488. tmp = mathutils.Vector([matrix[0][1], matrix[1][1], matrix[2][1], matrix[3][1]])
  489. matrix[0][1] = matrix[0][2]
  490. matrix[1][1] = matrix[1][2]
  491. matrix[2][1] = matrix[2][2]
  492. matrix[3][1] = matrix[3][2]
  493. matrix[0][2] = tmp[0]
  494. matrix[1][2] = tmp[1]
  495. matrix[2][2] = tmp[2]
  496. matrix[3][2] = tmp[3]
  497. else:
  498. tmp = mathutils.Vector(matrix[1])
  499. matrix[1] = matrix[2]
  500. matrix[2] = tmp
  501. temp_buf.append(write_string(obj.name)) #Node Name
  502. #print("Matrix : ", matrix)
  503. position = matrix.to_translation()
  504. temp_buf.append(write_float_triplet(position[0], position[2], position[1]))
  505. scale = scale_matrix.to_scale()
  506. temp_buf.append(write_float_triplet(scale[0], scale[2], scale[1]))
  507. quat = matrix.to_quaternion()
  508. quat.normalize()
  509. temp_buf.append(write_float_quad(quat.w, quat.x, quat.z, quat.y))
  510. if DEBUG:
  511. print(" <position>",position[0],position[2],position[1],"</position>")
  512. print(" <scale>",scale[0],scale[1],scale[2],"</scale>")
  513. print(" <rotation>", quat.w, quat.x, quat.y, quat.z, "</rotation>")
  514. if anim_data:
  515. the_scene.frame_set(1,subframe=0.0)
  516. arm_matrix = arm.matrix_world
  517. if b3d_parameters.get("local-space"):
  518. arm_matrix = mathutils.Matrix()
  519. def read_armature(arm_matrix,bone,parent = None):
  520. if (parent and not bone.parent.name == parent.name):
  521. return
  522. matrix = mathutils.Matrix(bone.matrix)
  523. if parent:
  524. #print("==== "+bone.name+" ====")
  525. a = (bone.matrix_local)
  526. #print("A : [%.2f %.2f %.2f %.2f]" % (a[0][0], a[0][1], a[0][2], a[0][3]))
  527. #print(" [%.2f %.2f %.2f %.2f]" % (a[1][0], a[1][1], a[1][2], a[1][3]))
  528. #print(" [%.2f %.2f %.2f %.2f]" % (a[2][0], a[2][1], a[2][2], a[2][3]))
  529. #print(" [%.2f %.2f %.2f %.2f]" % (a[3][0], a[3][1], a[3][2], a[3][3]))
  530. b = (parent.matrix_local.inverted().to_4x4())
  531. #print("B : [%.2f %.2f %.2f %.2f]" % (b[0][0], b[0][1], b[0][2], b[0][3]))
  532. #print(" [%.2f %.2f %.2f %.2f]" % (b[1][0], b[1][1], b[1][2], b[1][3]))
  533. #print(" [%.2f %.2f %.2f %.2f]" % (b[2][0], b[2][1], b[2][2], b[2][3]))
  534. #print(" [%.2f %.2f %.2f %.2f]" % (b[3][0], b[3][1], b[3][2], b[3][3]))
  535. par_matrix = b * a
  536. transform = mathutils.Matrix([[1,0,0,0],[0,0,-1,0],[0,-1,0,0],[0,0,0,1]])
  537. par_matrix = transform*par_matrix*transform
  538. # FIXME: that's ugly, find a clean way to change the matrix.....
  539. if bpy.app.version[1] >= 62:
  540. # blender 2.62 broke the API : Column-major access was changed to row-major access
  541. # TODO: test me
  542. par_matrix[1][3] = -par_matrix[1][3]
  543. par_matrix[2][3] = -par_matrix[2][3]
  544. else:
  545. par_matrix[3][1] = -par_matrix[3][1]
  546. par_matrix[3][2] = -par_matrix[3][2]
  547. #c = par_matrix
  548. #print("With parent")
  549. #print("C : [%.3f %.3f %.3f %.3f]" % (c[0][0], c[0][1], c[0][2], c[0][3]))
  550. #print(" [%.3f %.3f %.3f %.3f]" % (c[1][0], c[1][1], c[1][2], c[1][3]))
  551. #print(" [%.3f %.3f %.3f %.3f]" % (c[2][0], c[2][1], c[2][2], c[2][3]))
  552. #print(" [%.3f %.3f %.3f %.3f]" % (c[3][0], c[3][1], c[3][2], c[3][3]))
  553. else:
  554. #print("==== "+bone.name+" ====")
  555. #print("Without parent")
  556. m = arm_matrix*bone.matrix_local
  557. #c = arm.matrix_world
  558. #print("A : [%.3f %.3f %.3f %.3f]" % (c[0][0], c[0][1], c[0][2], c[0][3]))
  559. #print(" [%.3f %.3f %.3f %.3f]" % (c[1][0], c[1][1], c[1][2], c[1][3]))
  560. #print(" [%.3f %.3f %.3f %.3f]" % (c[2][0], c[2][1], c[2][2], c[2][3]))
  561. #print(" [%.3f %.3f %.3f %.3f]" % (c[3][0], c[3][1], c[3][2], c[3][3]))
  562. #c = bone.matrix_local
  563. #print("B : [%.3f %.3f %.3f %.3f]" % (c[0][0], c[0][1], c[0][2], c[0][3]))
  564. #print(" [%.3f %.3f %.3f %.3f]" % (c[1][0], c[1][1], c[1][2], c[1][3]))
  565. #print(" [%.3f %.3f %.3f %.3f]" % (c[2][0], c[2][1], c[2][2], c[2][3]))
  566. #print(" [%.3f %.3f %.3f %.3f]" % (c[3][0], c[3][1], c[3][2], c[3][3]))
  567. par_matrix = m*mathutils.Matrix([[-1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]])
  568. #c = par_matrix
  569. #print("C : [%.3f %.3f %.3f %.3f]" % (c[0][0], c[0][1], c[0][2], c[0][3]))
  570. #print(" [%.3f %.3f %.3f %.3f]" % (c[1][0], c[1][1], c[1][2], c[1][3]))
  571. #print(" [%.3f %.3f %.3f %.3f]" % (c[2][0], c[2][1], c[2][2], c[2][3]))
  572. #print(" [%.3f %.3f %.3f %.3f]" % (c[3][0], c[3][1], c[3][2], c[3][3]))
  573. bone_stack[bone.name] = [par_matrix,parent,bone]
  574. if bone.children:
  575. for child in bone.children: read_armature(arm_matrix,child,bone)
  576. for bone in arm.data.bones.values():
  577. if not bone.parent:
  578. read_armature(arm_matrix,bone)
  579. frame_count = first_frame
  580. last_frame = int(getArmatureAnimationEnd(arm))
  581. num_frames = last_frame - first_frame
  582. while frame_count <= last_frame:
  583. the_scene.frame_set(int(frame_count), subframe=0.0)
  584. if DEBUG: print(" <frame id=", int(frame_count), ">")
  585. arm_pose = arm.pose
  586. arm_matrix = arm.matrix_world
  587. transform = mathutils.Matrix([[-1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]])
  588. arm_matrix = transform*arm_matrix
  589. #arm_quat = arm_matrix.to_quaternion()
  590. #arm_quat.normalize()
  591. for bone_name in arm.data.bones.keys():
  592. #bone_matrix = mathutils.Matrix(arm_pose.bones[bone_name].poseMatrix)
  593. bone_matrix = mathutils.Matrix(arm_pose.bones[bone_name].matrix)
  594. #print("(outer loop) bone_matrix for",bone_name,"=", bone_matrix)
  595. #print(bone_name,":",bone_matrix)
  596. #bone_matrix = bpy.data.scenes[0].objects[0].pose.bones['Bone'].matrix
  597. for ibone in bone_stack:
  598. bone = bone_stack[ibone]
  599. if bone[BONE_ITSELF].name == bone_name:
  600. if DEBUG: print(" <bone id=",ibone,"name=",bone_name,">")
  601. # == 2.4 exporter ==
  602. #if bone_stack[ibone][1]:
  603. # par_matrix = Blender.Mathutils.Matrix(arm_pose.bones[bone_stack[ibone][1].name].poseMatrix)
  604. # bone_matrix *= par_matrix.invert()
  605. #else:
  606. # if b3d_parameters.get("local-space"):
  607. # bone_matrix *= TRANS_MATRIX
  608. # else:
  609. # bone_matrix *= arm_matrix
  610. #bone_loc = bone_matrix.translationPart()
  611. #bone_rot = bone_matrix.rotationPart().toQuat()
  612. #bone_rot.normalize()
  613. #bone_sca = bone_matrix.scalePart()
  614. #keys_stack.append([frame_count - first_frame.val+1,bone_name,bone_loc,bone_sca,bone_rot])
  615. # if has parent
  616. if bone[BONE_PARENT]:
  617. par_matrix = mathutils.Matrix(arm_pose.bones[bone[BONE_PARENT].name].matrix)
  618. bone_matrix = par_matrix.inverted()*bone_matrix
  619. else:
  620. if b3d_parameters.get("local-space"):
  621. bone_matrix = bone_matrix*mathutils.Matrix([[-1,0,0,0],[0,0,1,0],[0,1,0,0],[0,0,0,1]])
  622. else:
  623. #if frame_count == 1:
  624. # print("====",bone_name,"====")
  625. # print("arm_matrix = ", arm_matrix)
  626. # print("bone_matrix = ", bone_matrix)
  627. bone_matrix = arm_matrix*bone_matrix
  628. #if frame_count == 1:
  629. # print("arm_matrix*bone_matrix", bone_matrix)
  630. #print("bone_matrix =", bone_matrix)
  631. bone_sca = bone_matrix.to_scale()
  632. bone_loc = bone_matrix.to_translation()
  633. # FIXME: silly tweaks to resemble the Blender 2.4 exporter output
  634. if b3d_parameters.get("local-space"):
  635. bone_rot = bone_matrix.to_quaternion()
  636. bone_rot.normalize()
  637. if not bone[BONE_PARENT]:
  638. tmp = bone_rot.z
  639. bone_rot.z = bone_rot.y
  640. bone_rot.y = tmp
  641. bone_rot.x = -bone_rot.x
  642. else:
  643. tmp = bone_loc.z
  644. bone_loc.z = bone_loc.y
  645. bone_loc.y = tmp
  646. else:
  647. bone_rot = bone_matrix.to_quaternion()
  648. bone_rot.normalize()
  649. # Sometimes to_quaternion exhibits precision issue with parent bone
  650. # Use quaternion product instead of getting quaternion from the product.
  651. #if not bone[BONE_PARENT]:
  652. # bone_rot = arm_pose.bones[bone_name].matrix.to_quaternion() * arm_quat
  653. # bone_rot.x = -bone_rot.x
  654. # tmp = bone_rot.z
  655. # bone_rot.z = bone_rot.y
  656. # bone_rot.y = tmp
  657. keys_stack.append([frame_count - first_frame+1, bone_name, bone_loc, bone_sca, bone_rot])
  658. if DEBUG: print(" <loc>", bone_loc, "</loc>")
  659. if DEBUG: print(" <rot>", bone_rot, "</rot>")
  660. if DEBUG: print(" <scale>", bone_sca, "</scale>")
  661. if DEBUG: print(" </bone>")
  662. frame_count += 1
  663. if DEBUG: print(" </frame>")
  664. #Blender.Set("curframe",0)
  665. #Blender.Window.Redraw()
  666. temp_buf.append(write_node_mesh(obj,obj_count,anim_data,exp_root)) #NODE MESH
  667. if anim_data:
  668. temp_buf.append(write_node_anim(num_frames)) #NODE ANIM
  669. for ibone in bone_stack:
  670. if not bone_stack[ibone][BONE_PARENT]:
  671. temp_buf.append(write_node_node(ibone)) #NODE NODE
  672. obj_count += 1
  673. if len(temp_buf) > 0:
  674. node_buf.append(write_chunk(b"NODE",b"".join(temp_buf)))
  675. temp_buf = []
  676. if DEBUG: print(" </mesh>")
  677. if b3d_parameters.get("cameras"):
  678. if obj.type == "CAMERA":
  679. data = obj.data
  680. matrix = obj.getMatrix("worldspace")
  681. matrix *= TRANS_MATRIX
  682. if data.type == "ORTHO":
  683. cam_type = 2
  684. cam_zoom = round(data.scale,4)
  685. else:
  686. cam_type = 1
  687. cam_zoom = round(data.lens,4)
  688. cam_near = round(data.clipStart,4)
  689. cam_far = round(data.clipEnd,4)
  690. node_name = ("CAMS"+"\n%s"%obj.name+"\n%s"%cam_type+\
  691. "\n%s"%cam_zoom+"\n%s"%cam_near+"\n%s"%cam_far)
  692. temp_buf.append(write_string(node_name)) #Node Name
  693. position = matrix.translation_part()
  694. temp_buf.append(write_float_triplet(-position[0], position[1], position[2]))
  695. scale = matrix.scale_part()
  696. temp_buf.append(write_float_triplet(scale[0], scale[1], scale[2]))
  697. matrix *= mathutils.Matrix.Rotation(180,4,'Y')
  698. quat = matrix.to_quat()
  699. quat.normalize()
  700. temp_buf.append(write_float_quad(quat.w, quat.x, quat.y, -quat.z))
  701. if len(temp_buf) > 0:
  702. node_buf.append(write_chunk(b"NODE",b"".join(temp_buf)))
  703. temp_buf = []
  704. if b3d_parameters.get("lights"):
  705. if amb_light == 0:
  706. data = Blender.World.GetCurrent()
  707. amb_light = 1
  708. amb_color = (int(data.amb[2]*255) |(int(data.amb[1]*255) << 8) | (int(data.amb[0]*255) << 16))
  709. node_name = (b"AMBI"+"\n%s"%amb_color)
  710. temp_buf.append(write_string(node_name)) #Node Name
  711. temp_buf.append(write_float_triplet(0, 0, 0)) #Position X, Y, Z
  712. temp_buf.append(write_float_triplet(1, 1, 1)) #Scale X, Y, Z
  713. temp_buf.append(write_float_quad(1, 0, 0, 0)) #Rotation W, X, Y, Z
  714. if len(temp_buf) > 0:
  715. node_buf.append(write_chunk(b"NODE",b"".join(temp_buf)))
  716. temp_buf = []
  717. if obj.type == "LAMP":
  718. data = obj.getData()
  719. matrix = obj.getMatrix("worldspace")
  720. matrix *= TRANS_MATRIX
  721. if data.type == 0:
  722. lig_type = 2
  723. elif data.type == 2:
  724. lig_type = 3
  725. else:
  726. lig_type = 1
  727. lig_angle = round(data.spotSize,4)
  728. lig_color = (int(data.b*255) |(int(data.g*255) << 8) | (int(data.r*255) << 16))
  729. lig_range = round(data.dist,4)
  730. node_name = ("LIGS"+"\n%s"%obj.name+"\n%s"%lig_type+\
  731. "\n%s"%lig_angle+"\n%s"%lig_color+"\n%s"%lig_range)
  732. temp_buf.append(write_string(node_name)) #Node Name
  733. position = matrix.translation_part()
  734. temp_buf.append(write_float_triplet(-position[0], position[1], position[2]))
  735. if DEBUG: print(" <position>",-position[0],position[1],position[2],"</position>")
  736. scale = matrix.scale_part()
  737. temp_buf.append(write_float_triplet(scale[0], scale[1], scale[2]))
  738. if DEBUG: print(" <scale>",scale[0],scale[1],scale[2],"</scale>")
  739. matrix *= mathutils.Matrix.Rotation(180,4,'Y')
  740. quat = matrix.toQuat()
  741. quat.normalize()
  742. temp_buf.append(write_float_quad(quat.w, quat.x, quat.y, -quat.z))
  743. if DEBUG: print(" <rotation>", quat.w, quat.x, quat.y, quat.z, "</rotation>")
  744. if len(temp_buf) > 0:
  745. node_buf.append(write_chunk(b"NODE","b".join(temp_buf)))
  746. temp_buf = []
  747. if len(node_buf) > 0:
  748. if exp_root:
  749. main_buf += write_chunk(b"NODE",b"".join(root_buf) + b"".join(node_buf))
  750. else:
  751. main_buf += b"".join(node_buf)
  752. node_buf = []
  753. root_buf = []
  754. if DEBUG: print("</node>")
  755. return main_buf
  756. # ==== Write NODE MESH Chunk ====
  757. def write_node_mesh(obj,obj_count,arm_action,exp_root):
  758. global vertex_groups
  759. vertex_groups = []
  760. mesh_buf = bytearray()
  761. temp_buf = bytearray()
  762. if arm_action:
  763. data = obj.data
  764. elif b3d_parameters.get("apply-modifiers"):
  765. data = obj.to_mesh(the_scene, True, 'PREVIEW') # Apply modifiers
  766. else:
  767. data = obj.data
  768. temp_buf += write_int(-1) #Brush ID
  769. temp_buf += write_node_mesh_vrts(obj, data, obj_count, arm_action, exp_root) #NODE MESH VRTS
  770. temp_buf += write_node_mesh_tris(obj, data, obj_count, arm_action, exp_root) #NODE MESH TRIS
  771. if len(temp_buf) > 0:
  772. mesh_buf += write_chunk(b"MESH",temp_buf)
  773. temp_buf = ""
  774. return mesh_buf
  775. def build_vertex_groups(data):
  776. for f in getFaces(data):
  777. for v in f.vertices:
  778. vertex_groups.append({})
  779. # ==== Write NODE MESH VRTS Chunk ====
  780. def write_node_mesh_vrts(obj, data, obj_count, arm_action, exp_root):
  781. vrts_buf = bytearray()
  782. temp_buf = []
  783. obj_flags = 0
  784. #global time_in_a
  785. #global time_in_b
  786. #global time_in_b1
  787. #global time_in_b2
  788. #global time_in_b3
  789. #global time_in_b4
  790. #data = obj.getData(mesh = True)
  791. global the_scene
  792. # FIXME: port to 2.5 API?
  793. #orig_uvlayer = data.activeUVLayer
  794. if b3d_parameters.get("vertex-normals"):
  795. obj_flags += 1
  796. #if b3d_parameters.get("vertex-colors") and data.getColorLayerNames():
  797. if b3d_parameters.get("vertex-colors") and len(getVertexColors(data)) > 0:
  798. obj_flags += 2
  799. temp_buf.append(write_int(obj_flags)) #Flags
  800. #temp_buf += write_int(len(data.getUVLayerNames())) #UV Set
  801. temp_buf.append(write_int(len(getUVTextures(data)))) #UV Set
  802. temp_buf.append(write_int(2)) #UV Set Size
  803. # ---- Prepare the mesh "stack"
  804. build_vertex_groups(data)
  805. # ---- Fill the mesh "stack"
  806. if DEBUG: print("")
  807. if DEBUG: print(" <!-- Building vertex_groups -->\n")
  808. ivert = -1
  809. #if PROGRESS_VERBOSE:
  810. # progress = 0
  811. # print(" vertex_groups, face:",0,"/",len(getFaces(data)))
  812. the_scene.frame_set(1,subframe=0.0)
  813. if b3d_parameters.get("local-space"):
  814. mesh_matrix = mathutils.Matrix()
  815. else:
  816. mesh_matrix = obj.matrix_world.copy()
  817. #import time
  818. uvdata = getUVTextures(data)
  819. uv_layers_count = len(uvdata)
  820. uv_textures = {}
  821. weldable_vertices = {}
  822. vertex_id_mapping = {}
  823. for face in getFaces(data):
  824. for vertex_index,vert in enumerate(face.vertices):
  825. #for id,vert in enumerate(data.vertices):
  826. if vert not in uv_textures:
  827. uv_textures[vert] = {}
  828. if face not in uv_textures[vert]:
  829. uv_textures[vert][face] = {}
  830. for iuvlayer in range(uv_layers_count):
  831. if vertex_index == 0:
  832. uv_textures[vert][face][iuvlayer] = uvdata[iuvlayer].data[face.index].uv1
  833. elif vertex_index == 1:
  834. uv_textures[vert][face][iuvlayer] = uvdata[iuvlayer].data[face.index].uv2
  835. elif vertex_index == 2:
  836. uv_textures[vert][face][iuvlayer] = uvdata[iuvlayer].data[face.index].uv3
  837. elif vertex_index == 3:
  838. uv_textures[vert][face][iuvlayer] = uvdata[iuvlayer].data[face.index].uv4
  839. for idVertex, vertexFaces in uv_textures.items():
  840. is_weldable = True
  841. for iuvlayer in range(uv_layers_count):
  842. u = None
  843. v = None
  844. for idFace, face in vertexFaces.items():
  845. if iuvlayer not in face:
  846. continue
  847. faceuvlayer = face[iuvlayer]
  848. if u is None:
  849. u = round(faceuvlayer[0]*1000)
  850. v = round(faceuvlayer[1]*1000)
  851. else:
  852. if round(faceuvlayer[0] * 1000) != u or round(faceuvlayer[1]*1000) != v:
  853. is_weldable = False
  854. break
  855. if not is_weldable:
  856. break
  857. weldable_vertices[idVertex] = is_weldable
  858. #print('uv_textures', uv_textures)
  859. #print('weldable_vertices', weldable_vertices)
  860. for face in getFaces(data):
  861. if DEBUG: print(" <!-- Face",face.index,"-->")
  862. #if PROGRESS_VERBOSE:
  863. # progress += 1
  864. # if (progress % 50 == 0): print(" vertex_groups, face:",progress,"/",len(data.faces))
  865. per_face_vertices[face.index] = []
  866. for vertex_id,vert in enumerate(face.vertices):
  867. if vert in weldable_vertices and weldable_vertices[vert] and vert in vertex_id_mapping:
  868. per_face_vertices[face.index].append(vertex_id_mapping[vert])
  869. continue
  870. ivert += 1
  871. vertex_id_mapping[vert] = ivert
  872. per_face_vertices[face.index].append(ivert)
  873. #a = time.time()
  874. if arm_action:
  875. v = mesh_matrix * data.vertices[vert].co
  876. vert_matrix = mathutils.Matrix.Translation(v)
  877. else:
  878. vert_matrix = mathutils.Matrix.Translation(data.vertices[vert].co)
  879. vert_matrix *= TRANS_MATRIX
  880. vcoord = vert_matrix.to_translation()
  881. temp_buf.append(write_float_triplet(vcoord.x, vcoord.z, vcoord.y))
  882. #b = time.time()
  883. #time_in_a += b - a
  884. if b3d_parameters.get("vertex-normals"):
  885. norm_matrix = mathutils.Matrix.Translation(data.vertices[vert].normal)
  886. if arm_action:
  887. norm_matrix *= mesh_matrix
  888. norm_matrix *= TRANS_MATRIX
  889. normal_vector = norm_matrix.to_translation()
  890. temp_buf.append(write_float_triplet(normal_vector.x, #NX
  891. normal_vector.z, #NY
  892. normal_vector.y)) #NZ
  893. #c = time.time()
  894. #time_in_b += c - b
  895. if b3d_parameters.get("vertex-colors") and len(getVertexColors(data)) > 0:
  896. vertex_colors = getVertexColors(data)
  897. if vertex_id == 0:
  898. vcolor = vertex_colors[0].data[face.index].color1
  899. elif vertex_id == 1:
  900. vcolor = vertex_colors[0].data[face.index].color2
  901. elif vertex_id == 2:
  902. vcolor = vertex_colors[0].data[face.index].color3
  903. elif vertex_id == 3:
  904. vcolor = vertex_colors[0].data[face.index].color4
  905. valpha = 1.0
  906. if (len(vertex_colors) > 1):
  907. if vertex_id == 0:
  908. valpha = vertex_colors[1].data[face.index].color1.r
  909. elif vertex_id == 1:
  910. valpha = vertex_colors[1].data[face.index].color2.r
  911. elif vertex_id == 2:
  912. valpha = vertex_colors[1].data[face.index].color3.r
  913. elif vertex_id == 3:
  914. valpha = vertex_colors[1].data[face.index].color4.r
  915. temp_buf.append(write_float_quad(vcolor.r, #R
  916. vcolor.g, #G
  917. vcolor.b, #B
  918. valpha)) #A (FIXME?)
  919. #d = time.time()
  920. #time_in_b1 += d - c
  921. for vg in obj.vertex_groups:
  922. w = 0.0
  923. try:
  924. w = vg.weight(vert)
  925. except:
  926. pass
  927. vertex_groups[ivert][vg.name] = w
  928. #e = time.time()
  929. #time_in_b2 += e - d
  930. for iuvlayer in range(uv_layers_count):
  931. uv = [10, 10]
  932. if vert in uv_textures:
  933. vertex_uv = uv_textures[vert]
  934. if face in vertex_uv:
  935. vertex_face_uv = vertex_uv[face]
  936. if iuvlayer in vertex_face_uv:
  937. uv = vertex_face_uv[iuvlayer]
  938. temp_buf.append(write_float_couple(uv[0], 1-uv[1]) ) # U, V
  939. if DEBUG: print("")
  940. #c = time.time()
  941. #time_in_b += c - b
  942. #print("time_in_a = ",time_in_a)
  943. #print("time_in_b = ",time_in_b)
  944. #print("time_in_b1 = ", time_in_b1)
  945. #print("time_in_b2 = ", time_in_b2)
  946. #print("time_in_b3 = ", time_in_b3)
  947. #print("time_in_b4 = ", time_in_b4)
  948. if len(temp_buf) > 0:
  949. vrts_buf += write_chunk(b"VRTS",b"".join(temp_buf))
  950. temp_buf = []
  951. return vrts_buf
  952. # ==== Write NODE MESH TRIS Chunk ====
  953. def write_node_mesh_tris(obj, data, obj_count,arm_action,exp_root):
  954. global texture_count
  955. #FIXME?
  956. #orig_uvlayer = data.activeUVLayer
  957. # An dictoriary that maps all brush-ids to a list of faces
  958. # using this brush. This helps to sort the triangles by
  959. # brush, creating less mesh buffer in irrlicht.
  960. dBrushId2Face = {}
  961. if DEBUG: print("")
  962. for face in getFaces(data):
  963. img_found = 0
  964. face_stack = []
  965. uv_textures = getUVTextures(data)
  966. uv_layer_count = len(uv_textures)
  967. for iuvlayer,uvlayer in enumerate(uv_textures):
  968. if iuvlayer < 8:
  969. if iuvlayer >= uv_layer_count:
  970. continue
  971. img_id = -1
  972. img = uv_textures[iuvlayer].data[face.index].image
  973. if img:
  974. if img.filepath in trimmed_paths:
  975. img_name = trimmed_paths[img.filepath]
  976. else:
  977. img_name = os.path.basename(img.filepath)
  978. trimmed_paths[img.filepath] = img_name
  979. img_found = 1
  980. if img_name in texs_stack:
  981. img_id = texs_stack[img_name][TEXTURE_ID]
  982. face_stack.insert(iuvlayer,img_id)
  983. for i in range(len(face_stack),texture_count):
  984. face_stack.append(-1)
  985. if img_found == 0:
  986. brus_id = -1
  987. if data.materials and data.materials[face.material_index]:
  988. mat_name = data.materials[face.material_index].name
  989. for i in range(len(brus_stack)):
  990. if brus_stack[i] == mat_name:
  991. brus_id = i
  992. break
  993. else:
  994. for i in range(len(brus_stack)):
  995. if brus_stack[i] == face_stack:
  996. brus_id = i
  997. break
  998. else:
  999. brus_id = -1
  1000. for i in range(len(brus_stack)):
  1001. if brus_stack[i] == face_stack:
  1002. brus_id = i
  1003. break
  1004. if brus_id == -1:
  1005. print("Cannot find in brus stack : ", face_stack)
  1006. if brus_id in dBrushId2Face:
  1007. dBrushId2Face[brus_id].append(face)
  1008. else:
  1009. dBrushId2Face[brus_id] = [face]
  1010. if DEBUG: print(" <!-- Face",face.index,"in brush",brus_id,"-->")
  1011. tris_buf = bytearray()
  1012. if DEBUG: print("")
  1013. if DEBUG: print(" <!-- TRIS chunk -->")
  1014. if PROGRESS_VERBOSE: progress = 0
  1015. for brus_id in dBrushId2Face.keys():
  1016. if PROGRESS_VERBOSE:
  1017. progress += 1
  1018. print("BRUS:",progress,"/",len(dBrushId2Face.keys()))
  1019. temp_buf = [write_int(brus_id)] #Brush ID
  1020. if DEBUG: print(" <brush id=", brus_id, ">")
  1021. if PROGRESS_VERBOSE: progress2 = 0
  1022. for face in dBrushId2Face[brus_id]:
  1023. if PROGRESS_VERBOSE:
  1024. progress2 += 1
  1025. if (progress2 % 50 == 0): print(" TRIS:",progress2,"/",len(dBrushId2Face[brus_id]))
  1026. vertices = per_face_vertices[face.index]
  1027. temp_buf.append(write_int(vertices[2])) #A
  1028. temp_buf.append(write_int(vertices[1])) #B
  1029. temp_buf.append(write_int(vertices[0])) #C
  1030. if DEBUG: print(" <face id=", vertices[2], vertices[1], vertices[0],"/> <!-- face",face.index,"-->")
  1031. if len(face.vertices) == 4:
  1032. temp_buf.append(write_int(vertices[3])) #A
  1033. temp_buf.append(write_int(vertices[2])) #B
  1034. temp_buf.append(write_int(vertices[0])) #C
  1035. if DEBUG: print(" <face id=", vertices[3], vertices[2], vertices[0],"/> <!-- face",face.index,"-->")
  1036. if DEBUG: print(" </brush>")
  1037. tris_buf += write_chunk(b"TRIS", b"".join(temp_buf))
  1038. return tris_buf
  1039. # ==== Write NODE ANIM Chunk ====
  1040. def write_node_anim(num_frames):
  1041. anim_buf = bytearray()
  1042. temp_buf = bytearray()
  1043. temp_buf += write_int(0) #Flags
  1044. temp_buf += write_int(num_frames) #Frames
  1045. temp_buf += write_float(60) #FPS
  1046. if len(temp_buf) > 0:
  1047. anim_buf += write_chunk(b"ANIM",temp_buf)
  1048. temp_buf = ""
  1049. return anim_buf
  1050. # ==== Write NODE NODE Chunk ====
  1051. def write_node_node(ibone):
  1052. node_buf = bytearray()
  1053. temp_buf = []
  1054. bone = bone_stack[ibone]
  1055. matrix = bone[BONE_PARENT_MATRIX]
  1056. temp_buf.append(write_string(bone[BONE_ITSELF].name)) #Node Name
  1057. # FIXME: we should use the same matrix format everywhere to not require this
  1058. position = matrix.to_translation()
  1059. if bone[BONE_PARENT]:
  1060. temp_buf.append(write_float_triplet(-position[0], position[2], position[1]))
  1061. else:
  1062. temp_buf.append(write_float_triplet(position[0], position[2], position[1]))
  1063. scale = matrix.to_scale()
  1064. temp_buf.append(write_float_triplet(scale[0], scale[2], scale[1]))
  1065. quat = matrix.to_quaternion()
  1066. quat.normalize()
  1067. temp_buf.append(write_float_quad(quat.w, quat.x, quat.z, quat.y))
  1068. temp_buf.append(write_node_bone(ibone))
  1069. temp_buf.append(write_node_keys(ibone))
  1070. for iibone in bone_stack:
  1071. if bone_stack[iibone][BONE_PARENT] == bone_stack[ibone][BONE_ITSELF]:
  1072. temp_buf.append(write_node_node(iibone))
  1073. if len(temp_buf) > 0:
  1074. node_buf += write_chunk(b"NODE", b"".join(temp_buf))
  1075. temp_buf = []
  1076. return node_buf
  1077. # ==== Write NODE BONE Chunk ====
  1078. def write_node_bone(ibone):
  1079. bone_buf = bytearray()
  1080. temp_buf = []
  1081. my_name = bone_stack[ibone][BONE_ITSELF].name
  1082. for ivert in range(len(vertex_groups)):
  1083. if my_name in vertex_groups[ivert]:
  1084. vert_influ = vertex_groups[ivert][my_name]
  1085. #if DEBUG: print(" <bone name=",bone_stack[ibone][BONE_ITSELF].name,"face_vertex_id=", ivert + iuv,
  1086. # " weigth=", vert_influ[1] , "/>")
  1087. temp_buf.append(write_int(ivert)) # Face Vertex ID
  1088. temp_buf.append(write_float(vert_influ)) #Weight
  1089. bone_buf += write_chunk(b"BONE", b"".join(temp_buf))
  1090. temp_buf = []
  1091. return bone_buf
  1092. # ==== Write NODE KEYS Chunk ====
  1093. def write_node_keys(ibone):
  1094. keys_buf = bytearray()
  1095. temp_buf = []
  1096. temp_buf.append(write_int(7)) #Flags
  1097. my_name = bone_stack[ibone][BONE_ITSELF].name
  1098. for ikeys in range(len(keys_stack)):
  1099. if keys_stack[ikeys][1] == my_name:
  1100. temp_buf.append(write_int(keys_stack[ikeys][0])) #Frame
  1101. position = keys_stack[ikeys][2]
  1102. # FIXME: we should use the same matrix format everywhere and not require this
  1103. if b3d_parameters.get("local-space"):
  1104. if bone_stack[ibone][BONE_PARENT]:
  1105. temp_buf.append(write_float_triplet(-position[0], position[2], position[1]))
  1106. else:
  1107. temp_buf.append(write_float_triplet(position[0], position[2], position[1]))
  1108. else:
  1109. temp_buf.append(write_float_triplet(-position[0], position[1], position[2]))
  1110. scale = keys_stack[ikeys][3]
  1111. temp_buf.append(write_float_triplet(scale[0], scale[1], scale[2]))
  1112. quat = keys_stack[ikeys][4]
  1113. quat.normalize()
  1114. temp_buf.append(write_float_quad(quat.w, -quat.x, quat.y, quat.z))
  1115. #break
  1116. keys_buf += write_chunk(b"KEYS",b"".join(temp_buf))
  1117. temp_buf = []
  1118. return keys_buf
  1119. # ==== CONFIRM OPERATOR ====
  1120. class B3D_Confirm_Operator(bpy.types.Operator):
  1121. bl_idname = ("screen.b3d_confirm")
  1122. bl_label = ("File Exists, Overwrite?")
  1123. def invoke(self, context, event):
  1124. wm = context.window_manager
  1125. return wm.invoke_props_dialog(self)
  1126. def execute(self, context):
  1127. write_b3d_file(B3D_Confirm_Operator.filepath)
  1128. return {'FINISHED'}
  1129. #class ObjectListItem(bpy.types.PropertyGroup):
  1130. # id = bpy.props.IntProperty(name="ID")
  1131. #
  1132. #bpy.utils.register_class(ObjectListItem)
  1133. # ==== EXPORT OPERATOR ====
  1134. class B3D_Export_Operator(bpy.types.Operator):
  1135. bl_idname = ("screen.b3d_export")
  1136. bl_label = ("B3D Export")
  1137. filepath = bpy.props.StringProperty(subtype="FILE_PATH")
  1138. selected = bpy.props.BoolProperty(name="Export Selected Only", default=False)
  1139. vnormals = bpy.props.BoolProperty(name="Export Vertex Normals", default=True)
  1140. vcolors = bpy.props.BoolProperty(name="Export Vertex Colors", default=True)
  1141. cameras = bpy.props.BoolProperty(name="Export Cameras", default=False)
  1142. lights = bpy.props.BoolProperty(name="Export Lights", default=False)
  1143. mipmap = bpy.props.BoolProperty(name="Mipmap", default=False)
  1144. localsp = bpy.props.BoolProperty(name="Use Local Space Coords", default=False)
  1145. applymodifiers = bpy.props.BoolProperty(name="Apply modifiers", default=True)
  1146. overwrite_without_asking = bpy.props.BoolProperty(name="Overwrite without asking", default=False)
  1147. #skip_dialog = False
  1148. #objects = bpy.props.CollectionProperty(type=ObjectListItem, options={'HIDDEN'})
  1149. def invoke(self, context, event):
  1150. blend_filepath = context.blend_data.filepath
  1151. if not blend_filepath:
  1152. blend_filepath = "Untitled.b3d"
  1153. else:
  1154. blend_filepath = os.path.splitext(blend_filepath)[0] + ".b3d"
  1155. self.filepath = blend_filepath
  1156. context.window_manager.fileselect_add(self)
  1157. return {'RUNNING_MODAL'}
  1158. def execute(self, context):
  1159. global b3d_parameters
  1160. global the_scene
  1161. b3d_parameters["export-selected"] = self.selected
  1162. b3d_parameters["vertex-normals" ] = self.vnormals
  1163. b3d_parameters["vertex-colors" ] = self.vcolors
  1164. b3d_parameters["cameras" ] = self.cameras
  1165. b3d_parameters["lights" ] = self.lights
  1166. b3d_parameters["mipmap" ] = self.mipmap
  1167. b3d_parameters["local-space" ] = self.localsp
  1168. b3d_parameters["apply-modifiers"] = self.applymodifiers
  1169. the_scene = context.scene
  1170. if self.filepath == "":
  1171. return {'FINISHED'}
  1172. if not self.filepath.endswith(".b3d"):
  1173. self.filepath += ".b3d"
  1174. print("EXPORT", self.filepath," vcolor = ", self.vcolors)
  1175. obj_list = []
  1176. try:
  1177. # FIXME: silly and ugly hack, the list of objects to export is passed through
  1178. # a custom scene property
  1179. obj_list = context.scene.obj_list
  1180. except:
  1181. pass
  1182. if len(obj_list) > 0:
  1183. #objlist = []
  1184. #for a in self.objects:
  1185. # objlist.append(bpy.data.objects[a.id])
  1186. #
  1187. #write_b3d_file(self.filepath, obj_list)
  1188. write_b3d_file(self.filepath, obj_list)
  1189. else:
  1190. if os.path.exists(self.filepath) and not self.overwrite_without_asking:
  1191. #self.report({'ERROR'}, "File Exists")
  1192. B3D_Confirm_Operator.filepath = self.filepath
  1193. bpy.ops.screen.b3d_confirm('INVOKE_DEFAULT')
  1194. return {'FINISHED'}
  1195. else:
  1196. write_b3d_file(self.filepath)
  1197. return {'FINISHED'}
  1198. # Add to a menu
  1199. def menu_func_export(self, context):
  1200. global the_scene
  1201. the_scene = context.scene
  1202. self.layout.operator(B3D_Export_Operator.bl_idname, text="B3D (.b3d)")
  1203. def register():
  1204. bpy.types.INFO_MT_file_export.append(menu_func_export)
  1205. bpy.utils.register_module(__name__)
  1206. def unregister():
  1207. bpy.types.INFO_MT_file_export.remove(menu_func_export)
  1208. if __name__ == "__main__":
  1209. register()