export_dae.py 51 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606
  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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  16. #
  17. # ##### END GPL LICENSE BLOCK #####
  18. # <pep8 compliant>
  19. # Script copyright (C) Juan Linietsky
  20. # Contact Info: [email protected]
  21. """
  22. This script is an exporter to the Khronos Collada file format.
  23. http://www.khronos.org/collada/
  24. """
  25. # TODO:
  26. # Materials & Textures
  27. # Optionally export Vertex Colors
  28. # Morph Targets
  29. # Control bone removal
  30. # Copy textures
  31. # Export Keyframe Optimization
  32. # --
  33. # Morph Targets
  34. # Blender native material? (?)
  35. import os
  36. import time
  37. import math # math.pi
  38. import shutil
  39. import bpy
  40. import bmesh
  41. from mathutils import Vector, Matrix
  42. #according to collada spec, order matters
  43. S_ASSET=0
  44. S_IMGS=1
  45. S_FX=2
  46. S_MATS=3
  47. S_GEOM=4
  48. S_MORPH=5
  49. S_SKIN=6
  50. S_CONT=7
  51. S_CAMS=8
  52. S_LAMPS=9
  53. S_ANIM_CLIPS=10
  54. S_NODES=11
  55. S_ANIM=12
  56. CMP_EPSILON=0.0001
  57. def snap_tup(tup):
  58. ret=()
  59. for x in tup:
  60. ret+=( x-math.fmod(x,0.0001), )
  61. return tup
  62. def strmtx(mtx):
  63. s=" "
  64. for x in range(4):
  65. for y in range(4):
  66. s+=str(mtx[x][y])
  67. s+=" "
  68. s+=" "
  69. return s
  70. def numarr(a,mult=1.0):
  71. s=" "
  72. for x in a:
  73. s+=" "+str(x*mult)
  74. s+=" "
  75. return s
  76. def strarr(arr):
  77. s=" "
  78. for x in arr:
  79. s+=" "+str(x)
  80. s+=" "
  81. return s
  82. class DaeExporter:
  83. def validate_id(self,d):
  84. if (d.find("id-")==0):
  85. return "z"+d
  86. return d
  87. def new_id(self,t):
  88. self.last_id+=1
  89. return "id-"+t+"-"+str(self.last_id)
  90. class Vertex:
  91. def close_to(v):
  92. if ( (self.vertex-v.vertex).length() > CMP_EPSILON ):
  93. return False
  94. if ( (self.normal-v.normal).length() > CMP_EPSILON ):
  95. return False
  96. if ( (self.uv-v.uv).length() > CMP_EPSILON ):
  97. return False
  98. if ( (self.uv2-v.uv2).length() > CMP_EPSILON ):
  99. return False
  100. return True
  101. def get_tup(self):
  102. tup = (self.vertex.x,self.vertex.y,self.vertex.z,self.normal.x,self.normal.y,self.normal.z)
  103. for t in self.uv:
  104. tup = tup + (t.x,t.y)
  105. if (self.color!=None):
  106. tup = tup + (self.color.x,self.color.y,self.color.z)
  107. if (self.tangent!=None):
  108. tup = tup + (self.tangent.x,self.tangent.y,self.tangent.z)
  109. if (self.bitangent!=None):
  110. tup = tup + (self.bitangent.x,self.bitangent.y,self.bitangent.z)
  111. #for t in self.bones:
  112. # tup = tup + (t)
  113. #for t in self.weights:
  114. # tup = tup + (t)
  115. return tup
  116. def __init__(self):
  117. self.vertex = Vector( (0.0,0.0,0.0) )
  118. self.normal = Vector( (0.0,0.0,0.0) )
  119. self.tangent = None
  120. self.bitangent = None
  121. self.color = None
  122. self.uv = []
  123. self.uv2 = Vector( (0.0,0.0) )
  124. self.bones=[]
  125. self.weights=[]
  126. def writel(self,section,indent,text):
  127. if (not (section in self.sections)):
  128. self.sections[section]=[]
  129. line=""
  130. for x in range(indent):
  131. line+="\t"
  132. line+=text
  133. self.sections[section].append(line)
  134. def export_image(self,image):
  135. if (image in self.image_cache):
  136. return self.image_cache[image]
  137. imgpath = image.filepath
  138. if (imgpath.find("//")==0 or imgpath.find("\\\\")==0):
  139. #if relative, convert to absolute
  140. imgpath = bpy.path.abspath(imgpath)
  141. #path is absolute, now do something!
  142. if (self.config["use_copy_images"]):
  143. #copy image
  144. basedir = os.path.dirname(self.path)+"/images"
  145. if (not os.path.isdir(basedir)):
  146. os.makedirs(basedir)
  147. dstfile=basedir+"/"+os.path.basename(imgpath)
  148. if (not os.path.isfile(dstfile)):
  149. shutil.copy(imgpath,dstfile)
  150. imgpath="images/"+os.path.basename(imgpath)
  151. else:
  152. #export relative, always, no one wants absolute paths.
  153. try:
  154. imgpath = os.path.relpath(imgpath,os.path.dirname(self.path)).replace("\\","/") # export unix compatible always
  155. except:
  156. pass #fails sometimes, not sure why
  157. imgid = self.new_id("image")
  158. self.writel(S_IMGS,1,'<image id="'+imgid+'" name="'+image.name+'">')
  159. self.writel(S_IMGS,2,'<init_from>'+imgpath+'</init_from>"/>')
  160. self.writel(S_IMGS,1,'</image>')
  161. self.image_cache[image]=imgid
  162. return imgid
  163. def export_material(self,material,double_sided_hint=True):
  164. if (material in self.material_cache):
  165. return self.material_cache[material]
  166. fxid = self.new_id("fx")
  167. self.writel(S_FX,1,'<effect id="'+fxid+'" name="'+material.name+'-fx">')
  168. self.writel(S_FX,2,'<profile_COMMON>')
  169. #Find and fetch the textures and create sources
  170. sampler_table={}
  171. diffuse_tex=None
  172. specular_tex=None
  173. emission_tex=None
  174. normal_tex=None
  175. for i in range(len(material.texture_slots)):
  176. ts=material.texture_slots[i]
  177. if (not ts):
  178. continue
  179. if (not ts.use):
  180. continue
  181. if (not ts.texture):
  182. continue
  183. if (ts.texture.type!="IMAGE"):
  184. continue
  185. if (ts.texture.image==None):
  186. continue
  187. #image
  188. imgid = self.export_image(ts.texture.image)
  189. #surface
  190. surface_sid = self.new_id("fx_surf")
  191. self.writel(S_FX,3,'<newparam sid="'+surface_sid+'">')
  192. self.writel(S_FX,4,'<surface type="2D">')
  193. self.writel(S_FX,5,'<init_from>'+imgid+'</init_from>') #this is sooo weird
  194. self.writel(S_FX,5,'<format>A8R8G8B8</format>')
  195. self.writel(S_FX,4,'</surface>')
  196. self.writel(S_FX,3,'</newparam>')
  197. #sampler, collada sure likes it difficult
  198. sampler_sid = self.new_id("fx_sampler")
  199. self.writel(S_FX,3,'<newparam sid="'+sampler_sid+'">')
  200. self.writel(S_FX,4,'<sampler2D>')
  201. self.writel(S_FX,5,'<source>'+surface_sid+'</source>')
  202. self.writel(S_FX,4,'</sampler2D>')
  203. self.writel(S_FX,3,'</newparam>')
  204. sampler_table[i]=sampler_sid
  205. if (ts.use_map_color_diffuse and diffuse_tex==None):
  206. diffuse_tex=sampler_sid
  207. if (ts.use_map_color_spec and specular_tex==None):
  208. specular_tex=sampler_sid
  209. if (ts.use_map_emit and emission_tex==None):
  210. emission_tex=sampler_sid
  211. if (ts.use_map_normal and normal_tex==None):
  212. normal_tex=sampler_sid
  213. self.writel(S_FX,3,'<technique sid="common">')
  214. shtype="blinn"
  215. self.writel(S_FX,4,'<'+shtype+'>')
  216. #ambient? from where?
  217. self.writel(S_FX,5,'<emission>')
  218. if (emission_tex!=None):
  219. self.writel(S_FX,6,'<texture texture="'+emission_tex+'" texcoord="CHANNEL1"/>')
  220. else:
  221. self.writel(S_FX,6,'<color>'+numarr(material.diffuse_color,material.emit)+' </color>') # not totally right but good enough
  222. self.writel(S_FX,5,'</emission>')
  223. self.writel(S_FX,5,'<ambient>')
  224. self.writel(S_FX,6,'<color>'+numarr(self.scene.world.ambient_color,material.ambient)+' </color>')
  225. self.writel(S_FX,5,'</ambient>')
  226. self.writel(S_FX,5,'<diffuse>')
  227. if (diffuse_tex!=None):
  228. self.writel(S_FX,6,'<texture texture="'+diffuse_tex+'" texcoord="CHANNEL1"/>')
  229. else:
  230. self.writel(S_FX,6,'<color>'+numarr(material.diffuse_color,material.diffuse_intensity)+'</color>')
  231. self.writel(S_FX,5,'</diffuse>')
  232. self.writel(S_FX,5,'<specular>')
  233. if (specular_tex!=None):
  234. self.writel(S_FX,6,'<texture texture="'+specular_tex+'" texcoord="CHANNEL1"/>')
  235. else:
  236. self.writel(S_FX,6,'<color>'+numarr(material.specular_color,material.specular_intensity)+'</color>')
  237. self.writel(S_FX,5,'</specular>')
  238. self.writel(S_FX,5,'<shininess>')
  239. self.writel(S_FX,6,'<float>'+str(material.specular_hardness)+'</float>')
  240. self.writel(S_FX,5,'</shininess>')
  241. self.writel(S_FX,5,'<reflective>')
  242. self.writel(S_FX,6,'<color>'+strarr(material.mirror_color)+'</color>')
  243. self.writel(S_FX,5,'</reflective>')
  244. if (material.use_transparency):
  245. self.writel(S_FX,5,'<transparency>')
  246. self.writel(S_FX,6,'<float>'+str(material.alpha)+'</float>')
  247. self.writel(S_FX,5,'</transparency>')
  248. self.writel(S_FX,4,'</'+shtype+'>')
  249. self.writel(S_FX,4,'<index_of_refraction>'+str(material.specular_ior)+'</index_of_refraction>')
  250. self.writel(S_FX,4,'<extra>')
  251. self.writel(S_FX,5,'<technique profile="FCOLLADA">')
  252. if (normal_tex):
  253. self.writel(S_FX,6,'<bump bumptype="NORMALMAP">')
  254. self.writel(S_FX,7,'<texture texture="'+normal_tex+'" texcoord="CHANNEL1"/>')
  255. self.writel(S_FX,6,'</bump>')
  256. self.writel(S_FX,5,'</technique>')
  257. self.writel(S_FX,5,'<technique profile="GOOGLEEARTH">')
  258. self.writel(S_FX,6,'<double_sided>'+["0","1"][double_sided_hint]+"</double_sided>")
  259. self.writel(S_FX,5,'</technique>')
  260. self.writel(S_FX,4,'</extra>')
  261. self.writel(S_FX,3,'</technique>')
  262. self.writel(S_FX,2,'</profile_COMMON>')
  263. self.writel(S_FX,1,'</effect>')
  264. # Also export blender material in all it's glory (if set as active)
  265. #Material
  266. matid = self.new_id("material")
  267. self.writel(S_MATS,1,'<material id="'+matid+'" name="'+material.name+'">')
  268. self.writel(S_MATS,2,'<instance_effect url="#'+fxid+'"/>')
  269. self.writel(S_MATS,1,'</material>')
  270. self.material_cache[material]=matid
  271. return matid
  272. def export_mesh(self,node,armature=None,skeyindex=-1,skel_source=None):
  273. mesh = node.data
  274. if (node.data in self.mesh_cache):
  275. return self.mesh_cache[mesh]
  276. if (skeyindex==-1 and mesh.shape_keys!=None and len(mesh.shape_keys.key_blocks)):
  277. values=[]
  278. morph_targets=[]
  279. md=None
  280. for k in range(0,len(mesh.shape_keys.key_blocks)):
  281. shape = node.data.shape_keys.key_blocks[k]
  282. values+=[shape.value] #save value
  283. shape.value=0
  284. mid = self.new_id("morph")
  285. for k in range(0,len(mesh.shape_keys.key_blocks)):
  286. shape = node.data.shape_keys.key_blocks[k]
  287. node.show_only_shape_key=True
  288. node.active_shape_key_index = k
  289. shape.value = 1.0
  290. mesh.update()
  291. """
  292. oldval = shape.value
  293. shape.value = 1.0
  294. """
  295. p = node.data
  296. v = node.to_mesh(bpy.context.scene, True, "RENDER")
  297. node.data = v
  298. # self.export_node(node,il,shape.name)
  299. node.data.update()
  300. if (armature and k==0):
  301. md=self.export_mesh(node,armature,k,mid)
  302. else:
  303. md=self.export_mesh(node,None,k)
  304. node.data = p
  305. node.data.update()
  306. shape.value = 0.0
  307. morph_targets.append(md)
  308. """
  309. shape.value = oldval
  310. """
  311. node.show_only_shape_key=False
  312. node.active_shape_key_index = 0
  313. self.writel(S_MORPH,1,'<controller id="'+mid+'" name="">')
  314. #if ("skin_id" in morph_targets[0]):
  315. # self.writel(S_MORPH,2,'<morph source="#'+morph_targets[0]["skin_id"]+'" method="NORMALIZED">')
  316. #else:
  317. self.writel(S_MORPH,2,'<morph source="#'+morph_targets[0]["id"]+'" method="NORMALIZED">')
  318. self.writel(S_MORPH,3,'<source id="'+mid+'-morph-targets">')
  319. self.writel(S_MORPH,4,'<IDREF_array id="'+mid+'-morph-targets-array" count="'+str(len(morph_targets)-1)+'">')
  320. marr=""
  321. warr=""
  322. for i in range(len(morph_targets)):
  323. if (i==0):
  324. continue
  325. elif (i>1):
  326. marr+=" "
  327. if ("skin_id" in morph_targets[i]):
  328. marr+=morph_targets[i]["skin_id"]
  329. else:
  330. marr+=morph_targets[i]["id"]
  331. warr+=" 0"
  332. self.writel(S_MORPH,5,marr)
  333. self.writel(S_MORPH,4,'</IDREF_array>')
  334. self.writel(S_MORPH,4,'<technique_common>')
  335. self.writel(S_MORPH,5,'<accessor source="#'+mid+'-morph-targets-array" count="'+str(len(morph_targets)-1)+'" stride="1">')
  336. self.writel(S_MORPH,6,'<param name="MORPH_TARGET" type="IDREF"/>')
  337. self.writel(S_MORPH,5,'</accessor>')
  338. self.writel(S_MORPH,4,'</technique_common>')
  339. self.writel(S_MORPH,3,'</source>')
  340. self.writel(S_MORPH,3,'<source id="'+mid+'-morph-weights">')
  341. self.writel(S_MORPH,4,'<float_array id="'+mid+'-morph-weights-array" count="'+str(len(morph_targets)-1)+'" >')
  342. self.writel(S_MORPH,5,warr)
  343. self.writel(S_MORPH,4,'</float_array>')
  344. self.writel(S_MORPH,4,'<technique_common>')
  345. self.writel(S_MORPH,5,'<accessor source="#'+mid+'-morph-weights-array" count="'+str(len(morph_targets)-1)+'" stride="1">')
  346. self.writel(S_MORPH,6,'<param name="MORPH_WEIGHT" type="float"/>')
  347. self.writel(S_MORPH,5,'</accessor>')
  348. self.writel(S_MORPH,4,'</technique_common>')
  349. self.writel(S_MORPH,3,'</source>')
  350. self.writel(S_MORPH,3,'<targets>')
  351. self.writel(S_MORPH,4,'<input semantic="MORPH_TARGET" source="#'+mid+'-morph-targets"/>')
  352. self.writel(S_MORPH,4,'<input semantic="MORPH_WEIGHT" source="#'+mid+'-morph-weights"/>')
  353. self.writel(S_MORPH,3,'</targets>')
  354. self.writel(S_MORPH,2,'</morph>')
  355. self.writel(S_MORPH,1,'</controller>')
  356. if (armature!=None):
  357. self.armature_for_morph[node]=armature
  358. meshdata={}
  359. if (armature):
  360. meshdata = morph_targets[0]
  361. meshdata["morph_id"]=mid
  362. else:
  363. meshdata["id"]=morph_targets[0]["id"]
  364. meshdata["morph_id"]=mid
  365. meshdata["material_assign"]=morph_targets[0]["material_assign"]
  366. self.mesh_cache[node.data]=meshdata
  367. return meshdata
  368. apply_modifiers = len(node.modifiers) and self.config["use_mesh_modifiers"]
  369. mesh=node.to_mesh(self.scene,apply_modifiers,"RENDER") #is this allright?
  370. triangulate=self.config["use_triangles"]
  371. if (triangulate):
  372. bm = bmesh.new()
  373. bm.from_mesh(mesh)
  374. bmesh.ops.triangulate(bm, faces=bm.faces)
  375. bm.to_mesh(mesh)
  376. bm.free()
  377. mesh.update(calc_tessface=True)
  378. vertices=[]
  379. vertex_map={}
  380. surface_indices={}
  381. materials={}
  382. materials={}
  383. si=None
  384. if (armature!=None):
  385. si=self.skeleton_info[armature]
  386. has_uv=False
  387. has_uv2=False
  388. has_weights=armature!=None
  389. has_tangents=self.config["use_tangent_arrays"] # could detect..
  390. has_colors=len(mesh.vertex_colors)
  391. mat_assign=[]
  392. uv_layer_count=len(mesh.uv_textures)
  393. if (len(mesh.uv_textures)):
  394. try:
  395. mesh.calc_tangents()
  396. except:
  397. print("Warning, blender API is fucked up, not exporting UVs for this object.")
  398. uv_layer_count=0
  399. mesh.calc_normals_split()
  400. has_tangents=False
  401. else:
  402. mesh.calc_normals_split()
  403. has_tangents=False
  404. for fi in range(len(mesh.polygons)):
  405. f=mesh.polygons[fi]
  406. if (not (f.material_index in surface_indices)):
  407. surface_indices[f.material_index]=[]
  408. print("Type: "+str(type(f.material_index)))
  409. print("IDX: "+str(f.material_index)+"/"+str(len(mesh.materials)))
  410. try:
  411. #Bizarre blender behavior i don't understand, so catching exception
  412. mat = mesh.materials[f.material_index]
  413. except:
  414. mat= None
  415. if (mat!=None):
  416. materials[f.material_index]=self.export_material( mat,mesh.show_double_sided )
  417. else:
  418. materials[f.material_index]=None #weird, has no material?
  419. indices = surface_indices[f.material_index]
  420. vi=[]
  421. #vertices always 3
  422. """
  423. if (len(f.vertices)==3):
  424. vi.append(0)
  425. vi.append(1)
  426. vi.append(2)
  427. elif (len(f.vertices)==4):
  428. #todo, should use shortest path
  429. vi.append(0)
  430. vi.append(1)
  431. vi.append(2)
  432. vi.append(0)
  433. vi.append(2)
  434. vi.append(3)
  435. """
  436. for lt in range(f.loop_total):
  437. loop_index = f.loop_start + lt
  438. ml = mesh.loops[loop_index]
  439. mv = mesh.vertices[ml.vertex_index]
  440. v = self.Vertex()
  441. v.vertex = Vector( mv.co )
  442. for xt in mesh.uv_layers:
  443. v.uv.append( Vector( xt.data[loop_index].uv ) )
  444. if (has_colors):
  445. v.color = Vector( mesh.vertex_colors[0].data[loop_index].color )
  446. v.normal = Vector( ml.normal )
  447. if (has_tangents):
  448. v.tangent = Vector( ml.tangent )
  449. v.bitangent = Vector( ml.bitangent )
  450. # if (armature):
  451. # v.vertex = node.matrix_world * v.vertex
  452. #v.color=Vertex(mv. ???
  453. if (armature!=None):
  454. wsum=0.0
  455. for vg in mv.groups:
  456. if vg.group >= len(node.vertex_groups):
  457. continue;
  458. name = node.vertex_groups[vg.group].name
  459. if (name in si["bone_index"]):
  460. #could still put the weight as 0.0001 maybe
  461. if (vg.weight>0.001): #blender has a lot of zero weight stuff
  462. v.bones.append(si["bone_index"][name])
  463. v.weights.append(vg.weight)
  464. wsum+=vg.weight
  465. tup = v.get_tup()
  466. idx = 0
  467. if (skeyindex==-1 and tup in vertex_map): #do not optmize if using shapekeys
  468. idx = vertex_map[tup]
  469. else:
  470. idx = len(vertices)
  471. vertices.append(v)
  472. vertex_map[tup]=idx
  473. vi.append(idx)
  474. if (len(vi)>2):
  475. #only triangles and above
  476. indices.append(vi)
  477. meshid = self.new_id("mesh")
  478. self.writel(S_GEOM,1,'<geometry id="'+meshid+'" name="'+mesh.name+'">')
  479. self.writel(S_GEOM,2,'<mesh>')
  480. # Vertex Array
  481. self.writel(S_GEOM,3,'<source id="'+meshid+'-positions">')
  482. float_values=""
  483. for v in vertices:
  484. float_values+=" "+str(v.vertex.x)+" "+str(v.vertex.y)+" "+str(v.vertex.z)
  485. self.writel(S_GEOM,4,'<float_array id="'+meshid+'-positions-array" count="'+str(len(vertices)*3)+'">'+float_values+'</float_array>')
  486. self.writel(S_GEOM,4,'<technique_common>')
  487. self.writel(S_GEOM,4,'<accessor source="#'+meshid+'-positions-array" count="'+str(len(vertices))+'" stride="3">')
  488. self.writel(S_GEOM,5,'<param name="X" type="float"/>')
  489. self.writel(S_GEOM,5,'<param name="Y" type="float"/>')
  490. self.writel(S_GEOM,5,'<param name="Z" type="float"/>')
  491. self.writel(S_GEOM,4,'</accessor>')
  492. self.writel(S_GEOM,4,'</technique_common>')
  493. self.writel(S_GEOM,3,'</source>')
  494. # Normal Array
  495. self.writel(S_GEOM,3,'<source id="'+meshid+'-normals">')
  496. float_values=""
  497. for v in vertices:
  498. float_values+=" "+str(v.normal.x)+" "+str(v.normal.y)+" "+str(v.normal.z)
  499. self.writel(S_GEOM,4,'<float_array id="'+meshid+'-normals-array" count="'+str(len(vertices)*3)+'">'+float_values+'</float_array>')
  500. self.writel(S_GEOM,4,'<technique_common>')
  501. self.writel(S_GEOM,4,'<accessor source="#'+meshid+'-normals-array" count="'+str(len(vertices))+'" stride="3">')
  502. self.writel(S_GEOM,5,'<param name="X" type="float"/>')
  503. self.writel(S_GEOM,5,'<param name="Y" type="float"/>')
  504. self.writel(S_GEOM,5,'<param name="Z" type="float"/>')
  505. self.writel(S_GEOM,4,'</accessor>')
  506. self.writel(S_GEOM,4,'</technique_common>')
  507. self.writel(S_GEOM,3,'</source>')
  508. if (has_tangents):
  509. self.writel(S_GEOM,3,'<source id="'+meshid+'-tangents">')
  510. float_values=""
  511. for v in vertices:
  512. float_values+=" "+str(v.tangent.x)+" "+str(v.tangent.y)+" "+str(v.tangent.z)
  513. self.writel(S_GEOM,4,'<float_array id="'+meshid+'-tangents-array" count="'+str(len(vertices)*3)+'">'+float_values+'</float_array>')
  514. self.writel(S_GEOM,4,'<technique_common>')
  515. self.writel(S_GEOM,4,'<accessor source="#'+meshid+'-tangents-array" count="'+str(len(vertices))+'" stride="3">')
  516. self.writel(S_GEOM,5,'<param name="X" type="float"/>')
  517. self.writel(S_GEOM,5,'<param name="Y" type="float"/>')
  518. self.writel(S_GEOM,5,'<param name="Z" type="float"/>')
  519. self.writel(S_GEOM,4,'</accessor>')
  520. self.writel(S_GEOM,4,'</technique_common>')
  521. self.writel(S_GEOM,3,'</source>')
  522. self.writel(S_GEOM,3,'<source id="'+meshid+'-bitangents">')
  523. float_values=""
  524. for v in vertices:
  525. float_values+=" "+str(v.bitangent.x)+" "+str(v.bitangent.y)+" "+str(v.bitangent.z)
  526. self.writel(S_GEOM,4,'<float_array id="'+meshid+'-bitangents-array" count="'+str(len(vertices)*3)+'">'+float_values+'</float_array>')
  527. self.writel(S_GEOM,4,'<technique_common>')
  528. self.writel(S_GEOM,4,'<accessor source="#'+meshid+'-bitangents-array" count="'+str(len(vertices))+'" stride="3">')
  529. self.writel(S_GEOM,5,'<param name="X" type="float"/>')
  530. self.writel(S_GEOM,5,'<param name="Y" type="float"/>')
  531. self.writel(S_GEOM,5,'<param name="Z" type="float"/>')
  532. self.writel(S_GEOM,4,'</accessor>')
  533. self.writel(S_GEOM,4,'</technique_common>')
  534. self.writel(S_GEOM,3,'</source>')
  535. # UV Arrays
  536. for uvi in range(uv_layer_count):
  537. self.writel(S_GEOM,3,'<source id="'+meshid+'-texcoord-'+str(uvi)+'">')
  538. float_values=""
  539. for v in vertices:
  540. try:
  541. float_values+=" "+str(v.uv[uvi].x)+" "+str(v.uv[uvi].y)
  542. except:
  543. # I don't understand this weird multi-uv-layer API, but with this it seems to works
  544. float_values+=" 0 0 "
  545. self.writel(S_GEOM,4,'<float_array id="'+meshid+'-texcoord-'+str(uvi)+'-array" count="'+str(len(vertices)*2)+'">'+float_values+'</float_array>')
  546. self.writel(S_GEOM,4,'<technique_common>')
  547. self.writel(S_GEOM,4,'<accessor source="#'+meshid+'-texcoord-'+str(uvi)+'-array" count="'+str(len(vertices))+'" stride="2">')
  548. self.writel(S_GEOM,5,'<param name="S" type="float"/>')
  549. self.writel(S_GEOM,5,'<param name="T" type="float"/>')
  550. self.writel(S_GEOM,4,'</accessor>')
  551. self.writel(S_GEOM,4,'</technique_common>')
  552. self.writel(S_GEOM,3,'</source>')
  553. # Color Arrays
  554. if (has_colors):
  555. self.writel(S_GEOM,3,'<source id="'+meshid+'-colors">')
  556. float_values=""
  557. for v in vertices:
  558. float_values+=" "+str(v.color.x)+" "+str(v.color.y)+" "+str(v.color.z)
  559. self.writel(S_GEOM,4,'<float_array id="'+meshid+'-colors-array" count="'+str(len(vertices)*3)+'">'+float_values+'</float_array>')
  560. self.writel(S_GEOM,4,'<technique_common>')
  561. self.writel(S_GEOM,4,'<accessor source="#'+meshid+'-colors-array" count="'+str(len(vertices))+'" stride="3">')
  562. self.writel(S_GEOM,5,'<param name="X" type="float"/>')
  563. self.writel(S_GEOM,5,'<param name="Y" type="float"/>')
  564. self.writel(S_GEOM,5,'<param name="Z" type="float"/>')
  565. self.writel(S_GEOM,4,'</accessor>')
  566. self.writel(S_GEOM,4,'</technique_common>')
  567. self.writel(S_GEOM,3,'</source>')
  568. # Triangle Lists
  569. self.writel(S_GEOM,3,'<vertices id="'+meshid+'-vertices">')
  570. self.writel(S_GEOM,4,'<input semantic="POSITION" source="#'+meshid+'-positions"/>')
  571. self.writel(S_GEOM,3,'</vertices>')
  572. prim_type=""
  573. if (triangulate):
  574. prim_type="triangles"
  575. else:
  576. prim_type="polygons"
  577. for m in surface_indices:
  578. indices = surface_indices[m]
  579. mat = materials[m]
  580. if (mat!=None):
  581. matref = self.new_id("trimat")
  582. self.writel(S_GEOM,3,'<'+prim_type+' count="'+str(int(len(indices)))+'" material="'+matref+'">') # todo material
  583. mat_assign.append( (mat,matref) )
  584. else:
  585. self.writel(S_GEOM,3,'<'+prim_type+' count="'+str(int(len(indices)))+'">') # todo material
  586. self.writel(S_GEOM,4,'<input semantic="VERTEX" source="#'+meshid+'-vertices" offset="0"/>')
  587. self.writel(S_GEOM,4,'<input semantic="NORMAL" source="#'+meshid+'-normals" offset="0"/>')
  588. for uvi in range(uv_layer_count):
  589. self.writel(S_GEOM,4,'<input semantic="TEXCOORD" source="#'+meshid+'-texcoord-'+str(uvi)+'" offset="0" set="'+str(uvi)+'"/>')
  590. if (has_colors):
  591. self.writel(S_GEOM,4,'<input semantic="COLOR" source="#'+meshid+'-colors" offset="0"/>')
  592. if (has_tangents):
  593. self.writel(S_GEOM,4,'<input semantic="TEXTANGENT" source="#'+meshid+'-tangents" offset="0"/>')
  594. self.writel(S_GEOM,4,'<input semantic="TEXBINORMAL" source="#'+meshid+'-bitangents" offset="0"/>')
  595. if (triangulate):
  596. int_values="<p>"
  597. for p in indices:
  598. for i in p:
  599. int_values+=" "+str(i)
  600. int_values+=" </p>"
  601. self.writel(S_GEOM,4,int_values)
  602. else:
  603. for p in indices:
  604. int_values="<p>"
  605. for i in p:
  606. int_values+=" "+str(i)
  607. int_values+=" </p>"
  608. self.writel(S_GEOM,4,int_values)
  609. self.writel(S_GEOM,3,'</'+prim_type+'>')
  610. self.writel(S_GEOM,2,'</mesh>')
  611. self.writel(S_GEOM,1,'</geometry>')
  612. meshdata={}
  613. meshdata["id"]=meshid
  614. meshdata["material_assign"]=mat_assign
  615. if (skeyindex==-1):
  616. self.mesh_cache[node.data]=meshdata
  617. # Export armature data (if armature exists)
  618. if (armature!=None and (skel_source!=None or skeyindex==-1)):
  619. contid = self.new_id("controller")
  620. self.writel(S_SKIN,1,'<controller id="'+contid+'">')
  621. if (skel_source!=None):
  622. self.writel(S_SKIN,2,'<skin source="#'+skel_source+'">')
  623. else:
  624. self.writel(S_SKIN,2,'<skin source="#'+meshid+'">')
  625. self.writel(S_SKIN,3,'<bind_shape_matrix>'+strmtx(node.matrix_world)+'</bind_shape_matrix>')
  626. #Joint Names
  627. self.writel(S_SKIN,3,'<source id="'+contid+'-joints">')
  628. name_values=""
  629. for v in si["bone_names"]:
  630. name_values+=" "+v
  631. self.writel(S_SKIN,4,'<Name_array id="'+contid+'-joints-array" count="'+str(len(si["bone_names"]))+'">'+name_values+'</Name_array>')
  632. self.writel(S_SKIN,4,'<technique_common>')
  633. self.writel(S_SKIN,4,'<accessor source="#'+contid+'-joints-array" count="'+str(len(si["bone_names"]))+'" stride="1">')
  634. self.writel(S_SKIN,5,'<param name="JOINT" type="Name"/>')
  635. self.writel(S_SKIN,4,'</accessor>')
  636. self.writel(S_SKIN,4,'</technique_common>')
  637. self.writel(S_SKIN,3,'</source>')
  638. #Pose Matrices!
  639. self.writel(S_SKIN,3,'<source id="'+contid+'-bind_poses">')
  640. pose_values=""
  641. for v in si["bone_bind_poses"]:
  642. pose_values+=" "+strmtx(v)
  643. self.writel(S_SKIN,4,'<float_array id="'+contid+'-bind_poses-array" count="'+str(len(si["bone_bind_poses"])*16)+'">'+pose_values+'</float_array>')
  644. self.writel(S_SKIN,4,'<technique_common>')
  645. self.writel(S_SKIN,4,'<accessor source="#'+contid+'-bind_poses-array" count="'+str(len(si["bone_bind_poses"]))+'" stride="16">')
  646. self.writel(S_SKIN,5,'<param name="TRANSFORM" type="float4x4"/>')
  647. self.writel(S_SKIN,4,'</accessor>')
  648. self.writel(S_SKIN,4,'</technique_common>')
  649. self.writel(S_SKIN,3,'</source>')
  650. #Skin Weights!
  651. self.writel(S_SKIN,3,'<source id="'+contid+'-skin_weights">')
  652. skin_weights=""
  653. skin_weights_total=0
  654. for v in vertices:
  655. skin_weights_total+=len(v.weights)
  656. for w in v.weights:
  657. skin_weights+=" "+str(w)
  658. self.writel(S_SKIN,4,'<float_array id="'+contid+'-skin_weights-array" count="'+str(skin_weights_total)+'">'+skin_weights+'</float_array>')
  659. self.writel(S_SKIN,4,'<technique_common>')
  660. self.writel(S_SKIN,4,'<accessor source="#'+contid+'-skin_weights-array" count="'+str(skin_weights_total)+'" stride="1">')
  661. self.writel(S_SKIN,5,'<param name="WEIGHT" type="float"/>')
  662. self.writel(S_SKIN,4,'</accessor>')
  663. self.writel(S_SKIN,4,'</technique_common>')
  664. self.writel(S_SKIN,3,'</source>')
  665. self.writel(S_SKIN,3,'<joints>')
  666. self.writel(S_SKIN,4,'<input semantic="JOINT" source="#'+contid+'-joints"/>')
  667. self.writel(S_SKIN,4,'<input semantic="INV_BIND_MATRIX" source="#'+contid+'-bind_poses"/>')
  668. self.writel(S_SKIN,3,'</joints>')
  669. self.writel(S_SKIN,3,'<vertex_weights count="'+str(len(vertices))+'">')
  670. self.writel(S_SKIN,4,'<input semantic="JOINT" source="#'+contid+'-joints" offset="0"/>')
  671. self.writel(S_SKIN,4,'<input semantic="WEIGHT" source="#'+contid+'-skin_weights" offset="1"/>')
  672. vcounts=""
  673. vs=""
  674. vcount=0
  675. for v in vertices:
  676. vcounts+=" "+str(len(v.weights))
  677. for b in v.bones:
  678. vs+=" "+str(b)
  679. vs+=" "+str(vcount)
  680. vcount+=1
  681. self.writel(S_SKIN,4,'<vcount>'+vcounts+'</vcount>')
  682. self.writel(S_SKIN,4,'<v>'+vs+'</v>')
  683. self.writel(S_SKIN,3,'</vertex_weights>')
  684. self.writel(S_SKIN,2,'</skin>')
  685. self.writel(S_SKIN,1,'</controller>')
  686. meshdata["skin_id"]=contid
  687. return meshdata
  688. def export_mesh_node(self,node,il):
  689. if (node.data==None):
  690. return
  691. armature=None
  692. if (node.parent!=None):
  693. if (node.parent.type=="ARMATURE"):
  694. armature=node.parent
  695. if (node.data.shape_keys!=None):
  696. sk = node.data.shape_keys
  697. if (sk.animation_data):
  698. print("HAS ANIM")
  699. print("DRIVERS: "+str(len(sk.animation_data.drivers)))
  700. for d in sk.animation_data.drivers:
  701. if (d.driver):
  702. for v in d.driver.variables:
  703. for t in v.targets:
  704. if (t.id!=None and t.id.name in self.scene.objects):
  705. print("LINKING "+str(node)+" WITH "+str(t.id.name))
  706. self.armature_for_morph[node]=self.scene.objects[t.id.name]
  707. meshdata = self.export_mesh(node,armature)
  708. close_controller=False
  709. if ("skin_id" in meshdata):
  710. close_controller=True
  711. self.writel(S_NODES,il,'<instance_controller url="#'+meshdata["skin_id"]+'">')
  712. for sn in self.skeleton_info[armature]["skeleton_nodes"]:
  713. self.writel(S_NODES,il+1,'<skeleton>#'+sn+'</skeleton>')
  714. elif ("morph_id" in meshdata):
  715. self.writel(S_NODES,il,'<instance_controller url="#'+meshdata["morph_id"]+'">')
  716. close_controller=True
  717. elif (armature==None):
  718. self.writel(S_NODES,il,'<instance_geometry url="#'+meshdata["id"]+'">')
  719. if (len(meshdata["material_assign"])>0):
  720. self.writel(S_NODES,il+1,'<bind_material>')
  721. self.writel(S_NODES,il+2,'<technique_common>')
  722. for m in meshdata["material_assign"]:
  723. self.writel(S_NODES,il+3,'<instance_material symbol="'+m[1]+'" target="#'+m[0]+'"/>')
  724. self.writel(S_NODES,il+2,'</technique_common>')
  725. self.writel(S_NODES,il+1,'</bind_material>')
  726. if (close_controller):
  727. self.writel(S_NODES,il,'</instance_controller>')
  728. else:
  729. self.writel(S_NODES,il,'</instance_geometry>')
  730. def export_armature_bone(self,bone,il,si):
  731. boneid = self.new_id("bone")
  732. boneidx = si["bone_count"]
  733. si["bone_count"]+=1
  734. bonesid = si["id"]+"-"+str(boneidx)
  735. si["bone_index"][bone.name]=boneidx
  736. si["bone_ids"][bone]=boneid
  737. si["bone_names"].append(bonesid)
  738. self.writel(S_NODES,il,'<node id="'+boneid+'" sid="'+bonesid+'" name="'+bone.name+'" type="JOINT">')
  739. il+=1
  740. xform = bone.matrix_local
  741. si["bone_bind_poses"].append((si["armature_xform"] * xform).inverted())
  742. if (bone.parent!=None):
  743. xform = bone.parent.matrix_local.inverted() * xform
  744. else:
  745. si["skeleton_nodes"].append(boneid)
  746. self.writel(S_NODES,il,'<matrix sid="transform">'+strmtx(xform)+'</matrix>')
  747. for c in bone.children:
  748. self.export_armature_bone(c,il,si)
  749. il-=1
  750. self.writel(S_NODES,il,'</node>')
  751. def export_armature_node(self,node,il):
  752. if (node.data==None):
  753. return
  754. self.skeletons.append(node)
  755. armature = node.data
  756. self.skeleton_info[node]={ "bone_count":0, "id":self.new_id("skelbones"),"name":node.name, "bone_index":{},"bone_ids":{},"bone_names":[],"bone_bind_poses":[],"skeleton_nodes":[],"armature_xform":node.matrix_world }
  757. for b in armature.bones:
  758. if (b.parent!=None):
  759. continue
  760. self.export_armature_bone(b,il,self.skeleton_info[node])
  761. if (node.pose):
  762. for b in node.pose.bones:
  763. for x in b.constraints:
  764. if (x.type=='ACTION'):
  765. self.action_constraints.append(x.action)
  766. def export_camera_node(self,node,il):
  767. if (node.data==None):
  768. return
  769. camera=node.data
  770. camid=self.new_id("camera")
  771. self.writel(S_CAMS,1,'<camera id="'+camid+'" name="'+camera.name+'">')
  772. self.writel(S_CAMS,2,'<optics>')
  773. self.writel(S_CAMS,3,'<technique_common>')
  774. if (camera.type=="PERSP"):
  775. self.writel(S_CAMS,4,'<perspective>')
  776. self.writel(S_CAMS,5,'<yfov> '+str(math.degrees(camera.angle))+' </yfov>') # I think?
  777. self.writel(S_CAMS,5,'<aspect_ratio> '+str(self.scene.render.resolution_x / self.scene.render.resolution_y)+' </aspect_ratio>')
  778. self.writel(S_CAMS,5,'<znear> '+str(camera.clip_start)+' </znear>')
  779. self.writel(S_CAMS,5,'<zfar> '+str(camera.clip_end)+' </zfar>')
  780. self.writel(S_CAMS,4,'</perspective>')
  781. else:
  782. self.writel(S_CAMS,4,'<orthografic>')
  783. self.writel(S_CAMS,5,'<xmag> '+str(camera.ortho_scale)+' </xmag>') # I think?
  784. self.writel(S_CAMS,5,'<aspect_ratio> '+str(self.scene.render.resolution_x / self.scene.render.resolution_y)+' </aspect_ratio>')
  785. self.writel(S_CAMS,5,'<znear> '+str(camera.clip_start)+' </znear>')
  786. self.writel(S_CAMS,5,'<zfar> '+str(camera.clip_end)+' </zfar>')
  787. self.writel(S_CAMS,4,'</orthografic>')
  788. self.writel(S_CAMS,3,'</technique_common>')
  789. self.writel(S_CAMS,2,'</optics>')
  790. self.writel(S_CAMS,1,'</camera>')
  791. self.writel(S_NODES,il,'<instance_camera url="#'+camid+'"/>')
  792. def export_lamp_node(self,node,il):
  793. if (node.data==None):
  794. return
  795. light=node.data
  796. lightid=self.new_id("light")
  797. self.writel(S_LAMPS,1,'<light id="'+lightid+'" name="'+light.name+'">')
  798. #self.writel(S_LAMPS,2,'<optics>')
  799. self.writel(S_LAMPS,3,'<technique_common>')
  800. if (light.type=="POINT"):
  801. self.writel(S_LAMPS,4,'<point>')
  802. self.writel(S_LAMPS,5,'<color>'+strarr(light.color)+'</color>')
  803. att_by_distance = 2.0 / light.distance # convert to linear attenuation
  804. self.writel(S_LAMPS,5,'<linear_attenuation>'+str(att_by_distance)+'</linear_attenuation>')
  805. if (light.use_sphere):
  806. self.writel(S_LAMPS,5,'<zfar>'+str(light.distance)+'</zfar>')
  807. self.writel(S_LAMPS,4,'</point>')
  808. elif (light.type=="SPOT"):
  809. self.writel(S_LAMPS,4,'<spot>')
  810. self.writel(S_LAMPS,5,'<color>'+strarr(light.color)+'</color>')
  811. att_by_distance = 2.0 / light.distance # convert to linear attenuation
  812. self.writel(S_LAMPS,5,'<linear_attenuation>'+str(att_by_distance)+'</linear_attenuation>')
  813. self.writel(S_LAMPS,5,'<falloff_angle>'+str(math.degrees(light.spot_size))+'</falloff_angle>')
  814. self.writel(S_LAMPS,4,'</spot>')
  815. else: #write a sun lamp for everything else (not supported)
  816. self.writel(S_LAMPS,4,'<directional>')
  817. self.writel(S_LAMPS,5,'<color>'+strarr(light.color)+'</color>')
  818. self.writel(S_LAMPS,4,'</directional>')
  819. self.writel(S_LAMPS,3,'</technique_common>')
  820. #self.writel(S_LAMPS,2,'</optics>')
  821. self.writel(S_LAMPS,1,'</light>')
  822. self.writel(S_NODES,il,'<instance_light url="#'+lightid+'"/>')
  823. def export_curve(self,curve):
  824. splineid = self.new_id("spline")
  825. self.writel(S_GEOM,1,'<geometry id="'+splineid+'" name="'+curve.name+'">')
  826. self.writel(S_GEOM,2,'<spline closed="0">')
  827. points=[]
  828. interps=[]
  829. handles_in=[]
  830. handles_out=[]
  831. tilts=[]
  832. for cs in curve.splines:
  833. if (cs.type=="BEZIER"):
  834. for s in cs.bezier_points:
  835. points.append(s.co[0])
  836. points.append(s.co[1])
  837. points.append(s.co[2])
  838. handles_in.append(s.handle_left[0])
  839. handles_in.append(s.handle_left[1])
  840. handles_in.append(s.handle_left[2])
  841. handles_out.append(s.handle_right[0])
  842. handles_out.append(s.handle_right[1])
  843. handles_out.append(s.handle_right[2])
  844. tilts.append(s.tilt)
  845. interps.append("BEZIER")
  846. else:
  847. for s in cs.points:
  848. points.append(s.co[0])
  849. points.append(s.co[1])
  850. points.append(s.co[2])
  851. handles_in.append(s.co[0])
  852. handles_in.append(s.co[1])
  853. handles_in.append(s.co[2])
  854. handles_out.append(s.co[0])
  855. handles_out.append(s.co[1])
  856. handles_out.append(s.co[2])
  857. tilts.append(s.tilt)
  858. interps.append("LINEAR")
  859. self.writel(S_GEOM,3,'<source id="'+splineid+'-positions">')
  860. position_values=""
  861. for x in points:
  862. position_values+=" "+str(x)
  863. self.writel(S_GEOM,4,'<float_array id="'+splineid+'-positions-array" count="'+str(len(points))+'">'+position_values+'</float_array>')
  864. self.writel(S_GEOM,4,'<technique_common>')
  865. self.writel(S_GEOM,4,'<accessor source="#'+splineid+'-positions-array" count="'+str(len(points)/3)+'" stride="3">')
  866. self.writel(S_GEOM,5,'<param name="X" type="float"/>')
  867. self.writel(S_GEOM,5,'<param name="Y" type="float"/>')
  868. self.writel(S_GEOM,5,'<param name="Z" type="float"/>')
  869. self.writel(S_GEOM,4,'</accessor>')
  870. self.writel(S_GEOM,3,'</source>')
  871. self.writel(S_GEOM,3,'<source id="'+splineid+'-intangents">')
  872. intangent_values=""
  873. for x in handles_in:
  874. intangent_values+=" "+str(x)
  875. self.writel(S_GEOM,4,'<float_array id="'+splineid+'-intangents-array" count="'+str(len(points))+'">'+intangent_values+'</float_array>')
  876. self.writel(S_GEOM,4,'<technique_common>')
  877. self.writel(S_GEOM,4,'<accessor source="#'+splineid+'-intangents-array" count="'+str(len(points)/3)+'" stride="3">')
  878. self.writel(S_GEOM,5,'<param name="X" type="float"/>')
  879. self.writel(S_GEOM,5,'<param name="Y" type="float"/>')
  880. self.writel(S_GEOM,5,'<param name="Z" type="float"/>')
  881. self.writel(S_GEOM,4,'</accessor>')
  882. self.writel(S_GEOM,3,'</source>')
  883. self.writel(S_GEOM,3,'<source id="'+splineid+'-outtangents">')
  884. outtangent_values=""
  885. for x in handles_out:
  886. outtangent_values+=" "+str(x)
  887. self.writel(S_GEOM,4,'<float_array id="'+splineid+'-outtangents-array" count="'+str(len(points))+'">'+outtangent_values+'</float_array>')
  888. self.writel(S_GEOM,4,'<technique_common>')
  889. self.writel(S_GEOM,4,'<accessor source="#'+splineid+'-outtangents-array" count="'+str(len(points)/3)+'" stride="3">')
  890. self.writel(S_GEOM,5,'<param name="X" type="float"/>')
  891. self.writel(S_GEOM,5,'<param name="Y" type="float"/>')
  892. self.writel(S_GEOM,5,'<param name="Z" type="float"/>')
  893. self.writel(S_GEOM,4,'</accessor>')
  894. self.writel(S_GEOM,3,'</source>')
  895. self.writel(S_GEOM,3,'<source id="'+splineid+'-interpolations">')
  896. interpolation_values=""
  897. for x in interps:
  898. interpolation_values+=" "+x
  899. self.writel(S_GEOM,4,'<Name_array id="'+splineid+'-interpolations-array" count="'+str(len(interps))+'">'+interpolation_values+'</Name_array>')
  900. self.writel(S_GEOM,4,'<technique_common>')
  901. self.writel(S_GEOM,4,'<accessor source="#'+splineid+'-interpolations-array" count="'+str(len(interps))+'" stride="1">')
  902. self.writel(S_GEOM,5,'<param name="INTERPOLATION" type="name"/>')
  903. self.writel(S_GEOM,4,'</accessor>')
  904. self.writel(S_GEOM,3,'</source>')
  905. self.writel(S_GEOM,3,'<source id="'+splineid+'-tilts">')
  906. tilt_values=""
  907. for x in tilts:
  908. tilt_values+=" "+str(x)
  909. self.writel(S_GEOM,4,'<float_array id="'+splineid+'-tilts-array" count="'+str(len(tilts))+'">'+tilt_values+'</float_array>')
  910. self.writel(S_GEOM,4,'<technique_common>')
  911. self.writel(S_GEOM,4,'<accessor source="#'+splineid+'-tilts-array" count="'+str(len(tilts))+'" stride="1">')
  912. self.writel(S_GEOM,5,'<param name="TILT" type="float"/>')
  913. self.writel(S_GEOM,4,'</accessor>')
  914. self.writel(S_GEOM,3,'</source>')
  915. self.writel(S_GEOM,3,'<control_vertices>')
  916. self.writel(S_GEOM,4,'<input semantic="POSITION" source="#'+splineid+'-positions"/>')
  917. self.writel(S_GEOM,4,'<input semantic="IN_TANGENT" source="#'+splineid+'-intangents"/>')
  918. self.writel(S_GEOM,4,'<input semantic="OUT_TANGENT" source="#'+splineid+'-outtangents"/>')
  919. self.writel(S_GEOM,4,'<input semantic="INTERPOLATION" source="#'+splineid+'-interpolations"/>')
  920. self.writel(S_GEOM,4,'<input semantic="TILT" source="#'+splineid+'-tilts"/>')
  921. self.writel(S_GEOM,3,'</control_vertices>')
  922. self.writel(S_GEOM,2,'</spline>')
  923. self.writel(S_GEOM,1,'</geometry>')
  924. return splineid
  925. def export_curve_node(self,node,il):
  926. if (node.data==None):
  927. return
  928. curveid = self.export_curve(node.data)
  929. self.writel(S_NODES,il,'<instance_geometry url="#'+curveid+'">')
  930. self.writel(S_NODES,il,'</instance_geometry>')
  931. def export_node(self,node,il):
  932. if (not node in self.valid_nodes):
  933. return
  934. bpy.context.scene.objects.active = node
  935. self.writel(S_NODES,il,'<node id="'+self.validate_id(node.name)+'" name="'+node.name+'" type="NODE">')
  936. il+=1
  937. self.writel(S_NODES,il,'<matrix sid="transform">'+strmtx(node.matrix_local)+'</matrix>')
  938. print("NODE TYPE: "+node.type+" NAME: "+node.name)
  939. if (node.type=="MESH"):
  940. self.export_mesh_node(node,il)
  941. elif (node.type=="CURVE"):
  942. self.export_curve_node(node,il)
  943. elif (node.type=="ARMATURE"):
  944. self.export_armature_node(node,il)
  945. elif (node.type=="CAMERA"):
  946. self.export_camera_node(node,il)
  947. elif (node.type=="LAMP"):
  948. self.export_lamp_node(node,il)
  949. for x in node.children:
  950. self.export_node(x,il)
  951. il-=1
  952. self.writel(S_NODES,il,'</node>')
  953. def is_node_valid(self,node):
  954. if (not node.type in self.config["object_types"]):
  955. return False
  956. if (self.config["use_active_layers"]):
  957. valid=False
  958. print("NAME: "+node.name)
  959. for i in range(20):
  960. if (node.layers[i] and self.scene.layers[i]):
  961. valid=True
  962. break
  963. if (not valid):
  964. return False
  965. if (self.config["use_export_selected"] and not node.select):
  966. return False
  967. return True
  968. def export_scene(self):
  969. self.writel(S_NODES,0,'<library_visual_scenes>')
  970. self.writel(S_NODES,1,'<visual_scene id="'+self.scene_name+'" name="scene">')
  971. #validate nodes
  972. for obj in self.scene.objects:
  973. if (obj in self.valid_nodes):
  974. continue
  975. if (self.is_node_valid(obj)):
  976. n = obj
  977. while (n!=None):
  978. if (not n in self.valid_nodes):
  979. self.valid_nodes.append(n)
  980. n=n.parent
  981. for obj in self.scene.objects:
  982. if (obj in self.valid_nodes and obj.parent==None):
  983. self.export_node(obj,2)
  984. self.writel(S_NODES,1,'</visual_scene>')
  985. self.writel(S_NODES,0,'</library_visual_scenes>')
  986. def export_asset(self):
  987. self.writel(S_ASSET,0,'<asset>')
  988. # Why is this time stuff mandatory?, no one could care less...
  989. self.writel(S_ASSET,1,'<contributor>')
  990. self.writel(S_ASSET,2,'<author> Anonymous </author>') #Who made Collada, the FBI ?
  991. self.writel(S_ASSET,2,'<authoring_tool> Collada Exporter for Blender 2.6+, by Juan Linietsky ([email protected]) </authoring_tool>') #Who made Collada, the FBI ?
  992. self.writel(S_ASSET,1,'</contributor>')
  993. self.writel(S_ASSET,1,'<created>'+time.strftime("%Y-%m-%dT%H:%M:%SZ ")+'</created>')
  994. self.writel(S_ASSET,1,'<modified>'+time.strftime("%Y-%m-%dT%H:%M:%SZ")+'</modified>')
  995. self.writel(S_ASSET,1,'<unit meter="1.0" name="meter"/>')
  996. self.writel(S_ASSET,1,'<up_axis>Z_UP</up_axis>')
  997. self.writel(S_ASSET,0,'</asset>')
  998. def export_animation_transform_channel(self,target,keys,matrices=True):
  999. frame_total=len(keys)
  1000. anim_id=self.new_id("anim")
  1001. self.writel(S_ANIM,1,'<animation id="'+anim_id+'">')
  1002. source_frames = ""
  1003. source_transforms = ""
  1004. source_interps = ""
  1005. for k in keys:
  1006. source_frames += " "+str(k[0])
  1007. if (matrices):
  1008. source_transforms += " "+strmtx(k[1])
  1009. else:
  1010. source_transforms += " "+str(k[1])
  1011. source_interps +=" LINEAR"
  1012. # Time Source
  1013. self.writel(S_ANIM,2,'<source id="'+anim_id+'-input">')
  1014. self.writel(S_ANIM,3,'<float_array id="'+anim_id+'-input-array" count="'+str(frame_total)+'">'+source_frames+'</float_array>')
  1015. self.writel(S_ANIM,3,'<technique_common>')
  1016. self.writel(S_ANIM,4,'<accessor source="#'+anim_id+'-input-array" count="'+str(frame_total)+'" stride="1">')
  1017. self.writel(S_ANIM,5,'<param name="TIME" type="float"/>')
  1018. self.writel(S_ANIM,4,'</accessor>')
  1019. self.writel(S_ANIM,3,'</technique_common>')
  1020. self.writel(S_ANIM,2,'</source>')
  1021. if (matrices):
  1022. # Transform Source
  1023. self.writel(S_ANIM,2,'<source id="'+anim_id+'-transform-output">')
  1024. self.writel(S_ANIM,3,'<float_array id="'+anim_id+'-transform-output-array" count="'+str(frame_total*16)+'">'+source_transforms+'</float_array>')
  1025. self.writel(S_ANIM,3,'<technique_common>')
  1026. self.writel(S_ANIM,4,'<accessor source="#'+anim_id+'-transform-output-array" count="'+str(frame_total)+'" stride="16">')
  1027. self.writel(S_ANIM,5,'<param name="TRANSFORM" type="float4x4"/>')
  1028. self.writel(S_ANIM,4,'</accessor>')
  1029. self.writel(S_ANIM,3,'</technique_common>')
  1030. self.writel(S_ANIM,2,'</source>')
  1031. else:
  1032. # Value Source
  1033. self.writel(S_ANIM,2,'<source id="'+anim_id+'-transform-output">')
  1034. self.writel(S_ANIM,3,'<float_array id="'+anim_id+'-transform-output-array" count="'+str(frame_total)+'">'+source_transforms+'</float_array>')
  1035. self.writel(S_ANIM,3,'<technique_common>')
  1036. self.writel(S_ANIM,4,'<accessor source="#'+anim_id+'-transform-output-array" count="'+str(frame_total)+'" stride="1">')
  1037. self.writel(S_ANIM,5,'<param name="X" type="float"/>')
  1038. self.writel(S_ANIM,4,'</accessor>')
  1039. self.writel(S_ANIM,3,'</technique_common>')
  1040. self.writel(S_ANIM,2,'</source>')
  1041. # Interpolation Source
  1042. self.writel(S_ANIM,2,'<source id="'+anim_id+'-interpolation-output">')
  1043. self.writel(S_ANIM,3,'<Name_array id="'+anim_id+'-interpolation-output-array" count="'+str(frame_total)+'">'+source_interps+'</Name_array>')
  1044. self.writel(S_ANIM,3,'<technique_common>')
  1045. self.writel(S_ANIM,4,'<accessor source="#'+anim_id+'-interpolation-output-array" count="'+str(frame_total)+'" stride="1">')
  1046. self.writel(S_ANIM,5,'<param name="INTERPOLATION" type="Name"/>')
  1047. self.writel(S_ANIM,4,'</accessor>')
  1048. self.writel(S_ANIM,3,'</technique_common>')
  1049. self.writel(S_ANIM,2,'</source>')
  1050. self.writel(S_ANIM,2,'<sampler id="'+anim_id+'-sampler">')
  1051. self.writel(S_ANIM,3,'<input semantic="INPUT" source="#'+anim_id+'-input"/>')
  1052. self.writel(S_ANIM,3,'<input semantic="OUTPUT" source="#'+anim_id+'-transform-output"/>')
  1053. self.writel(S_ANIM,3,'<input semantic="INTERPOLATION" source="#'+anim_id+'-interpolation-output"/>')
  1054. self.writel(S_ANIM,2,'</sampler>')
  1055. if (matrices):
  1056. self.writel(S_ANIM,2,'<channel source="#'+anim_id+'-sampler" target="'+target+'/transform"/>')
  1057. else:
  1058. self.writel(S_ANIM,2,'<channel source="#'+anim_id+'-sampler" target="'+target+'"/>')
  1059. self.writel(S_ANIM,1,'</animation>')
  1060. return [anim_id]
  1061. def export_animation(self,start,end,allowed=None):
  1062. #Blender -> Collada frames needs a little work
  1063. #Collada starts from 0, blender usually from 1
  1064. #The last frame must be included also
  1065. frame_orig = self.scene.frame_current
  1066. frame_len = 1.0 / self.scene.render.fps
  1067. frame_total = end - start + 1
  1068. frame_sub = 0
  1069. if (start>0):
  1070. frame_sub=start*frame_len
  1071. tcn = []
  1072. xform_cache={}
  1073. blend_cache={}
  1074. # Change frames first, export objects last
  1075. # This improves performance enormously
  1076. print("anim from: "+str(start)+" to "+str(end)+" allowed: "+str(allowed))
  1077. for t in range(start,end+1):
  1078. self.scene.frame_set(t)
  1079. key = t * frame_len - frame_sub
  1080. # print("Export Anim Frame "+str(t)+"/"+str(self.scene.frame_end+1))
  1081. for node in self.scene.objects:
  1082. if (not node in self.valid_nodes):
  1083. continue
  1084. if (allowed!=None and not (node in allowed)):
  1085. if (node.type=="MESH" and node.data!=None and (node in self.armature_for_morph) and (self.armature_for_morph[node] in allowed)):
  1086. pass #all good you pass with flying colors for morphs inside of action
  1087. else:
  1088. #print("fail "+str((node in self.armature_for_morph)))
  1089. continue
  1090. if (node.type=="MESH" and node.data!=None and node.data.shape_keys!=None and (node.data in self.mesh_cache) and len(node.data.shape_keys.key_blocks)):
  1091. target = self.mesh_cache[node.data]["morph_id"]
  1092. for i in range(len(node.data.shape_keys.key_blocks)):
  1093. if (i==0):
  1094. continue
  1095. name=target+"-morph-weights("+str(i-1)+")"
  1096. if (not (name in blend_cache)):
  1097. blend_cache[name]=[]
  1098. blend_cache[name].append( (key,node.data.shape_keys.key_blocks[i].value) )
  1099. if (node.type=="MESH" and node.parent and node.parent.type=="ARMATURE"):
  1100. continue #In Collada, nodes that have skin modifier must not export animation, animate the skin instead.
  1101. if (len(node.constraints)>0 or node.animation_data!=None):
  1102. #If the node has constraints, or animation data, then export a sampled animation track
  1103. name=self.validate_id(node.name)
  1104. if (not (name in xform_cache)):
  1105. xform_cache[name]=[]
  1106. mtx = node.matrix_world.copy()
  1107. if (node.parent):
  1108. mtx = node.parent.matrix_world.inverted() * mtx
  1109. xform_cache[name].append( (key,mtx) )
  1110. if (node.type=="ARMATURE"):
  1111. #All bones exported for now
  1112. for bone in node.data.bones:
  1113. bone_name=self.skeleton_info[node]["bone_ids"][bone]
  1114. if (not (bone_name in xform_cache)):
  1115. print("has bone: "+bone_name)
  1116. xform_cache[bone_name]=[]
  1117. posebone = node.pose.bones[bone.name]
  1118. parent_posebone=None
  1119. mtx = posebone.matrix.copy()
  1120. if (bone.parent):
  1121. parent_posebone=node.pose.bones[bone.parent.name]
  1122. parent_invisible=False
  1123. for i in range(3):
  1124. if (parent_posebone.scale[i]==0.0):
  1125. parent_invisible=True
  1126. if (not parent_invisible):
  1127. mtx = parent_posebone.matrix.inverted() * mtx
  1128. xform_cache[bone_name].append( (key,mtx) )
  1129. self.scene.frame_set(frame_orig)
  1130. #export animation xml
  1131. for nid in xform_cache:
  1132. tcn+=self.export_animation_transform_channel(nid,xform_cache[nid],True)
  1133. for nid in blend_cache:
  1134. tcn+=self.export_animation_transform_channel(nid,blend_cache[nid],False)
  1135. return tcn
  1136. def export_animations(self):
  1137. self.writel(S_ANIM,0,'<library_animations>')
  1138. if (self.config["use_anim_action_all"] and len(self.skeletons)):
  1139. cached_actions = {}
  1140. for s in self.skeletons:
  1141. if s.animation_data and s.animation_data.action:
  1142. cached_actions[s] = s.animation_data.action.name
  1143. self.writel(S_ANIM_CLIPS,0,'<library_animation_clips>')
  1144. for x in bpy.data.actions[:]:
  1145. if x.users==0 or x in self.action_constraints:
  1146. continue
  1147. if (self.config["use_anim_skip_noexp"] and x.name.endswith("-noexp")):
  1148. continue
  1149. bones=[]
  1150. #find bones used
  1151. for p in x.fcurves:
  1152. dp = str(p.data_path)
  1153. base = "pose.bones[\""
  1154. if (dp.find(base)==0):
  1155. dp=dp[len(base):]
  1156. if (dp.find('"')!=-1):
  1157. dp=dp[:dp.find('"')]
  1158. if (not dp in bones):
  1159. bones.append(dp)
  1160. allowed_skeletons=[]
  1161. for y in self.skeletons:
  1162. if (y.animation_data):
  1163. for z in y.pose.bones:
  1164. if (z.bone.name in bones):
  1165. if (not y in allowed_skeletons):
  1166. allowed_skeletons.append(y)
  1167. y.animation_data.action=x;
  1168. print("allowed skeletons "+str(allowed_skeletons))
  1169. print(str(x))
  1170. tcn = self.export_animation(int(x.frame_range[0]),int(x.frame_range[1]+0.5),allowed_skeletons)
  1171. framelen=(1.0/self.scene.render.fps)
  1172. start = x.frame_range[0]*framelen
  1173. end = x.frame_range[1]*framelen
  1174. print("Export anim: "+x.name)
  1175. self.writel(S_ANIM_CLIPS,1,'<animation_clip name="'+x.name+'" start="'+str(start)+'" end="'+str(end)+'">')
  1176. for z in tcn:
  1177. self.writel(S_ANIM_CLIPS,2,'<instance_animation url="#'+z+'"/>')
  1178. self.writel(S_ANIM_CLIPS,1,'</animation_clip>')
  1179. self.writel(S_ANIM_CLIPS,0,'</library_animation_clips>')
  1180. for s in self.skeletons:
  1181. if (s.animation_data==None):
  1182. continue
  1183. if s in cached_actions:
  1184. s.animation_data.action = bpy.data.actions[cached_actions[s]]
  1185. else:
  1186. s.animation_data.action = None
  1187. else:
  1188. self.export_animation(self.scene.frame_start,self.scene.frame_end)
  1189. self.writel(S_ANIM,0,'</library_animations>')
  1190. def export(self):
  1191. self.writel(S_GEOM,0,'<library_geometries>')
  1192. self.writel(S_CONT,0,'<library_controllers>')
  1193. self.writel(S_CAMS,0,'<library_cameras>')
  1194. self.writel(S_LAMPS,0,'<library_lights>')
  1195. self.writel(S_IMGS,0,'<library_images>')
  1196. self.writel(S_MATS,0,'<library_materials>')
  1197. self.writel(S_FX,0,'<library_effects>')
  1198. self.skeletons=[]
  1199. self.action_constraints=[]
  1200. self.export_asset()
  1201. self.export_scene()
  1202. self.writel(S_GEOM,0,'</library_geometries>')
  1203. #morphs always go before skin controllers
  1204. if S_MORPH in self.sections:
  1205. for l in self.sections[S_MORPH]:
  1206. self.writel(S_CONT,0,l)
  1207. del self.sections[S_MORPH]
  1208. #morphs always go before skin controllers
  1209. if S_SKIN in self.sections:
  1210. for l in self.sections[S_SKIN]:
  1211. self.writel(S_CONT,0,l)
  1212. del self.sections[S_SKIN]
  1213. self.writel(S_CONT,0,'</library_controllers>')
  1214. self.writel(S_CAMS,0,'</library_cameras>')
  1215. self.writel(S_LAMPS,0,'</library_lights>')
  1216. self.writel(S_IMGS,0,'</library_images>')
  1217. self.writel(S_MATS,0,'</library_materials>')
  1218. self.writel(S_FX,0,'</library_effects>')
  1219. if (self.config["use_anim"]):
  1220. self.export_animations()
  1221. try:
  1222. f = open(self.path,"wb")
  1223. except:
  1224. return False
  1225. f.write(bytes('<?xml version="1.0" encoding="utf-8"?>\n',"UTF-8"))
  1226. f.write(bytes('<COLLADA xmlns="http://www.collada.org/2005/11/COLLADASchema" version="1.4.1">\n',"UTF-8"))
  1227. s=[]
  1228. for x in self.sections.keys():
  1229. s.append(x)
  1230. s.sort()
  1231. for x in s:
  1232. for l in self.sections[x]:
  1233. f.write(bytes(l+"\n","UTF-8"))
  1234. f.write(bytes('<scene>\n',"UTF-8"))
  1235. f.write(bytes('\t<instance_visual_scene url="#'+self.scene_name+'" />\n',"UTF-8"))
  1236. f.write(bytes('</scene>\n',"UTF-8"))
  1237. f.write(bytes('</COLLADA>\n',"UTF-8"))
  1238. return True
  1239. def __init__(self,path,kwargs):
  1240. self.scene=bpy.context.scene
  1241. self.last_id=0
  1242. self.scene_name=self.new_id("scene")
  1243. self.sections={}
  1244. self.path=path
  1245. self.mesh_cache={}
  1246. self.curve_cache={}
  1247. self.material_cache={}
  1248. self.image_cache={}
  1249. self.skeleton_info={}
  1250. self.config=kwargs
  1251. self.valid_nodes=[]
  1252. self.armature_for_morph={}
  1253. def save(operator, context,
  1254. filepath="",
  1255. use_selection=False,
  1256. **kwargs
  1257. ):
  1258. exp = DaeExporter(filepath,kwargs)
  1259. exp.export()
  1260. return {'FINISHED'} # so the script wont run after we have batch exported.