ThreeJSExporter.ms 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893
  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.2"
  31. (
  32. label msg "Exports selected meshes in Three.js ascii JSON format" align:#left
  33. hyperLink lab1 "Original source at GitHub" address:"https://github.com/mrdoob/three.js" align:#left
  34. checkbox exportUv "Export uvs" checked:true enabled:true
  35. checkbox exportNormal "Export normals" checked:true enabled:true
  36. checkbox smoothNormal "Use vertex normals" checked:false enabled:true
  37. checkbox exportColor "Export vertex colors" checked:false enabled:false
  38. checkbox flipYZ "Flip YZ" checked:true enabled:true
  39. checkbox flipUV "Flip UV" checked:true enabled:true
  40. checkbox flipFace "Flip all faces" checked:false enabled:true
  41. checkbox autoflipFace "Try fixing flipped faces" checked:false enabled:true
  42. )
  43. button btn_export "Export selected objects"
  44. -------------------------------------------------------------------------------------
  45. -- Dump vertices
  46. function DumpVertices src =
  47. (
  48. Format "'vertices': [" to:ostream
  49. num = src.count
  50. if num > 0 then
  51. (
  52. for i = 1 to num do
  53. (
  54. vert = src[i]
  55. if flipYZ.checked then
  56. (
  57. x = vert.x
  58. y = vert.z
  59. z = vert.y
  60. z *= -1
  61. )
  62. else
  63. (
  64. x = vert.x
  65. y = vert.y
  66. z = vert.z
  67. )
  68. Format vertexFormat x y z to:ostream
  69. if i < num then Format "," to:ostream
  70. )
  71. )
  72. Format "],\n\n" to:ostream
  73. )
  74. -------------------------------------------------------------------------------------
  75. -- Dump normals
  76. function DumpNormals src =
  77. (
  78. Format "'normals': [" to:ostream
  79. num = src.count
  80. if num > 0 and exportNormal.checked then
  81. (
  82. for i = 1 to num do
  83. (
  84. normal = src[i]
  85. normal = normalize normal as point3
  86. if flipYZ.checked then
  87. (
  88. x = normal.x
  89. y = normal.z
  90. z = normal.y
  91. z *= -1
  92. )
  93. else
  94. (
  95. x = normal.x
  96. y = normal.y
  97. z = normal.z
  98. )
  99. Format vertexNormalFormat x y z to:ostream
  100. if i < num then Format "," to:ostream
  101. )
  102. )
  103. Format "],\n\n" to:ostream
  104. )
  105. -------------------------------------------------------------------------------------
  106. -- Dump uvs
  107. function DumpUvs src =
  108. (
  109. Format "'uvs': [" to:ostream
  110. num = src.count
  111. if num > 0 and exportUv.checked then
  112. (
  113. for i = 1 to num do
  114. (
  115. uvw = src[i]
  116. u = uvw.x
  117. if flipUV.checked then
  118. (
  119. v = 1 - uvw.y
  120. )
  121. else
  122. (
  123. v = uvw.y
  124. )
  125. Format UVFormat u v to:ostream
  126. if i < num then Format "," to:ostream
  127. )
  128. )
  129. Format "],\n\n" to:ostream
  130. )
  131. -------------------------------------------------------------------------------------
  132. -- Dump face type
  133. function DumpFaceType label content =
  134. (
  135. Format "'%': [" label to:ostream
  136. num = content.count
  137. if num > 0 then
  138. (
  139. for i = 1 to num do
  140. (
  141. zface = content[i]
  142. fv = zface[1]
  143. fuv = zface[2]
  144. m = zface[3] - 1
  145. needsFlip = zface[4]
  146. hasUVs = (classof fuv == Point3)
  147. va = (fv.x - 1) as Integer
  148. vb = (fv.y - 1) as Integer
  149. vc = (fv.z - 1) as Integer
  150. if smoothNormal.checked then
  151. (
  152. -- normals have the same indices as vertices
  153. na = va
  154. nb = vb
  155. nc = vc
  156. )
  157. else
  158. (
  159. -- normals have the same indices as face
  160. na = i - 1
  161. nb = na
  162. nc = na
  163. )
  164. if hasUVs then
  165. (
  166. ua = (fuv.x - 1) as Integer
  167. ub = (fuv.y - 1) as Integer
  168. uc = (fuv.z - 1) as Integer
  169. )
  170. if flipFace.checked or needsFlip then
  171. (
  172. tmp = vb
  173. vb = vc
  174. vc = tmp
  175. tmp = nb
  176. nb = nc
  177. nc = tmp
  178. if hasUVs then
  179. (
  180. tmp = ub
  181. ub = uc
  182. uc = tmp
  183. )
  184. )
  185. if label == "triangles" then
  186. (
  187. Format triFormat va vb vc m to:ostream
  188. )
  189. else if label == "trianglesUvs" then
  190. (
  191. Format triUVFormat va vb vc m ua ub uc to:ostream
  192. )
  193. else if label == "trianglesNormals" then
  194. (
  195. Format triNFormat va vb vc m na nb nc to:ostream
  196. )
  197. else if label == "trianglesNormalsUvs" then
  198. (
  199. Format triUVNFormat va vb vc m na nb nc ua ub uc to:ostream
  200. )
  201. if i < num then Format "," to:ostream
  202. )
  203. )
  204. Format "],\n\n" to:ostream
  205. )
  206. -------------------------------------------------------------------------------------
  207. -- Dump faces
  208. function DumpFaces src =
  209. (
  210. hasUVs = true
  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 hasDummyMaterial =
  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 or hasDummyMaterial ) then Format "," to:ostream
  309. Format "\n\n" to:ostream
  310. )
  311. if hasDummyMaterial then
  312. (
  313. Format "{\n" to:ostream
  314. Format "'DbgIndex' : %,\n" totalMaterials to:ostream
  315. Format "'DbgName' : '%',\n" "dummy" to:ostream
  316. Format "}" to:ostream
  317. )
  318. Format "],\n\n" to:ostream
  319. )
  320. -------------------------------------------------------------------------------------
  321. -- Extract vertices from mesh
  322. function ExtractVertices obj whereto =
  323. (
  324. n = obj.numVerts
  325. for i = 1 to n do
  326. (
  327. v = GetVert obj i
  328. append whereto v
  329. )
  330. )
  331. -------------------------------------------------------------------------------------
  332. -- Extract normals from mesh
  333. function ExtractNormals obj whereto needsFlip =
  334. (
  335. if smoothNormal.checked then
  336. (
  337. num = obj.numVerts
  338. for i = 1 to num do
  339. (
  340. n = GetNormal obj i
  341. if flipFace.checked or needsFlip then
  342. (
  343. n.x *= -1
  344. n.y *= -1
  345. n.z *= -1
  346. )
  347. append whereto n
  348. )
  349. )
  350. else
  351. (
  352. num = obj.numFaces
  353. for i = 1 to num do
  354. (
  355. n = GetFaceNormal obj i
  356. if flipFace.checked or needsFlip then
  357. (
  358. n.x *= -1
  359. n.y *= -1
  360. n.z *= -1
  361. )
  362. append whereto n
  363. )
  364. )
  365. )
  366. -------------------------------------------------------------------------------------
  367. -- Extract uvs from mesh
  368. function ExtractUvs obj whereto =
  369. (
  370. n = obj.numTVerts
  371. for i = 1 to n do
  372. (
  373. v = GetTVert obj i
  374. append whereto v
  375. )
  376. )
  377. -------------------------------------------------------------------------------------
  378. -- Extract faces from mesh
  379. function ExtractFaces objMesh objMaterial whereto allMaterials needsFlip offsetVert offsetUv =
  380. (
  381. n = objMesh.numFaces
  382. hasUVs = objMesh.numTVerts > 0
  383. useMultiMaterial = false
  384. materialIDList = #()
  385. materialClass = classof objMaterial
  386. if materialClass == StandardMaterial then
  387. (
  388. fm = findItem allMaterials objMaterial
  389. )
  390. else if materialClass == MultiMaterial then
  391. (
  392. useMultiMaterial = true
  393. for i = 1 to n do
  394. (
  395. mID = GetFaceMatID objMesh i
  396. materialIndex = findItem objMaterial.materialIDList mID
  397. subMaterial = objMaterial.materialList[materialIndex]
  398. mMergedIndex = findItem allMaterials subMaterial
  399. if mMergedIndex > 0 then
  400. (
  401. materialIDList[mID] = mMergedIndex
  402. )
  403. )
  404. )
  405. else
  406. (
  407. -- undefined material
  408. fm = 0
  409. )
  410. for i = 1 to n do
  411. (
  412. zface = #()
  413. fv = GetFace objMesh i
  414. fv.x += offsetVert
  415. fv.y += offsetVert
  416. fv.z += offsetVert
  417. if useMultiMaterial then
  418. (
  419. mID = GetFaceMatID objMesh i
  420. fm = materialIDList[mID]
  421. )
  422. if hasUVs then
  423. (
  424. fuv = GetTVFace objMesh i
  425. fuv.x += offsetUv
  426. fuv.y += offsetUv
  427. fuv.z += offsetUv
  428. )
  429. else
  430. (
  431. fuv = false
  432. )
  433. append zface fv
  434. append zface fuv
  435. append zface fm
  436. append zface needsFlip
  437. append whereto zface
  438. )
  439. )
  440. -------------------------------------------------------------------------------------
  441. -- Extract materials from eventual multimaterial
  442. function ExtractMaterials objMesh objMaterial whereto =
  443. (
  444. materialClass = classof objMaterial
  445. if materialClass == StandardMaterial then
  446. (
  447. if ( findItem whereto objMaterial ) == 0 then
  448. (
  449. append whereto objMaterial
  450. )
  451. )
  452. else if materialClass == MultiMaterial then
  453. (
  454. n = objMesh.numFaces
  455. for i = 1 to n do
  456. (
  457. mID = getFaceMatId objMesh i
  458. materialIndex = findItem objMaterial.materialIDList mID
  459. subMaterial = objMaterial.materialList[materialIndex]
  460. if ( findItem whereto subMaterial ) == 0 then
  461. (
  462. append whereto subMaterial
  463. )
  464. )
  465. )
  466. )
  467. -------------------------------------------------------------------------------------
  468. -- Hack to figure out if normals are messed up
  469. function NeedsFaceFlip node =
  470. (
  471. needsFlip = false
  472. local tmp = Snapshot node
  473. face_normal = normalize ( getfacenormal tmp 1 )
  474. face = getface tmp 1
  475. va = getvert tmp face[1]
  476. vb = getvert tmp face[2]
  477. vc = getvert tmp face[3]
  478. computed_normal = normalize ( cross (vc - vb) (va - vb) )
  479. if distance computed_normal face_normal > 0.1 then needsFlip = true
  480. delete tmp
  481. return needsFlip
  482. )
  483. -------------------------------------------------------------------------------------
  484. -- Extract only things that either already are or can be converted to meshes
  485. function ExtractMesh node =
  486. (
  487. if SuperClassOf node == GeometryClass then
  488. (
  489. needsFlip = false
  490. if autoflipFace.checked then
  491. (
  492. needsFlip = NeedsFaceFlip node
  493. )
  494. return #( SnapshotAsMesh node, node.name, node.material, needsFlip )
  495. )
  496. -- Not geometry ... could be a camera, light, etc.
  497. return #( false, node.name, 0 )
  498. )
  499. -------------------------------------------------------------------------------------
  500. -- Export scene
  501. function ExportScene =
  502. (
  503. -- Extract meshes
  504. meshObjects = #()
  505. mergedVertices = #()
  506. mergedNormals = #()
  507. mergedUvs = #()
  508. mergedFaces = #()
  509. mergedMaterials = #()
  510. hasDummyMaterial = true
  511. for obj in selection do
  512. (
  513. result = ExtractMesh obj
  514. meshObj = result[1]
  515. meshName = result[2]
  516. meshMaterial = result[3]
  517. needsFlip = result[4]
  518. if ClassOf meshObj == TriMesh then
  519. (
  520. append meshObjects result
  521. vertexOffset = mergedVertices.count
  522. uvOffset = mergedUvs.count
  523. ExtractMaterials meshObj meshMaterial mergedMaterials
  524. ExtractVertices meshObj mergedVertices
  525. ExtractNormals meshObj mergedNormals needsFlip
  526. ExtractUvs meshObj mergedUvs
  527. ExtractFaces meshObj meshMaterial mergedFaces mergedMaterials needsFlip vertexOffset uvOffset
  528. )
  529. )
  530. totalVertices = mergedVertices.count
  531. totalFaces = mergedFaces.count
  532. totalMaterials = mergedMaterials.count
  533. totalNormals = 0
  534. if exportNormal.checked then
  535. (
  536. totalNormals = mergedNormals.count
  537. )
  538. totalUvs = 0
  539. if exportUv.checked then
  540. (
  541. totalUvs = mergedUvs.count
  542. )
  543. -- Dump header
  544. Format headerFormat maxFileName totalVertices totalNormals totalUvs totalFaces totalMaterials to:ostream
  545. -- Dump objects (debug)
  546. Format "// Source objects:\n\n" to:ostream
  547. i = 0
  548. for obj in meshObjects do
  549. (
  550. meshName = obj[2]
  551. Format "// %: %\n" i meshName to:ostream
  552. i += 1
  553. )
  554. -- Dump model
  555. Format "\n\nvar model = {\n\n" to:ostream
  556. -- Dump all materials in the scene
  557. ExportMaterials mergedMaterials hasDummyMaterial
  558. -- Dump merged data from all selected geometries
  559. DumpVertices mergedVertices
  560. DumpNormals mergedNormals
  561. DumpUvs mergedUvs
  562. DumpFaces mergedFaces
  563. -- Dump footer
  564. Format footerFormat to:ostream
  565. )
  566. -------------------------------------------------------------------------------------
  567. -- Open and prepare a file handle for writing
  568. function GetSaveFileStream =
  569. (
  570. zname = getFilenameFile maxFileName
  571. zname += ".js"
  572. fname = GetSaveFileName filename:zname types:"JavaScript file (*.js)|*.js|All Files(*.*)|*.*|"
  573. if fname == undefined then
  574. return undefined
  575. ostream = CreateFile fname
  576. if ostream == undefined then
  577. (
  578. MessageBox "Couldn't open file for writing !"
  579. return undefined
  580. )
  581. return ostream
  582. )
  583. -------------------------------------------------------------------------------------
  584. -- Export button click handler
  585. on btn_export pressed do
  586. (
  587. ostream = GetSaveFileStream()
  588. if ostream != undefined then
  589. (
  590. ExportScene()
  591. close ostream
  592. )
  593. )
  594. )
  595. createDialog ThreeJSExporter width:300