ThreeJSExporter.ms 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819
  1. -------------------------------------------------------------------------------------
  2. -- ThreeJSExporter.ms
  3. -- Exports geometry from 3ds max to Three.js models in ASCII JSON format
  4. -- By alteredq / http://alteredqualia.com
  5. -------------------------------------------------------------------------------------
  6. rollout ThreeJSExporter "ThreeJSExporter"
  7. (
  8. -- Variables
  9. local ostream,
  10. headerFormat = "// Converted from: %
  11. // vertices: %
  12. // normals: %
  13. // uvs: %
  14. // triangles: %
  15. // materials: %
  16. //
  17. // Generated with 3ds max ThreeJSExporter
  18. // http://github.com/alteredq/three.js/blob/master/utils/exporters/max/ThreeJSExporter.ms
  19. ",
  20. vertexFormat = "%,%,%",
  21. vertexNormalFormat = "%,%,%",
  22. UVFormat = "%,%",
  23. triFormat = "%,%,%, %",
  24. triUVFormat = "%,%,%, %, %,%,%",
  25. triNFormat = "%,%,%, %, %,%,%",
  26. triUVNFormat = "%,%,%, %, %,%,%, %,%,%",
  27. footerFormat = "}\n\npostMessage( model );"
  28. -------------------------------------------------------------------------------------
  29. -- User interface
  30. group "ThreeJSExporter v0.1"
  31. (
  32. label msg "Exports meshes in Three.js ascii JSON format" align:#left
  33. hyperLink lab1 "Original source can be found here" address:"https://github.com/mrdoob/three.js" align:#left
  34. checkbox flipYZ "Flip YZ" checked:true enabled:true
  35. checkbox flipUV "Flip UV" checked:true enabled:true
  36. checkbox flipFace "Flip faces" checked:false enabled:true
  37. checkbox exportNormal "Export normals" checked:true enabled:true
  38. checkbox smoothNormal "Use vertex normals" checked:true enabled:true
  39. checkbox exportUv "Export uvs" checked:true enabled:true
  40. checkbox exportColor "Export vertex colors" checked:false enabled:false
  41. )
  42. button btn_export "Export selected objects"
  43. -------------------------------------------------------------------------------------
  44. -- Dump vertices
  45. function DumpVertices src =
  46. (
  47. Format "'vertices': [" to:ostream
  48. num = src.count
  49. if num > 0 then
  50. (
  51. for i = 1 to num do
  52. (
  53. vert = src[i]
  54. if flipYZ.checked then
  55. (
  56. x = vert.x
  57. y = vert.z
  58. z = vert.y
  59. z *= -1
  60. )
  61. else
  62. (
  63. x = vert.x
  64. y = vert.y
  65. z = vert.z
  66. )
  67. Format vertexFormat x y z to:ostream
  68. if i < num then Format ", " to:ostream
  69. )
  70. )
  71. Format "],\n\n" to:ostream
  72. )
  73. -------------------------------------------------------------------------------------
  74. -- Dump normals
  75. function DumpNormals src =
  76. (
  77. Format "'normals': [" to:ostream
  78. num = src.count
  79. if num > 0 and exportNormal.checked then
  80. (
  81. for i = 1 to num do
  82. (
  83. normal = src[i]
  84. normal = normalize normal as point3
  85. if flipYZ.checked then
  86. (
  87. x = normal.x
  88. y = normal.z
  89. z = normal.y
  90. z *= -1
  91. )
  92. else
  93. (
  94. x = normal.x
  95. y = normal.y
  96. z = normal.z
  97. )
  98. Format vertexNormalFormat x y z to:ostream
  99. if i < num then Format ", " to:ostream
  100. )
  101. )
  102. Format "],\n\n" to:ostream
  103. )
  104. -------------------------------------------------------------------------------------
  105. -- Dump uvs
  106. function DumpUvs src =
  107. (
  108. Format "'uvs': [" to:ostream
  109. num = src.count
  110. if num > 0 and exportUv.checked then
  111. (
  112. for i = 1 to num do
  113. (
  114. uvw = src[i]
  115. u = uvw.x
  116. if flipUV.checked then
  117. (
  118. v = 1 - uvw.y
  119. )
  120. else
  121. (
  122. v = uvw.y
  123. )
  124. Format UVFormat u v to:ostream
  125. if i < num then Format ", " to:ostream
  126. )
  127. )
  128. Format "],\n\n" to:ostream
  129. )
  130. -------------------------------------------------------------------------------------
  131. -- Dump face type
  132. function DumpFaceType label content =
  133. (
  134. Format "'%': [" label to:ostream
  135. num = content.count
  136. if num > 0 then
  137. (
  138. for i = 1 to num do
  139. (
  140. zface = content[i]
  141. fv = zface[1]
  142. fuv = zface[2]
  143. m = zface[3] - 1
  144. needsFlip = zface[4]
  145. hasUVs = (classof fuv == Point3)
  146. va = (fv.x - 1) as Integer
  147. vb = (fv.y - 1) as Integer
  148. vc = (fv.z - 1) as Integer
  149. if smoothNormal.checked then
  150. (
  151. -- normals have the same indices as vertices
  152. na = va
  153. nb = vb
  154. nc = vc
  155. )
  156. else
  157. (
  158. -- normals have the same indices as faces
  159. na = i - 1
  160. nb = na
  161. nc = na
  162. )
  163. if hasUVs then
  164. (
  165. ua = (fuv.x - 1) as Integer
  166. ub = (fuv.y - 1) as Integer
  167. uc = (fuv.z - 1) as Integer
  168. )
  169. if flipFace.checked or needsFlip then
  170. (
  171. tmp = vb
  172. vb = vc
  173. vc = tmp
  174. tmp = nb
  175. nb = nc
  176. nc = tmp
  177. if hasUVs then
  178. (
  179. tmp = ub
  180. ub = uc
  181. uc = tmp
  182. )
  183. )
  184. if label == "triangles" then
  185. (
  186. Format triFormat va vb vc m to:ostream
  187. )
  188. else if label == "trianglesUvs" then
  189. (
  190. Format triUVFormat va vb vc m ua ub uc to:ostream
  191. )
  192. else if label == "trianglesNormals" then
  193. (
  194. Format triNFormat va vb vc m na nb nc to:ostream
  195. )
  196. else if label == "trianglesNormalsUvs" then
  197. (
  198. Format triUVNFormat va vb vc m na nb nc ua ub uc to:ostream
  199. )
  200. if i < num then Format ", " to:ostream
  201. )
  202. )
  203. Format "],\n\n" to:ostream
  204. )
  205. -------------------------------------------------------------------------------------
  206. -- Dump faces
  207. function DumpFaces src =
  208. (
  209. hasUVs = true
  210. if exportNormal.checked and exportUv.checked and hasUVs then
  211. triangles = #()
  212. trianglesUvs = #()
  213. trianglesNormals = #()
  214. trianglesNormalsUvs = #()
  215. quads = #()
  216. quadsUvs = #()
  217. quadsNormals = #()
  218. quadsNormalsUvs = #()
  219. num = src.count
  220. if num > 0 then
  221. (
  222. for i = 1 to num do
  223. (
  224. zface = src[i]
  225. fuv = zface[2]
  226. hasUVs = (classof fuv == Point3)
  227. if hasUVs and exportUv.checked and exportNormal.checked then
  228. (
  229. append trianglesNormalsUvs zface
  230. )
  231. else if exportNormal.checked then
  232. (
  233. append trianglesNormals zface
  234. )
  235. else if hasUVs and exportUv.checked then
  236. (
  237. append trianglesUvs zface
  238. )
  239. else
  240. (
  241. append triangles zface
  242. )
  243. )
  244. )
  245. DumpFaceType "triangles" triangles
  246. DumpFaceType "trianglesUvs" trianglesUvs
  247. DumpFaceType "trianglesNormals" trianglesNormals
  248. DumpFaceType "trianglesNormalsUvs" trianglesNormalsUvs
  249. DumpFaceType "quads" quads
  250. DumpFaceType "quadsUvs" quadsUvs
  251. DumpFaceType "quadsNormals" quadsNormals
  252. DumpFaceType "quadsNormalsUvs" quadsNormalsUvs
  253. )
  254. -------------------------------------------------------------------------------------
  255. -- Dump color
  256. function DumpColor pcolor label =
  257. (
  258. r = pcolor.r / 255
  259. g = pcolor.g / 255
  260. b = pcolor.b / 255
  261. fr = formattedPrint r format:".4f"
  262. fg = formattedPrint g format:".4f"
  263. fb = formattedPrint b format:".4f"
  264. Format "'%' : [%, %, %],\n" label fr fg fb to:ostream
  265. )
  266. -------------------------------------------------------------------------------------
  267. -- Dump map
  268. function DumpMap pmap label =
  269. (
  270. if classof pmap == BitmapTexture then
  271. (
  272. bm = pmap.bitmap
  273. if bm != undefined then
  274. (
  275. fname = filenameFromPath bm.filename
  276. Format "'%' : '%',\n" label fname to:ostream
  277. )
  278. )
  279. )
  280. -------------------------------------------------------------------------------------
  281. -- Export materials
  282. function ExportMaterials zmaterials =
  283. (
  284. Format "'materials': [\n" to:ostream
  285. totalMaterials = zmaterials.count
  286. for i = 1 to totalMaterials do
  287. (
  288. mat = zmaterials[i]
  289. Format "{\n" to:ostream
  290. -- debug
  291. Format "'DbgIndex' : %,\n" (i-1) to:ostream
  292. Format "'DbgName' : '%',\n" mat.name to:ostream
  293. -- colors
  294. DumpColor mat.diffuse "colorDiffuse"
  295. DumpColor mat.ambient "colorAmbient"
  296. DumpColor mat.specular "colorSpecular"
  297. t = mat.opacity / 100
  298. s = mat.glossiness
  299. Format "'transparency' : %,\n" t to:ostream
  300. Format "'specularCoef' : %,\n" s to:ostream
  301. -- maps
  302. DumpMap mat.diffuseMap "mapDiffuse"
  303. DumpMap mat.ambientMap "mapAmbient"
  304. DumpMap mat.specularMap "mapSpecular"
  305. DumpMap mat.bumpMap "mapBump"
  306. DumpMap mat.opacityMap "mapAlpha"
  307. Format "}" to:ostream
  308. if i < totalMaterials then Format "," to:ostream
  309. Format "\n\n" to:ostream
  310. )
  311. Format "],\n\n" to:ostream
  312. )
  313. -------------------------------------------------------------------------------------
  314. -- Extract vertices from mesh
  315. function ExtractVertices obj whereto =
  316. (
  317. n = obj.numVerts
  318. for i = 1 to n do
  319. (
  320. v = GetVert obj i
  321. append whereto v
  322. )
  323. )
  324. -------------------------------------------------------------------------------------
  325. -- Extract normals from mesh
  326. function ExtractNormals obj whereto =
  327. (
  328. if smoothNormal.checked then
  329. (
  330. num = obj.numVerts
  331. for i = 1 to num do
  332. (
  333. n = GetNormal obj i
  334. append whereto n
  335. )
  336. )
  337. else
  338. (
  339. num = obj.numFaces
  340. for i = 1 to num do
  341. (
  342. n = GetFaceNormal obj i
  343. append whereto n
  344. )
  345. )
  346. )
  347. -------------------------------------------------------------------------------------
  348. -- Extract uvs from mesh
  349. function ExtractUvs obj whereto =
  350. (
  351. n = obj.numTVerts
  352. for i = 1 to n do
  353. (
  354. v = GetTVert obj i
  355. append whereto v
  356. )
  357. )
  358. -------------------------------------------------------------------------------------
  359. -- Extract faces from mesh
  360. function ExtractFaces objMesh objMaterial whereto allMaterials needsFlip offsetVert offsetUv =
  361. (
  362. n = objMesh.numFaces
  363. hasUVs = objMesh.numTVerts > 0
  364. useMultiMaterial = false
  365. materialIDList = #()
  366. if ( classof objMaterial ) == StandardMaterial then
  367. (
  368. fm = findItem allMaterials objMaterial
  369. )
  370. else
  371. (
  372. useMultiMaterial = true
  373. for i = 1 to n do
  374. (
  375. mID = GetFaceMatID objMesh i
  376. materialIndex = findItem objMaterial.materialIDList mID
  377. subMaterial = objMaterial.materialList[materialIndex]
  378. mMergedIndex = findItem allMaterials subMaterial
  379. if mMergedIndex > 0 then
  380. (
  381. materialIDList[mID] = mMergedIndex
  382. )
  383. )
  384. )
  385. for i = 1 to n do
  386. (
  387. zface = #()
  388. fv = GetFace objMesh i
  389. fv.x += offsetVert
  390. fv.y += offsetVert
  391. fv.z += offsetVert
  392. if useMultiMaterial then
  393. (
  394. mID = GetFaceMatID objMesh i
  395. fm = materialIDList[mID]
  396. )
  397. if hasUVs then
  398. (
  399. fuv = GetTVFace objMesh i
  400. fuv.x += offsetUv
  401. fuv.y += offsetUv
  402. fuv.z += offsetUv
  403. )
  404. else
  405. (
  406. fuv = false
  407. )
  408. append zface fv
  409. append zface fuv
  410. append zface fm
  411. append zface needsFlip
  412. append whereto zface
  413. )
  414. )
  415. -------------------------------------------------------------------------------------
  416. -- Extract materials from eventual multimaterial
  417. function ExtractMaterials objMesh objMaterial whereto =
  418. (
  419. materialClass = classof objMaterial
  420. if materialClass == StandardMaterial then
  421. (
  422. if ( findItem whereto objMaterial ) == 0 then
  423. (
  424. append whereto objMaterial
  425. )
  426. )
  427. else if materialClass == MultiMaterial then
  428. (
  429. n = objMesh.numFaces
  430. for i = 1 to n do
  431. (
  432. mID = getFaceMatId objMesh i
  433. materialIndex = findItem objMaterial.materialIDList mID
  434. subMaterial = objMaterial.materialList[materialIndex]
  435. if ( findItem whereto subMaterial ) == 0 then
  436. (
  437. append whereto subMaterial
  438. )
  439. )
  440. )
  441. )
  442. -------------------------------------------------------------------------------------
  443. -- Hack to figure out if normals are messed up
  444. function NeedsFaceFlip node =
  445. (
  446. needsFlip = false
  447. local tmp = Snapshot node
  448. face_normal = normalize ( getfacenormal tmp 1 )
  449. face = getface tmp 1
  450. va = getvert tmp face[1]
  451. vb = getvert tmp face[2]
  452. vc = getvert tmp face[3]
  453. computed_normal = normalize ( cross (vc - vb) (va - vb) )
  454. if distance computed_normal face_normal > 0.1 then needsFlip = true
  455. delete tmp
  456. return needsFlip
  457. )
  458. -------------------------------------------------------------------------------------
  459. -- Extract only things that either already are or can be converted to meshes
  460. function ExtractMesh node =
  461. (
  462. if SuperClassOf node == GeometryClass then
  463. (
  464. return #( SnapshotAsMesh node, node.name, node.material, NeedsFaceFlip node )
  465. )
  466. -- Not geometry ... could be a camera, light, etc.
  467. return #( false, node.name, 0 )
  468. )
  469. -------------------------------------------------------------------------------------
  470. -- Export scene
  471. function ExportScene =
  472. (
  473. -- Extract meshes
  474. meshObjects = #()
  475. mergedVertices = #()
  476. mergedNormals = #()
  477. mergedUvs = #()
  478. mergedFaces = #()
  479. mergedMaterials = #()
  480. for obj in selection do
  481. (
  482. result = ExtractMesh obj
  483. meshObj = result[1]
  484. meshName = result[2]
  485. meshMaterial = result[3]
  486. needsFlip = result[4]
  487. if ClassOf meshObj == TriMesh then
  488. (
  489. append meshObjects result
  490. ExtractMaterials meshObj meshMaterial mergedMaterials
  491. ExtractVertices meshObj mergedVertices
  492. ExtractNormals meshObj mergedNormals
  493. ExtractUvs meshObj mergedUvs
  494. --ExtractFaces meshObj zmaterial mergedFaces mergedVertices.count mergedUvs.count
  495. ExtractFaces meshObj meshMaterial mergedFaces mergedMaterials needsFlip 0 0
  496. )
  497. )
  498. totalVertices = mergedVertices.count
  499. totalNormals = mergedNormals.count
  500. totalUvs = mergedUvs.count
  501. totalFaces = mergedFaces.count
  502. totalMaterials = sceneMaterials.count
  503. -- Dump header
  504. Format headerFormat maxFileName totalVertices totalNormals totalUvs totalFaces totalMaterials to:ostream
  505. Format "// Source objects:\n\n" to:ostream
  506. i = 0
  507. for obj in meshObjects do
  508. (
  509. meshName = obj[2]
  510. Format "// %: %\n" i meshName to:ostream
  511. i += 1
  512. )
  513. Format "\n\nvar model = {\n\n" to:ostream
  514. -- Dump all materials in the scene
  515. ExportMaterials mergedMaterials
  516. -- Dump merged data from all selected geometries
  517. DumpVertices mergedVertices
  518. DumpNormals mergedNormals
  519. DumpUvs mergedUvs
  520. DumpFaces mergedFaces
  521. -- Dump footer
  522. Format footerFormat to:ostream
  523. )
  524. -------------------------------------------------------------------------------------
  525. -- Open and prepare a file handle for writing
  526. function GetSaveFileStream =
  527. (
  528. zname = getFilenameFile maxFileName
  529. zname += ".js"
  530. fname = GetSaveFileName filename:zname types:"JavaScript file (*.js)|*.js|All Files(*.*)|*.*|"
  531. if fname == undefined then
  532. return undefined
  533. ostream = CreateFile fname
  534. if ostream == undefined then
  535. (
  536. MessageBox "Couldn't open file for writing !"
  537. return undefined
  538. )
  539. return ostream
  540. )
  541. -------------------------------------------------------------------------------------
  542. -- Export button click handler
  543. on btn_export pressed do
  544. (
  545. ostream = GetSaveFileStream()
  546. if ostream != undefined then
  547. (
  548. ExportScene()
  549. close ostream
  550. )
  551. )
  552. )
  553. createDialog ThreeJSExporter width:300