convert_to_threejs.py 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458
  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.FieldOfViewY.Get()
  876. output = [
  877. '\t\t' + LabelString( getObjectName( node ) ) + ' : {',
  878. ' "type" : "PerspectiveCamera",',
  879. ' "fov" : ' + str(fov) + ',',
  880. ' "aspect" : ' + str(aspect) + ',',
  881. ' "near" : ' + str(near) + ',',
  882. ' "far" : ' + str(far) + ',',
  883. ' "position" : ' + Vector3String( position ) + ( ',' if node.GetChildCount() > 0 else '' )
  884. ]
  885. elif projection == "orthogonal":
  886. left = ""
  887. right = ""
  888. top = ""
  889. bottom = ""
  890. output = [
  891. '\t\t' + LabelString( getObjectName( node ) ) + ' : {',
  892. ' "type" : "OrthographicCamera",',
  893. ' "left" : ' + left + ',',
  894. ' "right" : ' + right + ',',
  895. ' "top" : ' + top + ',',
  896. ' "bottom" : ' + bottom + ',',
  897. ' "near" : ' + str(near) + ',',
  898. ' "far" : ' + str(far) + ',',
  899. ' "position" : ' + Vector3String( position ) + ( ',' if node.GetChildCount() > 0 else '' )
  900. ]
  901. return generateMultiLineString( output, '\n\t\t', padding )
  902. # #####################################################
  903. # Generate - Mesh Object
  904. # #####################################################
  905. def generate_mesh_object_string(node, padding):
  906. mesh = node.GetNodeAttribute()
  907. transform = node.EvaluateLocalTransform()
  908. position = transform.GetT()
  909. scale = transform.GetS()
  910. rotation = getRadians(transform.GetR())
  911. material_count = node.GetMaterialCount()
  912. material_name = ""
  913. if material_count > 0:
  914. material_names = []
  915. for l in range(mesh.GetLayerCount()):
  916. materials = mesh.GetLayer(l).GetMaterials()
  917. if materials:
  918. if materials.GetReferenceMode() == FbxLayerElement.eIndex:
  919. #Materials are in an undefined external table
  920. continue
  921. for i in range(material_count):
  922. material = node.GetMaterial(i)
  923. material_names.append( getMaterialName(material) )
  924. #If this mesh has more than one material, use a proxy material
  925. material_name = getMaterialName( node ) if material_count > 1 else material_names[0]
  926. output = [
  927. '\t\t' + LabelString( getObjectName( node ) ) + ' : {',
  928. ' "geometry" : ' + LabelString( getGeometryName( node ) ) + ',',
  929. ' "material" : ' + LabelString( material_name ) + ',',
  930. ' "position" : ' + Vector3String( position ) + ',',
  931. ' "rotation" : ' + Vector3String( rotation ) + ',',
  932. ' "scale" : ' + Vector3String( scale ) + ',',
  933. ' "visible" : ' + getObjectVisible( node ) + ( ',' if node.GetChildCount() > 0 else '' )
  934. ]
  935. return generateMultiLineString( output, '\n\t\t', padding )
  936. # #####################################################
  937. # Generate - Object
  938. # #####################################################
  939. def generate_object_string(node, padding):
  940. node_types = ["Unknown", "Null", "Marker", "Skeleton", "Mesh", "Nurbs", "Patch", "Camera",
  941. "CameraStereo", "CameraSwitcher", "Light", "OpticalReference", "OpticalMarker", "NurbsCurve",
  942. "TrimNurbsSurface", "Boundary", "NurbsSurface", "Shape", "LODGroup", "SubDiv", "CachedEffect", "Line"]
  943. transform = node.EvaluateLocalTransform()
  944. position = transform.GetT()
  945. scale = transform.GetS()
  946. rotation = getRadians(transform.GetR())
  947. node_type = ""
  948. if node.GetNodeAttribute() == None:
  949. node_type = "Null"
  950. else:
  951. node_type = node_types[node.GetNodeAttribute().GetAttributeType()]
  952. output = [
  953. '\t\t' + LabelString( getObjectName( node ) ) + ' : {',
  954. ' "fbx_type" : ' + LabelString( node_type ) + ',',
  955. ' "position" : ' + Vector3String( position ) + ',',
  956. ' "rotation" : ' + Vector3String( rotation ) + ',',
  957. ' "scale" : ' + Vector3String( scale ) + ',',
  958. ' "visible" : ' + getObjectVisible( node ) + ( ',' if node.GetChildCount() > 0 else '' )
  959. ]
  960. return generateMultiLineString( output, '\n\t\t', padding )
  961. # #####################################################
  962. # Parse - Objects
  963. # #####################################################
  964. def generate_object_hierarchy(node, object_list, pad, siblings_left):
  965. object_count = 0
  966. if node.GetNodeAttribute() == None:
  967. object_string = generate_object_string(node, pad)
  968. object_list.append(object_string)
  969. object_count += 1
  970. else:
  971. attribute_type = (node.GetNodeAttribute().GetAttributeType())
  972. if attribute_type == FbxNodeAttribute.eMesh:
  973. object_string = generate_mesh_object_string(node, pad)
  974. object_list.append(object_string)
  975. object_count += 1
  976. elif attribute_type == FbxNodeAttribute.eLight:
  977. object_string = generate_light_string(node, pad)
  978. object_list.append(object_string)
  979. object_count += 1
  980. elif attribute_type == FbxNodeAttribute.eCamera:
  981. object_string = generate_camera_string(node, pad)
  982. object_list.append(object_string)
  983. object_count += 1
  984. else:
  985. object_string = generate_object_string(node, pad)
  986. object_list.append(object_string)
  987. object_count += 1
  988. if node.GetChildCount() > 0:
  989. object_list.append( PaddingString( pad + 1 ) + '\t\t"children" : {\n' )
  990. for i in range(node.GetChildCount()):
  991. object_count += generate_object_hierarchy(node.GetChild(i), object_list, pad + 2, node.GetChildCount() - i - 1)
  992. object_list.append( PaddingString( pad + 1 ) + '\t\t}' )
  993. object_list.append( PaddingString( pad ) + '\t\t}' + (',\n' if siblings_left > 0 else ''))
  994. return object_count
  995. def generate_scene_objects_string(scene):
  996. object_count = 0
  997. object_list = []
  998. ambient_light = generate_ambient_light_string(scene)
  999. if ambient_light:
  1000. if scene.GetNodeCount() > 0 or option_default_light or option_default_camera:
  1001. ambient_light += (',\n')
  1002. object_list.append(ambient_light)
  1003. object_count += 1
  1004. if option_default_light:
  1005. default_light = generate_default_light_string(0)
  1006. if scene.GetNodeCount() > 0 or option_default_camera:
  1007. default_light += (',\n')
  1008. object_list.append(default_light)
  1009. object_count += 1
  1010. if option_default_camera:
  1011. default_camera = generate_default_camera_string(0)
  1012. if scene.GetNodeCount() > 0:
  1013. default_camera += (',\n')
  1014. object_list.append(default_camera)
  1015. object_count += 1
  1016. node = scene.GetRootNode()
  1017. if node:
  1018. for i in range(node.GetChildCount()):
  1019. object_count += generate_object_hierarchy(node.GetChild(i), object_list, 0, node.GetChildCount() - i - 1)
  1020. return "\n".join(object_list), object_count
  1021. # #####################################################
  1022. # Parse - Scene
  1023. # #####################################################
  1024. def extract_scene(scene, filename):
  1025. objects, nobjects = generate_scene_objects_string(scene)
  1026. textures = generate_texture_list(scene)
  1027. materials = generate_material_list(scene)
  1028. geometries = generate_geometry_list(scene)
  1029. embeds = generate_embed_list(scene)
  1030. fogs = []
  1031. ntextures = len(textures)
  1032. nmaterials = len(materials)
  1033. ngeometries = len(geometries)
  1034. position = Vector3String( (0,0,0) )
  1035. rotation = Vector3String( (0,0,0) )
  1036. scale = Vector3String( (1,1,1) )
  1037. camera_names = generate_camera_name_list(scene)
  1038. scene_settings = scene.GetGlobalSettings()
  1039. bgcolor = Vector3String( (0.667,0.667,0.667) )
  1040. bgalpha = 1
  1041. defcamera = LabelString(camera_names[0] if len(camera_names) > 0 else "")
  1042. if option_default_camera:
  1043. defcamera = LabelString('default_camera')
  1044. #TODO: extract fog info from scene
  1045. deffog = LabelString("")
  1046. geometries = generateMultiLineString( geometries, ",\n\n\t", 0 )
  1047. materials = generateMultiLineString( materials, ",\n\n\t", 0 )
  1048. textures = generateMultiLineString( textures, ",\n\n\t", 0 )
  1049. embeds = generateMultiLineString( embeds, ",\n\n\t", 0 )
  1050. fogs = generateMultiLineString( fogs, ",\n\n\t", 0 )
  1051. output = [
  1052. '{',
  1053. ' "metadata": {',
  1054. ' "formatVersion" : 3.2,',
  1055. ' "type" : "scene",',
  1056. ' "generatedBy" : "convert-to-threejs.py",',
  1057. ' "objects" : ' + str(nobjects) + ',',
  1058. ' "geometries" : ' + str(ngeometries) + ',',
  1059. ' "materials" : ' + str(nmaterials) + ',',
  1060. ' "textures" : ' + str(ntextures),
  1061. ' },',
  1062. '',
  1063. ' "urlBaseType": "relativeToScene",',
  1064. '',
  1065. ' "objects" :',
  1066. ' {',
  1067. objects,
  1068. ' },',
  1069. '',
  1070. ' "geometries" :',
  1071. ' {',
  1072. '\t' + geometries,
  1073. ' },',
  1074. '',
  1075. ' "materials" :',
  1076. ' {',
  1077. '\t' + materials,
  1078. ' },',
  1079. '',
  1080. ' "textures" :',
  1081. ' {',
  1082. '\t' + textures,
  1083. ' },',
  1084. '',
  1085. ' "embeds" :',
  1086. ' {',
  1087. '\t' + embeds,
  1088. ' },',
  1089. '',
  1090. ' "fogs" :',
  1091. ' {',
  1092. '\t' + fogs,
  1093. ' },',
  1094. '',
  1095. ' "transform" :',
  1096. ' {',
  1097. ' "position" : ' + position + ',',
  1098. ' "rotation" : ' + rotation + ',',
  1099. ' "scale" : ' + scale,
  1100. ' },',
  1101. '',
  1102. ' "defaults" :',
  1103. ' {',
  1104. ' "bgcolor" : ' + str(bgcolor) + ',',
  1105. ' "bgalpha" : ' + str(bgalpha) + ',',
  1106. ' "camera" : ' + defcamera + ',',
  1107. ' "fog" : ' + deffog,
  1108. ' }',
  1109. '}'
  1110. ]
  1111. return "\n".join(output)
  1112. # #####################################################
  1113. # file helpers
  1114. # #####################################################
  1115. def write_file(fname, content):
  1116. out = open(fname, "w")
  1117. out.write(content)
  1118. out.close()
  1119. # #####################################################
  1120. # main
  1121. # #####################################################
  1122. if __name__ == "__main__":
  1123. from optparse import OptionParser
  1124. try:
  1125. from FbxCommon import *
  1126. except ImportError:
  1127. import platform
  1128. msg = 'Could not locate the python FBX SDK!\n'
  1129. msg += 'You need to copy the FBX SDK into your python install folder such as '
  1130. if platform.system() == 'Windows' or platform.system() == 'Microsoft':
  1131. msg += '"Python26/Lib/site-packages"'
  1132. elif platform.system() == 'Linux':
  1133. msg += '"/usr/local/lib/python2.6/site-packages"'
  1134. elif platform.system() == 'Darwin':
  1135. msg += '"/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages"'
  1136. msg += ' folder.'
  1137. print(msg)
  1138. sys.exit(1)
  1139. usage = "Usage: %prog [source_file.fbx] [output_file.js] [options]"
  1140. parser = OptionParser(usage=usage)
  1141. parser.add_option('-t', '--triangulate', action='store_true', dest='triangulate', help="force quad geometry into triangles", default=False)
  1142. parser.add_option('-x', '--no-textures', action='store_true', dest='notextures', help="don't include texture references in output file", default=False)
  1143. parser.add_option('-p', '--no-prefix', action='store_true', dest='noprefix', help="don't prefix object names in output file", default=False)
  1144. parser.add_option('-g', '--geometry-only', action='store_true', dest='geometry', help="output geometry only", default=False)
  1145. parser.add_option('-c', '--default-camera', action='store_true', dest='defcamera', help="include default camera in output scene", default=False)
  1146. parser.add_option('-l', '--defualt-light', action='store_true', dest='deflight', help="include default light in output scene", default=False)
  1147. (options, args) = parser.parse_args()
  1148. option_triangulate = options.triangulate
  1149. option_textures = True if not options.notextures else False
  1150. option_prefix = True if not options.noprefix else False
  1151. option_geometry = options.geometry
  1152. option_default_camera = options.defcamera
  1153. option_default_light = options.deflight
  1154. # Prepare the FBX SDK.
  1155. sdk_manager, scene = InitializeSdkObjects()
  1156. converter = FbxGeometryConverter(sdk_manager)
  1157. # The converter takes an FBX file as an argument.
  1158. if len(args) > 1:
  1159. print("\nLoading file: %s" % args[0])
  1160. result = LoadScene(sdk_manager, scene, args[0])
  1161. else:
  1162. result = False
  1163. print("\nUsage: convert_fbx_to_threejs [source_file.fbx] [output_file.js]\n")
  1164. if not result:
  1165. print("\nAn error occurred while loading the file...")
  1166. else:
  1167. if option_triangulate:
  1168. print("\nForcing geometry to triangles")
  1169. triangulate_scene(scene)
  1170. output_content = extract_scene(scene, os.path.basename(args[0]))
  1171. output_path = os.path.join(os.getcwd(), args[1])
  1172. write_file(output_path, output_content)
  1173. print("\nExported Three.js file to:\n%s\n" % output_path)
  1174. # SaveScene(sdk_manager, scene, args[2], 8)
  1175. # Destroy all objects created by the FBX SDK.
  1176. sdk_manager.Destroy()
  1177. sys.exit(0)