ThreeJSAnimationExporter.ms 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735
  1. rollout ThreeJSExporter "ThreeJSExporter"
  2. (
  3. -- Variables
  4. local ostream,
  5. threeMatrix = (matrix3 [1,0,0] [0,0,1] [0,-1,0] [0,0,0]),
  6. headerFormat = "\"metadata\":
  7. {
  8. \"sourceFile\": \"%\",
  9. \"generatedBy\": \"3ds max ThreeJSExporter\",
  10. \"formatVersion\": 3,
  11. \"vertices\": %,
  12. \"normals\": %,
  13. \"colors\": %,
  14. \"uvs\": %,
  15. \"triangles\": %,
  16. \"materials\": %
  17. },
  18. ",
  19. vertexFormat = "%,%,%",
  20. vertexNormalFormat = "%,%,%",
  21. UVFormat = "%,%",
  22. triFormat = "%,%,%,%",
  23. triUVFormat = "%,%,%,%,%,%,%",
  24. triNFormat = "%,%,%,%,%,%,%",
  25. triUVNFormat = "%,%,%,%,%,%,%,%,%,%",
  26. footerFormat = "\n}",
  27. boneFormat = "\t\t{
  28. \t\t\t\"parent\" : %,
  29. \t\t\t\"name\" : \"%\",
  30. \t\t\t\"pos\" : %,
  31. \t\t\t\"scl\" : %,
  32. \t\t\t\"rotq\" : [%,%,%,%]
  33. \t\t}",
  34. animHeaderFormat = "\t\"animation\" : {
  35. \t\t\"name\" : \"Action\",
  36. \t\t\"fps\" : %,
  37. \t\t\"length\" : %,
  38. \t\t\"hierarchy\" : [\n",
  39. animBoneHeaderFormat = "\t\t\t{
  40. \t\t\t\t\"parent\" : %,
  41. \t\t\t\t\"keys\" : [\n",
  42. keyFormat = "\t\t\t\t\t{
  43. \t\t\t\t\t\t\"time\":%,
  44. \t\t\t\t\t\t\"pos\" :[%,%,%],
  45. \t\t\t\t\t\t\"rot\" :[%,%,%,%],
  46. \t\t\t\t\t\t\"scl\" :%
  47. \t\t\t\t\t}",
  48. animBoneFooterFormat = "\t\t\t\t]
  49. \t\t\t}",
  50. animFooterFormat = "\n\n\t\t]
  51. \t}\n"
  52. -------------------------------------------------------------------------------------
  53. -- User interface
  54. group "ThreeJSExporter v0.8"
  55. (
  56. label msg "Exports selected meshes in Three.js ascii JSON format" align:#left
  57. hyperLink lab1 "Original source at GitHub" address:"https://github.com/alteredq/three.js/blob/master/utils/exporters/max/ThreeJSExporter.ms" color:(color 255 120 0) align:#left
  58. label dummy1 "--------------------------------------------------------" align:#left
  59. checkbox exportColor "Export vertex colors" checked:false enabled:true
  60. checkbox exportUv "Export uvs" checked:true enabled:true
  61. checkbox exportNormal "Export normals" checked:true enabled:true
  62. checkbox smoothNormal "Use vertex normals" checked:false enabled:true
  63. label dummy2 "--------------------------------------------------------" align:#left
  64. checkbox flipYZ "Flip YZ" checked:false enabled:false
  65. checkbox flipUV "Flip UV" checked:false enabled:false
  66. checkbox flipFace "Flip all faces" checked:false enabled:false
  67. checkbox autoflipFace "Try fixing flipped faces" checked:false enabled:false
  68. label dummy3 "--------------------------------------------------------" align:#left
  69. spinner fps "Animation speed (FPS)" range:[0,1000,25] type:#integer
  70. label dummy4 "--------------------------------------------------------" align:#left
  71. button btn_export "Export selected objects"
  72. )
  73. -------------------------------------------------------------------------------------
  74. -- Dump vertices
  75. function DumpVertices src =
  76. (
  77. Format "\"vertices\": [" to:ostream
  78. num = src.count
  79. if num > 0 then
  80. (
  81. for i = 1 to num do
  82. (
  83. vert = src[i]
  84. if flipYZ.checked then
  85. (
  86. x = vert.x
  87. y = vert.z
  88. z = vert.y
  89. z *= -1
  90. )
  91. else
  92. (
  93. x = vert.x
  94. y = vert.y
  95. z = vert.z
  96. )
  97. Format vertexFormat x y z to:ostream
  98. if i < num then Format "," to:ostream
  99. )
  100. )
  101. Format "],\n\n" to:ostream
  102. )
  103. -------------------------------------------------------------------------------------
  104. -- Dump colors
  105. function DumpColors src useColors =
  106. (
  107. Format "\"colors\": [" to:ostream
  108. num = src.count
  109. if num > 0 and useColors then
  110. (
  111. for i = 1 to num do
  112. (
  113. col = src[i]
  114. r = col.r as Integer
  115. g = col.g as Integer
  116. b = col.b as Integer
  117. hexNum = ( bit.shift r 16 ) + ( bit.shift g 8 ) + b
  118. -- hexColor = formattedPrint hexNum format:"#x"
  119. -- Format "%" hexColor to:ostream
  120. decColor = formattedPrint hexNum format:"#d"
  121. Format "%" decColor to:ostream
  122. if i < num then Format "," to:ostream
  123. )
  124. )
  125. Format "],\n\n" to:ostream
  126. )
  127. -------------------------------------------------------------------------------------
  128. -- Dump normals
  129. function DumpNormals src =
  130. (
  131. Format "\"normals\": [" to:ostream
  132. num = src.count
  133. if num > 0 and exportNormal.checked then
  134. (
  135. for i = 1 to num do
  136. (
  137. normal = src[i]
  138. normal = normalize normal as point3
  139. if flipYZ.checked then
  140. (
  141. x = normal.x
  142. y = normal.z
  143. z = normal.y
  144. z *= -1
  145. )
  146. else
  147. (
  148. x = normal.x
  149. y = normal.y
  150. z = normal.z
  151. )
  152. Format vertexNormalFormat x y z to:ostream
  153. if i < num then Format "," to:ostream
  154. )
  155. )
  156. Format "],\n\n" to:ostream
  157. )
  158. -------------------------------------------------------------------------------------
  159. -- Dump uvs
  160. function DumpUvs src =
  161. (
  162. Format "\"uvs\": [[" to:ostream
  163. num = src.count
  164. if num > 0 and exportUv.checked then
  165. (
  166. for i = 1 to num do
  167. (
  168. uvw = src[i]
  169. u = uvw.x
  170. if flipUV.checked then
  171. (
  172. v = 1 - uvw.y
  173. )
  174. else
  175. (
  176. v = uvw.y
  177. )
  178. Format UVFormat u v to:ostream
  179. if i < num then Format "," to:ostream
  180. )
  181. )
  182. Format "]],\n\n" to:ostream
  183. )
  184. -------------------------------------------------------------------------------------
  185. -- Dump faces
  186. function DumpFaces src useColors =
  187. (
  188. Format "\"faces\": [" to:ostream
  189. num = src.count
  190. if num > 0 then
  191. (
  192. for i = 1 to num do
  193. (
  194. zface = src[i]
  195. fv = zface[1]
  196. fuv = zface[2]
  197. m = zface[3] - 1
  198. fc = zface[4]
  199. needsFlip = zface[5]
  200. isTriangle = true
  201. hasMaterial = true
  202. hasFaceUvs = false
  203. hasFaceVertexUvs = ((classof fuv == Point3) and exportUv.checked)
  204. hasFaceNormals = false
  205. hasFaceVertexNormals = (exportNormal.checked)
  206. hasFaceColors = false
  207. hasFaceVertexColors = ((classof fc == Point3) and useColors)
  208. faceType = 0
  209. faceType = bit.set faceType 1 (not isTriangle)
  210. faceType = bit.set faceType 2 hasMaterial
  211. faceType = bit.set faceType 3 hasFaceUvs
  212. faceType = bit.set faceType 4 hasFaceVertexUvs
  213. faceType = bit.set faceType 5 hasFaceNormals
  214. faceType = bit.set faceType 6 hasFaceVertexNormals
  215. faceType = bit.set faceType 7 hasFaceColors
  216. faceType = bit.set faceType 8 hasFaceVertexColors
  217. if i > 1 then
  218. (
  219. Format "," faceType to:ostream
  220. )
  221. Format "%" faceType to:ostream
  222. if isTriangle then
  223. (
  224. va = (fv.x - 1) as Integer
  225. vb = (fv.y - 1) as Integer
  226. vc = (fv.z - 1) as Integer
  227. if flipFace.checked or needsFlip then
  228. (
  229. tmp = vb
  230. vb = vc
  231. vc = tmp
  232. )
  233. Format ",%,%,%" va vb vc to:ostream
  234. if hasMaterial then
  235. (
  236. Format ",%" m to:ostream
  237. )
  238. if hasFaceVertexUvs then
  239. (
  240. ua = (fuv.x - 1) as Integer
  241. ub = (fuv.y - 1) as Integer
  242. uc = (fuv.z - 1) as Integer
  243. if flipFace.checked or needsFlip then
  244. (
  245. tmp = ub
  246. ub = uc
  247. uc = tmp
  248. )
  249. Format ",%,%,%" ua ub uc to:ostream
  250. )
  251. if hasFaceVertexNormals then
  252. (
  253. if smoothNormal.checked then
  254. (
  255. -- normals have the same indices as vertices
  256. na = va
  257. nb = vb
  258. nc = vc
  259. )
  260. else
  261. (
  262. -- normals have the same indices as face
  263. na = i - 1
  264. nb = na
  265. nc = na
  266. )
  267. if flipFace.checked or needsFlip then
  268. (
  269. tmp = nb
  270. nb = nc
  271. nc = tmp
  272. )
  273. Format ",%,%,%" na nb nc to:ostream
  274. )
  275. if hasFaceVertexColors then
  276. (
  277. ca = (fc.x - 1) as Integer
  278. cb = (fc.y - 1) as Integer
  279. cc = (fc.z - 1) as Integer
  280. if flipFace.checked or needsFlip then
  281. (
  282. tmp = cb
  283. cb = cc
  284. cc = tmp
  285. )
  286. Format ",%,%,%" ca cb cc to:ostream
  287. )
  288. )
  289. )
  290. )
  291. Format "]" to:ostream
  292. )
  293. -------------------------------------------------------------------------------------
  294. -- Dump color
  295. function DumpColor pcolor label =
  296. (
  297. r = pcolor.r / 255
  298. g = pcolor.g / 255
  299. b = pcolor.b / 255
  300. fr = formattedPrint r format:".4f"
  301. fg = formattedPrint g format:".4f"
  302. fb = formattedPrint b format:".4f"
  303. Format "\"%\" : [%, %, %],\n" label fr fg fb to:ostream
  304. )
  305. -------------------------------------------------------------------------------------
  306. -- Dump map
  307. function DumpMap pmap label =
  308. (
  309. if classof pmap == BitmapTexture then
  310. (
  311. bm = pmap.bitmap
  312. if bm != undefined then
  313. (
  314. fname = filenameFromPath bm.filename
  315. Format "\"%\" : \"skins/%\",\n" label fname to:ostream
  316. )
  317. )
  318. )
  319. -------------------------------------------------------------------------------------
  320. -- Dump bones
  321. -- src = #( #(index, name, position, scale, rotation), .. )
  322. -- boneOrder lists the correct output order of the bones
  323. -- newIndices is inverse of boneOrder
  324. function DumpBones src boneOrder newIndices =
  325. (
  326. numBones = boneOrder.count
  327. Format ",\n\n\t\"bones\" : [\n" to:ostream
  328. for i = 1 to numBones do
  329. (
  330. b = src[boneOrder[i]]
  331. if b[1] == -1 then
  332. (
  333. parent_index = -1
  334. ) else (
  335. parent_index = newIndices[b[1]+1]
  336. )
  337. bone_name = b[2]
  338. p = b[3]
  339. s = b[4]
  340. r = b[5]
  341. Format boneFormat parent_index bone_name p s r.x r.y r.z r.w to:ostream
  342. if (i < numBones) then (Format "," to:ostream)
  343. Format "\n\n" to:ostream
  344. )
  345. Format "\t],\n\n" to:ostream
  346. )
  347. -------------------------------------------------------------------------------------
  348. -- Dump skin indices
  349. -- src = #( #(skinned?, #(index1A, index1B, ..), name )
  350. -- If the mesh wasn't skinned, look in boneNames for its parent to fix the index
  351. -- If it's not there, leave as 0
  352. -- boneOrder lists the correct output order of the bones
  353. -- newIndices is inverse of boneOrder
  354. function DumpIndices src boneNames newIndices =
  355. (
  356. output = #()
  357. for i=1 to src.count do
  358. (
  359. if src[i][1] then
  360. (
  361. join output src[i][2]
  362. ) else (
  363. bone = findItem boneNames src[i][3]
  364. for j=1 to src[i][2].count do
  365. (
  366. src[i][2][j] = bone
  367. )
  368. join output src[i][2]
  369. )
  370. )
  371. Format "\t\"skinIndices\" : [" to:ostream
  372. num = output.count
  373. if num > 0 then
  374. (
  375. for i = 1 to num do
  376. (
  377. Format "%" (newIndices[output[i] + 1]) to:ostream
  378. if i < num then
  379. (
  380. Format "," to:ostream
  381. )
  382. )
  383. )
  384. Format "],\n\n" to:ostream
  385. )
  386. -------------------------------------------------------------------------------------
  387. -- Dump skin weights
  388. -- src = #( weight1, weight2, .. )
  389. function DumpWeights src =
  390. (
  391. Format "\t\"skinWeights\" : [" to:ostream
  392. num = src.count
  393. if num > 0 then
  394. (
  395. for i = 1 to num do
  396. (
  397. Format "%" src[i] to:ostream
  398. if i < num then Format "," to:ostream
  399. )
  400. )
  401. Format "],\n\n" to:ostream
  402. )
  403. -------------------------------------------------------------------------------------
  404. -- Dump the keyframes for every bone
  405. -- src = #( #( parent, #( time, #( posx, posy, posz ), rot, scl ), .. ), .. )
  406. -- ||---Bone-- |---------------Keyframe----------------| ----||
  407. -- boneOrder lists the correct output order of the bones
  408. -- newIndices is inverse of boneOrder
  409. function DumpKeyframes src boneOrder newIndices fps =
  410. (
  411. Format animHeaderFormat fps src[1][2][src[1][2].count][1] to:ostream
  412. numBones = boneOrder.count
  413. for i = 1 to (numBones) do
  414. (
  415. if (src[boneOrder[i]][1] == -1) then
  416. (
  417. parent_index = -1
  418. ) else
  419. (
  420. parent_index = newIndices[src[boneOrder[i]][1]+1]
  421. )
  422. Format animBoneHeaderFormat parent_index to:ostream
  423. bnkeys = src[boneOrder[i]][2]
  424. for j = 1 to bnkeys.count do
  425. (
  426. Format keyFormat bnkeys[j][1] bnkeys[j][2][1] bnkeys[j][2][2] bnkeys[j][2][3] bnkeys[j][3].x bnkeys[j][3].y bnkeys[j][3].z bnkeys[j][3].w bnkeys[j][4] to:ostream
  427. if j < bnkeys.count then Format "," to:ostream
  428. Format "\n" to:ostream
  429. )
  430. Format animBoneFooterFormat to:ostream
  431. if i < (numBones) then
  432. (
  433. Format "," to:ostream
  434. )
  435. Format "\n" to:ostream
  436. )
  437. Format animFooterFormat to:ostream
  438. )
  439. -------------------------------------------------------------------------------------
  440. -- Dump the morphtargets
  441. -- src = #( #( #(index, name, vertices = #( #( x,y,z ), .. ) ), .. ), .. )
  442. -- |List of meshes ----------------------------------------------|
  443. -- |- |List of targets: only one mesh may have multiple ---| ----|
  444. -- |- |- |Individual target(s) ----------------------| ----| ----|
  445. function DumpMorphTargets src =
  446. (
  447. -- This procedure assumes that only one element of src has actual morph targets.
  448. -- The targets field of the other elements is used to store the vertices of static meshes.
  449. -- These vertices are duplicated and joined with the vertices of the actual morph targets.
  450. -- Initialize with whatever happens to be first
  451. output = src[1]
  452. for m=2 to src.count do
  453. (
  454. if (src[m].count == 1) then
  455. (
  456. -- This is a static mesh; attach its vertices, but do nothing else.
  457. for t=1 to output.count do
  458. (
  459. join output[t][3] src[m][1][3]
  460. )
  461. ) else (
  462. -- This mesh contains morph targets.
  463. -- Duplicate the static vertices, join with each morph target, and set the indices and names.
  464. while ( output.count < src[m].count ) do
  465. (
  466. -- Duplicate vertices
  467. append output (deepCopy output[1])
  468. )
  469. for t=1 to src[m].count do
  470. (
  471. -- Vertices
  472. join output[t][3] src[m][t][3]
  473. -- Index, name
  474. output[t][1] = src[m][t][1]
  475. output[t][2] = src[m][t][2]
  476. )
  477. )
  478. )
  479. Format "\"morphTargets\": [" to:ostream
  480. for k=1 to output.count do
  481. (
  482. target = output[k]
  483. Format "{ \"name\": \"morph_%\", \"vertices\": [" target[2] to:ostream
  484. vertices = target[3]
  485. for j=1 to vertices.count do
  486. (
  487. Format "%,%,%" vertices[j][1] vertices[j][2] vertices[j][3] to:ostream
  488. if (j != vertices.count) then
  489. (
  490. Format "," to:ostream
  491. )
  492. )
  493. Format "] }" to:ostream
  494. if (k != output.count) then
  495. (
  496. Format ",\n" to:ostream
  497. )
  498. )
  499. Format "],\n" to:ostream
  500. )
  501. -------------------------------------------------------------------------------------
  502. -- Export materials
  503. function ExportMaterials zmaterials zcolors =
  504. (
  505. Format "\"materials\": [\n" to:ostream
  506. totalMaterials = zmaterials.count
  507. for i = 1 to totalMaterials do
  508. (
  509. mat = zmaterials[i]
  510. Format "{\n" to:ostream
  511. -- debug
  512. Format "\"DbgIndex\" : %,\n" (i-1) to:ostream
  513. if classof mat != BooleanClass then
  514. (
  515. useVertexColors = zcolors[i]
  516. Format "\"DbgName\" : \"%\",\n" mat.name to:ostream
  517. -- colors
  518. DumpColor mat.diffuse "colorDiffuse"
  519. DumpColor mat.ambient "colorAmbient"
  520. DumpColor mat.specular "colorSpecular"
  521. t = mat.opacity / 100
  522. s = mat.glossiness
  523. Format "\"transparency\" : %,\n" t to:ostream
  524. Format "\"specularCoef\" : %,\n" s to:ostream
  525. -- maps
  526. DumpMap mat.diffuseMap "mapDiffuse"
  527. DumpMap mat.ambientMap "mapAmbient"
  528. DumpMap mat.specularMap "mapSpecular"
  529. DumpMap mat.bumpMap "mapBump"
  530. DumpMap mat.opacityMap "mapAlpha"
  531. )
  532. else
  533. (
  534. useVertexColors = false
  535. Format "\"DbgName\" : \"%\",\n" "dummy" to:ostream
  536. DumpColor red "colorDiffuse"
  537. )
  538. Format "\"vertexColors\" : %\n" useVertexColors to:ostream
  539. Format "}" to:ostream
  540. if ( i < totalMaterials ) then Format "," to:ostream
  541. Format "\n\n" to:ostream
  542. )
  543. Format "],\n\n" to:ostream
  544. )
  545. -------------------------------------------------------------------------------------
  546. -- Extract vertices from mesh
  547. function ExtractVertices obj whereto =
  548. (
  549. n = obj.numVerts
  550. for i = 1 to n do
  551. (
  552. v = GetVert obj i
  553. append whereto v
  554. )
  555. )
  556. -------------------------------------------------------------------------------------
  557. -- Extract vertex colors from mesh
  558. function ExtractColors obj whereto =
  559. (
  560. nColors = GetNumCPVVerts obj
  561. if nColors > 0 then
  562. (
  563. for i = 1 to nColors do
  564. (
  565. c = GetVertColor obj i
  566. append whereto c
  567. )
  568. )
  569. )
  570. -------------------------------------------------------------------------------------
  571. -- Extract normals from mesh
  572. function ExtractNormals obj whereto needsFlip =
  573. (
  574. if smoothNormal.checked then
  575. (
  576. num = obj.numVerts
  577. for i = 1 to num do
  578. (
  579. n = GetNormal obj i
  580. if flipFace.checked or needsFlip then
  581. (
  582. n.x *= -1
  583. n.y *= -1
  584. n.z *= -1
  585. )
  586. append whereto n
  587. )
  588. )
  589. else
  590. (
  591. num = obj.numFaces
  592. for i = 1 to num do
  593. (
  594. n = GetFaceNormal obj i
  595. if flipFace.checked or needsFlip then
  596. (
  597. n.x *= -1
  598. n.y *= -1
  599. n.z *= -1
  600. )
  601. append whereto n
  602. )
  603. )
  604. )
  605. -------------------------------------------------------------------------------------
  606. -- Extract uvs from mesh
  607. function ExtractUvs obj whereto =
  608. (
  609. n = obj.numTVerts
  610. for i = 1 to n do
  611. (
  612. v = GetTVert obj i
  613. append whereto v
  614. )
  615. )
  616. -------------------------------------------------------------------------------------
  617. -- Extract faces from mesh
  618. function ExtractFaces objMesh objMaterial whereto allMaterials needsFlip hasVColors offsetVert offsetUv offsetColor =
  619. (
  620. n = objMesh.numFaces
  621. hasUVs = objMesh.numTVerts > 0
  622. useMultiMaterial = false
  623. materialIDList = #()
  624. materialClass = classof objMaterial
  625. if materialClass == StandardMaterial then
  626. (
  627. fm = findItem allMaterials objMaterial
  628. )
  629. else if materialClass == MultiMaterial then
  630. (
  631. useMultiMaterial = true
  632. for i = 1 to n do
  633. (
  634. mID = GetFaceMatID objMesh i
  635. materialIndex = findItem objMaterial.materialIDList mID
  636. if materialIndex > 0 then
  637. (
  638. subMaterial = objMaterial.materialList[materialIndex]
  639. mMergedIndex = findItem allMaterials subMaterial
  640. if mMergedIndex > 0 then
  641. (
  642. materialIDList[mID] = mMergedIndex
  643. )
  644. else
  645. (
  646. materialIDList[mID] = findItem allMaterials false
  647. )
  648. )
  649. else
  650. (
  651. materialIDList[mID] = findItem allMaterials false
  652. )
  653. )
  654. )
  655. else
  656. (
  657. -- undefined material
  658. fm = findItem allMaterials false
  659. )
  660. for i = 1 to n do
  661. (
  662. zface = #()
  663. fv = GetFace objMesh i
  664. fv.x += offsetVert
  665. fv.y += offsetVert
  666. fv.z += offsetVert
  667. if useMultiMaterial then
  668. (
  669. mID = GetFaceMatID objMesh i
  670. fm = materialIDList[mID]
  671. )
  672. if hasUVs then
  673. (
  674. fuv = GetTVFace objMesh i
  675. fuv.x += offsetUv
  676. fuv.y += offsetUv
  677. fuv.z += offsetUv
  678. )
  679. else
  680. (
  681. fuv = false
  682. )
  683. if hasVColors then
  684. (
  685. fc = GetVCFace objMesh i
  686. fc.x += offsetColor
  687. fc.y += offsetColor
  688. fc.z += offsetColor
  689. )
  690. else
  691. (
  692. fc = false
  693. )
  694. append zface fv
  695. append zface fuv
  696. append zface fm
  697. append zface fc
  698. append zface needsFlip
  699. append whereto zface
  700. )
  701. )
  702. -------------------------------------------------------------------------------------
  703. -- Extract materials from eventual multi-material
  704. function ExtractMaterials objMesh objMaterial whereto wheretoColors zname hasVColors =
  705. (
  706. materialClass = classof objMaterial
  707. if materialClass == StandardMaterial then
  708. (
  709. if ( findItem whereto objMaterial ) == 0 then
  710. (
  711. append whereto objMaterial
  712. append wheretoColors hasVColors
  713. )
  714. )
  715. else if materialClass == MultiMaterial then
  716. (
  717. n = objMesh.numFaces
  718. for i = 1 to n do
  719. (
  720. mID = getFaceMatId objMesh i
  721. materialIndex = findItem objMaterial.materialIDList mID
  722. if materialIndex > 0 then
  723. (
  724. subMaterial = objMaterial.materialList[materialIndex]
  725. if ( findItem whereto subMaterial ) == 0 then
  726. (
  727. append whereto subMaterial
  728. append wheretoColors hasVColors
  729. )
  730. )
  731. )
  732. )
  733. else
  734. (
  735. -- unknown or undefined material
  736. append whereto false
  737. append wheretoColors false
  738. )
  739. )
  740. -------------------------------------------------------------------------------------
  741. -- Hack to figure out if normals are messed up
  742. function NeedsFaceFlip node =
  743. (
  744. needsFlip = false
  745. local tmp = Snapshot node
  746. face_normal = normalize ( getfacenormal tmp 1 )
  747. face = getface tmp 1
  748. va = getvert tmp face[1]
  749. vb = getvert tmp face[2]
  750. vc = getvert tmp face[3]
  751. computed_normal = normalize ( cross (vc - vb) (va - vb) )
  752. if distance computed_normal face_normal > 0.1 then needsFlip = true
  753. delete tmp
  754. return needsFlip
  755. )
  756. -------------------------------------------------------------------------------------
  757. -- Extract only things that either already are or can be converted to meshes
  758. function ExtractMesh node =
  759. (
  760. if SuperClassOf node == GeometryClass then
  761. (
  762. needsFlip = false
  763. hasVColors = false
  764. zmesh = SnapshotAsMesh node
  765. if autoflipFace.checked then
  766. (
  767. needsFlip = NeedsFaceFlip node
  768. )
  769. if exportColor.checked and ( getNumCPVVerts zmesh ) > 0 then
  770. (
  771. hasVColors = true
  772. )
  773. return #( zmesh, node.name, node.material, needsFlip, hasVColors )
  774. )
  775. -- Not geometry ... could be a camera, light, etc.
  776. return #( false, node.name, 0, false, false )
  777. )
  778. -------------------------------------------------------------------------------------
  779. -- Extract the morph targets
  780. -- whereto = #( #( #(index, name, vertices = #( #( x,y,z ), .. ) ), .. ), .. )
  781. -- |List of meshes -----------------------------------------------|
  782. -- |- |List of targets -------------------------------------| ----|
  783. -- |- |- |Individual target --------------------------| ----| ----|
  784. function ExtractMorphTargets node whereto &morphFlag =
  785. (
  786. targets = #()
  787. morphs = #()
  788. if ( node.modifiers[#morpher] != undefined ) then (
  789. -- Export the morph target, if one exists
  790. morphFlag = true
  791. for i=1 to 100 do
  792. (
  793. nPts = WM3_MC_NumMPts node.morpher i
  794. if (nPts > 0) then
  795. (
  796. append targets #(i, nPts)
  797. )
  798. )
  799. --Set all to zero
  800. for k=1 to targets.count do
  801. (
  802. i = targets[k][1]
  803. node.morpher[i].controller.value = 0
  804. )
  805. --Max out one at a time, record it, then zero out again
  806. for k=1 to targets.count do
  807. (
  808. i = targets[k][1]
  809. numVerts = targets[k][2]
  810. name = WM3_MC_GetName node.morpher i
  811. verts = #()
  812. node.morpher[i].controller.value = 100
  813. for j = 1 to numVerts do
  814. (
  815. p = GetVert node j
  816. append verts #(p.x, p.y, p.z)
  817. )
  818. node.morpher[i].controller.value = 0
  819. append morphs #(i, name, verts)
  820. )
  821. append whereto morphs
  822. ) else (
  823. -- Export the mesh vertices as a dummy morph target
  824. verts = #()
  825. for k=1 to node.numVerts do
  826. (
  827. p = GetVert node k
  828. append verts #(p.x, p.y, p.z)
  829. )
  830. dummy = #()
  831. append dummy #(0, "DUMMY", verts)
  832. append whereto dummy
  833. )
  834. )
  835. -------------------------------------------------------------------------------------
  836. -- Transforms the matrix of a bone into its parent space, if a parent exists.
  837. function thinkLocally bone_node localForm =
  838. (
  839. localForm = bone_node.transform
  840. if ( bone_node.parent != undefined ) then
  841. (
  842. parentForm = bone_node.parent.transform
  843. localForm = localForm * inverse parentForm
  844. )
  845. newLocal = matrix3 1
  846. px = localForm.translationpart.x
  847. py = localForm.translationpart.y
  848. pz = localForm.translationpart.z
  849. lTran = transMatrix (localForm.translationpart)
  850. lRot = (inverse localForm.rotationpart) as matrix3
  851. lScale = scaleMatrix localForm.scalepart
  852. localForm = lScale * lRot * lTran * newLocal
  853. localForm
  854. )
  855. -------------------------------------------------------------------------------------
  856. -- Extract bones and keyframes
  857. function ExtractAnimation node bones keyframes FPS bone_names &skinFlag =
  858. (
  859. if node.modifiers[#skin] != undefined then
  860. (
  861. skinFlag = true
  862. ---------------------------------------------------------------------------------
  863. -- A dummy root bone is first created and roatated to orient the model
  864. p = (matrix3 1).translationpart
  865. s = (matrix3 1).scalepart
  866. r = (matrix3 1).rotationpart
  867. append bones #(-1,"flipRoot",p,s,r)
  868. /*
  869. if (flipYZ.checked) then
  870. (
  871. r = threeMatrix.rotationpart
  872. ) else (
  873. r = (matrix3 1).rotationpart
  874. )
  875. */
  876. r = threeMatrix.rotationpart
  877. root_keys = #(#(0, p, r, s))
  878. slidertime = animationrange.start
  879. while (slidertime < animationrange.end) do
  880. (
  881. slidertime += 1
  882. sTime = ((slidertime - animationrange.start) / FPS) as String
  883. append root_keys #(substring sTime 1 (sTime.count - 1), p, r, s)
  884. )
  885. append keyframes #(-1, root_keys)
  886. ---------------------------------------------------------------------------------
  887. -- The model's bones and keyframes are then extracted
  888. max modify mode
  889. modPanel.setCurrentObject node.modifiers[#skin]
  890. total_bones = skinops.getnumberbones node.modifiers[#skin]
  891. vertex_count = getNumverts node
  892. -- Find parents by looking up their names; bone names MUST be unique
  893. -- Can't guarantee that parent will be read before child; store all names beforehand
  894. for i = 1 to total_bones do
  895. (
  896. bone_name = skinops.getbonename node.modifiers[#skin] i 0
  897. append bone_names bone_name
  898. )
  899. for i = 1 to total_bones do
  900. (
  901. slidertime = animationrange.start
  902. bone_name = skinops.getbonename node.modifiers[#skin] i 0
  903. bone_node = getNodeByName bone_name
  904. parent_index = 0
  905. if ( bone_node.parent != undefined ) then
  906. (
  907. parent_name = bone_node.parent.name
  908. parent_index = (findItem bone_names parent_name)
  909. )
  910. localForm = bone_node.transform
  911. localForm = thinkLocally bone_node localForm
  912. p = localForm.translationpart
  913. r = localForm.rotationpart
  914. s = localForm.scalepart
  915. append bones #(parent_index, bone_name, p, bone_node.transform.scalepart, r)
  916. in coordsys parent bone_keys = #(#(0, p, r, bone_node.transform.scalepart))
  917. while (slidertime < animationrange.end) do
  918. (
  919. slidertime += 1
  920. sTime = ((slidertime - animationrange.start) / FPS) as String
  921. localForm = bone_node.transform
  922. localForm = thinkLocally bone_node localForm
  923. p = localForm.translationpart
  924. r = localForm.rotationpart
  925. s = localForm.translationpart
  926. append bone_keys #(substring sTime 1 (sTime.count - 1), p, r, bone_node.transform.scalepart)
  927. )
  928. append keyframes #(parent_index, bone_keys)
  929. )
  930. )
  931. )
  932. -------------------------------------------------------------------------------------
  933. -- Extract the skin indices and weights in one pass
  934. -- If it's a skin, skinned? = true and indices contains the bones
  935. -- If it's not, indices is dummied to #(0,..) and DumpIndices uses the parent to fix it in post
  936. -- indices = #( #( skinned?, indices, parent), ..)
  937. function ExtractInfluences node indices weights =
  938. (
  939. vertex_count = getNumverts node
  940. meshIndices = #()
  941. if node.modifiers[#skin] != undefined then
  942. (
  943. for i = 1 to vertex_count do
  944. (
  945. -- Insane defaults for the sort; these shouldn't escape into the output
  946. index1 = -1
  947. index2 = -1
  948. weight1 = -1
  949. weight2 = -1
  950. numBones = skinOps.GetVertexWeightCount node.modifiers[#skin] i
  951. --Two passes of a bubble sort to get the 2 heaviest weights
  952. for j = 1 to numBones do
  953. (
  954. thisIndex = skinops.getVertexWeightBoneID node.modifiers[#skin] i j
  955. thisWeight = skinops.getvertexweight node.modifiers[#skin] i j
  956. if (thisWeight) > weight1 then
  957. (
  958. weight1 = thisWeight
  959. index1 = thisIndex
  960. )
  961. )
  962. for j = 1 to numBones do
  963. (
  964. thisIndex = skinops.getVertexWeightBoneID node.modifiers[#skin] i j
  965. thisWeight = skinops.getvertexweight node.modifiers[#skin] i j
  966. if ((thisWeight > weight2) and (thisIndex != index1)) then
  967. (
  968. weight2 = thisWeight
  969. index2 = thisIndex
  970. )
  971. )
  972. -- Establish legal defaults: no weight from the root
  973. if (index1 == -1) then
  974. (
  975. index1 = 0
  976. weight1 = 0
  977. )
  978. if (index2 == -1) then
  979. (
  980. index2 = 0
  981. weight2 = 0
  982. )
  983. append meshIndices (index1)
  984. append meshIndices (index2)
  985. append weights weight1
  986. append weights weight2
  987. )
  988. append indices #(true, meshIndices, "ROOT")
  989. ) else (
  990. for i = 1 to vertex_count do
  991. (
  992. append meshIndices 0
  993. append meshIndices 0
  994. append weights 1
  995. append weights 1
  996. )
  997. name = "Scene Root"
  998. if node.parent != undefined then
  999. (
  1000. name = node.parent.name
  1001. )
  1002. append indices #(false, meshIndices, name)
  1003. )
  1004. )
  1005. -------------------------------------------------------------------------------------
  1006. -- Enforce that parent is above all of its children in the output
  1007. -- This fixes several amusing bugs (mostly fingers of infinite length)
  1008. --
  1009. -- boneOrder: order to dump the bones in #(bones)
  1010. -- newIndices: new positional indices of bones
  1011. function ReorderBones bones boneOrder newIndices =
  1012. (
  1013. /*************************************************************************************
  1014. * Reorder bones
  1015. * Python function prototype:
  1016. * for i in range(n):
  1017. * for b in range(n):
  1018. * #new bone parent of bone legal
  1019. * if not inOut[b] and (parents[b] == -1 or inOut[parents[b]]):
  1020. * inOut[b] = True
  1021. * boneOrder.append(b)
  1022. * break;
  1023. *************************************************************************************/
  1024. total_bones = bones.count
  1025. -- Keeps track of which parents have been accounted for
  1026. inOut = #()
  1027. for i = 1 to total_bones do
  1028. (
  1029. append inOut false
  1030. )
  1031. rootNotAdded = true
  1032. for i = 1 to total_bones do
  1033. (
  1034. notFound = true
  1035. for b = 1 to total_bones while notFound do
  1036. (
  1037. if (inOut[b] != true) then
  1038. (
  1039. if (rootNotAdded and bones[b][1] == -1) then
  1040. (
  1041. inOut[b] = true
  1042. append boneOrder b
  1043. notFound = false
  1044. rootNotAdded = false
  1045. ) else (
  1046. if (inOut[bones[b][1] + 1]) then
  1047. (
  1048. inOut[b] = true
  1049. append boneOrder b
  1050. notFound = false
  1051. )
  1052. )
  1053. )
  1054. )
  1055. )
  1056. -- Takes original bone index/parent + 1, returns new correct index for parent, skinIndices, etc
  1057. for i=1 to total_bones do
  1058. (
  1059. newIndices[boneOrder[i]] = i - 1
  1060. )
  1061. )
  1062. -------------------------------------------------------------------------------------
  1063. -- Export scene
  1064. --
  1065. -- This will BREAK in HORRIBLE WAYS if you feed it more than one object for now.
  1066. function ExportScene =
  1067. (
  1068. slidertime = animationrange.start
  1069. -- Extract meshes
  1070. meshObjects = #()
  1071. mergedVertices = #()
  1072. mergedNormals = #()
  1073. mergedColors = #()
  1074. mergedUvs = #()
  1075. mergedFaces = #()
  1076. mergedMaterials = #()
  1077. mergedMaterialsColors = #()
  1078. sceneHasVColors = false
  1079. hasSkin = false
  1080. bones = #()
  1081. keyframes = #()
  1082. influences = #()
  1083. weights = #()
  1084. boneOrder = #()
  1085. newIndices = #()
  1086. bone_names = #()
  1087. hasMorph = false
  1088. mergedMorphTargets = #()
  1089. -- The horrible hackery that is skinops requires only one object be selected.
  1090. original_selection = #()
  1091. for obj in selection do
  1092. (
  1093. append original_selection obj.name
  1094. )
  1095. max select none
  1096. for name in original_selection do
  1097. (
  1098. obj = getnodebyname name
  1099. select obj
  1100. result = ExtractMesh obj
  1101. meshObj = result[1]
  1102. if ClassOf meshObj == TriMesh then
  1103. (
  1104. meshName = result[2]
  1105. meshMaterial = result[3]
  1106. needsFlip = result[4]
  1107. hasVColors = result[5]
  1108. sceneHasVColors = sceneHasVColors or hasVColors
  1109. append meshObjects result
  1110. vertexOffset = mergedVertices.count
  1111. uvOffset = mergedUvs.count
  1112. colorOffset = mergedColors.count
  1113. ExtractMaterials meshObj meshMaterial mergedMaterials mergedMaterialsColors meshName hasVColors
  1114. ExtractVertices meshObj mergedVertices
  1115. ExtractNormals meshObj mergedNormals needsFlip
  1116. ExtractColors meshObj mergedColors
  1117. ExtractUvs meshObj mergedUvs
  1118. ExtractFaces meshObj meshMaterial mergedFaces mergedMaterials needsFlip hasVColors vertexOffset uvOffset colorOffset
  1119. ExtractAnimation obj bones keyframes fps.value bone_names &hasSkin
  1120. ExtractInfluences obj influences weights
  1121. ReorderBones bones boneOrder newIndices
  1122. ExtractMorphTargets obj mergedMorphTargets &hasMorph
  1123. )
  1124. max select none
  1125. )
  1126. totalVertices = mergedVertices.count
  1127. totalFaces = mergedFaces.count
  1128. totalMaterials = mergedMaterials.count
  1129. totalColors = 0
  1130. totalNormals = 0
  1131. totalUvs = 0
  1132. useColors = false
  1133. if sceneHasVColors and exportColor.checked then
  1134. (
  1135. totalColors = mergedColors.count
  1136. useColors = true
  1137. )
  1138. if exportNormal.checked then
  1139. (
  1140. totalNormals = mergedNormals.count
  1141. )
  1142. if exportUv.checked then
  1143. (
  1144. totalUvs = mergedUvs.count
  1145. )
  1146. -- Dump model
  1147. Format "{\n\n" to:ostream
  1148. -- Dump header
  1149. Format headerFormat maxFileName totalVertices totalNormals totalColors totalUvs totalFaces totalMaterials to:ostream
  1150. -- Dump all materials in the scene
  1151. ExportMaterials mergedMaterials mergedMaterialsColors
  1152. -- Dump merged data from all selected geometries
  1153. DumpVertices mergedVertices
  1154. if hasMorph then
  1155. (
  1156. DumpMorphTargets mergedMorphTargets
  1157. )
  1158. DumpNormals mergedNormals
  1159. DumpColors mergedColors useColors
  1160. DumpUvs mergedUvs
  1161. DumpFaces mergedFaces useColors
  1162. if hasSkin then
  1163. (
  1164. DumpBones bones boneOrder newIndices
  1165. DumpIndices influences bone_names newIndices
  1166. DumpWeights weights
  1167. DumpKeyframes keyframes boneOrder newIndices fps.value
  1168. )
  1169. -- Dump footer
  1170. Format footerFormat to:ostream
  1171. )
  1172. -------------------------------------------------------------------------------------
  1173. -- Open and prepare a file handle for writing
  1174. function GetSaveFileStream =
  1175. (
  1176. zname = getFilenameFile maxFileName
  1177. zname += ".js"
  1178. fname = GetSaveFileName filename:zname types:"JavaScript file (*.js)|*.js|All Files(*.*)|*.*|"
  1179. if fname == undefined then
  1180. (
  1181. return undefined
  1182. )
  1183. ostream = CreateFile fname
  1184. if ostream == undefined then
  1185. (
  1186. MessageBox "Couldn't open file for writing !"
  1187. return undefined
  1188. )
  1189. return ostream
  1190. )
  1191. -------------------------------------------------------------------------------------
  1192. -- Export button click handler
  1193. on btn_export pressed do
  1194. (
  1195. ostream = GetSaveFileStream()
  1196. if ostream != undefined then
  1197. (
  1198. ExportScene()
  1199. close ostream
  1200. )
  1201. )
  1202. )
  1203. createDialog ThreeJSExporter width:300