convert_fbx_three.py 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674
  1. """FBX to Three.js converter (based on FBX import example from Autodesk FBX SDK)
  2. Converts FBX scene file into Three.js scene format (one scene file and several ascii model files plus textures).
  3. -------------------------
  4. How to use this converter
  5. -------------------------
  6. python convert_fbx_three.py scene.fbx output_folder
  7. ------------
  8. Dependencies
  9. ------------
  10. Requires Autodesk FBX SDK Python bindings.
  11. If your platform is not included in "modules", full SDK can be downloaded from here:
  12. http://usa.autodesk.com/adsk/servlet/pc/index?siteID=123112&id=6837478
  13. ---------------------------
  14. How to load generated scene
  15. ---------------------------
  16. <script type="text/javascript" src="ThreeExtras.js"></script>
  17. <script type="text/javascript">
  18. ...
  19. SCENE_URL = "http://example.com/output_folder/scene.js";
  20. SceneUtils.loadScene( SCENE_URL, callback_sync, callback_async, callback_progress );
  21. ...
  22. /*
  23. callback_sync - called after the scene file is loaded (procedural elements like cubes and spheres or materials without textures are now ready)
  24. callback_progress - called after each asynchronous elements gets loaded (textures and model files)
  25. callback_async - called after all asynchronous elements (models and textures) are loaded, now whole scene is ready
  26. Scene is fully created, just needs to be passed to renderer, together with camera.
  27. Individual scene elements are also returned for convenient access via hashmap (addressable by their id).
  28. progress = {
  29. total_models: X,
  30. total_textures: Y,
  31. loaded_models: A,
  32. loaded_textures: B
  33. };
  34. result = {
  35. scene: new THREE.Scene(),
  36. geometries: { ... },
  37. materials: { ... },
  38. textures: { ... },
  39. objects: { ... },
  40. cameras: { ... },
  41. lights: { ... },
  42. fogs: { ... },
  43. currentCamera: THREE.Camera( ... )
  44. };
  45. */
  46. var callback_progress = function( progress, result ) { ... };
  47. var callback_sync = function( result ) { ... };
  48. var callback_async = function( result ) { ... };
  49. };
  50. </script>
  51. --------
  52. Features
  53. --------
  54. - scene + models + materials + textures
  55. - multiple UV layers
  56. -------------------
  57. Current limitations
  58. -------------------
  59. - only very small subset from FBX is currently supported
  60. - static meshes
  61. - triangles and quads
  62. - max two UV layers
  63. - Lambert and Phong materials (partial: Three doesn't support emissive color)
  64. - no lights yet
  65. - no cameras yet (default one is created instead)
  66. ------
  67. Author
  68. ------
  69. AlteredQualia http://alteredqualia.com
  70. """
  71. import os
  72. import sys
  73. import random
  74. import math
  75. import pprint
  76. import shutil
  77. import string
  78. import os.path
  79. # load platform specific binary modules
  80. platform_map = {
  81. "Windows" :
  82. {
  83. "folder": "win",
  84. "versions" : {
  85. 2 : "Python26_x86",
  86. 3 : "Python31_x86"
  87. }
  88. },
  89. "Linux" :
  90. {
  91. "folder": "linux",
  92. "versions" : {
  93. 2 : "Python26_x86",
  94. 3 : "Python31_x86"
  95. }
  96. },
  97. "Darwin" :
  98. {
  99. "folder": "mac",
  100. "versions" : {
  101. 2 : "Python26_x86",
  102. 3 : "Python31_x86"
  103. }
  104. }
  105. }
  106. import platform
  107. system = platform.system()
  108. version = sys.version_info[0]
  109. if system in platform_map:
  110. if version in platform_map[system]["versions"]:
  111. mod_path = os.path.join(sys.path[0], "modules", platform_map[system]["folder"], platform_map[system]["versions"][version])
  112. print mod_path
  113. sys.path.append(mod_path)
  114. # #####################################################
  115. # Configuration
  116. # #####################################################
  117. DEFAULTS = {
  118. "bgcolor" : [0, 0, 0],
  119. "bgalpha" : 1.0,
  120. "camera" :
  121. {
  122. "name" : "default_camera",
  123. "type" : "perspective",
  124. "near" : 1,
  125. "far" : 10000,
  126. "fov" : 60,
  127. "aspect": 1.333,
  128. "position" : [0, 0, 10],
  129. "target" : [0, 0, 0]
  130. }
  131. }
  132. MATERIALS_IN_SCENE = True
  133. DEBUG_FBX_JSON = True
  134. # default colors for debugging (each material gets one distinct color):
  135. # white, red, green, blue, yellow, cyan, magenta
  136. COLORS = [0xeeeeee, 0xee0000, 0x00ee00, 0x0000ee, 0xeeee00, 0x00eeee, 0xee00ee]
  137. # #####################################################
  138. # Templates - scene
  139. # #####################################################
  140. TEMPLATE_SCENE_ASCII = u"""\
  141. // Converted from: %(fname)s
  142. // Generated with FBX -> Three.js converter
  143. // http://github.com/alteredq/three.js/blob/master/utils/exporters/fbx/convert_fbx_three.py
  144. var url_base = %(url_base)s,
  145. url_models = url_base + "models/",
  146. url_textures = url_base + "textures/";
  147. var scene = {
  148. %(sections)s
  149. "defaults" :
  150. {
  151. "bgcolor" : %(bgcolor)s,
  152. "bgalpha" : %(bgalpha)f,
  153. "camera" : %(defcamera)s
  154. }
  155. }
  156. postMessage( scene );
  157. """
  158. TEMPLATE_SECTION = """
  159. "%s" :
  160. {
  161. %s
  162. },
  163. """
  164. TEMPLATE_OBJECT = """\
  165. %(object_id)s : {
  166. "geometry" : %(geometry_id)s,
  167. "materials" : [ %(material_id)s ],
  168. "position" : %(position)s,
  169. "rotation" : %(rotation)s,
  170. "scale" : %(scale)s,
  171. "visible" : true
  172. }"""
  173. TEMPLATE_GEOMETRY = """\
  174. %(geometry_id)s : {
  175. "type" : "ascii_mesh",
  176. "url" : url_models + %(model_file)s
  177. }"""
  178. TEMPLATE_TEXTURE = """\
  179. %(texture_id)s : {
  180. "url": url_textures + %(texture_file)s
  181. }"""
  182. TEMPLATE_MATERIAL_SCENE = """\
  183. %(material_id)s : {
  184. "type": %(type)s,
  185. "parameters": { %(parameters)s }
  186. }"""
  187. TEMPLATE_CAMERA_PERSPECTIVE = """\
  188. %(camera_id)s : {
  189. "type" : "perspective",
  190. "fov" : %(fov)f,
  191. "aspect": %(aspect)f,
  192. "near" : %(near)f,
  193. "far" : %(far)f,
  194. "position": %(position)s,
  195. "target" : %(target)s
  196. }"""
  197. TEMPLATE_CAMERA_ORTHO = """\
  198. %(camera_id)s: {
  199. "type" : "ortho",
  200. "left" : %(left)f,
  201. "right" : %(right)f,
  202. "top" : %(top)f,
  203. "bottom": %(bottom)f,
  204. "near" : %(near)f,
  205. "far" : %(far)f,
  206. "position": %(position)s,
  207. "target" : %(target)s
  208. }"""
  209. TEMPLATE_VEC3 = '[ %f, %f, %f ]'
  210. TEMPLATE_VEC2 = '[ %f, %f ]'
  211. TEMPLATE_STRING = '"%s"'
  212. TEMPLATE_HEX = "0x%06x"
  213. # #####################################################
  214. # Templates - model
  215. # #####################################################
  216. TEMPLATE_MODEL_ASCII = u"""\
  217. // Converted from: %(fname)s
  218. // vertices: %(nvertex)d
  219. // faces: %(nface)d
  220. // materials: %(nmaterial)d
  221. //
  222. // Generated with FBX -> Three.js converter
  223. // http://github.com/alteredq/three.js/blob/master/utils/exporters/fbx/convert_fbx_three.py
  224. var model = {
  225. 'materials': [%(materials)s],
  226. 'normals': [%(normals)s],
  227. 'vertices': [%(vertices)s],
  228. 'uvs': [%(uvs)s],
  229. 'uvs2': [%(uvs2)s],
  230. 'triangles': [%(triangles)s],
  231. 'triangles_n': [%(triangles_n)s],
  232. 'triangles_uv': [%(triangles_uv)s],
  233. 'triangles_n_uv': [%(triangles_n_uv)s],
  234. 'quads': [%(quads)s],
  235. 'quads_n': [%(quads_n)s],
  236. 'quads_uv': [%(quads_uv)s],
  237. 'quads_n_uv': [%(quads_n_uv)s],
  238. 'end': (new Date).getTime()
  239. }
  240. postMessage( model );
  241. """
  242. TEMPLATE_VERTEX = "%f,%f,%f"
  243. TEMPLATE_UV_TRI = "%f,%f,%f,%f,%f,%f"
  244. TEMPLATE_UV_QUAD = "%f,%f,%f,%f,%f,%f,%f,%f"
  245. TEMPLATE_TRI = "%d,%d,%d,%d"
  246. TEMPLATE_QUAD = "%d,%d,%d,%d,%d"
  247. TEMPLATE_TRI_UV = "%d,%d,%d,%d,%d,%d,%d"
  248. TEMPLATE_QUAD_UV = "%d,%d,%d,%d,%d,%d,%d,%d,%d"
  249. TEMPLATE_TRI_N = "%d,%d,%d,%d,%d,%d,%d"
  250. TEMPLATE_QUAD_N = "%d,%d,%d,%d,%d,%d,%d,%d,%d"
  251. TEMPLATE_TRI_N_UV = "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d"
  252. TEMPLATE_QUAD_N_UV = "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d"
  253. TEMPLATE_N = "%f,%f,%f"
  254. TEMPLATE_UV = "%f,%f"
  255. # #####################################################
  256. # Templates - misc
  257. # #####################################################
  258. TEMPLATE_HTACCESS = """\
  259. <Files *.js>
  260. SetOutputFilter DEFLATE
  261. </Files>
  262. """
  263. # #####################################################
  264. # Parser - global settings
  265. # #####################################################
  266. def extract_color(color):
  267. return [color.mRed, color.mGreen, color.mBlue]
  268. def extract_vec2(v):
  269. return [v[0], v[1]]
  270. def extract_vec3(v):
  271. return [v[0], v[1], v[2]]
  272. def extract_global_settings(settings):
  273. settings = {
  274. "ambient_color" : extract_color(settings.GetAmbientColor()),
  275. "default_camera": settings.GetDefaultCamera().Buffer()
  276. }
  277. return settings
  278. def extract_object_properties(object):
  279. info = {}
  280. property = object.GetFirstProperty()
  281. while property.IsValid():
  282. name = property.GetName().Buffer()
  283. value = "UNIDENTIFIED"
  284. ptype = property.GetPropertyDataType().GetType()
  285. if ptype in [eBOOL1, eDOUBLE1, eINTEGER1, eDOUBLE4, eDOUBLE3, eFLOAT1]:
  286. value = property.Get()
  287. elif ptype == eSTRING:
  288. value = property.Get().Buffer()
  289. info[name] = {
  290. "label" : property.GetLabel().Buffer(),
  291. "type" : property.GetPropertyDataType().GetName(),
  292. "value" : value
  293. }
  294. property = object.GetNextProperty(property)
  295. return info
  296. def extract_node_generic_info(node):
  297. info = []
  298. node_info = {
  299. "name" : node.GetName(),
  300. "type" : node.ClassId.GetFbxFileTypeName(),
  301. "properties" : extract_object_properties(node)
  302. }
  303. info.append(node_info)
  304. for i in range(node.GetChildCount()):
  305. info += extract_node_generic_info(node.GetChild(i))
  306. return info
  307. def extract_generic_info(scene):
  308. info_list = []
  309. root_node = scene.GetRootNode()
  310. for i in range(root_node.GetChildCount()):
  311. child = root_node.GetChild(i)
  312. info_list += extract_node_generic_info(child)
  313. info_dict = {}
  314. for item in info_list:
  315. name = item["name"]
  316. info_dict[name] = item["properties"]
  317. return info_dict
  318. # #####################################################
  319. # Parser - hierarchy
  320. # #####################################################
  321. def extract_node_hierarchy(node, depth):
  322. hierarchy = { "name" : node.GetName(), "depth" : depth, "children" : [] }
  323. for i in range(node.GetChildCount()):
  324. hierarchy["children"].append(extract_node_hierarchy(node.GetChild(i), depth + 1))
  325. return hierarchy
  326. def extract_hierarchy(scene):
  327. root = scene.GetRootNode()
  328. hierarchy = { "name" : "root", "depth" : 0, "children" : [] }
  329. for i in range(root.GetChildCount()):
  330. hierarchy["children"].append(extract_node_hierarchy(root.GetChild(i), 1))
  331. return hierarchy
  332. # #####################################################
  333. # Parser - mesh - layers
  334. # #####################################################
  335. def extract_layer_groups(layer, poly_count):
  336. group = []
  337. polygroups = layer.GetPolygonGroups()
  338. if polygroups and \
  339. polygroups.GetMappingMode() == KFbxLayerElement.eBY_POLYGON and \
  340. polygroups.GetReferenceMode() == KFbxLayerElement.eINDEX:
  341. polygroups_index_array = polygroups.GetIndexArray()
  342. group = []
  343. for p in range(poly_count):
  344. group_id = polygroups_index_array.GetAt(p)
  345. group.append(group_id)
  346. return group
  347. def extract_layer_materials(layer, poly_count, mesh):
  348. material_index_layer = []
  349. materials = layer.GetMaterials()
  350. if materials:
  351. index_array = materials.GetIndexArray()
  352. index_array_count = index_array.GetCount()
  353. for i in range(index_array_count):
  354. material_index_layer.append(index_array.GetAt(i))
  355. return material_index_layer
  356. def extract_layer_uvs(layer, poly_count, mesh):
  357. uv_values = []
  358. uv_index_layer = []
  359. uvs = layer.GetUVs()
  360. if uvs:
  361. uvs_array = uvs.GetDirectArray()
  362. uvs_count = uvs_array.GetCount()
  363. # values
  364. for i in range(uvs_count):
  365. uv = extract_vec2(uvs_array.GetAt(i))
  366. uv_values.append(uv)
  367. # indices
  368. if uvs.GetMappingMode() == KFbxLayerElement.eBY_CONTROL_POINT \
  369. and uvs.GetReferenceMode() == KFbxLayerElement.eDIRECT:
  370. for p in range(poly_count):
  371. tmp = []
  372. polygon_size = mesh.GetPolygonSize(p)
  373. for v in range(polygon_size):
  374. id = mesh.GetPolygonVertex(p, v)
  375. tmp.append(id)
  376. uv_index_layer.append(tmp)
  377. elif uvs.GetMappingMode() == KFbxLayerElement.eBY_CONTROL_POINT \
  378. and uvs.GetReferenceMode() == KFbxLayerElement.eINDEX_TO_DIRECT:
  379. uvs_index_array = uvs.GetIndexArray()
  380. for p in range(poly_count):
  381. tmp = []
  382. polygon_size = mesh.GetPolygonSize(p)
  383. for v in range(polygon_size):
  384. control_point_index = mesh.GetPolygonVertex(p, v)
  385. id = uvs_index_array.GetAt(control_point_index)
  386. tmp.append(id)
  387. uv_index_layer.append(tmp)
  388. elif uvs.GetMappingMode() == KFbxLayerElement.eBY_POLYGON_VERTEX \
  389. and ( uvs.GetReferenceMode() == KFbxLayerElement.eDIRECT \
  390. or uvs.GetReferenceMode() == KFbxLayerElement.eINDEX_TO_DIRECT ):
  391. for p in range(poly_count):
  392. tmp = []
  393. polygon_size = mesh.GetPolygonSize(p)
  394. for v in range(polygon_size):
  395. id = mesh.GetTextureUVIndex(p, v)
  396. tmp.append(id)
  397. uv_index_layer.append(tmp)
  398. return uv_values, uv_index_layer
  399. def extract_layer_colors(layer, poly_count, mesh):
  400. color_values = []
  401. color_index_layer = []
  402. colors = layer.GetVertexColors()
  403. if colors:
  404. colors_array = colors.GetDirectArray()
  405. colors_count = colors_array.GetCount()
  406. # values
  407. tmp = []
  408. for i in range(colors_count):
  409. color = extract_color(colors_array.GetAt(i))
  410. color_values.append(color)
  411. # indices
  412. if colors.GetMappingMode() == KFbxLayerElement.eBY_CONTROL_POINT \
  413. and colors.GetReferenceMode() == KFbxLayerElement.eDIRECT:
  414. for p in range(poly_count):
  415. tmp = []
  416. polygon_size = mesh.GetPolygonSize(p)
  417. for v in range(polygon_size):
  418. id = mesh.GetPolygonVertex(p, v)
  419. tmp.append(id)
  420. color_index_layer.append(tmp)
  421. elif colors.GetMappingMode() == KFbxLayerElement.eBY_CONTROL_POINT \
  422. and colors.GetReferenceMode() == KFbxLayerElement.eINDEX_TO_DIRECT:
  423. for p in range(poly_count):
  424. tmp = []
  425. polygon_size = mesh.GetPolygonSize(p)
  426. for v in range(polygon_size):
  427. control_point_index = mesh.GetPolygonVertex(p, v)
  428. id = colors.GetIndexArray().GetAt(control_point_index)
  429. tmp.append(id)
  430. color_index_layer.append(tmp)
  431. elif colors.GetMappingMode() == KFbxLayerElement.eBY_POLYGON_VERTEX \
  432. and colors.GetReferenceMode() == KFbxLayerElement.eDIRECT:
  433. vertex_id = 0
  434. for p in range(poly_count):
  435. tmp = []
  436. polygon_size = mesh.GetPolygonSize(p)
  437. for v in range(polygon_size):
  438. tmp.append(vertex_id)
  439. vertex_id += 1
  440. color_index_layer.append(tmp)
  441. elif colors.GetMappingMode() == KFbxLayerElement.eBY_POLYGON_VERTEX \
  442. and colors.GetReferenceMode() == KFbxLayerElement.eINDEX_TO_DIRECT:
  443. colors_index_array = colors.GetIndexArray()
  444. vertex_id = 0
  445. for p in range(poly_count):
  446. tmp = []
  447. polygon_size = mesh.GetPolygonSize(p)
  448. for v in range(polygon_size):
  449. id = colors_index_array.GetAt(vertex_id)
  450. tmp.append(id)
  451. vertex_id += 1
  452. color_index_layer.append(tmp)
  453. return color_values, color_index_layer
  454. # #####################################################
  455. # Parser - mesh - polygons
  456. # #####################################################
  457. def extract_polygons(mesh):
  458. poly_count = mesh.GetPolygonCount()
  459. layer_count = mesh.GetLayerCount()
  460. control_points = mesh.GetControlPoints()
  461. layers_groups = []
  462. layers_uvs = []
  463. layers_colors = []
  464. indices_uv = []
  465. indices_color = []
  466. indices_vertex = []
  467. indices_material = []
  468. # per layer data
  469. for l in range(layer_count):
  470. layer = mesh.GetLayer(l)
  471. # groups
  472. group = extract_layer_groups(layer, poly_count)
  473. if group:
  474. groups_layers.append(group)
  475. # uvs
  476. uv_values, uv_index_layer = extract_layer_uvs(layer, poly_count, mesh)
  477. if uv_values:
  478. layers_uvs.append(uv_values)
  479. if uv_index_layer:
  480. indices_uv.append(uv_index_layer)
  481. # colors
  482. color_values, color_index_layer = extract_layer_colors(layer, poly_count, mesh)
  483. if color_values:
  484. layers_colors.append(color_values)
  485. if color_index_layer:
  486. indices_color.append(color_index_layer)
  487. # materials
  488. material_index_layer = extract_layer_materials(layer, poly_count, mesh)
  489. if material_index_layer:
  490. indices_material.append(material_index_layer)
  491. # single layer data
  492. for p in range(poly_count):
  493. face_vertex_index = []
  494. polygon_size = mesh.GetPolygonSize(p)
  495. for v in range(polygon_size):
  496. control_point_index = mesh.GetPolygonVertex(p, v)
  497. face_vertex_index.append(control_point_index)
  498. indices_vertex.append(face_vertex_index)
  499. polygons = {}
  500. conditional_set(polygons, "indices_vertex", indices_vertex)
  501. conditional_set(polygons, "indices_uv", indices_uv)
  502. conditional_set(polygons, "indices_color", indices_color)
  503. conditional_set(polygons, "indices_material", indices_material)
  504. conditional_set(polygons, "layers_uvs", layers_uvs)
  505. conditional_set(polygons, "layers_colors", layers_colors)
  506. conditional_set(polygons, "layers_groups", layers_groups)
  507. return polygons
  508. def extract_control_points(mesh):
  509. control_points_count = mesh.GetControlPointsCount()
  510. control_points = mesh.GetControlPoints()
  511. layer_count = mesh.GetLayerCount()
  512. coordinates = []
  513. layers_normals = []
  514. for i in range(control_points_count):
  515. coordinates.append( extract_vec3( control_points[i] ) )
  516. for layer in range(layer_count):
  517. normals = mesh.GetLayer(layer).GetNormals()
  518. if normals:
  519. znormals = []
  520. for i in range(control_points_count):
  521. if normals.GetMappingMode() == KFbxLayerElement.eBY_CONTROL_POINT:
  522. if normals.GetReferenceMode() == KFbxLayerElement.eDIRECT:
  523. znormals.append( extract_vec3( normals.GetDirectArray().GetAt(i) ) )
  524. if znormals:
  525. layers_normals.append( znormals )
  526. points = {}
  527. conditional_set(points, "coordinates", coordinates)
  528. conditional_set(points, "normals", layers_normals)
  529. return points
  530. # #####################################################
  531. # Parser - mesh - materials
  532. # #####################################################
  533. def extract_materials(mesh):
  534. materials_layers = []
  535. material_count = 0
  536. layer_count = mesh.GetLayerCount()
  537. node = None
  538. if mesh:
  539. node = mesh.GetNode()
  540. if node:
  541. material_count = node.GetMaterialCount()
  542. for layer in range(layer_count):
  543. layer_materials = []
  544. materials = mesh.GetLayer(layer).GetMaterials()
  545. if materials:
  546. if materials.GetReferenceMode() == KFbxLayerElement.eINDEX:
  547. #Materials are in an undefined external table
  548. continue
  549. if material_count > 0:
  550. for i in range(material_count):
  551. material = node.GetMaterial(i)
  552. zmaterial = {
  553. "name" : material.GetName()
  554. }
  555. # Get the implementation to see if it's a hardware shader.
  556. implementation = GetImplementation(material, "ImplementationHLSL")
  557. implemenation_type = "HLSL"
  558. if not implementation:
  559. implementation = GetImplementation(material, "ImplementationCGFX")
  560. implemenation_type = "CGFX"
  561. if implementation:
  562. # Now we have a hardware shader, let's read it
  563. zmaterial["hardware_shader_type"] = implemenation_type.Buffer()
  564. # Skipped parsing of shaders
  565. elif material.GetClassId().Is(KFbxSurfaceLambert.ClassId):
  566. ambient = material.GetAmbientColor()
  567. diffuse = material.GetDiffuseColor()
  568. emissive = material.GetEmissiveColor()
  569. zmaterial["ambient"] = [ambient.Get()[0], ambient.Get()[1], ambient.Get()[2]]
  570. zmaterial["diffuse"] = [diffuse.Get()[0], diffuse.Get()[1], diffuse.Get()[2]]
  571. zmaterial["emissive"] = [emissive.Get()[0], emissive.Get()[1], emissive.Get()[2]]
  572. zmaterial["opacity"] = material.GetTransparencyFactor().Get()
  573. elif (material.GetClassId().Is(KFbxSurfacePhong.ClassId)):
  574. ambient = material.GetAmbientColor()
  575. diffuse = material.GetDiffuseColor()
  576. emissive = material.GetEmissiveColor()
  577. specular = material.GetSpecularColor()
  578. zmaterial["ambient"] = [ambient.Get()[0], ambient.Get()[1], ambient.Get()[2]]
  579. zmaterial["diffuse"] = [diffuse.Get()[0], diffuse.Get()[1], diffuse.Get()[2]]
  580. zmaterial["emissive"] = [emissive.Get()[0], emissive.Get()[1], emissive.Get()[2]]
  581. zmaterial["specular"] = [specular.Get()[0], specular.Get()[1], specular.Get()[2]]
  582. zmaterial["opacity"] = material.GetTransparencyFactor().Get()
  583. zmaterial["shininess"] = material.GetShininess().Get()
  584. zmaterial["reflectivity"] = material.GetReflectionFactor().Get()
  585. zmaterial["shading_model"] = material.GetShadingModel().Get().Buffer()
  586. layer_materials.append(zmaterial)
  587. if layer_materials:
  588. materials_layers.append(layer_materials)
  589. return materials_layers
  590. # #####################################################
  591. # Parser - mesh - textures
  592. # #####################################################
  593. def extract_texture_info(texture, blend_mode):
  594. mapping_types = [ "Null", "Planar", "Spherical", "Cylindrical", "Box", "Face", "UV", "Environment" ]
  595. alpha_sources = [ "None", "RGB Intensity", "Black" ]
  596. blend_modes = [ "Translucent", "Add", "Modulate", "Modulate2" ]
  597. material_uses = [ "Model Material", "Default Material" ]
  598. texture_uses = [ "Standard", "Shadow Map", "Light Map", "Spherical Reflection Map", "Sphere Reflection Map" ]
  599. planar_mapping_normals = [ "X", "Y", "Z" ]
  600. info = {
  601. "name" : texture.GetName(),
  602. "filename" : texture.GetFileName(),
  603. "scale_u" : texture.GetScaleU(),
  604. "scale_v" : texture.GetScaleV(),
  605. "swap_uv" : texture.GetSwapUV(),
  606. "translation_u" : texture.GetTranslationU(),
  607. "translation_v" : texture.GetTranslationV(),
  608. "rotation_u" : texture.GetRotationU(),
  609. "rotation_v" : texture.GetRotationV(),
  610. "rotation_w" : texture.GetRotationW(),
  611. "mapping" : mapping_types[texture.GetMappingType()],
  612. "alpha_source" : alpha_sources[texture.GetAlphaSource()],
  613. "cropping_left" : texture.GetCroppingLeft(),
  614. "cropping_top" : texture.GetCroppingTop(),
  615. "cropping_right" : texture.GetCroppingRight(),
  616. "cropping_bottom" : texture.GetCroppingBottom(),
  617. "alpha" : texture.GetDefaultAlpha(),
  618. "material_use" : material_uses[texture.GetMaterialUse()],
  619. "texture_use" : texture_uses[texture.GetTextureUse()]
  620. }
  621. if texture.GetMappingType() == KFbxTexture.ePLANAR:
  622. info["planar_mapping_normal"] = planar_mapping_normals[texture.GetPlanarMappingNormal()]
  623. if blend_mode >= 0:
  624. info["blend_mode"] = blend_modes[blend_mode]
  625. return info
  626. def extract_texture_info_by_property(property, material_index):
  627. textures = []
  628. if property.IsValid():
  629. #Here we have to check if it's layeredtextures, or just textures:
  630. layered_texture_count = property.GetSrcObjectCount(KFbxLayeredTexture.ClassId)
  631. if layered_texture_count > 0:
  632. for j in range(layered_texture_count):
  633. ltexture = {
  634. "layered_texture" : j,
  635. "layers" : []
  636. }
  637. layered_texture = property.GetSrcObject(KFbxLayeredTexture.ClassId, j)
  638. texture_count = layered_texture.GetSrcObjectCount(KFbxTexture.ClassId)
  639. for k in range(texture_count):
  640. ztexture = lLayeredTexture.GetSrcObject(KFbxTexture.ClassId, k)
  641. if ztexture:
  642. # NOTE the blend mode is ALWAYS on the LayeredTexture and NOT the one on the texture.
  643. # Why is that? because one texture can be shared on different layered textures and might
  644. # have different blend modes.
  645. blend_mode = layered_texture.GetTextureBlendMode(k)
  646. texture = {
  647. "material_index" : material_index,
  648. "texture_index" : k,
  649. "property_name" : property.GetName().Buffer(),
  650. "blend_mode" : blend_mode,
  651. "info" : extract_texture_info(ztexture, blend_mode)
  652. }
  653. ltexture["layers"].append(texture)
  654. textures.append(ltexture)
  655. # no layered texture simply get on the property
  656. else:
  657. texture_count = property.GetSrcObjectCount(KFbxTexture.ClassId)
  658. for j in range(texture_count):
  659. ztexture = property.GetSrcObject(KFbxTexture.ClassId, j)
  660. if ztexture:
  661. texture = {
  662. "material_index" : material_index,
  663. "info" : extract_texture_info(ztexture, -1)
  664. }
  665. textures.append(texture)
  666. return textures
  667. def extract_textures(mesh):
  668. textures = {}
  669. node = mesh.GetNode()
  670. materials_count = node.GetSrcObjectCount(KFbxSurfaceMaterial.ClassId)
  671. for material_index in range(materials_count):
  672. material = node.GetSrcObject(KFbxSurfaceMaterial.ClassId, material_index)
  673. #go through all the possible textures
  674. if material:
  675. for texture_index in range(KFbxLayerElement.LAYERELEMENT_TYPE_TEXTURE_COUNT):
  676. property = material.FindProperty(KFbxLayerElement.TEXTURE_CHANNEL_NAMES[texture_index])
  677. texture = extract_texture_info_by_property(property, material_index)
  678. if texture:
  679. textures[property.GetName().Buffer()] = texture
  680. return textures
  681. def extract_material_mapping(mesh):
  682. return {}
  683. def extract_material_connections(mesh):
  684. return {}
  685. def extract_link(mesh):
  686. return {}
  687. def extract_shape(mesh):
  688. return {}
  689. def extract_mesh(node):
  690. mesh = node.GetNodeAttribute()
  691. zmesh = {}
  692. conditional_set(zmesh, "name", node.GetName())
  693. conditional_set(zmesh, "shape", extract_shape(mesh))
  694. conditional_set(zmesh, "link", extract_link(mesh))
  695. conditional_set(zmesh, "control_points", extract_control_points(mesh))
  696. conditional_set(zmesh, "faces", extract_polygons(mesh))
  697. conditional_set(zmesh, "textures", extract_textures(mesh))
  698. conditional_set(zmesh, "materials", extract_materials(mesh))
  699. conditional_set(zmesh, "material_mapping", extract_material_mapping(mesh))
  700. conditional_set(zmesh, "material_connections", extract_material_connections(mesh))
  701. return zmesh
  702. # #####################################################
  703. # Parser - nodes (todo)
  704. # #####################################################
  705. def extract_marker(node):
  706. return {}
  707. def extract_nurb(node):
  708. return {}
  709. def extract_patch(node):
  710. return {}
  711. def extract_skeleton(node):
  712. return {}
  713. def extract_camera(node):
  714. return {}
  715. def extract_light(node):
  716. return {}
  717. # #####################################################
  718. # Parser - nodes (generic)
  719. # #####################################################
  720. def extract_target(node):
  721. if node.GetTarget():
  722. return node.GetTarget().GetName()
  723. return ""
  724. def extract_transform(node):
  725. translation = node.GetGeometricTranslation(KFbxNode.eSOURCE_SET)
  726. rotation = node.GetGeometricRotation(KFbxNode.eSOURCE_SET)
  727. scale = node.GetGeometricScaling(KFbxNode.eSOURCE_SET)
  728. transform = {
  729. "translation": [ translation[0], translation[1], translation[2] ],
  730. "rotation" : [ rotation[0], rotation[1], rotation[2] ],
  731. "scale" : [ scale[0], scale[1], scale[2] ],
  732. }
  733. return transform
  734. def extract_transform_propagation(node):
  735. rotation_order = node.GetRotationOrder(KFbxNode.eSOURCE_SET)
  736. order_map = {
  737. eEULER_XYZ : "Euler XYZ",
  738. eEULER_XZY : "Euler XZY",
  739. eEULER_YZX : "Euler YZX",
  740. eEULER_YXZ : "Euler YXZ",
  741. eEULER_ZXY : "Euler ZXY",
  742. eEULER_ZYX : "Euler ZYX",
  743. eSPHERIC_XYZ:"Spheric XYZ"
  744. }
  745. order = "Euler XYZ"
  746. if rotation_order in order_map:
  747. order = order_map[rotation_order]
  748. # Use the Rotation space only for the limits
  749. # (keep using eEULER_XYZ for the rest)
  750. if node.GetUseRotationSpaceForLimitOnly(KFbxNode.eSOURCE_SET):
  751. only_limits = 1
  752. else:
  753. only_limits = 0
  754. inherit_type = node.GetTransformationInheritType()
  755. inherit_map = {
  756. eINHERIT_RrSs : "RrSs",
  757. eINHERIT_RSrs : "RSrs",
  758. eINHERIT_Rrs : "Rrs"
  759. }
  760. if inherit_type in inherit_map:
  761. inheritance = inherit_map[inherit_type]
  762. transform_propagation = {
  763. "rotation_order" : order,
  764. "only_limits" : only_limits,
  765. "inheritance" : inheritance
  766. }
  767. return transform_propagation
  768. def extract_pivots(node):
  769. return {}
  770. def extract_user_properties(node):
  771. return {}
  772. def extract_node_content(node):
  773. nodes = []
  774. if node.GetNodeAttribute() == None:
  775. return "NULL"
  776. else:
  777. attribute_type = node.GetNodeAttribute().GetAttributeType()
  778. ztype = "undefined"
  779. data = {}
  780. type_map = {
  781. KFbxNodeAttribute.eMARKER : ["marker", extract_marker],
  782. KFbxNodeAttribute.eSKELETON : ["skeleton", extract_skeleton],
  783. KFbxNodeAttribute.eMESH : ["mesh", extract_mesh],
  784. KFbxNodeAttribute.eNURB : ["nurb", extract_nurb],
  785. KFbxNodeAttribute.ePATCH : ["patch", extract_patch],
  786. KFbxNodeAttribute.eCAMERA : ["camera", extract_camera],
  787. KFbxNodeAttribute.eLIGHT : ["light", extract_light]
  788. }
  789. if attribute_type in type_map:
  790. ztype = type_map[attribute_type][0]
  791. data = type_map[attribute_type][1](node)
  792. content = { }
  793. conditional_set(content, "type", ztype)
  794. conditional_set(content, "data", data)
  795. conditional_set(content, "target", extract_target(node))
  796. conditional_set(content, "pivots", extract_pivots(node))
  797. conditional_set(content, "transform", extract_transform(node))
  798. conditional_set(content, "transform_propagation", extract_transform_propagation(node))
  799. conditional_set(content, "user_properties", extract_user_properties(node))
  800. nodes.append(content)
  801. for i in range(node.GetChildCount()):
  802. nodes += extract_node_content(node.GetChild(i))
  803. return nodes
  804. def extract_nodes(scene):
  805. nodes = []
  806. root = scene.GetRootNode()
  807. if root:
  808. for i in range(root.GetChildCount()):
  809. nodes += extract_node_content(root.GetChild(i))
  810. return nodes
  811. def filter_mesh(item):
  812. return item["type"] == "mesh"
  813. def extract_meshes(scene):
  814. nodes = extract_nodes(scene)
  815. meshes = filter(filter_mesh, nodes)
  816. return meshes
  817. # #####################################################
  818. # JSON extractors
  819. # #####################################################
  820. def get_material_texture(material_index, textures, property):
  821. result = [t for t in textures.get(property, []) if t["material_index"] == material_index]
  822. return result
  823. def collect_textures(data):
  824. texture_set = set()
  825. for mesh in data["meshes"]:
  826. for texture_type in mesh["data"]["textures"]:
  827. for texture in mesh["data"]["textures"][texture_type]:
  828. texture_file = base_filename(texture["info"]["filename"])
  829. texture_set.add(texture_file)
  830. return list(texture_set)
  831. # #####################################################
  832. # Generator - model
  833. # #####################################################
  834. def generate_vertex(v):
  835. return TEMPLATE_VERTEX % (v[0], v[1], v[2])
  836. def generate_triangle(f):
  837. v = f['vertex']
  838. return TEMPLATE_TRI % (v[0], v[1], v[2],
  839. f['material'])
  840. def generate_triangle_uv(f):
  841. v = f['vertex']
  842. uv = f['uv']
  843. return TEMPLATE_TRI_UV % (v[0], v[1], v[2],
  844. f['material'],
  845. uv[0], uv[1], uv[2])
  846. def generate_triangle_n(f):
  847. v = f['vertex']
  848. n = f['normal']
  849. return TEMPLATE_TRI_N % (v[0], v[1], v[2],
  850. f['material'],
  851. n[0], n[1], n[2])
  852. def generate_triangle_n_uv(f):
  853. v = f['vertex']
  854. n = f['normal']
  855. uv = f['uv']
  856. return TEMPLATE_TRI_N_UV % (v[0], v[1], v[2],
  857. f['material'],
  858. n[0], n[1], n[2],
  859. uv[0], uv[1], uv[2])
  860. def generate_quad(f):
  861. vi = f['vertex']
  862. return TEMPLATE_QUAD % (vi[0], vi[1], vi[2], vi[3],
  863. f['material'])
  864. def generate_quad_uv(f):
  865. v = f['vertex']
  866. uv = f['uv']
  867. return TEMPLATE_QUAD_UV % (v[0], v[1], v[2], v[3],
  868. f['material'],
  869. uv[0], uv[1], uv[2], uv[3])
  870. def generate_quad_n(f):
  871. v = f['vertex']
  872. n = f['normal']
  873. return TEMPLATE_QUAD_N % (v[0], v[1], v[2], v[3],
  874. f['material'],
  875. n[0], n[1], n[2], n[3])
  876. def generate_quad_n_uv(f):
  877. v = f['vertex']
  878. n = f['normal']
  879. uv = f['uv']
  880. return TEMPLATE_QUAD_N_UV % (v[0], v[1], v[2], v[3],
  881. f['material'],
  882. n[0], n[1], n[2], n[3],
  883. uv[0], uv[1], uv[2], uv[3])
  884. def generate_normal(n):
  885. return TEMPLATE_N % (n[0], n[1], n[2])
  886. def generate_uv(uv):
  887. return TEMPLATE_UV % (uv[0], uv[1])
  888. # #####################################################
  889. # Generator - scene
  890. # #####################################################
  891. def generate_vec3(vec):
  892. return TEMPLATE_VEC3 % (vec[0], vec[1], vec[2])
  893. def generate_vec2(vec):
  894. return TEMPLATE_VEC2 % (vec[0], vec[1])
  895. def generate_hex(number):
  896. return TEMPLATE_HEX % number
  897. def generate_string(s):
  898. return TEMPLATE_STRING % s
  899. def generate_section(label, content):
  900. return TEMPLATE_SECTION % (label, content)
  901. def get_mesh_filename(mesh):
  902. object_id = mesh["data"]["name"]
  903. filename = "%s.js" % sanitize(object_id)
  904. return filename
  905. def generate_material_id_list(materials):
  906. chunks = []
  907. for layer in materials:
  908. for material in layer:
  909. chunks.append(material["name"])
  910. return ",".join(chunks)
  911. def generate_objects(data):
  912. chunks = []
  913. for mesh in data["meshes"]:
  914. object_id = mesh["data"]["name"]
  915. geometry_id = "geo_%s" % object_id
  916. material_id = generate_material_id_list(mesh["data"]["materials"])
  917. position = mesh["transform"]["translation"]
  918. rotation = mesh["transform"]["rotation"]
  919. scale = mesh["transform"]["scale"]
  920. # hunt for local transform
  921. if object_id in data["generic_info"]:
  922. gi = data["generic_info"][object_id]
  923. lt = gi.get("Lcl Translation", {})
  924. local_translation = lt.get("value", [0,0,0])
  925. position[0] += local_translation[0]
  926. position[1] += local_translation[1]
  927. position[2] += local_translation[2]
  928. object_string = TEMPLATE_OBJECT % {
  929. "object_id" : generate_string(object_id),
  930. "geometry_id" : generate_string(geometry_id),
  931. "material_id" : generate_string(material_id),
  932. "position" : generate_vec3(position),
  933. "rotation" : generate_vec3(rotation),
  934. "scale" : generate_vec3(scale)
  935. }
  936. chunks.append(object_string)
  937. return ",\n\n".join(chunks)
  938. def generate_geometries(data):
  939. chunks = []
  940. for mesh in data["meshes"]:
  941. geometry_id = "geo_%s" % mesh["data"]["name"]
  942. model_filename = get_mesh_filename(mesh)
  943. geometry_string = TEMPLATE_GEOMETRY % {
  944. "geometry_id" : generate_string(geometry_id),
  945. "model_file" : generate_string(model_filename)
  946. }
  947. chunks.append(geometry_string)
  948. return ",\n\n".join(chunks)
  949. def generate_textures_scene(data):
  950. chunks = []
  951. texture_set = set()
  952. for mesh in data["meshes"]:
  953. for texture_type in mesh["data"]["textures"]:
  954. for texture in mesh["data"]["textures"][texture_type]:
  955. texture_id = texture["info"]["name"]
  956. if texture_id not in texture_set:
  957. texture_set.add(texture_id)
  958. texture_file = base_filename(texture["info"]["filename"])
  959. texture_string = TEMPLATE_TEXTURE % {
  960. "texture_id" : generate_string(texture_id),
  961. "texture_file" : generate_string(texture_file)
  962. }
  963. chunks.append(texture_string)
  964. return ",\n\n".join(chunks)
  965. def generate_materials_scene(data):
  966. chunks = []
  967. type_map = {
  968. "Lambert" : "MeshLambertMaterial",
  969. "Phong" : "MeshPhongMaterial"
  970. }
  971. for mesh in data["meshes"]:
  972. for layer in mesh["data"]["materials"]:
  973. for material_index in range(len(layer)):
  974. material = layer[material_index]
  975. material_id = material["name"]
  976. shading = material["shading_model"]
  977. material_type = type_map.get(shading, "MeshBasicMaterial")
  978. parameters = "color: %s" % generate_hex(rgb2int(material["diffuse"]))
  979. if shading == "Phong":
  980. parameters += ", ambient: %s" % generate_hex(rgb2int(material["ambient"]))
  981. parameters += ", specular: %s" % generate_hex(rgb2int(material["specular"]))
  982. parameters += ", shininess: %f" % material["shininess"]
  983. # TODO: proper handling of textures
  984. color_map = get_material_texture(material_index, mesh["data"]["textures"], "DiffuseColor")
  985. light_map = get_material_texture(material_index, mesh["data"]["textures"], "AmbientColor")
  986. bump_map = get_material_texture(material_index, mesh["data"]["textures"], "Bump")
  987. if map:
  988. parameters += ", map: %s" % generate_string(color_map[0]["info"]["name"])
  989. if light_map:
  990. parameters += ", light_map: %s" % generate_string(light_map[0]["info"]["name"])
  991. if bump_map:
  992. parameters += ", bump_map: %s" % generate_string(bump_map[0]["info"]["name"])
  993. material_string = TEMPLATE_MATERIAL_SCENE % {
  994. "material_id" : generate_string(material_id),
  995. "type" : generate_string(material_type),
  996. "parameters" : parameters
  997. }
  998. chunks.append(material_string)
  999. return ",\n\n".join(chunks)
  1000. # TODO
  1001. def generate_cameras(data):
  1002. cameras = data.get("cameras", [])
  1003. if not cameras:
  1004. cameras.append(DEFAULTS["camera"])
  1005. chunks = []
  1006. for camera in cameras:
  1007. if camera["type"] == "perspective":
  1008. camera_string = TEMPLATE_CAMERA_PERSPECTIVE % {
  1009. "camera_id" : generate_string(camera["name"]),
  1010. "fov" : camera["fov"],
  1011. "aspect" : camera["aspect"],
  1012. "near" : camera["near"],
  1013. "far" : camera["far"],
  1014. "position" : generate_vec3(camera["position"]),
  1015. "target" : generate_vec3(camera["target"])
  1016. }
  1017. elif camera["type"] == "ortho":
  1018. camera_string = TEMPLATE_CAMERA_ORTHO % {
  1019. "camera_id" : generate_string(camera["name"]),
  1020. "left" : camera["left"],
  1021. "right" : camera["right"],
  1022. "top" : camera["top"],
  1023. "bottom" : camera["bottom"],
  1024. "near" : camera["near"],
  1025. "far" : camera["far"],
  1026. "position" : generate_vec3(camera["position"]),
  1027. "target" : generate_vec3(camera["target"])
  1028. }
  1029. chunks.append(camera_string)
  1030. return ",\n\n".join(chunks)
  1031. def generate_lights(data):
  1032. return ""
  1033. def generate_ascii_scene(data):
  1034. objects = generate_objects(data)
  1035. geometries = generate_geometries(data)
  1036. textures = generate_textures_scene(data)
  1037. materials = generate_materials_scene(data)
  1038. cameras = generate_cameras(data)
  1039. lights = generate_lights(data)
  1040. sections = [
  1041. ["objects", objects],
  1042. ["geometries", geometries],
  1043. ["textures", textures],
  1044. ["materials", materials],
  1045. ["cameras", cameras],
  1046. ["lights", lights]
  1047. ]
  1048. chunks = []
  1049. for label, content in sections:
  1050. if content:
  1051. chunks.append(generate_section(label, content))
  1052. sections_string = "\n".join(chunks)
  1053. default_camera = "default_camera"
  1054. parameters = {
  1055. "fname" : data["source_file"],
  1056. "url_base" : generate_string(data["base_folder"]),
  1057. "sections" : sections_string,
  1058. "bgcolor" : generate_vec3(DEFAULTS["bgcolor"]),
  1059. "bgalpha" : DEFAULTS["bgalpha"],
  1060. "defcamera" : generate_string(default_camera)
  1061. }
  1062. text = TEMPLATE_SCENE_ASCII % parameters
  1063. return text
  1064. # #####################################################
  1065. # Generator - materials
  1066. # #####################################################
  1067. def generate_color(i):
  1068. """Generate hex color corresponding to integer.
  1069. Colors should have well defined ordering.
  1070. First N colors are hardcoded, then colors are random
  1071. (must seed random number generator with deterministic value
  1072. before getting colors).
  1073. """
  1074. if i < len(COLORS):
  1075. return "0x%06x" % COLORS[i]
  1076. else:
  1077. return "0x%06x" % int(0xffffff * random.random())
  1078. def value2string(v):
  1079. if type(v)==str and v[0:2] != "0x":
  1080. return '"%s"' % v
  1081. return str(v)
  1082. def generate_material_model(material, index):
  1083. m = {
  1084. 'a_dbg_name' :generate_string(material["name"]),
  1085. 'a_dbg_index' :index,
  1086. 'a_dbg_color' :generate_color(index),
  1087. "shading" :generate_string(material["shading_model"]),
  1088. "opacity" : material["opacity"]
  1089. }
  1090. if material["shading_model"] in ["Lambert", "Phong"]:
  1091. m["col_ambient"] = material["ambient"]
  1092. m["col_diffuse"] = material["diffuse"]
  1093. m["col_emissive"] = material["emissive"]
  1094. if material["shading_model"] in ["Phong"]:
  1095. m["col_specular"] = material["specular"]
  1096. m["shininess"] = material["shininess"]
  1097. if not MATERIALS_IN_SCENE:
  1098. conditional_set(m, "map_diffuse", material.get("map_diffuse", 0))
  1099. conditional_set(m, "map_lightmap", material.get("map_lightmap", 0))
  1100. mtl_raw = ",\n".join(['\t"%s" : %s' % (n, value2string(v)) for n,v in sorted(m.items())])
  1101. mtl_string = "\t{\n%s\n\t}" % mtl_raw
  1102. return mtl_string
  1103. # #####################################################
  1104. # Generator - models
  1105. # #####################################################
  1106. def generate_ascii_model(data):
  1107. materials = data["materials"]
  1108. vertices = data["control_points"]["coordinates"]
  1109. normals = []
  1110. if "normals" in data["control_points"]:
  1111. normals = data["control_points"]["normals"][0]
  1112. uvs = []
  1113. uvs2 = []
  1114. if "layers_uvs" in data["faces"]:
  1115. n_uvs = len(data["faces"]["layers_uvs"])
  1116. if n_uvs > 0:
  1117. uvs = data["faces"]["layers_uvs"][0]
  1118. if n_uvs > 1:
  1119. uvs2 = data["faces"]["layers_uvs"][1]
  1120. triangles = []
  1121. triangles_n = []
  1122. triangles_uv = []
  1123. triangles_n_uv = []
  1124. quads = []
  1125. quads_n = []
  1126. quads_uv = []
  1127. quads_n_uv = []
  1128. indices_vertex = data["faces"]["indices_vertex"]
  1129. for vi in range(len(indices_vertex)):
  1130. vertex_index = indices_vertex[vi]
  1131. face = {
  1132. 'vertex' : vertex_index,
  1133. 'material' : data["faces"]["indices_material"][0][vi]
  1134. }
  1135. if normals:
  1136. face["normal"] = vertex_index
  1137. if uvs and "indices_uv" in data["faces"]:
  1138. indices_uv = data["faces"]["indices_uv"]
  1139. face["uv"] = indices_uv[0][vi]
  1140. if len(indices_uv) > 1:
  1141. face["uv2"] = indices_uv[1][vi]
  1142. if len(vertex_index) == 3:
  1143. if normals:
  1144. if uvs:
  1145. where = triangles_n_uv
  1146. else:
  1147. where = triangles_n
  1148. else:
  1149. if uvs:
  1150. where = triangles_uv
  1151. else:
  1152. where = triangles
  1153. elif len(vertex_index) == 4:
  1154. if normals:
  1155. if uvs:
  1156. where = quads_n_uv
  1157. else:
  1158. where = quads_n
  1159. else:
  1160. if uvs:
  1161. where = quads_uv
  1162. else:
  1163. where = quads
  1164. where.append(face)
  1165. nvertex = len(vertices)
  1166. nface = len(indices_vertex)
  1167. nmaterial = 0
  1168. text = TEMPLATE_MODEL_ASCII % {
  1169. "fname" : source_file,
  1170. "nvertex" : nvertex,
  1171. "nface" : nface,
  1172. "nmaterial" : nmaterial,
  1173. "materials" : "".join(generate_material_model(m, i) for i, m in enumerate(materials[0])),
  1174. "normals" : ",".join(generate_normal(n) for n in normals),
  1175. "vertices" : ",".join(generate_vertex(v) for v in vertices),
  1176. "uvs" : ",".join(generate_uv(u) for u in uvs),
  1177. "uvs2" : ",".join(generate_uv(u) for u in uvs2),
  1178. "triangles" : ",".join(generate_triangle(f) for f in triangles),
  1179. "triangles_n" : ",".join(generate_triangle_n(f) for f in triangles_n),
  1180. "triangles_uv" : ",".join(generate_triangle_uv(f) for f in triangles_uv),
  1181. "triangles_n_uv": ",".join(generate_triangle_n_uv(f) for f in triangles_n_uv),
  1182. "quads" : ",".join(generate_quad(f) for f in quads),
  1183. "quads_n" : ",".join(generate_quad_n(f) for f in quads_n),
  1184. "quads_uv" : ",".join(generate_quad_uv(f) for f in quads_uv),
  1185. "quads_n_uv" : ",".join(generate_quad_n_uv(f) for f in quads_n_uv)
  1186. }
  1187. return text
  1188. # #####################################################
  1189. # Helpers
  1190. # #####################################################
  1191. def sanitize(text):
  1192. chunks = []
  1193. for ch in text:
  1194. if ch in (string.ascii_letters + string.digits + "_."):
  1195. chunks.append(ch)
  1196. else:
  1197. chunks.append("_")
  1198. return "".join(chunks)
  1199. def base_filename(path):
  1200. return os.path.basename(path)
  1201. def rgb2int(rgb):
  1202. color = (int(rgb[0]*255) << 16) + (int(rgb[1]*255) << 8) + int(rgb[2]*255);
  1203. return color
  1204. def conditional_set(where, label, what):
  1205. """Set dictionary property only if it exists"""
  1206. if what:
  1207. where[label] = what
  1208. def dump_data(data):
  1209. """Generate pretty printed view of data."""
  1210. chunks = []
  1211. pp = pprint.PrettyPrinter(indent=2, width=160)
  1212. if type(data) == list:
  1213. for d in data:
  1214. chunks.append(pp.pformat(d))
  1215. elif type(data) == dict:
  1216. chunks.append(pp.pformat(data))
  1217. return "\n\n".join(chunks)
  1218. def ensure_folder_exist(foldername):
  1219. """Create folder (with whole path) if it doesn't exist yet."""
  1220. if not os.access(foldername, os.R_OK|os.W_OK|os.X_OK):
  1221. os.makedirs(foldername)
  1222. def abort(message):
  1223. print message
  1224. sys.exit(1)
  1225. def write_file(fname, content):
  1226. out = open(fname, "w")
  1227. out.write(content)
  1228. out.close()
  1229. def copy_files(textures, src_folder, dst_folder):
  1230. for texture in textures:
  1231. src_file = os.path.join(src_folder, texture)
  1232. if os.path.isfile(src_file):
  1233. shutil.copy(src_file, dst_folder)
  1234. else:
  1235. print "WARNING: couldn't find [%s]" % src_file
  1236. # #####################################################
  1237. # Main
  1238. # #####################################################
  1239. if __name__ == "__main__":
  1240. try:
  1241. from FbxCommon import *
  1242. except ImportError:
  1243. import platform
  1244. msg = ""
  1245. if platform.system() == 'Windows' or platform.system() == 'Microsoft':
  1246. msg = '"Python26/Lib/site-packages"'
  1247. elif platform.system() == 'Linux':
  1248. msg = '"/usr/local/lib/python2.6/site-packages"'
  1249. elif platform.system() == 'Darwin':
  1250. msg = '"/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/site-packages"'
  1251. abort('You need to copy the content in compatible subfolder under /lib/python<version> into your python install folder such as %s folder.' % msg)
  1252. sdkManager, scene = InitializeSdkObjects()
  1253. if len(sys.argv) < 3:
  1254. abort("Usage: convert_fbx_three.py [scene.fbx] [scene_folder]")
  1255. source_file = sys.argv[1]
  1256. output_folder = sys.argv[2]
  1257. junk, base_folder = os.path.split(os.path.normpath(output_folder))
  1258. result = LoadScene(sdkManager, scene, source_file)
  1259. if not result:
  1260. abort("An error occurred while loading the scene ...")
  1261. random.seed(42) # to get well defined debug color order for materials
  1262. ensure_folder_exist(output_folder)
  1263. ensure_folder_exist(output_folder+"/models")
  1264. ensure_folder_exist(output_folder+"/textures")
  1265. meshes = extract_meshes(scene)
  1266. generic_info = extract_generic_info(scene)
  1267. scene_text = ""
  1268. data = {
  1269. "meshes" : meshes,
  1270. "generic_info": generic_info,
  1271. "source_file" : source_file,
  1272. "base_folder" : base_folder+"/"
  1273. }
  1274. scene_text += generate_ascii_scene(data)
  1275. if DEBUG_FBX_JSON:
  1276. scene_text += "/*" + dump_data(meshes) + "\n\n\n" + dump_data(generic_info) + "*/"
  1277. scene_file = os.path.join(output_folder, "scene.js")
  1278. htaccess_file = os.path.join(output_folder, ".htaccess")
  1279. write_file(scene_file, scene_text)
  1280. write_file(htaccess_file, TEMPLATE_HTACCESS)
  1281. for mesh in meshes:
  1282. model_text = generate_ascii_model(mesh["data"])
  1283. model_file = os.path.join(output_folder, "models", get_mesh_filename(mesh))
  1284. write_file(model_file, model_text)
  1285. textures_src_folder = os.path.dirname(source_file)
  1286. textures_dst_folder = os.path.join(output_folder, "textures")
  1287. copy_files(collect_textures(data), textures_src_folder, textures_dst_folder)
  1288. # Destroy all objects created by the FBX SDK
  1289. sdkManager.Destroy()