Code: Select all
-- TPMImport script.
-- This is a horrible mish-mash of code from lots of places
-- Plain mesh import should work fine
-- There are lots of problems when creating MAX Skins/Joints/Bones
-- Known problems:
-- - Rotation order not checked
-- - Scaling mesh instance stuffs up the skin
-- - Skin ends up with many extra weights that I don't assign, automatically added when bones created?
-- (they seem to always be zero though, so not too bad)
-- Pushing the Skin.always_deform button twice nicely resets the reference position of the verts,
-- but setting it in a script doesn't! ARGHHHHHHHHHHH!!!
utility ImportTPM "TPM Import"
(
local currentPath
--------------------
-- User Interface --
--------------------
group "About"
(
label lab1 "TPM Import v0.2 alpha"
hyperLink addy "by Andres James" align:#center address:"mailto:tresutils@yahoo.com" color:(color 0 0 170) hoverColor:(color 170 0 0)
label lab2 "Modified by M.A.Ch.F."
button dlgTPMImportHelp "HELP"
)
on dlgTPMImportHelp pressed do (
local helpStr = "
TPM Import lets you import Trespasser Model (.TPM) files, for example those exported by TresEd.
Usage:
Hit the \"Import TPM\" button and select the file to import. If you have any problems,
or if you intend to make extensive use of this script, please completely read this
dialog and the included readme.txt file.
Tips and Caveats:
- All meshes exported by TresEd should work.
- In theory a dino can be imported, edited, exported, and imported back into
Trespasser with GeomAdd. I've tested changing vertex and tvert positions, but
I haven't tried modifying which bone a vertex is attached to.
- Bumpmaps have not been tested
- For 'skin' types, a Skin modifier is added to the mesh, using bones named
'$J%NN' where % is the mesh name, and NN is the bone number.
NOTE: Following the Trespasser convention, bone numbers start at zero, as
do the bone indicies in the vertex definitions.
IMPORTANT NOTES ABOUT IMPORTING JOINTS/BONES
- You must not scale the imported object (ie. use Import Scale = 1.0)
- Before the skin modifier (ie. joints/bones) will correctly act on a mesh you must
do the following: Open the Modifier panel and select the \"Skin\" modifier. In the
\"Advance Params\" rollout deselect \"Always Deform\" then select it again.
- Some bone parameters are stored in the User Properties text buffer and can
only be modified there. eg. Anim00, Anim01, Ratio, and RotationRatio.
- Bone hierarchy is not set up ie. bones have no parents/children.
- Loading a mesh with bones can be MUCH slower than loading a plain mesh, be patient.
"
messageBox helpStr title:"TPM Import Help"
)
local gImportScale = 1.0
group "Import"
(
checkbox resetCheck "Reset MAX scene" checked:false disabled:true
spinner spnImportScale "Import scale" range:[0.0000001,100,gImportScale]
button importTPMButton "Import TPM..." toolTip:""
--button testImportTPMButton "My test fn..." toolTip:""
)
on spnImportScale changed val do (
gImportScale = val
)
--------------------
-- Misc Functions --
--------------------
fn IsInstance objA objB = (
(isKindOf objA node) AND
(isKindOf objB node) AND
(objA.baseObject == objB.baseObject)
)
fn GetInstances obj = (
local objs
local bObj = obj.baseObject
local rObjs = refs.dependents bObj
objs = for rObj in rObjs where ( (isKindOf rObj node) AND
(bObj == rObj.baseObject) AND
(obj != rObj) ) collect rObj
return objs
)
--------------------------
-- TPM Import Functions --
--------------------------
-- Load a texture file
fn GetTexture name = (
local bmpFilename = name
local texMap = sceneMaterials[bmpFilename]
if texMap != undefined then
(
if (classof texMap) != BitmapTexture then
(
texMap = undefined
) else
(
return texMap
)
)
try
(
--print currentpath
--print bmpfilename
local bmp = openBitMap (currentPath + bmpFilename)
texMap = BitmapTexture bitmap:bmp name:bmpFileName
) catch
(
return undefined
)
texMap
)
-- Set parent of bone b to be bone p
fn SetBoneParent b p = (
-- Want to re-align bone so finishes at parent, not working...
--p.dir = b.pos - p.pos
--p.length = 20 --distance p.pos b.pos
skinops.setendpoint
b.parent = p
)
fn SetBoneParent2 ms b p = (
-- Why won't this work!!!!
local pos = skinops.getStartPoint ms (p as integer)
skinops.setendpoint ms (b as integer) pos
--b.parent = p
)
-- given mesh, array of bones for mesh, and bone number for each vertex, setup bones and a Skin modifier
-- bone numbers and boneVerts start at '1'
fn SetupBones m bones boneVerts = (
-- Skin stuff only works if the modifer panel is open, and mesh is selected
max modify mode
select m
ms = Skin()
ms.rigid_vertices = true
addModifier m ms
-- Hack to try mark bone parents, should be in seperate script as they're different for each dino type
local gpar = #( [4,3],[3,2],[2,1],[1,0], [15,14],[14,13],[13,12], [19,18],[18,17],[17,16],[14,16], [7,6],[8,7],[9,7],[23,7],[26,7])
--for i=1 to gpar.count do
--SetBoneParent bones[gpar[i][1]+1] bones[gpar[i][2]+1]
-- Add bones to mesh, using update flag '-1' for last bone
for i=1 to bones.count-1 do
skinops.addbone ms bones[i] 0
if bones.count > 0 then
skinops.addbone ms bones[bones.count] -1
update m -- do we need this?
--for i=1 to gpar.count do
--SetBoneParent2 ms (gpar[i][1]+1) (gpar[i][2]+1)
-- Add vertex weights
for i=1 to m.verts.count do (
skinops.setVertexWeights ms i boneverts[i] 1
)
update m -- do we need this?
)
-- Return list of bones for given mesh, searching through the given object list
-- Bone name is of form "$J%NN" Where % is the mesh name, and NN is a number
-- If zeroIsValid is false, bone number 'k' is returned in list position 'k' as expected.
-- If zeroIsValid is true, then 00 is a valid bone number, and bone number 'k' is
-- returned in position 'k+1' in the list (because MAX lists can only start at 1...)
fn FindBones m objs zeroIsValid:false = (
-- Find bones for given mesh,
format "FindBones\n"
local tempBones = #()
local startIndex = 1
if zeroIsValid == true then startIndex = 0
for j=startIndex to 40 do (
local n = (j as string)
if j < 10 then
n = "0" + (j as string)
local boneName = "$J" + m.name + n
local b = (for b in objs where b.name == boneName collect b)[1]
if b != undefined then (
tempBones[j+1-startIndex] = b
format "Found %\n" b.name
)
)
return tempBones
)
fn ImportTPM O_File = (
if O_File == undefined then
(
print "File not found"
return false
)
format "Importing '%'...\n" O_File
currentPath = getFilenamePath O_File
I_Stream = openFile O_File mode:"r"
local verts = #()
local tverts = #()
local faces = #()
local tvfaces = #()
local boneverts = #()
local faceMats = #()
local meshName = "DefaultMeshName"
local meshMats = #()
local usedMeshes = #() -- keep track of which meshes we've created instances of already
local instName
local instPos, instRot, instScale
local matName = undefined
local matDiffuseMap = undefined
local matOpacityMap = undefined
local matBumpMap = undefined
local materials = #()
local materialNames = #()
local bones = #()
local bonePosition
local boneName
local userProps = ""
local parseMode = "root"
local unknownMode = false
local doneSkinWarning = false
while ((eof I_Stream) == false) do
(
Cur_Line = readLine I_Stream
stringArray = filterString Cur_Line " =()<>,\""
--LineToken = read_token Cur_Line
LineToken = stringArray[1]
-- Skip empty lines and comments
if (LineToken == undefined) then continue
if (LineToken == "") then continue
if (substring LineToken 1 2 == "//") then continue
if unknownMode == true then ( -- Skip interior of unknown blocks
if LineToken == "}" then (
unknownMode = false
)
) else
if parseMode == "root" then (
userProps = ""
case LineToken of
(
"fileinfo": (
unknownMode = true
)
"bone": (
parseMode = "bone"
boneName = stringArray[2]
bonePosition = [0,0,0]
)
"j": ( --@HACK, TresEd only supports bones (joints) in old format
boneName = stringArray[2]
x = (stringArray[3] as float)
y = (stringArray[4] as float)
z = (stringArray[5] as float)
local size = gImportScale * 0.1
local bone = BoneSys.createBone [0,0,0] [size,0,0] [0,0,1];
bone.name = boneName
bone.width = size
bone.height = size
bone.pos = [x, y, z] * gImportScale
append bones bone
format "bone % % % %\n" boneName x y z
)
"material": (
parseMode = "material"
matName = stringArray[2]
matDiffuseMap = undefined
matOpacityMap = undefined
matBumpMap = undefined
)
"mesh": (
parseMode = "mesh"
meshName = stringArray[2]
verts = #()
faces = #()
tverts = #()
tvfaces = #()
faceMats = #()
meshMats = #()
boneverts = #()
)
"skin": (
parseMode = "skin"
meshName = stringArray[2]
verts = #()
faces = #()
tverts = #()
tvfaces = #()
faceMats = #()
meshMats = #()
boneverts = #()
)
"instance": (
parseMode = "instance"
instName = stringArray[2]
meshName = instName
instPos = [0,0,0]
instRot = [0,0,0]
instScale = [1,1,1]
)
"{": ()
"}":(
unknownMode = false
)
default: (
if unknownMode == false then (
format "Unknown root element %\n" LineToken
)
unknownMode = true
)
)
) else
if parseMode == "bone" then ( -- untested
case LineToken of
(
"position": (
x = (stringArray[2] as float)
y = (stringArray[3] as float)
z = (stringArray[4] as float)
bonePosition = [x, y, z] * gImportScale
--format "bonepos % % %\n" x y z
)
"}": (
parseMode = "root"
local size = gImportScale * 0.1
local bone = BoneSys.createBone [0,0,0] [size,0,0] [0,0,1];
bone.name = boneName
bone.width = size
bone.height = size
bone.pos = [x, y, z] * gImportScale
setuserpropbuffer bone userProps
append bones bone
format "bone % % % %\n" boneName x y z
)
"Anim00": ( userProps += Cur_Line + "\n" )
"Anim01": ( userProps += Cur_Line + "\n" )
"Ratio": ( userProps += Cur_Line + "\n" )
"RotationRatio": ( userProps += Cur_Line + "\n" )
"rotation": ( userProps += Cur_Line + "\n" )
)
) else
if parseMode == "material" then (
case LineToken of
(
"colormap": (
matDiffuseMap = fileNameFromPath stringArray[2]
--format "diffusemap %\n" matDiffuseMap
)
"opacitymap": (
matOpacityMap = fileNameFromPath stringArray[2]
)
"bumpmap": (
matBumpMap = fileNameFromPath stringArray[2]
)
"}": (
parseMode = "root"
local mat = standard name:matName showInViewport:true
if matDiffuseMap != undefined then (
local texmap = GetTexture matDiffuseMap
if texmap != undefined then (
mat.maps[2] = texmap
mat.mapEnables[2] = true
showTextureMap mat texmap true
) else
format "Warning: Can't find texture map file %\n" matDiffuseMap
)
if matOpacityMap != undefined then (
local texmap = GetTexture matOpacityMap
if texmap != undefined then (
mat.maps[7] = texmap
mat.mapEnables[7] = true
showTextureMap mat texmap true
) else
format "Warning: Can't find opacity map fimle %\n" matOpacityMap
)
if matBumpMap != undefined then (
local texmap = GetTexture matBumpMap
if texmap != undefined then (
mat.maps[9] = texmap
mat.mapEnables[9] = true
showTextureMap mat texmap true
) else
format "Warning: Can't find bump map file %\n" matBumpMap
)
showTextureMap mat on
append materialNames matName
append materials mat
format "material %\n" matName
)
)
) else
if parseMode == "mesh" or parseMode == "skin" then (
case LineToken of
(
"m":
(
-- Find material in global list
local index = findItem materialNames stringArray[2]
if index != 0 then
(
--format "material %\n" stringArray[2]
append meshMats materials[index]
) else (
format "material % not found, substituted default\n" stringArray[2]
local tempmat = standard name:stringArray[2]
append meshMats tempmat
)
)
"v":
(
x = (stringArray[2] as float)
y = (stringArray[3] as float)
z = (stringArray[4] as float)
append verts ([x, y, z] * gImportScale)
local b = undefined
if stringArray[5] != undefined then b = (stringArray[5] as integer)
if (parseMode=="skin") then (
if b == undefined then (
format "missing bone index\n"
b = 0
)
if b < 0 then (
format "unknown bone index %\n" b
b = 0
)
b = b + 1 -- because TPM bones and bone indicies start at zero
append boneverts b
)
--format "vertex % % %\n" x y z
)
"t":
(
x = (stringArray[2] as float)
y = (stringArray[3] as float)
append tverts [x, y, 0]
--format "tvert % %\n" x y
)
"f":
(
a = (stringArray[2] as integer)
b = (stringArray[3] as integer)
c = (stringArray[4] as integer)
append faces [a, b, c]
a2 = (stringArray[5] as integer)
b2 = (stringArray[6] as integer)
c2 = (stringArray[7] as integer)
append tvfaces [a2, b2, c2]
m = (stringArray[11] as integer)
append faceMats m
--format "face (% % %) (% % %) %\n" a b c a2 b2 c2 m
)
"}": (
if parseMode == "skin" then
format "skin %\n" meshName
else
format "mesh %\n" meshName
--@TODO what if no meshMats?
local material = multiMaterial numsubs:meshMats.count name:meshName
for i = 1 to meshMats.count do (
--format "mat: %\n" meshMats[i].name
material[i] = meshMats[i]
)
-- Make an editable mesh
local ss = 1
--local ss = 2.47791 --@HACK for raptorC
local m = mesh name:meshName position:[0, 0, 0] scale:[ss, ss, ss] \
faces:faces vertices:verts material:material
--update m
setNumTVerts m tverts.count false
for i = 1 to tverts.count do settvert m i tverts[i]
buildTVFaces m false
for i = 1 to tvfaces.count do settvface m i tvfaces[i]
-- Assign material ID's NB. materials must be already defined
for i = 1 to faces.count do
(
local matID = 1
--local matSlot = 1
--matID = material.materialIDList[matSlot]
setFaceMatID m i faceMats[i]
)
update m
-- Setup Bones
if parseMode == "skin" then (
if doneSkinWarning == false then (
messageBox "NOTE: Importing skins can take a VERY long time" title:"TPM Skin import"
doneSkinWarning = true
)
local meshBones = FindBones m bones zeroIsValid:true
-- Make sure all bones are defined or SetupBones will complain
if meshBones != undefined then (
format "Found % bones\n" meshBones.count
SetupBones m meshBones boneVerts
)
)
parseMode = "root"
)
)
) else
if parseMode == "instance" then (
case LineToken of
(
"position": (
instPos = ([(stringArray[2] as float), (stringArray[3] as float), (stringArray[4] as float)] * gImportScale)
)
"rotation": (
instRot = ([(stringArray[2] as float), (stringArray[3] as float), (stringArray[4] as float)])
)
"scale": (
local s = (stringArray[2] as float)
instScale = [s, s, s]
)
"mesh": (
meshName = stringArray[2]
)
"}": (
parseMode = "root"
-- find mesh
local bmesh = undefined
for m in objects where m.name==meshName do bmesh = m
if bmesh != undefined then (
local doneBefore = false
-- save 'mesh' object so we can delete it later (as we now have at least one instance of it)
for o in usedMeshes where o.name==meshName do
doneBefore = true
if doneBefore == false then
append usedMeshes bmesh
-- create new instance
local inst = instance bmesh
inst.name = instName
inst.pos = instPos
inst.rotation = quat 0 0 0 1
inst.scale = instScale
--local euler = eulerAngles instRot.x instRot.y instRot.z
--@HACK MAX says rotations are right-handed, but seems to rotate in the left-handed way...
--Perhaps things just aren't being done in the world coordinate system.
--local euler = eulerAngles -instRot.x -instRot.y -instRot.z
--rotate inst (eulerToQuat euler order:1) -- rotate using Euler angles
local euler = eulerAngles -instRot.x 0 0
rotate inst (eulerToQuat euler order:1) -- rotate using Euler angles
local euler = eulerAngles 0 -instRot.y 0
rotate inst (eulerToQuat euler order:1) -- rotate using Euler angles
local euler = eulerAngles 0 0 -instRot.z
rotate inst (eulerToQuat euler order:1) -- rotate using Euler angles
format "instance % mesh:%" instName meshName
format " pos:% rot:% scl:%\n" instPos instRot instScale
) else (
format "NOTE: Mesh % for instance % not found.\n" meshName instName
)
)
)
) -- endif parseMode
) -- endif while !eof
close i_stream
-- remove the extra meshes that we now have instances of
for m in usedMeshes do (
delete m
)
max views redraw
)
--------------------------
-- Main Import Function --
--------------------------
fn ResetMAX = (
resetMAXFile #noprompt
max utility mode
)
on importTPMButton pressed do
(
O_File = getOpenFileName "Import TPM" types:"TPM Files (*.tpm)|*.tpm|All Files (*.*)|*.*"
if O_File != undefined then (
if resetCheck.checked then
ResetMAX()
ImportTPM O_File
)
)
on testImportTPMButton pressed do
(
ResetMAX()
ImportTPM "D:/Andres/Temp/bonetest.tpm"
--ImportTPM "D:/Andres/Temp/test.txt"
--ImportTPM "D:/Andres/Temp/crate.tpm"
max utility mode
if false then (
for o in objects do (
local bob = ""
format "name: % % %\n" o o.name (classof o)
if (classof o) == Editable_Mesh then (
format " name:% %\n" o.name (classof o.mesh)
--format " mesh:%\n" o.mesh.name
format " pos:%\n" o.pos
)
)
)
)
)
Now I'll have to look at how to fix the export script so that rotations match the ones from TresEd and the fixed import script...