|
@@ -0,0 +1,1733 @@
|
|
|
+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
|