io_export_babylon.py 50 KB

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