rollout ThreeJSExporter "ThreeJSExporter" ( -- Variables local ostream, threeMatrix = (matrix3 [1,0,0] [0,0,1] [0,-1,0] [0,0,0]), headerFormat = "\"metadata\": { \"sourceFile\": \"%\", \"generatedBy\": \"3ds max ThreeJSExporter\", \"formatVersion\": 3, \"vertices\": %, \"normals\": %, \"colors\": %, \"uvs\": %, \"triangles\": %, \"materials\": % }, ", vertexFormat = "%,%,%", vertexNormalFormat = "%,%,%", UVFormat = "%,%", triFormat = "%,%,%,%", triUVFormat = "%,%,%,%,%,%,%", triNFormat = "%,%,%,%,%,%,%", triUVNFormat = "%,%,%,%,%,%,%,%,%,%", footerFormat = "\n}", boneFormat = "\t\t{ \t\t\t\"parent\" : %, \t\t\t\"name\" : \"%\", \t\t\t\"pos\" : %, \t\t\t\"scl\" : %, \t\t\t\"rotq\" : [%,%,%,%] \t\t}", animHeaderFormat = "\t\"animation\" : { \t\t\"name\" : \"Action\", \t\t\"fps\" : %, \t\t\"length\" : %, \t\t\"hierarchy\" : [\n", animBoneHeaderFormat = "\t\t\t{ \t\t\t\t\"parent\" : %, \t\t\t\t\"keys\" : [\n", keyFormat = "\t\t\t\t\t{ \t\t\t\t\t\t\"time\":%, \t\t\t\t\t\t\"pos\" :[%,%,%], \t\t\t\t\t\t\"rot\" :[%,%,%,%], \t\t\t\t\t\t\"scl\" :% \t\t\t\t\t}", animBoneFooterFormat = "\t\t\t\t] \t\t\t}", animFooterFormat = "\n\n\t\t] \t}\n" ------------------------------------------------------------------------------------- -- User interface group "ThreeJSExporter v0.8" ( label msg "Exports selected meshes in Three.js ascii JSON format" align:#left 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 label dummy1 "--------------------------------------------------------" align:#left checkbox exportColor "Export vertex colors" checked:false enabled:true checkbox exportUv "Export uvs" checked:true enabled:true checkbox exportNormal "Export normals" checked:true enabled:true checkbox smoothNormal "Use vertex normals" checked:false enabled:true label dummy2 "--------------------------------------------------------" align:#left checkbox flipYZ "Flip YZ" checked:false enabled:false checkbox flipUV "Flip UV" checked:false enabled:false checkbox flipFace "Flip all faces" checked:false enabled:false checkbox autoflipFace "Try fixing flipped faces" checked:false enabled:false label dummy3 "--------------------------------------------------------" align:#left spinner fps "Animation speed (FPS)" range:[0,1000,25] type:#integer label dummy4 "--------------------------------------------------------" align:#left button btn_export "Export selected objects" ) ------------------------------------------------------------------------------------- -- Dump vertices function DumpVertices src = ( Format "\"vertices\": [" to:ostream num = src.count if num > 0 then ( for i = 1 to num do ( vert = src[i] if flipYZ.checked then ( x = vert.x y = vert.z z = vert.y z *= -1 ) else ( x = vert.x y = vert.y z = vert.z ) Format vertexFormat x y z to:ostream if i < num then Format "," to:ostream ) ) Format "],\n\n" to:ostream ) ------------------------------------------------------------------------------------- -- Dump colors function DumpColors src useColors = ( Format "\"colors\": [" to:ostream num = src.count if num > 0 and useColors then ( for i = 1 to num do ( col = src[i] r = col.r as Integer g = col.g as Integer b = col.b as Integer hexNum = ( bit.shift r 16 ) + ( bit.shift g 8 ) + b -- hexColor = formattedPrint hexNum format:"#x" -- Format "%" hexColor to:ostream decColor = formattedPrint hexNum format:"#d" Format "%" decColor to:ostream if i < num then Format "," to:ostream ) ) Format "],\n\n" to:ostream ) ------------------------------------------------------------------------------------- -- Dump normals function DumpNormals src = ( Format "\"normals\": [" to:ostream num = src.count if num > 0 and exportNormal.checked then ( for i = 1 to num do ( normal = src[i] normal = normalize normal as point3 if flipYZ.checked then ( x = normal.x y = normal.z z = normal.y z *= -1 ) else ( x = normal.x y = normal.y z = normal.z ) Format vertexNormalFormat x y z to:ostream if i < num then Format "," to:ostream ) ) Format "],\n\n" to:ostream ) ------------------------------------------------------------------------------------- -- Dump uvs function DumpUvs src = ( Format "\"uvs\": [[" to:ostream num = src.count if num > 0 and exportUv.checked then ( for i = 1 to num do ( uvw = src[i] u = uvw.x if flipUV.checked then ( v = 1 - uvw.y ) else ( v = uvw.y ) Format UVFormat u v to:ostream if i < num then Format "," to:ostream ) ) Format "]],\n\n" to:ostream ) ------------------------------------------------------------------------------------- -- Dump faces function DumpFaces src useColors = ( Format "\"faces\": [" to:ostream num = src.count if num > 0 then ( for i = 1 to num do ( zface = src[i] fv = zface[1] fuv = zface[2] m = zface[3] - 1 fc = zface[4] needsFlip = zface[5] isTriangle = true hasMaterial = true hasFaceUvs = false hasFaceVertexUvs = ((classof fuv == Point3) and exportUv.checked) hasFaceNormals = false hasFaceVertexNormals = (exportNormal.checked) hasFaceColors = false hasFaceVertexColors = ((classof fc == Point3) and useColors) faceType = 0 faceType = bit.set faceType 1 (not isTriangle) faceType = bit.set faceType 2 hasMaterial faceType = bit.set faceType 3 hasFaceUvs faceType = bit.set faceType 4 hasFaceVertexUvs faceType = bit.set faceType 5 hasFaceNormals faceType = bit.set faceType 6 hasFaceVertexNormals faceType = bit.set faceType 7 hasFaceColors faceType = bit.set faceType 8 hasFaceVertexColors if i > 1 then ( Format "," faceType to:ostream ) Format "%" faceType to:ostream if isTriangle then ( va = (fv.x - 1) as Integer vb = (fv.y - 1) as Integer vc = (fv.z - 1) as Integer if flipFace.checked or needsFlip then ( tmp = vb vb = vc vc = tmp ) Format ",%,%,%" va vb vc to:ostream if hasMaterial then ( Format ",%" m to:ostream ) if hasFaceVertexUvs then ( ua = (fuv.x - 1) as Integer ub = (fuv.y - 1) as Integer uc = (fuv.z - 1) as Integer if flipFace.checked or needsFlip then ( tmp = ub ub = uc uc = tmp ) Format ",%,%,%" ua ub uc to:ostream ) if hasFaceVertexNormals then ( if smoothNormal.checked then ( -- normals have the same indices as vertices na = va nb = vb nc = vc ) else ( -- normals have the same indices as face na = i - 1 nb = na nc = na ) if flipFace.checked or needsFlip then ( tmp = nb nb = nc nc = tmp ) Format ",%,%,%" na nb nc to:ostream ) if hasFaceVertexColors then ( ca = (fc.x - 1) as Integer cb = (fc.y - 1) as Integer cc = (fc.z - 1) as Integer if flipFace.checked or needsFlip then ( tmp = cb cb = cc cc = tmp ) Format ",%,%,%" ca cb cc to:ostream ) ) ) ) Format "]" to:ostream ) ------------------------------------------------------------------------------------- -- Dump color function DumpColor pcolor label = ( r = pcolor.r / 255 g = pcolor.g / 255 b = pcolor.b / 255 fr = formattedPrint r format:".4f" fg = formattedPrint g format:".4f" fb = formattedPrint b format:".4f" Format "\"%\" : [%, %, %],\n" label fr fg fb to:ostream ) ------------------------------------------------------------------------------------- -- Dump map function DumpMap pmap label = ( if classof pmap == BitmapTexture then ( bm = pmap.bitmap if bm != undefined then ( fname = filenameFromPath bm.filename Format "\"%\" : \"skins/%\",\n" label fname to:ostream ) ) ) ------------------------------------------------------------------------------------- -- Dump bones -- src = #( #(index, name, position, scale, rotation), .. ) -- boneOrder lists the correct output order of the bones -- newIndices is inverse of boneOrder function DumpBones src boneOrder newIndices = ( numBones = boneOrder.count Format ",\n\n\t\"bones\" : [\n" to:ostream for i = 1 to numBones do ( b = src[boneOrder[i]] if b[1] == -1 then ( parent_index = -1 ) else ( parent_index = newIndices[b[1]+1] ) bone_name = b[2] p = b[3] s = b[4] r = b[5] Format boneFormat parent_index bone_name p s r.x r.y r.z r.w to:ostream if (i < numBones) then (Format "," to:ostream) Format "\n\n" to:ostream ) Format "\t],\n\n" to:ostream ) ------------------------------------------------------------------------------------- -- Dump skin indices -- src = #( #(skinned?, #(index1A, index1B, ..), name ) -- If the mesh wasn't skinned, look in boneNames for its parent to fix the index -- If it's not there, leave as 0 -- boneOrder lists the correct output order of the bones -- newIndices is inverse of boneOrder function DumpIndices src boneNames newIndices = ( output = #() for i=1 to src.count do ( if src[i][1] then ( join output src[i][2] ) else ( bone = findItem boneNames src[i][3] for j=1 to src[i][2].count do ( src[i][2][j] = bone ) join output src[i][2] ) ) Format "\t\"skinIndices\" : [" to:ostream num = output.count if num > 0 then ( for i = 1 to num do ( Format "%" (newIndices[output[i] + 1]) to:ostream if i < num then ( Format "," to:ostream ) ) ) Format "],\n\n" to:ostream ) ------------------------------------------------------------------------------------- -- Dump skin weights -- src = #( weight1, weight2, .. ) function DumpWeights src = ( Format "\t\"skinWeights\" : [" to:ostream num = src.count if num > 0 then ( for i = 1 to num do ( Format "%" src[i] to:ostream if i < num then Format "," to:ostream ) ) Format "],\n\n" to:ostream ) ------------------------------------------------------------------------------------- -- Dump the keyframes for every bone -- src = #( #( parent, #( time, #( posx, posy, posz ), rot, scl ), .. ), .. ) -- ||---Bone-- |---------------Keyframe----------------| ----|| -- boneOrder lists the correct output order of the bones -- newIndices is inverse of boneOrder function DumpKeyframes src boneOrder newIndices fps = ( Format animHeaderFormat fps src[1][2][src[1][2].count][1] to:ostream numBones = boneOrder.count for i = 1 to (numBones) do ( if (src[boneOrder[i]][1] == -1) then ( parent_index = -1 ) else ( parent_index = newIndices[src[boneOrder[i]][1]+1] ) Format animBoneHeaderFormat parent_index to:ostream bnkeys = src[boneOrder[i]][2] for j = 1 to bnkeys.count do ( 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 if j < bnkeys.count then Format "," to:ostream Format "\n" to:ostream ) Format animBoneFooterFormat to:ostream if i < (numBones) then ( Format "," to:ostream ) Format "\n" to:ostream ) Format animFooterFormat to:ostream ) ------------------------------------------------------------------------------------- -- Dump the morphtargets -- src = #( #( #(index, name, vertices = #( #( x,y,z ), .. ) ), .. ), .. ) -- |List of meshes ----------------------------------------------| -- |- |List of targets: only one mesh may have multiple ---| ----| -- |- |- |Individual target(s) ----------------------| ----| ----| function DumpMorphTargets src = ( -- This procedure assumes that only one element of src has actual morph targets. -- The targets field of the other elements is used to store the vertices of static meshes. -- These vertices are duplicated and joined with the vertices of the actual morph targets. -- Initialize with whatever happens to be first output = src[1] for m=2 to src.count do ( if (src[m].count == 1) then ( -- This is a static mesh; attach its vertices, but do nothing else. for t=1 to output.count do ( join output[t][3] src[m][1][3] ) ) else ( -- This mesh contains morph targets. -- Duplicate the static vertices, join with each morph target, and set the indices and names. while ( output.count < src[m].count ) do ( -- Duplicate vertices append output (deepCopy output[1]) ) for t=1 to src[m].count do ( -- Vertices join output[t][3] src[m][t][3] -- Index, name output[t][1] = src[m][t][1] output[t][2] = src[m][t][2] ) ) ) Format "\"morphTargets\": [" to:ostream for k=1 to output.count do ( target = output[k] Format "{ \"name\": \"morph_%\", \"vertices\": [" target[2] to:ostream vertices = target[3] for j=1 to vertices.count do ( Format "%,%,%" vertices[j][1] vertices[j][2] vertices[j][3] to:ostream if (j != vertices.count) then ( Format "," to:ostream ) ) Format "] }" to:ostream if (k != output.count) then ( Format ",\n" to:ostream ) ) Format "],\n" to:ostream ) ------------------------------------------------------------------------------------- -- Export materials function ExportMaterials zmaterials zcolors = ( Format "\"materials\": [\n" to:ostream totalMaterials = zmaterials.count for i = 1 to totalMaterials do ( mat = zmaterials[i] Format "{\n" to:ostream -- debug Format "\"DbgIndex\" : %,\n" (i-1) to:ostream if classof mat != BooleanClass then ( useVertexColors = zcolors[i] Format "\"DbgName\" : \"%\",\n" mat.name to:ostream -- colors DumpColor mat.diffuse "colorDiffuse" DumpColor mat.ambient "colorAmbient" DumpColor mat.specular "colorSpecular" t = mat.opacity / 100 s = mat.glossiness Format "\"transparency\" : %,\n" t to:ostream Format "\"specularCoef\" : %,\n" s to:ostream -- maps DumpMap mat.diffuseMap "mapDiffuse" DumpMap mat.ambientMap "mapAmbient" DumpMap mat.specularMap "mapSpecular" DumpMap mat.bumpMap "mapBump" DumpMap mat.opacityMap "mapAlpha" ) else ( useVertexColors = false Format "\"DbgName\" : \"%\",\n" "dummy" to:ostream DumpColor red "colorDiffuse" ) Format "\"vertexColors\" : %\n" useVertexColors to:ostream Format "}" to:ostream if ( i < totalMaterials ) then Format "," to:ostream Format "\n\n" to:ostream ) Format "],\n\n" to:ostream ) ------------------------------------------------------------------------------------- -- Extract vertices from mesh function ExtractVertices obj whereto = ( n = obj.numVerts for i = 1 to n do ( v = GetVert obj i append whereto v ) ) ------------------------------------------------------------------------------------- -- Extract vertex colors from mesh function ExtractColors obj whereto = ( nColors = GetNumCPVVerts obj if nColors > 0 then ( for i = 1 to nColors do ( c = GetVertColor obj i append whereto c ) ) ) ------------------------------------------------------------------------------------- -- Extract normals from mesh function ExtractNormals obj whereto needsFlip = ( if smoothNormal.checked then ( num = obj.numVerts for i = 1 to num do ( n = GetNormal obj i if flipFace.checked or needsFlip then ( n.x *= -1 n.y *= -1 n.z *= -1 ) append whereto n ) ) else ( num = obj.numFaces for i = 1 to num do ( n = GetFaceNormal obj i if flipFace.checked or needsFlip then ( n.x *= -1 n.y *= -1 n.z *= -1 ) append whereto n ) ) ) ------------------------------------------------------------------------------------- -- Extract uvs from mesh function ExtractUvs obj whereto = ( n = obj.numTVerts for i = 1 to n do ( v = GetTVert obj i append whereto v ) ) ------------------------------------------------------------------------------------- -- Extract faces from mesh function ExtractFaces objMesh objMaterial whereto allMaterials needsFlip hasVColors offsetVert offsetUv offsetColor = ( n = objMesh.numFaces hasUVs = objMesh.numTVerts > 0 useMultiMaterial = false materialIDList = #() materialClass = classof objMaterial if materialClass == StandardMaterial then ( fm = findItem allMaterials objMaterial ) else if materialClass == MultiMaterial then ( useMultiMaterial = true for i = 1 to n do ( mID = GetFaceMatID objMesh i materialIndex = findItem objMaterial.materialIDList mID if materialIndex > 0 then ( subMaterial = objMaterial.materialList[materialIndex] mMergedIndex = findItem allMaterials subMaterial if mMergedIndex > 0 then ( materialIDList[mID] = mMergedIndex ) else ( materialIDList[mID] = findItem allMaterials false ) ) else ( materialIDList[mID] = findItem allMaterials false ) ) ) else ( -- undefined material fm = findItem allMaterials false ) for i = 1 to n do ( zface = #() fv = GetFace objMesh i fv.x += offsetVert fv.y += offsetVert fv.z += offsetVert if useMultiMaterial then ( mID = GetFaceMatID objMesh i fm = materialIDList[mID] ) if hasUVs then ( fuv = GetTVFace objMesh i fuv.x += offsetUv fuv.y += offsetUv fuv.z += offsetUv ) else ( fuv = false ) if hasVColors then ( fc = GetVCFace objMesh i fc.x += offsetColor fc.y += offsetColor fc.z += offsetColor ) else ( fc = false ) append zface fv append zface fuv append zface fm append zface fc append zface needsFlip append whereto zface ) ) ------------------------------------------------------------------------------------- -- Extract materials from eventual multi-material function ExtractMaterials objMesh objMaterial whereto wheretoColors zname hasVColors = ( materialClass = classof objMaterial if materialClass == StandardMaterial then ( if ( findItem whereto objMaterial ) == 0 then ( append whereto objMaterial append wheretoColors hasVColors ) ) else if materialClass == MultiMaterial then ( n = objMesh.numFaces for i = 1 to n do ( mID = getFaceMatId objMesh i materialIndex = findItem objMaterial.materialIDList mID if materialIndex > 0 then ( subMaterial = objMaterial.materialList[materialIndex] if ( findItem whereto subMaterial ) == 0 then ( append whereto subMaterial append wheretoColors hasVColors ) ) ) ) else ( -- unknown or undefined material append whereto false append wheretoColors false ) ) ------------------------------------------------------------------------------------- -- Hack to figure out if normals are messed up function NeedsFaceFlip node = ( needsFlip = false local tmp = Snapshot node face_normal = normalize ( getfacenormal tmp 1 ) face = getface tmp 1 va = getvert tmp face[1] vb = getvert tmp face[2] vc = getvert tmp face[3] computed_normal = normalize ( cross (vc - vb) (va - vb) ) if distance computed_normal face_normal > 0.1 then needsFlip = true delete tmp return needsFlip ) ------------------------------------------------------------------------------------- -- Extract only things that either already are or can be converted to meshes function ExtractMesh node = ( if SuperClassOf node == GeometryClass then ( needsFlip = false hasVColors = false zmesh = SnapshotAsMesh node if autoflipFace.checked then ( needsFlip = NeedsFaceFlip node ) if exportColor.checked and ( getNumCPVVerts zmesh ) > 0 then ( hasVColors = true ) return #( zmesh, node.name, node.material, needsFlip, hasVColors ) ) -- Not geometry ... could be a camera, light, etc. return #( false, node.name, 0, false, false ) ) ------------------------------------------------------------------------------------- -- Extract the morph targets -- whereto = #( #( #(index, name, vertices = #( #( x,y,z ), .. ) ), .. ), .. ) -- |List of meshes -----------------------------------------------| -- |- |List of targets -------------------------------------| ----| -- |- |- |Individual target --------------------------| ----| ----| function ExtractMorphTargets node whereto &morphFlag = ( targets = #() morphs = #() if ( node.modifiers[#morpher] != undefined ) then ( -- Export the morph target, if one exists morphFlag = true for i=1 to 100 do ( nPts = WM3_MC_NumMPts node.morpher i if (nPts > 0) then ( append targets #(i, nPts) ) ) --Set all to zero for k=1 to targets.count do ( i = targets[k][1] node.morpher[i].controller.value = 0 ) --Max out one at a time, record it, then zero out again for k=1 to targets.count do ( i = targets[k][1] numVerts = targets[k][2] name = WM3_MC_GetName node.morpher i verts = #() node.morpher[i].controller.value = 100 for j = 1 to numVerts do ( p = GetVert node j append verts #(p.x, p.y, p.z) ) node.morpher[i].controller.value = 0 append morphs #(i, name, verts) ) append whereto morphs ) else ( -- Export the mesh vertices as a dummy morph target verts = #() for k=1 to node.numVerts do ( p = GetVert node k append verts #(p.x, p.y, p.z) ) dummy = #() append dummy #(0, "DUMMY", verts) append whereto dummy ) ) ------------------------------------------------------------------------------------- -- Transforms the matrix of a bone into its parent space, if a parent exists. function thinkLocally bone_node localForm = ( localForm = bone_node.transform if ( bone_node.parent != undefined ) then ( parentForm = bone_node.parent.transform localForm = localForm * inverse parentForm ) newLocal = matrix3 1 px = localForm.translationpart.x py = localForm.translationpart.y pz = localForm.translationpart.z lTran = transMatrix (localForm.translationpart) lRot = (inverse localForm.rotationpart) as matrix3 lScale = scaleMatrix localForm.scalepart localForm = lScale * lRot * lTran * newLocal localForm ) ------------------------------------------------------------------------------------- -- Extract bones and keyframes function ExtractAnimation node bones keyframes FPS bone_names &skinFlag = ( if node.modifiers[#skin] != undefined then ( skinFlag = true --------------------------------------------------------------------------------- -- A dummy root bone is first created and roatated to orient the model p = (matrix3 1).translationpart s = (matrix3 1).scalepart r = (matrix3 1).rotationpart append bones #(-1,"flipRoot",p,s,r) /* if (flipYZ.checked) then ( r = threeMatrix.rotationpart ) else ( r = (matrix3 1).rotationpart ) */ r = threeMatrix.rotationpart root_keys = #(#(0, p, r, s)) slidertime = 0 while (slidertime < animationrange.end) do ( slidertime += 1 sTime = (slidertime / FPS) as String append root_keys #(substring sTime 1 (sTime.count - 1), p, r, s) ) append keyframes #(-1, root_keys) --------------------------------------------------------------------------------- -- The model's bones and keyframes are then extracted max modify mode total_bones = skinops.getnumberbones node.modifiers[#skin] vertex_count = getNumverts node -- Find parents by looking up their names; bone names MUST be unique -- Can't guarantee that parent will be read before child; store all names beforehand for i = 1 to total_bones do ( bone_name = skinops.getbonename node.modifiers[#skin] i 0 append bone_names bone_name ) for i = 1 to total_bones do ( slidertime = 0 bone_name = skinops.getbonename node.modifiers[#skin] i 0 bone_node = getNodeByName bone_name parent_index = 0 if ( bone_node.parent != undefined ) then ( parent_name = bone_node.parent.name parent_index = (findItem bone_names parent_name) ) localForm = bone_node.transform localForm = thinkLocally bone_node localForm p = localForm.translationpart r = localForm.rotationpart s = localForm.scalepart append bones #(parent_index, bone_name, p, bone_node.transform.scalepart, r) in coordsys parent bone_keys = #(#(0, p, r, bone_node.transform.scalepart)) while (slidertime < animationrange.end) do ( slidertime += 1 sTime = (slidertime / FPS) as String localForm = bone_node.transform localForm = thinkLocally bone_node localForm p = localForm.translationpart r = localForm.rotationpart s = localForm.translationpart append bone_keys #(substring sTime 1 (sTime.count - 1), p, r, bone_node.transform.scalepart) ) append keyframes #(parent_index, bone_keys) ) ) ) ------------------------------------------------------------------------------------- -- Extract the skin indices and weights in one pass -- If it's a skin, skinned? = true and indices contains the bones -- If it's not, indices is dummied to #(0,..) and DumpIndices uses the parent to fix it in post -- indices = #( #( skinned?, indices, parent), ..) function ExtractInfluences node indices weights = ( vertex_count = getNumverts node meshIndices = #() if node.modifiers[#skin] != undefined then ( for i = 1 to vertex_count do ( -- Insane defaults for the sort; these shouldn't escape into the output index1 = -1 index2 = -1 weight1 = -1 weight2 = -1 numBones = skinOps.GetVertexWeightCount node.modifiers[#skin] i --Two passes of a bubble sort to get the 2 heaviest weights for j = 1 to numBones do ( thisIndex = skinops.getVertexWeightBoneID node.modifiers[#skin] i j thisWeight = skinops.getvertexweight node.modifiers[#skin] i j if (thisWeight) > weight1 then ( weight1 = thisWeight index1 = thisIndex ) ) for j = 1 to numBones do ( thisIndex = skinops.getVertexWeightBoneID node.modifiers[#skin] i j thisWeight = skinops.getvertexweight node.modifiers[#skin] i j if ((thisWeight > weight2) and (thisIndex != index1)) then ( weight2 = thisWeight index2 = thisIndex ) ) -- Establish legal defaults: no weight from the root if (index1 == -1) then ( index1 = 0 weight1 = 0 ) if (index2 == -1) then ( index2 = 0 weight2 = 0 ) append meshIndices (index1) append meshIndices (index2) append weights weight1 append weights weight2 ) append indices #(true, meshIndices, "ROOT") ) else ( for i = 1 to vertex_count do ( append meshIndices 0 append meshIndices 0 append weights 1 append weights 1 ) name = "Scene Root" if node.parent != undefined then ( name = node.parent.name ) append indices #(false, meshIndices, name) ) ) ------------------------------------------------------------------------------------- -- Enforce that parent is above all of its children in the output -- This fixes several amusing bugs (mostly fingers of infinite length) -- -- boneOrder: order to dump the bones in #(bones) -- newIndices: new positional indices of bones function ReorderBones bones boneOrder newIndices = ( /************************************************************************************* * Reorder bones * Python function prototype: * for i in range(n): * for b in range(n): * #new bone parent of bone legal * if not inOut[b] and (parents[b] == -1 or inOut[parents[b]]): * inOut[b] = True * boneOrder.append(b) * break; *************************************************************************************/ total_bones = bones.count -- Keeps track of which parents have been accounted for inOut = #() for i = 1 to total_bones do ( append inOut false ) rootNotAdded = true for i = 1 to total_bones do ( notFound = true for b = 1 to total_bones while notFound do ( if (inOut[b] != true) then ( if (rootNotAdded and bones[b][1] == -1) then ( inOut[b] = true append boneOrder b notFound = false rootNotAdded = false ) else ( if (inOut[bones[b][1] + 1]) then ( inOut[b] = true append boneOrder b notFound = false ) ) ) ) ) -- Takes original bone index/parent + 1, returns new correct index for parent, skinIndices, etc for i=1 to total_bones do ( newIndices[boneOrder[i]] = i - 1 ) ) ------------------------------------------------------------------------------------- -- Export scene -- -- This will BREAK in HORRIBLE WAYS if you feed it more than one object for now. function ExportScene = ( -- Extract meshes meshObjects = #() mergedVertices = #() mergedNormals = #() mergedColors = #() mergedUvs = #() mergedFaces = #() mergedMaterials = #() mergedMaterialsColors = #() sceneHasVColors = false hasSkin = false bones = #() keyframes = #() influences = #() weights = #() boneOrder = #() newIndices = #() bone_names = #() hasMorph = false mergedMorphTargets = #() -- The horrible hackery that is skinops requires only one object be selected. original_selection = #() for obj in selection do ( append original_selection obj.name ) max select none for name in original_selection do ( obj = getnodebyname name select obj result = ExtractMesh obj meshObj = result[1] if ClassOf meshObj == TriMesh then ( meshName = result[2] meshMaterial = result[3] needsFlip = result[4] hasVColors = result[5] sceneHasVColors = sceneHasVColors or hasVColors append meshObjects result vertexOffset = mergedVertices.count uvOffset = mergedUvs.count colorOffset = mergedColors.count ExtractMaterials meshObj meshMaterial mergedMaterials mergedMaterialsColors meshName hasVColors ExtractVertices meshObj mergedVertices ExtractNormals meshObj mergedNormals needsFlip ExtractColors meshObj mergedColors ExtractUvs meshObj mergedUvs ExtractFaces meshObj meshMaterial mergedFaces mergedMaterials needsFlip hasVColors vertexOffset uvOffset colorOffset ExtractAnimation obj bones keyframes fps.value bone_names &hasSkin ExtractInfluences obj influences weights ReorderBones bones boneOrder newIndices ExtractMorphTargets obj mergedMorphTargets &hasMorph ) max select none ) totalVertices = mergedVertices.count totalFaces = mergedFaces.count totalMaterials = mergedMaterials.count totalColors = 0 totalNormals = 0 totalUvs = 0 useColors = false if sceneHasVColors and exportColor.checked then ( totalColors = mergedColors.count useColors = true ) if exportNormal.checked then ( totalNormals = mergedNormals.count ) if exportUv.checked then ( totalUvs = mergedUvs.count ) -- Dump model Format "{\n\n" to:ostream -- Dump header Format headerFormat maxFileName totalVertices totalNormals totalColors totalUvs totalFaces totalMaterials to:ostream -- Dump all materials in the scene ExportMaterials mergedMaterials mergedMaterialsColors -- Dump merged data from all selected geometries DumpVertices mergedVertices if hasMorph then ( DumpMorphTargets mergedMorphTargets ) DumpNormals mergedNormals DumpColors mergedColors useColors DumpUvs mergedUvs DumpFaces mergedFaces useColors if hasSkin then ( DumpBones bones boneOrder newIndices DumpIndices influences bone_names newIndices DumpWeights weights DumpKeyframes keyframes boneOrder newIndices fps.value ) -- Dump footer Format footerFormat to:ostream ) ------------------------------------------------------------------------------------- -- Open and prepare a file handle for writing function GetSaveFileStream = ( zname = getFilenameFile maxFileName zname += ".js" fname = GetSaveFileName filename:zname types:"JavaScript file (*.js)|*.js|All Files(*.*)|*.*|" if fname == undefined then ( return undefined ) ostream = CreateFile fname if ostream == undefined then ( MessageBox "Couldn't open file for writing !" return undefined ) return ostream ) ------------------------------------------------------------------------------------- -- Export button click handler on btn_export pressed do ( ostream = GetSaveFileStream() if ostream != undefined then ( ExportScene() close ostream ) ) ) createDialog ThreeJSExporter width:300