io_export_babylon.py 41 KB

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