io_export_babylon.py 42 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001
  1. bl_info = {
  2. "name": "Babylon.js",
  3. "author": "David Catuhe",
  4. "version": (1, 0),
  5. "blender": (2, 67, 0),
  6. "location": "File > Export > Babylon.js (.babylon)",
  7. "description": "Export Babylon.js scenes (.babylon)",
  8. "warning": "",
  9. "wiki_url": "",
  10. "tracker_url": "",
  11. "category": "Import-Export"}
  12. import math
  13. import os
  14. import bpy
  15. import string
  16. import bpy_extras.io_utils
  17. from bpy.props import *
  18. import mathutils, math
  19. import struct
  20. import shutil
  21. from os import remove
  22. from bpy_extras.io_utils import (ExportHelper, axis_conversion)
  23. from bpy.props import (BoolProperty, FloatProperty, StringProperty, EnumProperty, FloatVectorProperty)
  24. from math import radians
  25. from mathutils import *
  26. class SubMesh:
  27. materialIndex = 0
  28. verticesStart = 0
  29. verticesCount = 0
  30. indexStart = 0
  31. indexCount = 0
  32. class MultiMaterial:
  33. name = ""
  34. materials = []
  35. class Export_babylon(bpy.types.Operator, ExportHelper):
  36. """Export Babylon.js scene (.babylon)"""
  37. bl_idname = "scene.babylon"
  38. bl_label = "Export Babylon.js scene"
  39. filename_ext = ".babylon"
  40. filepath = ""
  41. # global_scale = FloatProperty(name="Scale", min=0.01, max=1000.0, default=1.0)
  42. def execute(self, context):
  43. return Export_babylon.save(self, context, **self.as_keywords(ignore=("check_existing", "filter_glob", "global_scale")))
  44. def mesh_triangulate(mesh):
  45. try:
  46. import bmesh
  47. bm = bmesh.new()
  48. bm.from_mesh(mesh)
  49. bmesh.ops.triangulate(bm, faces=bm.faces)
  50. bm.to_mesh(mesh)
  51. mesh.calc_tessface()
  52. bm.free()
  53. except:
  54. pass
  55. def write_matrix4(file_handler, name, matrix):
  56. file_handler.write(",\""+name+"\":[")
  57. tempMatrix = matrix.copy()
  58. tempMatrix.transpose()
  59. first = True
  60. for vect in tempMatrix:
  61. if (first != True):
  62. file_handler.write(",")
  63. first = False;
  64. file_handler.write("%.4f,%.4f,%.4f,%.4f"%(vect[0],vect[1], vect[2], vect[3]))
  65. file_handler.write("]")
  66. def write_array3(file_handler, name, array):
  67. file_handler.write(",\""+name+"\":[" + "%.4f,%.4f,%.4f"%(array[0],array[1],array[2]) + "]")
  68. def write_color(file_handler, name, color):
  69. file_handler.write(",\""+name+"\":[" + "%.4f,%.4f,%.4f"%(color.r,color.g,color.b) + "]")
  70. def write_vector(file_handler, name, vector):
  71. file_handler.write(",\""+name+"\":[" + "%.4f,%.4f,%.4f"%(vector.x,vector.z,vector.y) + "]")
  72. def write_quaternion(file_handler, name, quaternion):
  73. file_handler.write(",\""+name+"\":[" + "%.4f,%.4f,%.4f,%.4f"%(quaternion.x,quaternion.z,quaternion.y, -quaternion.w) + "]")
  74. def write_vectorScaled(file_handler, name, vector, mult):
  75. file_handler.write(",\""+name+"\":[" + "%.4f,%.4f,%.4f"%(vector.x * mult, vector.z * mult, vector.y * mult) + "]")
  76. def write_string(file_handler, name, string, noComma=False):
  77. if noComma == False:
  78. file_handler.write(",")
  79. file_handler.write("\""+name+"\":\"" + string + "\"")
  80. def write_float(file_handler, name, float):
  81. file_handler.write(",\""+name+"\":" + "%.4f"%(float))
  82. def write_int(file_handler, name, int, noComma=False):
  83. if noComma == False:
  84. file_handler.write(",")
  85. file_handler.write("\""+name+"\":" + str(int))
  86. def write_bool(file_handler, name, bool, noComma=False):
  87. if noComma == False:
  88. file_handler.write(",")
  89. if bool:
  90. file_handler.write("\""+name+"\":" + "true")
  91. else:
  92. file_handler.write("\""+name+"\":" + "false")
  93. def getDirection(matrix):
  94. return (matrix.to_3x3() * mathutils.Vector((0.0, 0.0, -1.0))).normalized()
  95. def export_camera(object, scene, file_handler):
  96. invWorld = object.matrix_world.copy()
  97. invWorld.invert()
  98. target = mathutils.Vector((0, 1, 0)) * invWorld
  99. file_handler.write("{")
  100. Export_babylon.write_string(file_handler, "name", object.name, True)
  101. Export_babylon.write_string(file_handler, "id", object.name)
  102. Export_babylon.write_vector(file_handler, "position", object.location)
  103. Export_babylon.write_vector(file_handler, "target", target)
  104. Export_babylon.write_float(file_handler, "fov", object.data.angle)
  105. Export_babylon.write_float(file_handler, "minZ", object.data.clip_start)
  106. Export_babylon.write_float(file_handler, "maxZ", object.data.clip_end)
  107. Export_babylon.write_float(file_handler, "speed", 1.0)
  108. Export_babylon.write_float(file_handler, "inertia", 0.9)
  109. Export_babylon.write_bool(file_handler, "checkCollisions", object.data.checkCollisions)
  110. Export_babylon.write_bool(file_handler, "applyGravity", object.data.applyGravity)
  111. Export_babylon.write_array3(file_handler, "ellipsoid", object.data.ellipsoid)
  112. locAnim = False
  113. coma = False
  114. if object.animation_data:
  115. if object.animation_data.action:
  116. file_handler.write(",\"animations\":[")
  117. for fcurve in object.animation_data.action.fcurves:
  118. if fcurve.data_path == "location" and locAnim == False:
  119. Export_babylon.export_animation(object, scene, file_handler, "location", "position", coma, 1)
  120. locAnim = coma = True
  121. file_handler.write("]")
  122. #Set Animations
  123. Export_babylon.write_bool(file_handler, "autoAnimate", True)
  124. Export_babylon.write_int(file_handler, "autoAnimateFrom", 0)
  125. Export_babylon.write_int(file_handler, "autoAnimateTo", bpy.context.scene.frame_end - bpy.context.scene.frame_start + 1)
  126. Export_babylon.write_bool(file_handler, "autoAnimateLoop", True)
  127. file_handler.write("}")
  128. def export_light(object, scene, file_handler):
  129. light_type_items = {'POINT': 0, 'SUN': 1, 'SPOT': 2, 'HEMI': 3, 'AREA': 0}
  130. light_type = light_type_items[object.data.type]
  131. file_handler.write("{")
  132. Export_babylon.write_string(file_handler, "name", object.name, True)
  133. Export_babylon.write_string(file_handler, "id", object.name)
  134. Export_babylon.write_float(file_handler, "type", light_type)
  135. if light_type == 0:
  136. Export_babylon.write_vector(file_handler, "position", object.location)
  137. elif light_type == 1:
  138. direction = Export_babylon.getDirection(object.matrix_world)
  139. Export_babylon.write_vector(file_handler, "position", object.location)
  140. Export_babylon.write_vector(file_handler, "direction", direction)
  141. elif light_type == 2:
  142. Export_babylon.write_vector(file_handler, "position", object.location)
  143. direction = Export_babylon.getDirection(object.matrix_world)
  144. Export_babylon.write_vector(file_handler, "direction", direction)
  145. Export_babylon.write_float(file_handler, "angle", object.data.spot_size)
  146. Export_babylon.write_float(file_handler, "exponent", object.data.spot_blend * 2)
  147. else:
  148. matrix_world = object.matrix_world.copy()
  149. matrix_world.translation = mathutils.Vector((0, 0, 0))
  150. direction = mathutils.Vector((0, 0, -1)) * matrix_world
  151. Export_babylon.write_vector(file_handler, "direction", -direction)
  152. Export_babylon.write_color(file_handler, "groundColor", mathutils.Color((0, 0, 0)))
  153. Export_babylon.write_float(file_handler, "intensity", object.data.energy)
  154. if object.data.use_diffuse:
  155. Export_babylon.write_color(file_handler, "diffuse", object.data.color)
  156. else:
  157. Export_babylon.write_color(file_handler, "diffuse", mathutils.Color((0, 0, 0)))
  158. if object.data.use_specular:
  159. Export_babylon.write_color(file_handler, "specular", object.data.color)
  160. else:
  161. Export_babylon.write_color(file_handler, "specular", mathutils.Color((0, 0, 0)))
  162. file_handler.write("}")
  163. def export_texture(slot, level, texture, scene, file_handler, filepath):
  164. # Copy image to output
  165. try:
  166. image = texture.texture.image
  167. imageFilepath = os.path.normpath(bpy.path.abspath(image.filepath))
  168. basename = os.path.basename(imageFilepath)
  169. targetdir = os.path.dirname(filepath)
  170. targetpath = os.path.join(targetdir, basename)
  171. if image.packed_file:
  172. image.save_render(targetpath)
  173. else:
  174. sourcepath = bpy.path.abspath(image.filepath)
  175. shutil.copy(sourcepath, targetdir)
  176. except:
  177. pass
  178. # Export
  179. file_handler.write(",\""+slot+"\":{")
  180. Export_babylon.write_string(file_handler, "name", basename, True)
  181. Export_babylon.write_float(file_handler, "level", level)
  182. Export_babylon.write_float(file_handler, "hasAlpha", texture.texture.use_alpha)
  183. coordinatesMode = 0;
  184. if (texture.mapping == "CUBE"):
  185. coordinatesMode = 3;
  186. if (texture.mapping == "SPHERE"):
  187. coordinatesMode = 1;
  188. Export_babylon.write_int(file_handler, "coordinatesMode", coordinatesMode)
  189. Export_babylon.write_float(file_handler, "uOffset", texture.offset.x)
  190. Export_babylon.write_float(file_handler, "vOffset", texture.offset.y)
  191. Export_babylon.write_float(file_handler, "uScale", texture.scale.x)
  192. Export_babylon.write_float(file_handler, "vScale", texture.scale.y)
  193. Export_babylon.write_float(file_handler, "uAng", 0)
  194. Export_babylon.write_float(file_handler, "vAng", 0)
  195. Export_babylon.write_float(file_handler, "wAng", 0)
  196. if (texture.texture.extension == "REPEAT"):
  197. Export_babylon.write_bool(file_handler, "wrapU", True)
  198. Export_babylon.write_bool(file_handler, "wrapV", True)
  199. else:
  200. Export_babylon.write_bool(file_handler, "wrapU", False)
  201. Export_babylon.write_bool(file_handler, "wrapV", False)
  202. Export_babylon.write_int(file_handler, "coordinatesIndex", 0)
  203. file_handler.write("}")
  204. def export_material(material, scene, file_handler, filepath):
  205. file_handler.write("{")
  206. Export_babylon.write_string(file_handler, "name", material.name, True)
  207. Export_babylon.write_string(file_handler, "id", material.name)
  208. Export_babylon.write_color(file_handler, "ambient", material.ambient * material.diffuse_color)
  209. Export_babylon.write_color(file_handler, "diffuse", material.diffuse_intensity * material.diffuse_color)
  210. Export_babylon.write_color(file_handler, "specular", material.specular_intensity * material.specular_color)
  211. Export_babylon.write_float(file_handler, "specularPower", material.specular_hardness)
  212. Export_babylon.write_color(file_handler, "emissive", material.emit * material.diffuse_color)
  213. Export_babylon.write_float(file_handler, "alpha", material.alpha)
  214. Export_babylon.write_bool(file_handler, "backFaceCulling", material.game_settings.use_backface_culling)
  215. # Textures
  216. for mtex in material.texture_slots:
  217. if mtex and mtex.texture and mtex.texture.type == 'IMAGE':
  218. if mtex.texture.image:
  219. if (mtex.use_map_color_diffuse and(mtex.texture_coords != 'REFLECTION')):
  220. # Diffuse
  221. Export_babylon.export_texture("diffuseTexture", mtex.diffuse_color_factor, mtex, scene, file_handler, filepath)
  222. if mtex.use_map_ambient:
  223. # Ambient
  224. Export_babylon.export_texture("ambientTexture", mtex.ambient_factor, mtex, scene, file_handler, filepath)
  225. if mtex.use_map_alpha:
  226. # Opacity
  227. Export_babylon.export_texture("opacityTexture", mtex.alpha_factor, mtex, scene, file_handler, filepath)
  228. if mtex.use_map_color_diffuse and (mtex.texture_coords == 'REFLECTION'):
  229. # Reflection
  230. Export_babylon.export_texture("reflectionTexture", mtex.diffuse_color_factor, mtex, scene, file_handler, filepath)
  231. if mtex.use_map_emit:
  232. # Emissive
  233. Export_babylon.export_texture("emissiveTexture", mtex.emit_factor, mtex, scene, file_handler, filepath)
  234. if mtex.use_map_normal:
  235. # Bump
  236. Export_babylon.export_texture("bumpTexture", mtex.emit_factor, mtex, scene, file_handler, filepath)
  237. file_handler.write("}")
  238. def export_multimaterial(multimaterial, scene, file_handler):
  239. file_handler.write("{")
  240. Export_babylon.write_string(file_handler, "name", multimaterial.name, True)
  241. Export_babylon.write_string(file_handler, "id", multimaterial.name)
  242. file_handler.write(",\"materials\":[")
  243. first = True
  244. for materialName in multimaterial.materials:
  245. if first != True:
  246. file_handler.write(",")
  247. file_handler.write("\"" + materialName +"\"")
  248. first = False
  249. file_handler.write("]")
  250. file_handler.write("}")
  251. def export_animation(object, scene, file_handler, typeBl, typeBa, coma, mult):
  252. if coma == True:
  253. file_handler.write(",")
  254. file_handler.write("{")
  255. Export_babylon.write_int(file_handler, "dataType", 1, True)
  256. Export_babylon.write_int(file_handler, "framePerSecond", 30)
  257. Export_babylon.write_int(file_handler, "loopBehavior", 1)
  258. Export_babylon.write_string(file_handler, "name", typeBa+" animation")
  259. Export_babylon.write_string(file_handler, "property", typeBa)
  260. file_handler.write(",\"keys\":[")
  261. frames = dict()
  262. for fcurve in object.animation_data.action.fcurves:
  263. if fcurve.data_path == typeBl:
  264. for key in fcurve.keyframe_points:
  265. frame = key.co.x
  266. frames[frame] = 1
  267. #for each frame (next step ==> set for key frames)
  268. i = 0
  269. for Frame in sorted(frames):
  270. if i == 0 and Frame != 0.0:
  271. file_handler.write("{")
  272. Export_babylon.write_int(file_handler, "frame", 0, True)
  273. bpy.context.scene.frame_set(int(Frame + bpy.context.scene.frame_start))
  274. Export_babylon.write_vectorScaled(file_handler, "values", getattr(object,typeBl), mult)
  275. file_handler.write("},")
  276. i = i + 1
  277. file_handler.write("{")
  278. Export_babylon.write_int(file_handler, "frame", Frame, True)
  279. bpy.context.scene.frame_set(int(Frame + bpy.context.scene.frame_start))
  280. Export_babylon.write_vectorScaled(file_handler, "values", getattr(object,typeBl), mult)
  281. file_handler.write("}")
  282. if i != len(frames):
  283. file_handler.write(",")
  284. else:
  285. file_handler.write(",{")
  286. Export_babylon.write_int(file_handler, "frame", bpy.context.scene.frame_end - bpy.context.scene.frame_start + 1, True)
  287. bpy.context.scene.frame_set(int(Frame + bpy.context.scene.frame_start))
  288. Export_babylon.write_vectorScaled(file_handler, "values", getattr(object,typeBl), mult)
  289. file_handler.write("}")
  290. file_handler.write("]}")
  291. def export_mesh(object, scene, file_handler, multiMaterials):
  292. # Get mesh
  293. mesh = object.to_mesh(scene, True, "PREVIEW")
  294. # Transform
  295. loc = mathutils.Vector((0, 0, 0))
  296. rot = mathutils.Quaternion((0, 0, 0, 1))
  297. scale = mathutils.Vector((1, 1, 1))
  298. if object.parent and object.parent.type == "ARMATURE" and len(object.vertex_groups) > 0:
  299. mesh.transform(object.matrix_world)
  300. hasSkeleton = True
  301. else:
  302. hasSkeleton = False
  303. world = object.matrix_world
  304. if (object.parent):
  305. world = object.parent.matrix_world.inverted() * object.matrix_world
  306. loc, rot, scale = world.decompose()
  307. # Triangulate mesh if required
  308. Export_babylon.mesh_triangulate(mesh)
  309. # Getting vertices and indices
  310. positions=",\"positions\":["
  311. normals=",\"normals\":["
  312. indices=",\"indices\":["
  313. hasUV = True;
  314. hasUV2 = True;
  315. hasVertexColor = True
  316. if len(mesh.tessface_uv_textures) > 0:
  317. UVmap=mesh.tessface_uv_textures[0].data
  318. uvs=",\"uvs\":["
  319. else:
  320. hasUV = False
  321. if len(mesh.tessface_uv_textures) > 1:
  322. UV2map=mesh.tessface_uv_textures[1].data
  323. uvs2=",\"uvs2\":["
  324. else:
  325. hasUV2 = False
  326. if len(mesh.vertex_colors) > 0:
  327. Colormap = mesh.tessface_vertex_colors.active.data
  328. colors=",\"colors\":["
  329. else:
  330. hasVertexColor = False
  331. if hasSkeleton:
  332. skeletonWeight = ",\"matricesWeights\":["
  333. skeletonIndices = ",\"matricesIndices\":["
  334. alreadySavedVertices = []
  335. vertices_UVs=[]
  336. vertices_UV2s=[]
  337. vertices_Colors=[]
  338. vertices_indices=[]
  339. subMeshes = []
  340. for v in range(0, len(mesh.vertices)):
  341. alreadySavedVertices.append(False)
  342. vertices_UVs.append([])
  343. vertices_UV2s.append([])
  344. vertices_Colors.append([])
  345. vertices_indices.append([])
  346. materialsCount = max(1, len(object.material_slots))
  347. verticesCount = 0
  348. indicesCount = 0
  349. for materialIndex in range(materialsCount):
  350. subMeshes.append(SubMesh())
  351. subMeshes[materialIndex].materialIndex = materialIndex
  352. subMeshes[materialIndex].verticesStart = verticesCount
  353. subMeshes[materialIndex].indexStart = indicesCount
  354. for face in mesh.tessfaces: # For each face
  355. if face.material_index != materialIndex:
  356. continue
  357. for v in range(3): # For each vertex in face
  358. vertex_index = face.vertices[v]
  359. vertex = mesh.vertices[vertex_index]
  360. position = vertex.co
  361. normal = vertex.normal
  362. #skeletons
  363. if hasSkeleton:
  364. matricesWeights = []
  365. matricesIndices = 0
  366. matricesWeights.append(0.0)
  367. matricesWeights.append(0.0)
  368. matricesWeights.append(0.0)
  369. matricesWeights.append(0.0)
  370. # Getting influences
  371. i = 0
  372. offset = 0
  373. for group in vertex.groups:
  374. index = group.group
  375. weight = group.weight
  376. for boneIndex, bone in enumerate(object.parent.pose.bones):
  377. if object.vertex_groups[index].name == bone.name:
  378. matricesWeights[i] = weight
  379. matricesIndices += boneIndex << offset
  380. offset = offset + 8
  381. i = i + 1
  382. if (i == 4):
  383. break;
  384. # Texture coordinates
  385. if hasUV:
  386. vertex_UV = UVmap[face.index].uv[v]
  387. if hasUV2:
  388. vertex_UV2 = UV2map[face.index].uv[v]
  389. # Vertex color
  390. if hasVertexColor:
  391. if v == 0:
  392. vertex_Color = Colormap[face.index].color1
  393. if v == 1:
  394. vertex_Color = Colormap[face.index].color2
  395. if v == 2:
  396. vertex_Color = Colormap[face.index].color3
  397. # Check if the current vertex is already saved
  398. alreadySaved = alreadySavedVertices[vertex_index] and not hasSkeleton
  399. if alreadySaved:
  400. alreadySaved=False
  401. # UV
  402. index_UV = 0
  403. for savedIndex in vertices_indices[vertex_index]:
  404. if hasUV:
  405. vUV = vertices_UVs[vertex_index][index_UV]
  406. if (vUV[0]!=vertex_UV[0] or vUV[1]!=vertex_UV[1]):
  407. continue
  408. if hasUV2:
  409. vUV2 = vertices_UV2s[vertex_index][index_UV]
  410. if (vUV2[0]!=vertex_UV2[0] or vUV2[1]!=vertex_UV2[1]):
  411. continue
  412. if hasVertexColor:
  413. vColor = vertices_Colors[vertex_index][index_UV]
  414. if (vColor.r!=vertex_Color.r or vColor.g!=vertex_Color.g or vColor.b!=vertex_Color.b):
  415. continue
  416. if vertices_indices[vertex_index][index_UV] >= subMeshes[materialIndex].verticesStart:
  417. alreadySaved=True
  418. break
  419. index_UV+=1
  420. if (alreadySaved):
  421. # Reuse vertex
  422. index=vertices_indices[vertex_index][index_UV]
  423. else:
  424. # Export new one
  425. index=verticesCount
  426. alreadySavedVertices[vertex_index]=True
  427. if hasUV:
  428. vertices_UVs[vertex_index].append(vertex_UV)
  429. uvs+="%.4f,%.4f,"%(vertex_UV[0], vertex_UV[1])
  430. if hasUV2:
  431. vertices_UV2s[vertex_index].append(vertex_UV2)
  432. uvs2+="%.4f,%.4f,"%(vertex_UV2[0], vertex_UV2[1])
  433. if hasVertexColor:
  434. vertices_Colors[vertex_index].append(vertex_Color)
  435. colors+="%.4f,%.4f,%.4f,"%(vertex_Color.r,vertex_Color.g,vertex_Color.b)
  436. if hasSkeleton:
  437. skeletonWeight+="%.4f,%.4f,%.4f,%.4f,"%(matricesWeights[0], matricesWeights[1], matricesWeights[2], matricesWeights[3])
  438. skeletonIndices+="%i,"%(matricesIndices)
  439. vertices_indices[vertex_index].append(index)
  440. positions+="%.4f,%.4f,%.4f,"%(position.x,position.z,position.y)
  441. normals+="%.4f,%.4f,%.4f,"%(normal.x,normal.z,normal.y)
  442. verticesCount += 1
  443. indices+="%i,"%(index)
  444. indicesCount += 1
  445. subMeshes[materialIndex].verticesCount = verticesCount - subMeshes[materialIndex].verticesStart
  446. subMeshes[materialIndex].indexCount = indicesCount - subMeshes[materialIndex].indexStart
  447. positions=positions.rstrip(',')
  448. normals=normals.rstrip(',')
  449. indices=indices.rstrip(',')
  450. positions+="]\n"
  451. normals+="]\n"
  452. indices+="]\n"
  453. if hasUV:
  454. uvs=uvs.rstrip(',')
  455. uvs+="]\n"
  456. if hasUV2:
  457. uvs2=uvs.rstrip(',')
  458. uvs2+="]\n"
  459. if hasVertexColor:
  460. colors=uvs.rstrip(',')
  461. colors+="]\n"
  462. if hasSkeleton:
  463. skeletonWeight=skeletonWeight.rstrip(', ')
  464. skeletonWeight+="]\n"
  465. skeletonIndices= skeletonIndices.rstrip(', ')
  466. skeletonIndices+="]\n"
  467. # Writing mesh
  468. file_handler.write("{")
  469. Export_babylon.write_string(file_handler, "name", object.name, True)
  470. Export_babylon.write_string(file_handler, "id", object.name)
  471. if object.parent != None:
  472. Export_babylon.write_string(file_handler, "parentId", object.parent.name)
  473. if len(object.material_slots) == 1:
  474. material = object.material_slots[0].material
  475. Export_babylon.write_string(file_handler, "materialId", object.material_slots[0].name)
  476. if material.game_settings.face_orientation != "BILLBOARD":
  477. billboardMode = 0
  478. else:
  479. billboardMode = 7
  480. elif len(object.material_slots) > 1:
  481. multimat = MultiMaterial()
  482. multimat.name = "Multimaterial#" + str(len(multiMaterials))
  483. Export_babylon.write_string(file_handler, "materialId", multimat.name)
  484. for mat in object.material_slots:
  485. multimat.materials.append(mat.name)
  486. multiMaterials.append(multimat)
  487. billboardMode = 0
  488. else:
  489. billboardMode = 0
  490. Export_babylon.write_vector(file_handler, "position", loc)
  491. Export_babylon.write_quaternion(file_handler, "rotation", rot)
  492. Export_babylon.write_vector(file_handler, "scaling", scale)
  493. Export_babylon.write_bool(file_handler, "isVisible", object.is_visible(scene))
  494. Export_babylon.write_bool(file_handler, "isEnabled", True)
  495. Export_babylon.write_bool(file_handler, "checkCollisions", object.data.checkCollisions)
  496. Export_babylon.write_int(file_handler, "billboardMode", billboardMode)
  497. Export_babylon.write_bool(file_handler, "receiveShadows", object.data.receiveShadows)
  498. if hasSkeleton:
  499. i = 0
  500. for obj in [object for object in scene.objects if object.is_visible(scene)]:
  501. if (obj.type == 'ARMATURE'):
  502. if (obj.name == object.parent.name):
  503. Export_babylon.write_int(file_handler, "skeletonId", i)
  504. else:
  505. i = i+1
  506. file_handler.write(positions)
  507. file_handler.write(normals)
  508. if hasUV:
  509. file_handler.write(uvs)
  510. if hasUV2:
  511. file_handler.write(uvs2)
  512. if hasVertexColor:
  513. file_handler.write(colors)
  514. if hasSkeleton:
  515. file_handler.write(skeletonWeight)
  516. file_handler.write(skeletonIndices)
  517. file_handler.write(indices)
  518. # Sub meshes
  519. file_handler.write(",\"subMeshes\":[")
  520. first = True
  521. for subMesh in subMeshes:
  522. if first == False:
  523. file_handler.write(",")
  524. file_handler.write("{")
  525. Export_babylon.write_int(file_handler, "materialIndex", subMesh.materialIndex, True)
  526. Export_babylon.write_int(file_handler, "verticesStart", subMesh.verticesStart)
  527. Export_babylon.write_int(file_handler, "verticesCount", subMesh.verticesCount)
  528. Export_babylon.write_int(file_handler, "indexStart", subMesh.indexStart)
  529. Export_babylon.write_int(file_handler, "indexCount", subMesh.indexCount)
  530. file_handler.write("}")
  531. first = False
  532. file_handler.write("]")
  533. #Export Animations
  534. rotAnim = False
  535. locAnim = False
  536. scaAnim = False
  537. coma = False
  538. if object.animation_data:
  539. if object.animation_data.action:
  540. file_handler.write(",\"animations\":[")
  541. for fcurve in object.animation_data.action.fcurves:
  542. if fcurve.data_path == "rotation_euler" and rotAnim == False:
  543. Export_babylon.export_animation(object, scene, file_handler, "rotation_euler", "rotation", coma, -1)
  544. rotAnim = coma = True
  545. elif fcurve.data_path == "location" and locAnim == False:
  546. Export_babylon.export_animation(object, scene, file_handler, "location", "position", coma, 1)
  547. locAnim = coma = True
  548. elif fcurve.data_path == "scale" and scaAnim == False:
  549. Export_babylon.export_animation(object, scene, file_handler, "scale", "scaling", coma, 1)
  550. locAnim = coma = True
  551. file_handler.write("]")
  552. #Set Animations
  553. Export_babylon.write_bool(file_handler, "autoAnimate", True)
  554. Export_babylon.write_int(file_handler, "autoAnimateFrom", 0)
  555. Export_babylon.write_int(file_handler, "autoAnimateTo", bpy.context.scene.frame_end - bpy.context.scene.frame_start + 1)
  556. Export_babylon.write_bool(file_handler, "autoAnimateLoop", True)
  557. # Closing
  558. file_handler.write("}")
  559. def export_shadowGenerator(lamp, scene, file_handler):
  560. file_handler.write("{")
  561. if lamp.data.shadowMap == 'VAR':
  562. Export_babylon.write_bool(file_handler, "useVarianceShadowMap", True, True)
  563. else:
  564. Export_babylon.write_bool(file_handler, "useVarianceShadowMap", False, True)
  565. Export_babylon.write_int(file_handler, "mapSize", lamp.data.shadowMapSize)
  566. Export_babylon.write_string(file_handler, "lightId", lamp.name)
  567. file_handler.write(",\"renderList\":[")
  568. multiMaterials = []
  569. first = True
  570. for object in [object for object in scene.objects]:
  571. if (object.type == 'MESH' and object.data.castShadows):
  572. if first != True:
  573. file_handler.write(",")
  574. first = False
  575. file_handler.write("\"" + object.name + "\"")
  576. file_handler.write("]")
  577. file_handler.write("}")
  578. def export_bone_matrix(armature, bone, label, file_handler):
  579. SystemMatrix = Matrix.Scale(-1, 4, Vector((0, 0, 1))) * Matrix.Rotation(radians(-90), 4, 'X')
  580. if (bone.parent):
  581. Export_babylon.write_matrix4(file_handler, label, (SystemMatrix * armature.matrix_world * bone.parent.matrix).inverted() * (SystemMatrix * armature.matrix_world * bone.matrix))
  582. else:
  583. Export_babylon.write_matrix4(file_handler, label, SystemMatrix * armature.matrix_world * bone.matrix)
  584. def export_bones(armature, scene, file_handler, id):
  585. file_handler.write("{")
  586. Export_babylon.write_string(file_handler, "name", armature.name, True)
  587. Export_babylon.write_int(file_handler, "id", id)
  588. file_handler.write(",\"bones\":[")
  589. bones = armature.pose.bones
  590. first = True
  591. j = 0
  592. for bone in bones:
  593. if first != True:
  594. file_handler.write(",")
  595. first = False
  596. file_handler.write("{")
  597. Export_babylon.write_string(file_handler, "name", bone.name, True)
  598. Export_babylon.write_int(file_handler, "index", j)
  599. Export_babylon.export_bone_matrix(armature, bone, "matrix", file_handler)
  600. if (bone.parent):
  601. parentId = 0
  602. for parent in bones:
  603. if parent == bone.parent:
  604. break;
  605. parentId += 1
  606. Export_babylon.write_int(file_handler, "parentBoneIndex", parentId)
  607. else:
  608. Export_babylon.write_int(file_handler, "parentBoneIndex", -1)
  609. #animation
  610. if (armature.animation_data.action):
  611. Export_babylon.export_bonesAnimation(armature, bone, scene, file_handler)
  612. j = j + 1
  613. file_handler.write("}")
  614. file_handler.write("]")
  615. file_handler.write("}")
  616. def export_bonesAnimation(armature, bone, scene, file_handler):
  617. file_handler.write(",\"animation\": {")
  618. start_frame = scene.frame_start
  619. end_frame = scene.frame_end
  620. fps = scene.render.fps
  621. Export_babylon.write_int(file_handler, "dataType", 3, True)
  622. Export_babylon.write_int(file_handler, "framePerSecond", fps)
  623. #keys
  624. file_handler.write(",\"keys\":[")
  625. for Frame in range(start_frame, end_frame + 1):
  626. file_handler.write("{")
  627. Export_babylon.write_int(file_handler, "frame", Frame, True)
  628. bpy.context.scene.frame_set(Frame)
  629. Export_babylon.export_bone_matrix(armature, bone, "values", file_handler)
  630. if Frame == end_frame:
  631. file_handler.write("}")
  632. else:
  633. file_handler.write("},")
  634. file_handler.write("]")
  635. Export_babylon.write_int(file_handler, "loopBehavior", 1)
  636. Export_babylon.write_string(file_handler, "name", "anim")
  637. Export_babylon.write_string(file_handler, "property", "_matrix")
  638. file_handler.write("}")
  639. bpy.context.scene.frame_set(start_frame)
  640. def save(operator, context, filepath="",
  641. use_apply_modifiers=False,
  642. use_triangulate=True,
  643. use_compress=False):
  644. # Open file
  645. file_handler = open(filepath, 'w')
  646. if bpy.ops.object.mode_set.poll():
  647. bpy.ops.object.mode_set(mode='OBJECT')
  648. # Writing scene
  649. scene=context.scene
  650. world = scene.world
  651. if world:
  652. world_ambient = world.ambient_color
  653. else:
  654. world_ambient = Color((0.0, 0.0, 0.0))
  655. bpy.ops.screen.animation_cancel()
  656. currentFrame = bpy.context.scene.frame_current
  657. bpy.context.scene.frame_set(0)
  658. file_handler.write("{")
  659. file_handler.write("\"autoClear\":true")
  660. Export_babylon.write_color(file_handler, "clearColor", world_ambient)
  661. Export_babylon.write_color(file_handler, "ambientColor", world_ambient)
  662. Export_babylon.write_vector(file_handler, "gravity", scene.gravity)
  663. if world and world.mist_settings.use_mist:
  664. Export_babylon.write_int(file_handler, "fogMode", 3)
  665. Export_babylon.write_color(file_handler, "fogColor", world.horizon_color)
  666. Export_babylon.write_float(file_handler, "fogStart", world.mist_settings.start)
  667. Export_babylon.write_float(file_handler, "fogEnd", world.mist_settings.depth)
  668. Export_babylon.write_float(file_handler, "fogDensity", 0.1)
  669. # Cameras
  670. file_handler.write(",\"cameras\":[")
  671. first = True
  672. for object in [object for object in scene.objects if object.is_visible(scene)]:
  673. if (object.type == 'CAMERA'):
  674. if first != True:
  675. file_handler.write(",")
  676. first = False
  677. data_string = Export_babylon.export_camera(object, scene, file_handler)
  678. file_handler.write("]")
  679. # Active camera
  680. if scene.camera != None:
  681. Export_babylon.write_string(file_handler, "activeCamera", scene.camera.name)
  682. # Lights
  683. file_handler.write(",\"lights\":[")
  684. first = True
  685. for object in [object for object in scene.objects if object.is_visible(scene)]:
  686. if (object.type == 'LAMP'):
  687. if first != True:
  688. file_handler.write(",")
  689. first = False
  690. data_string = Export_babylon.export_light(object, scene, file_handler)
  691. file_handler.write("]")
  692. # Materials
  693. materials = [mat for mat in bpy.data.materials if mat.users >= 1]
  694. file_handler.write(",\"materials\":[")
  695. first = True
  696. for material in materials:
  697. if first != True:
  698. file_handler.write(",")
  699. first = False
  700. data_string = Export_babylon.export_material(material, scene, file_handler, filepath)
  701. file_handler.write("]")
  702. # Meshes
  703. file_handler.write(",\"meshes\":[")
  704. multiMaterials = []
  705. first = True
  706. for object in [object for object in scene.objects]:
  707. if (object.type == 'MESH'):
  708. if first != True:
  709. file_handler.write(",")
  710. first = False
  711. data_string = Export_babylon.export_mesh(object, scene, file_handler, multiMaterials)
  712. file_handler.write("]")
  713. # Multi-materials
  714. file_handler.write(",\"multiMaterials\":[")
  715. first = True
  716. for multimaterial in multiMaterials:
  717. if first != True:
  718. file_handler.write(",")
  719. first = False
  720. data_string = Export_babylon.export_multimaterial(multimaterial, scene, file_handler)
  721. file_handler.write("]")
  722. # Shadow generators
  723. file_handler.write(",\"shadowGenerators\":[")
  724. first = True
  725. for object in [object for object in scene.objects if object.is_visible(scene)]:
  726. if (object.type == 'LAMP' and object.data.shadowMap != 'NONE'):
  727. if first != True:
  728. file_handler.write(",")
  729. first = False
  730. data_string = Export_babylon.export_shadowGenerator(object, scene, file_handler)
  731. file_handler.write("]")
  732. # Armatures/Bones
  733. file_handler.write(",\"skeletons\":[")
  734. first = True
  735. i = 0
  736. for object in [object for object in scene.objects if object.is_visible(scene)]:
  737. if (object.type == 'ARMATURE'):
  738. if first != True:
  739. file_handler.write(",")
  740. first = False
  741. data_string = Export_babylon.export_bones(object, scene, file_handler, i)
  742. i = i+1
  743. file_handler.write("]")
  744. # Closing
  745. file_handler.write("}")
  746. file_handler.close()
  747. bpy.context.scene.frame_set(currentFrame)
  748. return {'FINISHED'}
  749. # UI
  750. bpy.types.Mesh.checkCollisions = BoolProperty(
  751. name="Check Collisions",
  752. default = False)
  753. bpy.types.Mesh.castShadows = BoolProperty(
  754. name="Cast Shadows",
  755. default = False)
  756. bpy.types.Mesh.receiveShadows = BoolProperty(
  757. name="Receive Shadows",
  758. default = False)
  759. bpy.types.Camera.checkCollisions = BoolProperty(
  760. name="Check Collisions",
  761. default = False)
  762. bpy.types.Camera.applyGravity = BoolProperty(
  763. name="Apply Gravity",
  764. default = False)
  765. bpy.types.Camera.ellipsoid = FloatVectorProperty(
  766. name="Ellipsoid",
  767. default = mathutils.Vector((0.2, 0.9, 0.2)))
  768. bpy.types.Lamp.shadowMap = EnumProperty(
  769. name="Shadow Map Type",
  770. items = (('NONE', "None", "No Shadow Maps"), ('STD', "Standard", "Use Standard Shadow Maps"), ('VAR', "Variance", "Use Variance Shadow Maps")),
  771. default = 'NONE')
  772. bpy.types.Lamp.shadowMapSize = IntProperty(
  773. name="Shadow Map Size",
  774. default = 512)
  775. class ObjectPanel(bpy.types.Panel):
  776. bl_label = "Babylon.js"
  777. bl_space_type = "PROPERTIES"
  778. bl_region_type = "WINDOW"
  779. bl_context = "data"
  780. def draw(self, context):
  781. ob = context.object
  782. if not ob or not ob.data:
  783. return
  784. layout = self.layout
  785. isMesh = isinstance(ob.data, bpy.types.Mesh)
  786. isCamera = isinstance(ob.data, bpy.types.Camera)
  787. isLight = isinstance(ob.data, bpy.types.Lamp)
  788. if isMesh:
  789. layout.prop(ob.data, 'checkCollisions')
  790. layout.prop(ob.data, 'castShadows')
  791. layout.prop(ob.data, 'receiveShadows')
  792. elif isCamera:
  793. layout.prop(ob.data, 'checkCollisions')
  794. layout.prop(ob.data, 'applyGravity')
  795. layout.prop(ob.data, 'ellipsoid')
  796. elif isLight:
  797. layout.prop(ob.data, 'shadowMap')
  798. layout.prop(ob.data, 'shadowMapSize')
  799. ### REGISTER ###
  800. def menu_func(self, context):
  801. self.layout.operator(Export_babylon.bl_idname, text="Babylon.js (.babylon)")
  802. def register():
  803. bpy.utils.register_module(__name__)
  804. bpy.types.INFO_MT_file_export.append(menu_func)
  805. def unregister():
  806. bpy.utils.unregister_module(__name__)
  807. bpy.types.INFO_MT_file_export.remove(menu_func)
  808. if __name__ == "__main__":
  809. register()