convert_to_threejs.py 54 KB

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