Doodle3D-Slicer/three.js-master/utils/exporters/max/ThreeJSAnimationExporter.ms
2015-06-12 15:58:26 +02:00

1733 lines
33 KiB
Plaintext
Executable File

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