mirror of
https://github.com/Doodle3D/Doodle3D-Slicer.git
synced 2024-12-25 12:23:49 +01:00
1733 lines
33 KiB
Plaintext
Executable File
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 |