convert_to_threejs.py 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460
  1. # @author zfedoran / http://github.com/zfedoran
  2. import os
  3. import sys
  4. import math
  5. # #####################################################
  6. # Globals
  7. # #####################################################
  8. option_triangulate = True
  9. option_textures = True
  10. option_prefix = True
  11. option_geometry = False
  12. option_default_camera = False
  13. option_default_light = False
  14. converter = None
  15. # #####################################################
  16. # Templates
  17. # #####################################################
  18. def Vector2String(v):
  19. return '[ %g, %g ]' % (v[0], v[1])
  20. def Vector3String(v):
  21. return '[ %g, %g, %g ]' % (v[0], v[1], v[2])
  22. def ColorString(c):
  23. return '[ %g, %g, %g ]' % (c[0], c[1], c[2])
  24. def LabelString(s):
  25. return '"%s"' % s
  26. def ArrayString(s):
  27. return '[ %s ]' % s
  28. def PaddingString(n):
  29. output = ""
  30. for i in range(n):
  31. output += "\t"
  32. return output
  33. def BoolString(value):
  34. if value:
  35. return "true"
  36. return "false"
  37. # #####################################################
  38. # Helpers
  39. # #####################################################
  40. def getObjectName(o):
  41. if not o:
  42. return ""
  43. prefix = ""
  44. if option_prefix:
  45. prefix = "Object_"
  46. return prefix + o.GetName()
  47. def getGeometryName(g):
  48. prefix = ""
  49. if option_prefix:
  50. prefix = "Geometry_"
  51. return prefix + g.GetName()
  52. def getEmbedName(e):
  53. prefix = ""
  54. if option_prefix:
  55. prefix = "Embed_"
  56. return prefix + e.GetName()
  57. def getMaterialName(m):
  58. prefix = ""
  59. if option_prefix:
  60. prefix = "Material_"
  61. return prefix + m.GetName()
  62. def getTextureName(t):
  63. texture_file = t.GetFileName()
  64. texture_id = os.path.splitext(os.path.basename(texture_file))[0]
  65. prefix = ""
  66. if option_prefix:
  67. prefix = "Texture_"
  68. return prefix + texture_id
  69. def getFogName(f):
  70. prefix = ""
  71. if option_prefix:
  72. prefix = "Fog_"
  73. return prefix + f.GetName()
  74. def getObjectVisible(n):
  75. return BoolString(True)
  76. def getRadians(v):
  77. return ((v[0]*math.pi)/180, (v[1]*math.pi)/180, (v[2]*math.pi)/180)
  78. def getHex(c):
  79. color = (int(c[0]*255) << 16) + (int(c[1]*255) << 8) + int(c[2]*255)
  80. return color
  81. def generateMultiLineString(lines, separator, padding):
  82. cleanLines = []
  83. for i in range(len(lines)):
  84. line = lines[i]
  85. line = PaddingString(padding) + line
  86. cleanLines.append(line)
  87. return separator.join(cleanLines)
  88. # #####################################################
  89. # Generate - Triangles
  90. # #####################################################
  91. def triangulate_node_hierarchy(node):
  92. node_attribute = node.GetNodeAttribute();
  93. if node_attribute:
  94. if node_attribute.GetAttributeType() == FbxNodeAttribute.eMesh or \
  95. node_attribute.GetAttributeType() == FbxNodeAttribute.eNurbs or \
  96. node_attribute.GetAttributeType() == FbxNodeAttribute.eNurbsSurface or \
  97. node_attribute.GetAttributeType() == FbxNodeAttribute.ePatch:
  98. converter.TriangulateInPlace(node);
  99. child_count = node.GetChildCount()
  100. for i in range(child_count):
  101. triangulate_node_hierarchy(node.GetChild(i))
  102. def triangulate_scene(scene):
  103. node = scene.GetRootNode()
  104. if node:
  105. for i in range(node.GetChildCount()):
  106. triangulate_node_hierarchy(node.GetChild(i))
  107. # #####################################################
  108. # Generate - Material String
  109. # #####################################################
  110. def generate_texture_bindings(material_property, texture_list):
  111. binding_types = {
  112. "DiffuseColor": "map", "DiffuseFactor": "diffuseFactor", "EmissiveColor": "emissiveMap",
  113. "EmissiveFactor": "emissiveFactor", "AmbientColor": "ambientMap", "AmbientFactor": "ambientFactor",
  114. "SpecularColor": "specularMap", "SpecularFactor": "specularFactor", "ShininessExponent": "shininessExponent",
  115. "NormalMap": "normalMap", "Bump": "bumpMap", "TransparentColor": "transparentMap",
  116. "TransparencyFactor": "transparentFactor", "ReflectionColor": "reflectionMap",
  117. "ReflectionFactor": "reflectionFactor", "DisplacementColor": "displacementMap",
  118. "VectorDisplacementColor": "vectorDisplacementMap"
  119. }
  120. if material_property.IsValid():
  121. #Here we have to check if it's layeredtextures, or just textures:
  122. layered_texture_count = material_property.GetSrcObjectCount(FbxLayeredTexture.ClassId)
  123. if layered_texture_count > 0:
  124. for j in range(layered_texture_count):
  125. layered_texture = material_property.GetSrcObject(FbxLayeredTexture.ClassId, j)
  126. texture_count = layered_texture.GetSrcObjectCount(FbxTexture.ClassId)
  127. for k in range(texture_count):
  128. texture = layered_texture.GetSrcObject(FbxTexture.ClassId,k)
  129. if texture:
  130. texture_id = getTextureName(texture)
  131. texture_binding = ' "%s": "%s",' % (binding_types[str(material_property.GetName())], texture_id)
  132. texture_list.append(texture_binding)
  133. else:
  134. # no layered texture simply get on the property
  135. texture_count = material_property.GetSrcObjectCount(FbxTexture.ClassId)
  136. for j in range(texture_count):
  137. texture = material_property.GetSrcObject(FbxTexture.ClassId,j)
  138. if texture:
  139. texture_id = getTextureName(texture)
  140. texture_binding = ' "%s": "%s",' % (binding_types[str(material_property.GetName())], texture_id)
  141. texture_list.append(texture_binding)
  142. def generate_material_string(material):
  143. #Get the implementation to see if it's a hardware shader.
  144. implementation = GetImplementation(material, "ImplementationHLSL")
  145. implementation_type = "HLSL"
  146. if not implementation:
  147. implementation = GetImplementation(material, "ImplementationCGFX")
  148. implementation_type = "CGFX"
  149. output = []
  150. if implementation:
  151. # This material is a hardware shader, skip it
  152. print("Shader materials are not supported")
  153. return ''
  154. elif material.GetClassId().Is(FbxSurfaceLambert.ClassId):
  155. ambient = str(getHex(material.Ambient.Get()))
  156. diffuse = str(getHex(material.Diffuse.Get()))
  157. emissive = str(getHex(material.Emissive.Get()))
  158. opacity = 1.0 - material.TransparencyFactor.Get()
  159. opacity = 1.0 if opacity == 0 else opacity
  160. opacity = str(opacity)
  161. transparent = BoolString(False)
  162. reflectivity = "1"
  163. output = [
  164. '\t' + LabelString( getMaterialName( material ) ) + ': {',
  165. ' "type" : "MeshLambertMaterial",',
  166. ' "parameters" : {',
  167. ' "color" : ' + diffuse + ',',
  168. ' "ambient" : ' + ambient + ',',
  169. ' "emissive" : ' + emissive + ',',
  170. ' "reflectivity" : ' + reflectivity + ',',
  171. ' "transparent" : ' + transparent + ',',
  172. ' "opacity" : ' + opacity + ',',
  173. ]
  174. elif material.GetClassId().Is(FbxSurfacePhong.ClassId):
  175. ambient = str(getHex(material.Ambient.Get()))
  176. diffuse = str(getHex(material.Diffuse.Get()))
  177. emissive = str(getHex(material.Emissive.Get()))
  178. specular = str(getHex(material.Specular.Get()))
  179. opacity = 1.0 - material.TransparencyFactor.Get()
  180. opacity = 1.0 if opacity == 0 else opacity
  181. opacity = str(opacity)
  182. shininess = str(material.Shininess.Get())
  183. transparent = BoolString(False)
  184. reflectivity = "1"
  185. bumpScale = "1"
  186. output = [
  187. '\t' + LabelString( getMaterialName( material ) ) + ': {',
  188. ' "type" : "MeshPhongMaterial",',
  189. ' "parameters" : {',
  190. ' "color" : ' + diffuse + ',',
  191. ' "ambient" : ' + ambient + ',',
  192. ' "emissive" : ' + emissive + ',',
  193. ' "specular" : ' + specular + ',',
  194. ' "shininess" : ' + shininess + ',',
  195. ' "bumpScale" : ' + bumpScale + ',',
  196. ' "reflectivity" : ' + reflectivity + ',',
  197. ' "transparent" : ' + transparent + ',',
  198. ' "opacity" : ' + opacity + ',',
  199. ]
  200. else:
  201. print("Unknown type of Material")
  202. return ''
  203. if option_textures:
  204. texture_list = []
  205. texture_count = FbxLayerElement.sTypeTextureCount()
  206. for texture_index in range(texture_count):
  207. material_property = material.FindProperty(FbxLayerElement.sTextureChannelNames(texture_index))
  208. generate_texture_bindings(material_property, texture_list)
  209. output += texture_list
  210. wireframe = BoolString(False)
  211. wireframeLinewidth = "1"
  212. output.append(' "wireframe" : ' + wireframe + ',')
  213. output.append(' "wireframeLinewidth" : ' + wireframeLinewidth)
  214. output.append(' }')
  215. output.append('}')
  216. return generateMultiLineString( output, '\n\t\t', 0 )
  217. def generate_proxy_material_string(node, material_names):
  218. output = [
  219. '\t' + LabelString( getMaterialName( node ) ) + ': {',
  220. ' "type" : "MeshFaceMaterial",',
  221. ' "parameters" : {',
  222. ' "materials" : ' + ArrayString( ",".join(LabelString(m) for m in material_names) ),
  223. ' }',
  224. '}'
  225. ]
  226. return generateMultiLineString( output, '\n\t\t', 0 )
  227. # #####################################################
  228. # Parse - Materials
  229. # #####################################################
  230. def extract_materials_from_node(node, material_list):
  231. name = node.GetName()
  232. mesh = node.GetNodeAttribute()
  233. node = None
  234. if mesh:
  235. node = mesh.GetNode()
  236. if node:
  237. material_count = node.GetMaterialCount()
  238. material_names = []
  239. for l in range(mesh.GetLayerCount()):
  240. materials = mesh.GetLayer(l).GetMaterials()
  241. if materials:
  242. if materials.GetReferenceMode() == FbxLayerElement.eIndex:
  243. #Materials are in an undefined external table
  244. continue
  245. for i in range(material_count):
  246. material = node.GetMaterial(i)
  247. material_names.append(getMaterialName(material))
  248. material_string = generate_material_string(material)
  249. material_list.append(material_string)
  250. if material_count > 1:
  251. proxy_material = generate_proxy_material_string(node, material_names)
  252. material_list.append(proxy_material)
  253. def generate_materials_from_hierarchy(node, material_list):
  254. if node.GetNodeAttribute() == None:
  255. pass
  256. else:
  257. attribute_type = (node.GetNodeAttribute().GetAttributeType())
  258. if attribute_type == FbxNodeAttribute.eMesh:
  259. extract_materials_from_node(node, material_list)
  260. for i in range(node.GetChildCount()):
  261. generate_materials_from_hierarchy(node.GetChild(i), material_list)
  262. def generate_material_list(scene):
  263. material_list = []
  264. node = scene.GetRootNode()
  265. if node:
  266. for i in range(node.GetChildCount()):
  267. generate_materials_from_hierarchy(node.GetChild(i), material_list)
  268. return material_list
  269. # #####################################################
  270. # Generate - Texture String
  271. # #####################################################
  272. def generate_texture_string(texture):
  273. wrap_u = texture.GetWrapModeU()
  274. wrap_v = texture.GetWrapModeV()
  275. offset = texture.GetUVTranslation()
  276. output = [
  277. '\t' + LabelString( getTextureName( texture ) ) + ': {',
  278. ' "url" : "' + texture.GetFileName() + '",',
  279. ' "repeat" : ' + Vector2String( (1,1) ) + ',',
  280. ' "offset" : ' + Vector2String( texture.GetUVTranslation() ) + ',',
  281. ' "magFilter" : ' + LabelString( "LinearFilter" ) + ',',
  282. ' "minFilter" : ' + LabelString( "LinearMipMapLinearFilter" ) + ',',
  283. ' "anisotropy" : ' + BoolString( True ),
  284. '}'
  285. ]
  286. return generateMultiLineString( output, '\n\t\t', 0 )
  287. # #####################################################
  288. # Parse - Textures
  289. # #####################################################
  290. def extract_material_textures(material_property, texture_list):
  291. if material_property.IsValid():
  292. #Here we have to check if it's layeredtextures, or just textures:
  293. layered_texture_count = material_property.GetSrcObjectCount(FbxLayeredTexture.ClassId)
  294. if layered_texture_count > 0:
  295. for j in range(layered_texture_count):
  296. layered_texture = material_property.GetSrcObject(FbxLayeredTexture.ClassId, j)
  297. texture_count = layered_texture.GetSrcObjectCount(FbxTexture.ClassId)
  298. for k in range(texture_count):
  299. texture = layered_texture.GetSrcObject(FbxTexture.ClassId,k)
  300. if texture:
  301. texture_string = generate_texture_string(texture)
  302. texture_list.append(texture_string)
  303. else:
  304. # no layered texture simply get on the property
  305. texture_count = material_property.GetSrcObjectCount(FbxTexture.ClassId)
  306. for j in range(texture_count):
  307. texture = material_property.GetSrcObject(FbxTexture.ClassId,j)
  308. if texture:
  309. texture_string = generate_texture_string(texture)
  310. texture_list.append(texture_string)
  311. def extract_textures_from_node(node, texture_list):
  312. name = node.GetName()
  313. mesh = node.GetNodeAttribute()
  314. #for all materials attached to this mesh
  315. material_count = mesh.GetNode().GetSrcObjectCount(FbxSurfaceMaterial.ClassId)
  316. for material_index in range(material_count):
  317. material = mesh.GetNode().GetSrcObject(FbxSurfaceMaterial.ClassId, material_index)
  318. #go through all the possible textures types
  319. if material:
  320. texture_count = FbxLayerElement.sTypeTextureCount()
  321. for texture_index in range(texture_count):
  322. material_property = material.FindProperty(FbxLayerElement.sTextureChannelNames(texture_index))
  323. extract_material_textures(material_property, texture_list)
  324. def generate_textures_from_hierarchy(node, texture_list):
  325. if node.GetNodeAttribute() == None:
  326. pass
  327. else:
  328. attribute_type = (node.GetNodeAttribute().GetAttributeType())
  329. if attribute_type == FbxNodeAttribute.eMesh:
  330. extract_textures_from_node(node, texture_list)
  331. for i in range(node.GetChildCount()):
  332. generate_textures_from_hierarchy(node.GetChild(i), texture_list)
  333. def generate_texture_list(scene):
  334. if not option_textures:
  335. return []
  336. texture_list = []
  337. node = scene.GetRootNode()
  338. if node:
  339. for i in range(node.GetChildCount()):
  340. generate_textures_from_hierarchy(node.GetChild(i), texture_list)
  341. return texture_list
  342. # #####################################################
  343. # Generate - Mesh String
  344. # #####################################################
  345. def setBit(value, position, on):
  346. if on:
  347. mask = 1 << position
  348. return (value | mask)
  349. else:
  350. mask = ~(1 << position)
  351. return (value & mask)
  352. def extract_color(color):
  353. return [color.mRed, color.mGreen, color.mBlue]
  354. def extract_vec2(v):
  355. return [v[0], v[1]]
  356. def extract_vec3(v):
  357. return [v[0], v[1], v[2]]
  358. def join_vec2(v):
  359. return "%g,%g" % (v[0], v[1])
  360. def join_vec3(v):
  361. return "%g,%g,%g" % (v[0], v[1], v[2])
  362. def generate_uv(uv):
  363. return "%g,%g" % (uv[0], uv[1])
  364. def generate_uvs(uv_layers):
  365. layers = []
  366. for uvs in uv_layers:
  367. layer = ",".join(generate_uv(n) for n in uvs)
  368. layers.append(layer)
  369. return ",".join("[%s]" % n for n in layers)
  370. def extract_mesh_bounding_box(mesh):
  371. control_points_count = mesh.GetControlPointsCount()
  372. control_points = mesh.GetControlPoints()
  373. minx = 0
  374. miny = 0
  375. minz = 0
  376. maxx = 0
  377. maxy = 0
  378. maxz = 0
  379. for i in range(control_points_count):
  380. vertex = control_points[i]
  381. if vertex[0] < minx:
  382. minx = vertex[0]
  383. if vertex[1] < miny:
  384. miny = vertex[1]
  385. if vertex[2] < minz:
  386. minz = vertex[2]
  387. if vertex[0] > maxx:
  388. maxx = vertex[0]
  389. if vertex[1] > maxy:
  390. maxy = vertex[1]
  391. if vertex[2] > maxz:
  392. maxz = vertex[2]
  393. return [minx, miny, minz], [maxx, maxy, maxz]
  394. def extract_vertex_positions(mesh):
  395. control_points_count = mesh.GetControlPointsCount()
  396. control_points = mesh.GetControlPoints()
  397. positions = []
  398. for i in range(control_points_count):
  399. positions.append(extract_vec3(control_points[i]))
  400. return positions
  401. def extract_vertex_normals(mesh):
  402. # eNone The mapping is undetermined.
  403. # eByControlPoint There will be one mapping coordinate for each surface control point/vertex.
  404. # eByPolygonVertex There will be one mapping coordinate for each vertex, for every polygon of which it is a part. This means that a vertex will have as many mapping coordinates as polygons of which it is a part.
  405. # eByPolygon There can be only one mapping coordinate for the whole polygon.
  406. # eByEdge There will be one mapping coordinate for each unique edge in the mesh. This is meant to be used with smoothing layer elements.
  407. # eAllSame There can be only one mapping coordinate for the whole surface.
  408. layered_normal_indices = []
  409. layered_normal_values = []
  410. poly_count = mesh.GetPolygonCount()
  411. control_points = mesh.GetControlPoints()
  412. for l in range(mesh.GetLayerCount()):
  413. mesh_normals = mesh.GetLayer(l).GetNormals()
  414. if not mesh_normals:
  415. continue
  416. normals_array = mesh_normals.GetDirectArray()
  417. normals_count = normals_array.GetCount()
  418. if normals_count == 0:
  419. continue
  420. normal_indices = []
  421. normal_values = []
  422. # values
  423. for i in range(normals_count):
  424. normal = extract_vec3(normals_array.GetAt(i))
  425. normal_values.append(normal)
  426. # indices
  427. vertexId = 0
  428. for p in range(poly_count):
  429. poly_size = mesh.GetPolygonSize(p)
  430. poly_normals = []
  431. for v in range(poly_size):
  432. control_point_index = mesh.GetPolygonVertex(p, v)
  433. if mesh_normals.GetMappingMode() == FbxLayerElement.eByControlPoint:
  434. if mesh_normals.GetReferenceMode() == FbxLayerElement.eDirect:
  435. poly_normals.append(control_point_index)
  436. elif mesh_normals.GetReferenceMode() == FbxLayerElement.eIndexToDirect:
  437. index = mesh_normals.GetIndexArray().GetAt(control_point_index)
  438. poly_normals.append(index)
  439. elif mesh_normals.GetMappingMode() == FbxLayerElement.eByPolygonVertex:
  440. if mesh_normals.GetReferenceMode() == FbxLayerElement.eDirect:
  441. poly_normals.append(vertexId)
  442. elif mesh_normals.GetReferenceMode() == FbxLayerElement.eIndexToDirect:
  443. index = mesh_normals.GetIndexArray().GetAt(vertexId)
  444. poly_normals.append(index)
  445. elif mesh_normals.GetMappingMode() == FbxLayerElement.eByPolygon or \
  446. mesh_normals.GetMappingMode() == FbxLayerElement.eAllSame or \
  447. mesh_normals.GetMappingMode() == FbxLayerElement.eNone:
  448. print("unsupported normal mapping mode for polygon vertex")
  449. vertexId += 1
  450. normal_indices.append(poly_normals)
  451. layered_normal_values.append(normal_values)
  452. layered_normal_indices.append(normal_indices)
  453. return layered_normal_values, layered_normal_indices
  454. def extract_vertex_colors(mesh):
  455. # eNone The mapping is undetermined.
  456. # eByControlPoint There will be one mapping coordinate for each surface control point/vertex.
  457. # eByPolygonVertex There will be one mapping coordinate for each vertex, for every polygon of which it is a part. This means that a vertex will have as many mapping coordinates as polygons of which it is a part.
  458. # eByPolygon There can be only one mapping coordinate for the whole polygon.
  459. # eByEdge There will be one mapping coordinate for each unique edge in the mesh. This is meant to be used with smoothing layer elements.
  460. # eAllSame There can be only one mapping coordinate for the whole surface.
  461. layered_color_indices = []
  462. layered_color_values = []
  463. poly_count = mesh.GetPolygonCount()
  464. control_points = mesh.GetControlPoints()
  465. for l in range(mesh.GetLayerCount()):
  466. mesh_colors = mesh.GetLayer(l).GetVertexColors()
  467. if not mesh_colors:
  468. continue
  469. colors_array = mesh_colors.GetDirectArray()
  470. colors_count = colors_array.GetCount()
  471. if colors_count == 0:
  472. continue
  473. color_indices = []
  474. color_values = []
  475. # values
  476. for i in range(colors_count):
  477. color = extract_color(colors_array.GetAt(i))
  478. color_values.append(color)
  479. # indices
  480. vertexId = 0
  481. for p in range(poly_count):
  482. poly_size = mesh.GetPolygonSize(p)
  483. poly_colors = []
  484. for v in range(poly_size):
  485. control_point_index = mesh.GetPolygonVertex(p, v)
  486. if mesh_colors.GetMappingMode() == FbxLayerElement.eByControlPoint:
  487. if mesh_colors.GetReferenceMode() == FbxLayerElement.eDirect:
  488. poly_colors.append(control_point_index)
  489. elif mesh_colors.GetReferenceMode() == FbxLayerElement.eIndexToDirect:
  490. index = mesh_colors.GetIndexArray().GetAt(control_point_index)
  491. poly_colors.append(index)
  492. elif mesh_colors.GetMappingMode() == FbxLayerElement.eByPolygonVertex:
  493. if mesh_colors.GetReferenceMode() == FbxLayerElement.eDirect:
  494. poly_colors.append(vertexId)
  495. elif mesh_colors.GetReferenceMode() == FbxLayerElement.eIndexToDirect:
  496. index = mesh_colors.GetIndexArray().GetAt(vertexId)
  497. poly_colors.append(index)
  498. elif mesh_colors.GetMappingMode() == FbxLayerElement.eByPolygon or \
  499. mesh_colors.GetMappingMode() == FbxLayerElement.eAllSame or \
  500. mesh_colors.GetMappingMode() == FbxLayerElement.eNone:
  501. print("unsupported color mapping mode for polygon vertex")
  502. vertexId += 1
  503. color_indices.append(poly_colors)
  504. layered_color_values.append(color_values)
  505. layered_color_indices.append(color_indices)
  506. return layered_color_values, layered_color_indices
  507. def extract_vertex_uvs(mesh):
  508. # eNone The mapping is undetermined.
  509. # eByControlPoint There will be one mapping coordinate for each surface control point/vertex.
  510. # eByPolygonVertex There will be one mapping coordinate for each vertex, for every polygon of which it is a part. This means that a vertex will have as many mapping coordinates as polygons of which it is a part.
  511. # eByPolygon There can be only one mapping coordinate for the whole polygon.
  512. # eByEdge There will be one mapping coordinate for each unique edge in the mesh. This is meant to be used with smoothing layer elements.
  513. # eAllSame There can be only one mapping coordinate for the whole surface.
  514. layered_uv_indices = []
  515. layered_uv_values = []
  516. poly_count = mesh.GetPolygonCount()
  517. control_points = mesh.GetControlPoints()
  518. for l in range(mesh.GetLayerCount()):
  519. mesh_uvs = mesh.GetLayer(l).GetUVs()
  520. if not mesh_uvs:
  521. continue
  522. uvs_array = mesh_uvs.GetDirectArray()
  523. uvs_count = uvs_array.GetCount()
  524. if uvs_count == 0:
  525. continue
  526. uv_indices = []
  527. uv_values = []
  528. # values
  529. for i in range(uvs_count):
  530. uv = extract_vec2(uvs_array.GetAt(i))
  531. uv_values.append(uv)
  532. # indices
  533. vertexId = 0
  534. for p in range(poly_count):
  535. poly_size = mesh.GetPolygonSize(p)
  536. poly_uvs = []
  537. for v in range(poly_size):
  538. control_point_index = mesh.GetPolygonVertex(p, v)
  539. if mesh_uvs.GetMappingMode() == FbxLayerElement.eByControlPoint:
  540. if mesh_uvs.GetReferenceMode() == FbxLayerElement.eDirect:
  541. poly_uvs.append(control_point_index)
  542. elif mesh_uvs.GetReferenceMode() == FbxLayerElement.eIndexToDirect:
  543. index = mesh_uvs.GetIndexArray().GetAt(control_point_index)
  544. poly_uvs.append(index)
  545. elif mesh_uvs.GetMappingMode() == FbxLayerElement.eByPolygonVertex:
  546. uv_texture_index = mesh.GetTextureUVIndex(p, v)
  547. if mesh_uvs.GetReferenceMode() == FbxLayerElement.eDirect or \
  548. mesh_uvs.GetReferenceMode() == FbxLayerElement.eIndexToDirect:
  549. poly_uvs.append(uv_texture_index)
  550. elif mesh_uvs.GetMappingMode() == FbxLayerElement.eByPolygon or \
  551. mesh_uvs.GetMappingMode() == FbxLayerElement.eAllSame or \
  552. mesh_uvs.GetMappingMode() == FbxLayerElement.eNone:
  553. print("unsupported uv mapping mode for polygon vertex")
  554. vertexId += 1
  555. uv_indices.append(poly_uvs)
  556. layered_uv_values.append(uv_values)
  557. layered_uv_indices.append(uv_indices)
  558. return layered_uv_values, layered_uv_indices
  559. def generate_mesh_face(mesh, vertex_indices, polygon_index, normals, colors, uv_layers, material_count, material_is_same):
  560. isTriangle = ( len(vertex_indices) == 3 )
  561. nVertices = 3 if isTriangle else 4
  562. hasMaterial = material_count > 0
  563. hasFaceUvs = False
  564. hasFaceVertexUvs = len(uv_layers) > 0
  565. hasFaceNormals = False # don't export any face normals (as they are computed in engine)
  566. hasFaceVertexNormals = len(normals) > 0
  567. hasFaceColors = False
  568. hasFaceVertexColors = len(colors) > 0
  569. faceType = 0
  570. faceType = setBit(faceType, 0, not isTriangle)
  571. faceType = setBit(faceType, 1, hasMaterial)
  572. faceType = setBit(faceType, 2, hasFaceUvs)
  573. faceType = setBit(faceType, 3, hasFaceVertexUvs)
  574. faceType = setBit(faceType, 4, hasFaceNormals)
  575. faceType = setBit(faceType, 5, hasFaceVertexNormals)
  576. faceType = setBit(faceType, 6, hasFaceColors)
  577. faceType = setBit(faceType, 7, hasFaceVertexColors)
  578. faceData = []
  579. # order is important, must match order in JSONLoader
  580. # face type
  581. # vertex indices
  582. # material index
  583. # face uvs index
  584. # face vertex uvs indices
  585. # face color index
  586. # face vertex colors indices
  587. faceData.append(faceType)
  588. # must clamp in case on polygons bigger than quads
  589. for i in range(nVertices):
  590. index = vertex_indices[i]
  591. faceData.append(index)
  592. if hasMaterial:
  593. material_id = 0
  594. if not material_is_same:
  595. for l in range(mesh.GetLayerCount()):
  596. materials = mesh.GetLayer(l).GetMaterials()
  597. if materials:
  598. material_id = materials.GetIndexArray().GetAt(polygon_index)
  599. break
  600. faceData.append( material_id )
  601. if hasFaceVertexUvs:
  602. for layer_index, uvs in enumerate(uv_layers):
  603. polygon_uvs = uvs[polygon_index]
  604. for i in range(nVertices):
  605. index = polygon_uvs[i]
  606. faceData.append(index)
  607. if hasFaceVertexNormals:
  608. polygon_normals = normals[polygon_index]
  609. for i in range(nVertices):
  610. index = polygon_normals[i]
  611. faceData.append(index)
  612. if hasFaceVertexColors:
  613. polygon_colors = colors[polygon_index]
  614. for i in range(nVertices):
  615. index = polygon_colors[i]
  616. faceData.append(index)
  617. return ",".join( map(str, faceData) )
  618. def generate_mesh_faces(mesh, normals, colors, uv_layers):
  619. has_same_material_for_all_polygons = True
  620. for l in range(mesh.GetLayerCount()):
  621. materials = mesh.GetLayer(l).GetMaterials()
  622. if materials:
  623. if materials.GetMappingMode() == FbxLayerElement.eByPolygon:
  624. has_same_material_for_all_polygons = False
  625. break
  626. node = mesh.GetNode()
  627. if node:
  628. material_count = node.GetMaterialCount()
  629. poly_count = mesh.GetPolygonCount()
  630. control_points = mesh.GetControlPoints()
  631. faces = []
  632. for p in range(poly_count):
  633. poly_size = mesh.GetPolygonSize(p)
  634. vertex_indices = []
  635. for v in range(poly_size):
  636. control_point_index = mesh.GetPolygonVertex(p, v)
  637. vertex_indices.append(control_point_index)
  638. face = generate_mesh_face(mesh, vertex_indices, p, normals, colors, uv_layers, material_count, has_same_material_for_all_polygons)
  639. faces.append(face)
  640. return faces
  641. def generate_mesh_string(node):
  642. mesh = node.GetNodeAttribute()
  643. vertices = extract_vertex_positions(mesh)
  644. aabb_min, aabb_max = extract_mesh_bounding_box(mesh)
  645. normal_values, normal_indices = extract_vertex_normals(mesh)
  646. color_values, color_indices = extract_vertex_colors(mesh)
  647. uv_values, uv_indices = extract_vertex_uvs(mesh)
  648. # Three.js only supports one layer of normals
  649. if len(normal_values) > 0:
  650. normal_values = normal_values[0]
  651. normal_indices = normal_indices[0]
  652. # Three.js only supports one layer of colors
  653. if len(color_values) > 0:
  654. color_values = color_values[0]
  655. color_indices = color_indices[0]
  656. faces = generate_mesh_faces(mesh, normal_indices, color_indices, uv_indices)
  657. nuvs = []
  658. for layer_index, uvs in enumerate(uv_values):
  659. nuvs.append(str(len(uvs)))
  660. nvertices = len(vertices)
  661. nnormals = len(normal_values)
  662. ncolors = len(color_values)
  663. nfaces = len(faces)
  664. nuvs = ",".join(nuvs)
  665. vertices = ",".join(join_vec3(v) for v in vertices)
  666. normals = ",".join(join_vec3(v) for v in normal_values)
  667. colors = ",".join(join_vec3(v) for v in color_values)
  668. faces = ",".join(faces)
  669. uvs = generate_uvs(uv_values)
  670. aabb_min = ",".join(str(f) for f in aabb_min)
  671. aabb_max = ",".join(str(f) for f in aabb_max)
  672. output = [
  673. '\t' + LabelString( getEmbedName( node ) ) + ' : {',
  674. ' "metadata" : {',
  675. ' "vertices" : ' + str(nvertices) + ',',
  676. ' "normals" : ' + str(nnormals) + ',',
  677. ' "colors" : ' + str(ncolors) + ',',
  678. ' "faces" : ' + str(nfaces) + ',',
  679. ' "uvs" : ' + ArrayString(nuvs),
  680. ' },',
  681. ' "boundingBox" : {',
  682. ' "min" : ' + ArrayString(aabb_min) + ',',
  683. ' "max" : ' + ArrayString(aabb_max),
  684. ' },',
  685. ' "scale" : ' + str( 1 ) + ',',
  686. ' "materials" : ' + ArrayString("") + ',',
  687. ' "vertices" : ' + ArrayString(vertices) + ',',
  688. ' "normals" : ' + ArrayString(normals) + ',',
  689. ' "colors" : ' + ArrayString(colors) + ',',
  690. ' "uvs" : ' + ArrayString(uvs) + ',',
  691. ' "faces" : ' + ArrayString(faces),
  692. '}'
  693. ]
  694. return generateMultiLineString( output, '\n\t\t', 0 )
  695. # #####################################################
  696. # Generate - Embeds
  697. # #####################################################
  698. def generate_embed_list_from_hierarchy(node, embed_list):
  699. if node.GetNodeAttribute() == None:
  700. pass
  701. else:
  702. attribute_type = (node.GetNodeAttribute().GetAttributeType())
  703. if attribute_type == FbxNodeAttribute.eMesh or \
  704. attribute_type == FbxNodeAttribute.eNurbs or \
  705. attribute_type == FbxNodeAttribute.eNurbsSurface or \
  706. attribute_type == FbxNodeAttribute.ePatch:
  707. if attribute_type != FbxNodeAttribute.eMesh:
  708. converter.TriangulateInPlace(node);
  709. embed_string = generate_mesh_string(node)
  710. embed_list.append(embed_string)
  711. for i in range(node.GetChildCount()):
  712. generate_embed_list_from_hierarchy(node.GetChild(i), embed_list)
  713. def generate_embed_list(scene):
  714. embed_list = []
  715. node = scene.GetRootNode()
  716. if node:
  717. for i in range(node.GetChildCount()):
  718. generate_embed_list_from_hierarchy(node.GetChild(i), embed_list)
  719. return embed_list
  720. # #####################################################
  721. # Generate - Geometries
  722. # #####################################################
  723. def generate_geometry_string(node):
  724. output = [
  725. '\t' + LabelString( getGeometryName( node ) ) + ' : {',
  726. ' "type" : "embedded",',
  727. ' "id" : ' + LabelString( getEmbedName( node ) ),
  728. '}'
  729. ]
  730. return generateMultiLineString( output, '\n\t\t', 0 )
  731. def generate_geometry_list_from_hierarchy(node, geometry_list):
  732. if node.GetNodeAttribute() == None:
  733. pass
  734. else:
  735. attribute_type = (node.GetNodeAttribute().GetAttributeType())
  736. if attribute_type == FbxNodeAttribute.eMesh:
  737. geometry_string = generate_geometry_string(node)
  738. geometry_list.append(geometry_string)
  739. for i in range(node.GetChildCount()):
  740. generate_geometry_list_from_hierarchy(node.GetChild(i), geometry_list)
  741. def generate_geometry_list(scene):
  742. geometry_list = []
  743. node = scene.GetRootNode()
  744. if node:
  745. for i in range(node.GetChildCount()):
  746. generate_geometry_list_from_hierarchy(node.GetChild(i), geometry_list)
  747. return geometry_list
  748. # #####################################################
  749. # Generate - Camera Names
  750. # #####################################################
  751. def generate_camera_name_list_from_hierarchy(node, camera_list):
  752. if node.GetNodeAttribute() == None:
  753. pass
  754. else:
  755. attribute_type = (node.GetNodeAttribute().GetAttributeType())
  756. if attribute_type == FbxNodeAttribute.eCamera:
  757. camera_string = getObjectName(node)
  758. camera_list.append(camera_string)
  759. for i in range(node.GetChildCount()):
  760. generate_camera_name_list_from_hierarchy(node.GetChild(i), camera_list)
  761. def generate_camera_name_list(scene):
  762. camera_list = []
  763. node = scene.GetRootNode()
  764. if node:
  765. for i in range(node.GetChildCount()):
  766. generate_camera_name_list_from_hierarchy(node.GetChild(i), camera_list)
  767. return camera_list
  768. # #####################################################
  769. # Generate - Light Object
  770. # #####################################################
  771. def generate_default_light_string(padding):
  772. direction = (1,1,1)
  773. color = (1,1,1)
  774. intensity = 80.0
  775. output = [
  776. '\t\t' + LabelString( 'default_light' ) + ' : {',
  777. ' "type" : "DirectionalLight",',
  778. ' "color" : ' + str(getHex(color)) + ',',
  779. ' "intensity" : ' + str(intensity/100.0) + ',',
  780. ' "direction" : ' + Vector3String( direction ) + ',',
  781. ' "target" : ' + LabelString( getObjectName( None ) ),
  782. ' }'
  783. ]
  784. return generateMultiLineString( output, '\n\t\t', padding )
  785. def generate_light_string(node, padding):
  786. light = node.GetNodeAttribute()
  787. light_types = ["point", "directional", "spot", "area", "volume"]
  788. light_type = light_types[light.LightType.Get()]
  789. transform = node.EvaluateLocalTransform()
  790. position = transform.GetT()
  791. output = []
  792. if light_type == "directional":
  793. output = [
  794. '\t\t' + LabelString( getObjectName( node ) ) + ' : {',
  795. ' "type" : "DirectionalLight",',
  796. ' "color" : ' + str(getHex(light.Color.Get())) + ',',
  797. ' "intensity" : ' + str(light.Intensity.Get()/100.0) + ',',
  798. ' "direction" : ' + Vector3String( position ) + ',',
  799. ' "target" : ' + LabelString( getObjectName( node.GetTarget() ) ) + ( ',' if node.GetChildCount() > 0 else '' )
  800. ]
  801. elif light_type == "point":
  802. output = [
  803. '\t\t' + LabelString( getObjectName( node ) ) + ' : {',
  804. ' "type" : "PointLight",',
  805. ' "color" : ' + str(getHex(light.Color.Get())) + ',',
  806. ' "intensity" : ' + str(light.Intensity.Get()/100.0) + ',',
  807. ' "position" : ' + Vector3String( position ) + ',',
  808. ' "distance" : ' + str(light.FarAttenuationEnd.Get()) + ( ',' if node.GetChildCount() > 0 else '' )
  809. ]
  810. elif light_type == "spot":
  811. output = [
  812. '\t\t' + LabelString( getObjectName( node ) ) + ' : {',
  813. ' "type" : "SpotLight",',
  814. ' "color" : ' + str(getHex(light.Color.Get())) + ',',
  815. ' "intensity" : ' + str(light.Intensity.Get()/100.0) + ',',
  816. ' "position" : ' + Vector3String( position ) + ',',
  817. ' "distance" : ' + str(light.FarAttenuationEnd.Get()) + ',',
  818. ' "angle" : ' + str(light.OuterAngle.Get()) + ',',
  819. ' "exponent" : ' + str(light.DecayType.Get()) + ',',
  820. ' "target" : ' + LabelString( getObjectName( node.GetTarget() ) ) + ( ',' if node.GetChildCount() > 0 else '' )
  821. ]
  822. return generateMultiLineString( output, '\n\t\t', padding )
  823. def generate_ambient_light_string(scene):
  824. scene_settings = scene.GetGlobalSettings()
  825. ambient_color = scene_settings.GetAmbientColor()
  826. ambient_color = (ambient_color.mRed, ambient_color.mGreen, ambient_color.mBlue)
  827. if ambient_color[0] == 0 and ambient_color[1] == 0 and ambient_color[2] == 0:
  828. return None
  829. class AmbientLight:
  830. def GetName(self):
  831. return "AmbientLight"
  832. node = AmbientLight()
  833. output = [
  834. '\t\t' + LabelString( getObjectName( node ) ) + ' : {',
  835. ' "type" : "AmbientLight",',
  836. ' "color" : ' + str(getHex(ambient_color)),
  837. '}'
  838. ]
  839. return generateMultiLineString( output, '\n\t\t', 0 )
  840. # #####################################################
  841. # Generate - Camera Object
  842. # #####################################################
  843. def generate_default_camera_string(padding):
  844. position = (100, 100, 100)
  845. near = 0.1
  846. far = 1000
  847. fov = 75
  848. output = [
  849. '\t\t' + LabelString( 'default_camera' ) + ' : {',
  850. ' "type" : "PerspectiveCamera",',
  851. ' "fov" : ' + str(fov) + ',',
  852. ' "near" : ' + str(near) + ',',
  853. ' "far" : ' + str(far) + ',',
  854. ' "position" : ' + Vector3String( position ),
  855. ' }'
  856. ]
  857. return generateMultiLineString( output, '\n\t\t', padding )
  858. def generate_camera_string(node, padding):
  859. camera = node.GetNodeAttribute()
  860. target_node = node.GetTarget()
  861. target = ""
  862. if target_node:
  863. transform = target.EvaluateLocalTransform()
  864. target = transform.GetT()
  865. else:
  866. target = camera.InterestPosition.Get()
  867. position = camera.Position.Get()
  868. projection_types = [ "perspective", "orthogonal" ]
  869. projection = projection_types[camera.ProjectionType.Get()]
  870. near = camera.NearPlane.Get()
  871. far = camera.FarPlane.Get()
  872. output = []
  873. if projection == "perspective":
  874. aspect = camera.PixelAspectRatio.Get()
  875. fov = camera.FieldOfView.Get()
  876. fov = 75
  877. far = 1000
  878. output = [
  879. '\t\t' + LabelString( getObjectName( node ) ) + ' : {',
  880. ' "type" : "PerspectiveCamera",',
  881. ' "fov" : ' + str(fov) + ',',
  882. ' "aspect" : ' + str(aspect) + ',',
  883. ' "near" : ' + str(near) + ',',
  884. ' "far" : ' + str(far) + ',',
  885. ' "position" : ' + Vector3String( position ) + ( ',' if node.GetChildCount() > 0 else '' )
  886. ]
  887. elif projection == "orthogonal":
  888. left = ""
  889. right = ""
  890. top = ""
  891. bottom = ""
  892. output = [
  893. '\t\t' + LabelString( getObjectName( node ) ) + ' : {',
  894. ' "type" : "OrthographicCamera",',
  895. ' "left" : ' + left + ',',
  896. ' "right" : ' + right + ',',
  897. ' "top" : ' + top + ',',
  898. ' "bottom" : ' + bottom + ',',
  899. ' "near" : ' + str(near) + ',',
  900. ' "far" : ' + str(far) + ',',
  901. ' "position" : ' + Vector3String( position ) + ( ',' if node.GetChildCount() > 0 else '' )
  902. ]
  903. return generateMultiLineString( output, '\n\t\t', padding )
  904. # #####################################################
  905. # Generate - Mesh Object
  906. # #####################################################
  907. def generate_mesh_object_string(node, padding):
  908. mesh = node.GetNodeAttribute()
  909. transform = node.EvaluateLocalTransform()
  910. position = transform.GetT()
  911. scale = transform.GetS()
  912. rotation = getRadians(transform.GetR())
  913. material_count = node.GetMaterialCount()
  914. material_name = ""
  915. if material_count > 0:
  916. material_names = []
  917. for l in range(mesh.GetLayerCount()):
  918. materials = mesh.GetLayer(l).GetMaterials()
  919. if materials:
  920. if materials.GetReferenceMode() == FbxLayerElement.eIndex:
  921. #Materials are in an undefined external table
  922. continue
  923. for i in range(material_count):
  924. material = node.GetMaterial(i)
  925. material_names.append( getMaterialName(material) )
  926. #If this mesh has more than one material, use a proxy material
  927. material_name = getMaterialName( node ) if material_count > 1 else material_names[0]
  928. output = [
  929. '\t\t' + LabelString( getObjectName( node ) ) + ' : {',
  930. ' "geometry" : ' + LabelString( getGeometryName( node ) ) + ',',
  931. ' "material" : ' + LabelString( material_name ) + ',',
  932. ' "position" : ' + Vector3String( position ) + ',',
  933. ' "rotation" : ' + Vector3String( rotation ) + ',',
  934. ' "scale" : ' + Vector3String( scale ) + ',',
  935. ' "visible" : ' + getObjectVisible( node ) + ( ',' if node.GetChildCount() > 0 else '' )
  936. ]
  937. return generateMultiLineString( output, '\n\t\t', padding )
  938. # #####################################################
  939. # Generate - Object
  940. # #####################################################
  941. def generate_object_string(node, padding):
  942. node_types = ["Unknown", "Null", "Marker", "Skeleton", "Mesh", "Nurbs", "Patch", "Camera",
  943. "CameraStereo", "CameraSwitcher", "Light", "OpticalReference", "OpticalMarker", "NurbsCurve",
  944. "TrimNurbsSurface", "Boundary", "NurbsSurface", "Shape", "LODGroup", "SubDiv", "CachedEffect", "Line"]
  945. transform = node.EvaluateLocalTransform()
  946. position = transform.GetT()
  947. scale = transform.GetS()
  948. rotation = getRadians(transform.GetR())
  949. node_type = ""
  950. if node.GetNodeAttribute() == None:
  951. node_type = "Null"
  952. else:
  953. node_type = node_types[node.GetNodeAttribute().GetAttributeType()]
  954. output = [
  955. '\t\t' + LabelString( getObjectName( node ) ) + ' : {',
  956. ' "fbx_type" : ' + LabelString( node_type ) + ',',
  957. ' "position" : ' + Vector3String( position ) + ',',
  958. ' "rotation" : ' + Vector3String( rotation ) + ',',
  959. ' "scale" : ' + Vector3String( scale ) + ',',
  960. ' "visible" : ' + getObjectVisible( node ) + ( ',' if node.GetChildCount() > 0 else '' )
  961. ]
  962. return generateMultiLineString( output, '\n\t\t', padding )
  963. # #####################################################
  964. # Parse - Objects
  965. # #####################################################
  966. def generate_object_hierarchy(node, object_list, pad, siblings_left):
  967. object_count = 0
  968. if node.GetNodeAttribute() == None:
  969. object_string = generate_object_string(node, pad)
  970. object_list.append(object_string)
  971. object_count += 1
  972. else:
  973. attribute_type = (node.GetNodeAttribute().GetAttributeType())
  974. if attribute_type == FbxNodeAttribute.eMesh:
  975. object_string = generate_mesh_object_string(node, pad)
  976. object_list.append(object_string)
  977. object_count += 1
  978. elif attribute_type == FbxNodeAttribute.eLight:
  979. object_string = generate_light_string(node, pad)
  980. object_list.append(object_string)
  981. object_count += 1
  982. elif attribute_type == FbxNodeAttribute.eCamera:
  983. object_string = generate_camera_string(node, pad)
  984. object_list.append(object_string)
  985. object_count += 1
  986. else:
  987. object_string = generate_object_string(node, pad)
  988. object_list.append(object_string)
  989. object_count += 1
  990. if node.GetChildCount() > 0:
  991. object_list.append( PaddingString( pad + 1 ) + '\t\t"children" : {\n' )
  992. for i in range(node.GetChildCount()):
  993. object_count += generate_object_hierarchy(node.GetChild(i), object_list, pad + 2, node.GetChildCount() - i - 1)
  994. object_list.append( PaddingString( pad + 1 ) + '\t\t}' )
  995. object_list.append( PaddingString( pad ) + '\t\t}' + (',\n' if siblings_left > 0 else ''))
  996. return object_count
  997. def generate_scene_objects_string(scene):
  998. object_count = 0
  999. object_list = []
  1000. ambient_light = generate_ambient_light_string(scene)
  1001. if ambient_light:
  1002. if scene.GetNodeCount() > 0 or option_default_light or option_default_camera:
  1003. ambient_light += (',\n')
  1004. object_list.append(ambient_light)
  1005. object_count += 1
  1006. if option_default_light:
  1007. default_light = generate_default_light_string(0)
  1008. if scene.GetNodeCount() > 0 or option_default_camera:
  1009. default_light += (',\n')
  1010. object_list.append(default_light)
  1011. object_count += 1
  1012. if option_default_camera:
  1013. default_camera = generate_default_camera_string(0)
  1014. if scene.GetNodeCount() > 0:
  1015. default_camera += (',\n')
  1016. object_list.append(default_camera)
  1017. object_count += 1
  1018. node = scene.GetRootNode()
  1019. if node:
  1020. for i in range(node.GetChildCount()):
  1021. object_count += generate_object_hierarchy(node.GetChild(i), object_list, 0, node.GetChildCount() - i - 1)
  1022. return "\n".join(object_list), object_count
  1023. # #####################################################
  1024. # Parse - Scene
  1025. # #####################################################
  1026. def extract_scene(scene, filename):
  1027. objects, nobjects = generate_scene_objects_string(scene)
  1028. textures = generate_texture_list(scene)
  1029. materials = generate_material_list(scene)
  1030. geometries = generate_geometry_list(scene)
  1031. embeds = generate_embed_list(scene)
  1032. fogs = []
  1033. ntextures = len(textures)
  1034. nmaterials = len(materials)
  1035. ngeometries = len(geometries)
  1036. position = Vector3String( (0,0,0) )
  1037. rotation = Vector3String( (0,0,0) )
  1038. scale = Vector3String( (1,1,1) )
  1039. camera_names = generate_camera_name_list(scene)
  1040. scene_settings = scene.GetGlobalSettings()
  1041. bgcolor = Vector3String( (0.667,0.667,0.667) )
  1042. bgalpha = 1
  1043. defcamera = LabelString(camera_names[0] if len(camera_names) > 0 else "")
  1044. if option_default_camera:
  1045. defcamera = LabelString('default_camera')
  1046. #TODO: extract fog info from scene
  1047. deffog = LabelString("")
  1048. geometries = generateMultiLineString( geometries, ",\n\n\t", 0 )
  1049. materials = generateMultiLineString( materials, ",\n\n\t", 0 )
  1050. textures = generateMultiLineString( textures, ",\n\n\t", 0 )
  1051. embeds = generateMultiLineString( embeds, ",\n\n\t", 0 )
  1052. fogs = generateMultiLineString( fogs, ",\n\n\t", 0 )
  1053. output = [
  1054. '{',
  1055. ' "metadata": {',
  1056. ' "formatVersion" : 3.2,',
  1057. ' "type" : "scene",',
  1058. ' "generatedBy" : "convert-to-threejs.py",',
  1059. ' "objects" : ' + str(nobjects) + ',',
  1060. ' "geometries" : ' + str(ngeometries) + ',',
  1061. ' "materials" : ' + str(nmaterials) + ',',
  1062. ' "textures" : ' + str(ntextures),
  1063. ' },',
  1064. '',
  1065. ' "urlBaseType": "relativeToScene",',
  1066. '',
  1067. ' "objects" :',
  1068. ' {',
  1069. objects,
  1070. ' },',
  1071. '',
  1072. ' "geometries" :',
  1073. ' {',
  1074. '\t' + geometries,
  1075. ' },',
  1076. '',
  1077. ' "materials" :',
  1078. ' {',
  1079. '\t' + materials,
  1080. ' },',
  1081. '',
  1082. ' "textures" :',
  1083. ' {',
  1084. '\t' + textures,
  1085. ' },',
  1086. '',
  1087. ' "embeds" :',
  1088. ' {',
  1089. '\t' + embeds,
  1090. ' },',
  1091. '',
  1092. ' "fogs" :',
  1093. ' {',
  1094. '\t' + fogs,
  1095. ' },',
  1096. '',
  1097. ' "transform" :',
  1098. ' {',
  1099. ' "position" : ' + position + ',',
  1100. ' "rotation" : ' + rotation + ',',
  1101. ' "scale" : ' + scale,
  1102. ' },',
  1103. '',
  1104. ' "defaults" :',
  1105. ' {',
  1106. ' "bgcolor" : ' + str(bgcolor) + ',',
  1107. ' "bgalpha" : ' + str(bgalpha) + ',',
  1108. ' "camera" : ' + defcamera + ',',
  1109. ' "fog" : ' + deffog,
  1110. ' }',
  1111. '}'
  1112. ]
  1113. return "\n".join(output)
  1114. # #####################################################
  1115. # file helpers
  1116. # #####################################################
  1117. def write_file(fname, content):
  1118. out = open(fname, "w")
  1119. out.write(content)
  1120. out.close()
  1121. # #####################################################
  1122. # main
  1123. # #####################################################
  1124. if __name__ == "__main__":
  1125. from optparse import OptionParser
  1126. try:
  1127. from FbxCommon import *
  1128. except ImportError:
  1129. import platform
  1130. msg = 'Could not locate the python FBX SDK!\n'
  1131. msg += 'You need to copy the FBX SDK into your python install folder such as '
  1132. if platform.system() == 'Windows' or platform.system() == 'Microsoft':
  1133. msg += '"Python26/Lib/site-packages"'
  1134. elif platform.system() == 'Linux':
  1135. msg += '"/usr/local/lib/python2.6/site-packages"'
  1136. elif platform.system() == 'Darwin':
  1137. msg += '"/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages"'
  1138. msg += ' folder.'
  1139. print(msg)
  1140. sys.exit(1)
  1141. usage = "Usage: %prog [source_file.fbx] [output_file.js] [options]"
  1142. parser = OptionParser(usage=usage)
  1143. parser.add_option('-t', '--triangulate', action='store_true', dest='triangulate', help="force quad geometry into triangles", default=False)
  1144. parser.add_option('-x', '--no-textures', action='store_true', dest='notextures', help="don't include texture references in output file", default=False)
  1145. parser.add_option('-p', '--no-prefix', action='store_true', dest='noprefix', help="don't prefix object names in output file", default=False)
  1146. parser.add_option('-g', '--geometry-only', action='store_true', dest='geometry', help="output geometry only", default=False)
  1147. parser.add_option('-c', '--default-camera', action='store_true', dest='defcamera', help="include default camera in output scene", default=False)
  1148. parser.add_option('-l', '--defualt-light', action='store_true', dest='deflight', help="include default light in output scene", default=False)
  1149. (options, args) = parser.parse_args()
  1150. option_triangulate = options.triangulate
  1151. option_textures = True if not options.notextures else False
  1152. option_prefix = True if not options.noprefix else False
  1153. option_geometry = options.geometry
  1154. option_default_camera = options.defcamera
  1155. option_default_light = options.deflight
  1156. # Prepare the FBX SDK.
  1157. sdk_manager, scene = InitializeSdkObjects()
  1158. converter = FbxGeometryConverter(sdk_manager)
  1159. # The converter takes an FBX file as an argument.
  1160. if len(args) > 1:
  1161. print("\nLoading file: %s" % args[0])
  1162. result = LoadScene(sdk_manager, scene, args[0])
  1163. else:
  1164. result = False
  1165. print("\nUsage: convert_fbx_to_threejs [source_file.fbx] [output_file.js]\n")
  1166. if not result:
  1167. print("\nAn error occurred while loading the file...")
  1168. else:
  1169. if option_triangulate:
  1170. print("\nForcing geometry to triangles")
  1171. triangulate_scene(scene)
  1172. output_content = extract_scene(scene, os.path.basename(args[0]))
  1173. output_path = os.path.join(os.getcwd(), args[1])
  1174. write_file(output_path, output_content)
  1175. print("\nExported Three.js file to:\n%s\n" % output_path)
  1176. # SaveScene(sdk_manager, scene, args[2], 8)
  1177. # Destroy all objects created by the FBX SDK.
  1178. sdk_manager.Destroy()
  1179. sys.exit(0)