Page 2 of 2

Re: Fixed TPM import and export maxscripts (version 02 and 1

Posted: Sun Feb 25, 2018 4:20 am
by machf
It's all a huge mess.

Re: Fixed TPM import and export maxscripts (version 02 and 1

Posted: Sun Feb 25, 2018 4:53 am
by TheIdiot
machf wrote:It's all a huge mess.
Was kind of getting that message. Hmm. Would a complete re-write be too much? That way, you could at least avoid the errors the first time instead of having to dig around later, since as far as I know, the only major issues it still has are the vertex-breaking one and the smoothing group bug.

Re: Fixed TPM import and export maxscripts (version 02 and 1

Posted: Sun Feb 25, 2018 6:10 am
by machf
The problem is with MAX itself... apparently, some MaxScript functions have been broken for quite some time, and besides, MAX has a default behavior regarding normals...

Re: Fixed TPM import and export maxscripts (version 02 and 1

Posted: Sun Feb 25, 2018 7:05 am
by Draconisaurus
"And I foresee that trying to restore them to their original settings is going to be a nightmare..."

Now, as I understand it, all the systems will then come back on their original start-up modes. Correct?
Theoretically yes.

Re: Fixed TPM import and export maxscripts (version 02 and 1

Posted: Sun Feb 25, 2018 7:28 am
by tatu
How does this work with TresEd's export and import TPM function? Does the exported TPM's from TresEd have these issues or is it the importer script for Max that cause them? As models exported from TresEd and back into Tres without MAX doesn't seem to have any issue?

Re: Fixed TPM import and export maxscripts (version 02 and 1

Posted: Sun Feb 25, 2018 8:04 am
by Draconisaurus
The TresEd export function is the status-quo being used to locate errors in the maxscript export versions.

Re: Fixed TPM import and export maxscripts (version 02 and 1

Posted: Sun Feb 25, 2018 3:18 pm
by machf
tatu wrote:How does this work with TresEd's export and import TPM function? Does the exported TPM's from TresEd have these issues or is it the importer script for Max that cause them? As models exported from TresEd and back into Tres without MAX doesn't seem to have any issue?
As Drac just said:
Draconisaurus wrote:The TresEd export function is the status-quo being used to locate errors in the maxscript export versions.
The objective is to have a file exported from within MAX (after being imported into it) almost identical to the one exported from TresEd.
And there are two separate:
  1. The importer not restoring the original normals in MAX
  2. The exporter not saving the same number as normals as TresEd
TheIdiot wrote:Also, forgot to bring this up: machf, there seems to be a problem with the import script which causes all verticies to break, resulting in each face becoming a separate element of the mesh. This can easily be fixed by selecting all vertices and welding them back together, but then the smoothing groups are all completely destroyed. I think this might be part of the normals error you're working on fixing.
The third issue (unwelded vertices) isn't related. In fact, it may be best to leave it unattended. The problem you see is caused by the normals not being restored, rather than by the faces being separated.

Re: Fixed TPM import and export maxscripts (version 02 and 1

Posted: Thu Aug 25, 2022 1:22 am
by xelono
Does anyone have the old version of this script?? I have downloaded this file but it doesn't work and unfortunately I didn't make a backup of the old script that did work.

I can't find it anywhere anymore...

Re: Fixed TPM import and export maxscripts (version 02 and 1

Posted: Thu Aug 25, 2022 2:20 am
by Nick3069
The old scripts used to be hosted here:
https://www.trescom.org/hosted/andres_j ... cripts.zip
...but it gives a 404 error now.

Have you tried the scripts on the main website? I don't know if they are the old ones or the updated ones.
https://www.trescom.org/download/tpm-im ... axscripts/

Re: Fixed TPM import and export maxscripts (version 02 and 1

Posted: Thu Aug 25, 2022 5:55 am
by tatu
xelono wrote:Does anyone have the old version of this script?? I have downloaded this file but it doesn't work and unfortunately I didn't make a backup of the old script that did work.

I can't find it anywhere anymore...
What happens when you use them? They work fine on 3ds Max 8 and 2020 at least.

Re: Fixed TPM import and export maxscripts (version 02 and 1

Posted: Fri Aug 26, 2022 6:26 am
by machf
I have a backup somewhere... next time I visit the forum I'll see if I attach it to this topic.

Re: Fixed TPM import and export maxscripts (version 02 and 1

Posted: Fri Aug 26, 2022 3:16 pm
by tatu
No need machf, here is a package of all TPM scripts that I appear to have collected, so there are a few versions:
TPMScriptsCollection.zip
(112.7 KiB) Downloaded 66 times

Re: Fixed TPM import and export maxscripts (version 02 and 1

Posted: Mon Sep 26, 2022 5:36 pm
by machf
Well, let me know if it's still needed...

EDIT: never mind, here's version 1.2.3

Code: Select all

-- TPM Exporter
--  Version 1.2.3
--  by Martijn Buijs, Andres James
--  Copyright © TresCom, 2007

-- Version History:
-- 1.2.3
--  * tool now sits in a floating tool window
--  * script no longer crashes if file is not accessable
--  * smoothing groups are now properly exported
--  * fix scale option now actually works
--  * pivot offset is now properly handled (but not pivot rotation)
-- 1.2.2
--  * added export "Selection Only" option
--  * added several warnings
--  * fixed crash on hidden submaterials
--  * fixed several stability issues related to materials
--  * only materials of exported meshes are written to file
--  * non standard materials are now ignored
--  * disabled (unchecked) material maps are no longer exported
--  * cleaned up code and added comments

-- To do:
--  * create flipped triangles copies for twosided materials (optional)
--  * export mirrored objects correctly

-- clean up
clearlistener()
global TpmExp
if TpmExp != undefined do
(
 closeRolloutFloater TpmExp
)

-- file handle
global filename
global fileobj

-- options
global opt_scalefix = false

-- material redundancy list
global matlist = #()
global matlistnum = 0

-- warnings
global warn_notobj = false
global warn_mirror = false
global warn_twoside = false

-- write material
fn WriteMaterial mat =
(
 -- check if material is already exported
 for i=1 to matlistnum do
 (
  if matlist[i] == mat do return false
 )

 -- write material
 format "\nmaterial \"%\"\n{\n" mat.name to: fileobj
 if (classOf(mat.diffusemap) == BitmapTexture) AND (mat.diffusemapenable == true) do
 (
  format " colormap = \"%\"\n" mat.diffusemap.filename to: fileobj
 )
 if (classOf(mat.opacitymap) == BitmapTexture) AND (mat.opacitymapenable == true) do
 (
  format " opacitymap = \"%\"\n" mat.opacitymap.filename to: fileobj
 )
 format "}\n" to: fileobj
 
 -- add to redundancy list
 matlistnum = matlistnum+1
 matlist[matlistnum] = mat
 
 -- warnings
 if mat.twosided do warn_twoside = true
)

-- write bones
fn WriteBoneChildren obj =
(
 local i
 if obj.children.count > 0 do
 (
  for i=1 to obj.children.count do
  (
   format " j = \"%\"" obj.children[i].name to: fileobj
   format ",(%,%,%)" obj.children[i].pos.x obj.children[i].pos.y obj.children[i].pos.z to: fileobj
   format ",(-90,-90,-90),(90,90,90),\"%\"\n" obj.name to: fileobj
   WriteBoneChildren obj.children[i]
  )
 )
)

-- write bonesystem
fn WriteBonesys obj =
(
 if obj.parent == undefined do
 (
  format "\nbonesys \"%\"\n{\n" obj.name to: fileobj
  format " r = \"%\",(%,%,%)\n" obj.name obj.pos.x obj.pos.y obj.pos.z to: fileobj
  WriteBoneChildren obj
  format "}\n" to: fileobj
 )
)


-- adds float3 to list
fn addToList list v =
(
 for i=1 to list.count do
 (
  if list[i].x == v.x do (
   if list[i].y == v.y do (
    if list[i].z == v.z do (
     return i
    )
   )
  )
 )
 append list v
 return list.count
)


-- write mesh
fn WriteMesh obj =
(
 local op = obj.objectoffsetpos
 local os = obj.scale
 
 -- write mesh materials
 case classof(obj.mat) of
 (
  UndefinedClass:()
  StandardMaterial:
  (
   WriteMaterial obj.mat
  )
  MultiMaterial:
  (
   for i=1 to obj.mat.numsubs do
   (
    if classof(obj.mat[i]) == StandardMaterial do
    (
     WriteMaterial obj.mat[i]
    )
   )
  )
 )
 
 local isskin = false
 local boneVerts = #()
 local i
 local k
 if classof(obj.modifiers[1]) == Skin then
 (
  isskin = true
  local myskin = obj.modifiers[1]
  
  -- skin stuff only works if the modifer panel is open and mesh is selected
  max modify mode
  select obj
  
  -- collect up bone indicies for each vertex
  for i=1 to skinOps.getNumberVertices myskin do
  (
   local maxb = 0 -- best bone so far
   local maxw = 0.0 -- best weight so far
   -- Trespasser only supports singly weighted verticies, so choose bone with greatest influence on vertex
   for k=1 to (skinops.getVertexWeightCount myskin i) do
   (
    if (skinops.getVertexWeight myskin i k) > maxw then
    (
     maxw = (skinops.getVertexWeight myskin i k)
     maxb = (skinops.getVertexWeightBoneID myskin i k)
    )
    append boneVerts maxb -- TODO: should be error if not attached to any bone?
   )
   format "\nskin \"%\"\n{\n" obj.name to: fileobj
  )
 ) else (
  format "\nmesh \"%\"\n{\n" obj.name to: fileobj
 )
 
 -- standard material
 case classof(obj.mat) of
 (
  StandardMaterial:
  (
   format " m = \"%\"\n" obj.mat.name to: fileobj
  )
  MultiMaterial:
  (
   for i=1 to obj.mat.numsubs do
   (
    if classof(obj.mat[i]) == StandardMaterial then
    (
     format " m = \"%\"\n" obj.mat[i].name to: fileobj
    ) else (
     format " m = \"dummy\"\n" to: fileobj
    )
   )
  )
 )
 
 -- write vertices
 local v
 for i=1 to obj.numverts do
 (
  in coordsys local v = (getvert obj i)
  
  if opt_scalefix do
  (
   v = (v + op) * os
  )
  
  if isskin == true then
  (
   format " v = (%,%,%),%\n" v.x v.y v.z (boneVerts[i]-1) to: fileobj -- write extra bone index
  ) else (
   format " v = (%,%,%)\n" v.x v.y v.z to: fileobj
  )
 )
 
 -- write texcoords
 local t
 if obj.numtverts != 0 then
 (
  for i=1 to obj.numtverts do
  (
   t = (gettvert obj i)
   format " t = (%,%)\n" t.x t.y to: fileobj
  )
 ) else (
  format " t = (0,0)\n" to: fileobj
 )
 
 -- write normals
 local normlist = #()
 local fnlist = #()
 for i=1 to obj.numfaces do
 (
  if (getfacesmoothgroup obj i) == 0 then
  (
   in coordsys local frn = getfacenormal obj i
   n1 = addToList normlist frn
   n2 = n1
   n3 = n1
  ) else (
   in coordsys local frn = (meshop.getfacernormals obj i)
   n1 = addToList normlist frn[1]
   n2 = addToList normlist frn[2]
   n3 = addToList normlist frn[3]
  )
  append fnlist #(n1,n2,n3)
 )
 for i=1 to normlist.count do
 (
  format " n = (%,%,%)\n" normlist[i].x normlist[i].y normlist[i].z to: fileobj
 )
 normlist = #()
 
 -- write faces
 for i=1 to obj.numfaces do
 (
 
  -- face vertex pointers
  v = (getface obj i)
  if obj.numtverts != 0 do
  (
   t = (gettvface obj i)
  )
  n = (getface obj i)
  f = (getfacematid obj i)
  format " f = (%,%,%)" (v.x as integer) (v.y as integer) (v.z as integer) to: fileobj
  
  -- face texcoord indices
  if obj.numtverts == 0 then
  (
   -- no texcoords, so lets point texcoord indices to this dummy coord
   format ",(1,1,1)" to: fileobj
  ) else (
   format ",(%,%,%)" (t.x as integer) (t.y as integer) (t.z as integer) to: fileobj
  )
  
  -- face normal indices
  format ",(%,%,%)" (fnlist[i][1] as integer) (fnlist[i][2] as integer) (fnlist[i][3] as integer) to: fileobj
  
  -- material indices
  case classof(obj.material) of
  (
   UndefinedClass:
   (
    -- no material on mesh
    format ",0\n" to: fileobj
   )
   StandardMaterial:
   (
    -- single material for entire mesh
    format ",1\n" to: fileobj
   )
   MultiMaterial:
   (
    -- submaterials, per face
    format ",%\n" (getfacematid obj i as integer) to: fileobj
   )
  )
 )
 
 -- clean up
 fnlist = #()
 
 -- end of mesh chunk
 format "}\n" to: fileobj
)

-- write instance
fn WriteInstance obj =
(
 format "\ninstance \"%\"\n{\n" obj.name to: fileobj
 
 -- mesh binding
 format " mesh = \"%\"\n" obj.name to: fileobj
 
 -- position
 format " position = (%,%,%)\n" obj.pos.x obj.pos.y obj.pos.z to: fileobj
 
 -- rotation
 local ea = quatToEuler obj.rotation order:1 
 -- TODO: check which order is correct
 -- euler angles seem to be -ve what they should be, probably stuffing up the rotation order too, should check
 format " rotation = (%,%,%)\n" -ea.x -ea.y -ea.z to: fileobj
 
 -- scale
 if opt_scalefix then
 (
  format " scale = 1\n" to: fileobj
 ) else (
  format " scale = %\n" obj.scale.x to: fileobj
 )
 
 -- end of instance chunk
 format "}\n" to: fileobj
)

rollout rExport "TPM Exporter"
(
 group "Options"
 (
  checkbox chkScaleFix "Fix Scale" checked: true
 )
 group "Export"
 (
  checkbox chkSelected "Selection Only" checked: true
  checkbox chkSkipHidden "Skip Hidden Objects" checked: true
  checkbox chkSkipFrozen "Skip Frozen Objects" checked: true
  button cmdExport "Export Scene..." width: 140
 )
 on cmdExport pressed do
 (
  local j
  local b
  
  -- options
  opt_scalefix = chkScaleFix.checked
  
  -- reset material redundancy list
  matlist = #()
  matlistnum = 0
  
  -- reset warning flags
  warn_notobj = false
  warn_mirror = false
  warn_twoside = false
  
  -- save file dialog
  filename = getSaveFileName caption:"Export Scene" types:"Trespasser Models (*.tpm)|*.tpm|All Files (*.*)|*.*"
  if filename == undefined then
  (
   return false
  )
  
  fileobj = createfile filename
  if fileobj == undefined do
  (
   messagebox "Could not create file!"
   return false
  )
   
  -- print file header
  format "// Trespasser Model File (.tpm)\n" to: fileobj
  format "\nfileinfo\n{\n" to: fileobj
  format " formatversion = 1.0.1\n" to: fileobj
  format " name = \"%\"\n" maxFileName to: fileobj
  format " version = 1.2.2\n" to: fileobj
  format " source = \"%\"\n" maxFileName to: fileobj
  format " date = %\n" localTime to: fileobj
  format " comments = \"\"\n" to: fileobj
  format "}\n" to: fileobj
   
  -- write objects
  local obj
  for j=1 to $objects.count do
  (
   obj = $objects[j]
  
   -- skip hidden
   if chkSkipHidden.checked == true do
   (
    if obj.ishidden do continue
   )
   
   -- skip frozen
   if chkSkipFrozen.checked == true do
   (
    if obj.isFrozen do continue
   )
   
   -- skip unselected
   if chkSelected.checked == true do
   (
    if not obj.isSelected do continue
   )
   
   -- write data block
   case classof(obj) of
   (
    
    -- print bonesys (max 2.5)
    Bone:
    (
     WriteBonesys obj
    )
   
    -- write bonesys (max 3.0 and up)
    BoneGeometry:
    (
     format "\nbone \"%\"\n{\n" obj.name to: fileobj
     format " position = (%,%,%)\n" obj.pos.x obj.pos.y obj.pos.z to: fileobj
     local userProps = getuserpropbuffer obj
     if userProps != undefined AND userProps != "" do
     (
      format "%" userProps to: fileobj
     )
     format"}\n" to: fileobj
    )
    
    -- write mesh
    Editable_mesh:
    (
     WriteMesh obj
     WriteInstance obj
    )
    
    -- silently ignore
    OmniLight:()
    FreeSpot:()
    TargetSpot:()
    DirectionalLight:()
    TargetDirectionalLight:()
     
    -- other objects
    default:
    (
     warn_notobj = true
     messagebox obj.name
    )
    
   )
   
   -- warnings
   if obj.scale.x < 0 do warn_mirror = true
   if obj.scale.y < 0 do warn_mirror = true
   if obj.scale.z < 0 do warn_mirror = true
   
  )
  
  -- close file
  close fileobj
  
  -- clean up material redundancy list
  matlist = #()
  matlistnum = 0
  
  -- warnings
  local errstr = ""
  if warn_notobj do  errstr = errstr + "* not all scene objects could be exported\n"
  if warn_mirror do  errstr = errstr + "* some objects are mirrored, this may lead to object orientation problems\n"
  if warn_twoside do errstr = errstr + "* some materials are two sided, these are not preserved\n"
  if errstr.count > 0 do messagebox ("Warning:\n" + errstr)
   
  -- success confirmation
  messagebox "Scene exported successfully."
  
 )
)

rollout rAbout "About"
(
 label lab1 "TPM Exporter"
 label lab2 "Version 1.2.3"
 label lab3 "by Martijn Buijs, Andres James"
 label lab4 "Copyright © TresCom, 2007"
)

-- floater
TpmExp = newRolloutFloater "TpmExp" 180 300
addRollout rExport TpmExp
addRollout rAbout TpmExp

-- END OF FILE



Re: Fixed TPM import and export maxscripts (version 02 and 1

Posted: Mon Sep 26, 2022 5:59 pm
by machf
Remdul's ASA exporter, too:

Code: Select all

-- Animation Script Exporter
-- Version 0.2
-- by Martijn Buijs
-- Copyright TresCom, 2004

global fileobj
global forcedrate
global skiphidden
global skipfrozen
global cancelloop
global currentframe

fn WriteFrame obj =
(
 format "frame %\n" (sliderTime.frame as integer) to: fileobj
 format "pos % % %\n" obj.position.x obj.position.y obj.position.z to: fileobj
 format "rot % % % %\n" obj.rotation.x obj.rotation.y obj.rotation.z obj.rotation.w to: fileobj
 format "scale %\n\n" ((obj.scale.x + obj.scale.y + obj.scale.z) / 3) to: fileobj
)

fn WriteAllFrames obj =
(
 local j
 for j=animationRange.start to animationRange.end do
 (
  sliderTime = j
  WriteFrame obj
  cancelloop = getProgressCancel() --why is it shown twice???
  if cancelloop == true do exit
 )
)

fn WriteKeyFrames obj =
(
 --todo
)

fn ExpObj obj = 
( 
 local b
 b = false
 case superclassof(obj) of
 (
  GeometryClass:
  (
   case classof(obj) of
   (
    TargetObject: b = false
	default: b = true
   )
   if b == true then
   (
    format "object %\n\n" obj.name to: fileobj
   )
  )
  camera:
  (
   if classof(obj) == FreeCamera do
   (
    format "object Camera\n\n" obj.name to: fileobj
	b = true
   )
  )
 )
 if b == true then
 (
  if currentframe == true then
  (
   WriteFrame obj
  ) else (
   if forcedrate == true then
   (
    WriteAllFrames obj
   ) else (
    WriteKeyFrames obj
   )
  )
  format "end_object\n\n" to: fileobj
 ) else (
  format "Skipping %...\n" obj.name
 )
)

fn Export =
(
 local filename
 local i
 local b
 filename = getSaveFileName caption: "Export Animation" types: "Animation Script (*.asa)|*.asa|All Files (*.*)|*.*"
 if filename != undefined then
 (
  fileobj = createfile filename
  format "version 2\n" to: fileobj
  if forcedrate = true then
  (
   format "forced_rate %\n\n" frameRate to: fileobj
  ) else (
   format "forced_rate 0\n\n" to: fileobj
  )
  progressStart "Exporting..."
  cancelloop = false
  for i=1 to objects.count do
  (
   if skiphidden == true then
   (

    b = not objects[i].isHidden
	--if b == false do format "% is hidden!\n" objects[i].name
   ) else (
    b = true
   )
   if b == true do
   (
    if skipfrozen == true then
    (
     b = not objects[i].isFrozen
	 --if b == false do format "% is frozen!\n" objects[i].name
    ) else (
     b = true
    )
   )
   if b == true then
   (
    ExpObj objects[i]
   ) else (
    format "Skipping %...\n" objects[i].name
   )
   progressUpdate (100 * i / objects.count)
   if cancelloop == true do exit
  )
  progressEnd()
  close fileobj
  messageBox "Done."
 )
)

utility AsaExp "AsaExp"
(
 group "About"
 (
  label lab1 "Animation Script Exporter"
  label lab2 "Version 0.2"
  label lab3 "by Martijn Buijs"
  label lab4 "Copyright TresCom, 2004"
 )
 group "Export"
 (
  checkbox chkForcedRate "Forced Framerate" enabled: false checked: true
  checkbox chkSkipHidden "Skip Hidden Objects" checked: true
  checkbox chkSkipFrozen "Skip Frozen Objects" checked: true
  button cmdFrame "Export Current Frame..." width: 140
  button cmdAnimation "Export Animation..." width: 140
 )
 on cmdFrame pressed do
 (
  forcedrate = chkForcedRate.checked
  skiphidden = chkSkipHidden.checked
  skipfrozen = chkSkipFrozen.checked
  currentframe = true
  Export()
 )
 on cmdAnimation pressed do
 (
  forcedrate = chkForcedRate.checked
  skiphidden = chkSkipHidden.checked
  skipfrozen = chkSkipFrozen.checked
  currentframe = false
  Export()
 )
)

Re: Fixed TPM import and export maxscripts (version 02 and 1

Posted: Mon Sep 26, 2022 6:00 pm
by machf
And an earlier version (1.2.1):

Code: Select all

-- TPM Exporter
-- Version 1.2.1
-- by Martijn Buijs, Andres James
-- Copyright © TresCom, 2004

-- TODO:
-- -add export selection button

global filename
global fileobj

-- write material
fn WriteMaterial mat =
(
 format "\nmaterial \"%\"\n{\n" mat.name to: fileobj
 if classOf(mat.diffusemap) == BitmapTexture do
 (
  format " colormap = \"%\"\n" mat.diffusemap.filename to: fileobj
 )
 if classOf(mat.opacitymap) == BitmapTexture do
 (
  format " opacitymap = \"%\"\n" mat.opacitymap.filename to: fileobj
 )
 format "}\n" to: fileobj
)

-- write bones
fn WriteBoneChildren obj =
(
 local i
 if obj.children.count > 0 do
 (
  for i=1 to obj.children.count do
  (
   format " j = \"%\"" obj.children[i].name to: fileobj
   format ",(%,%,%)" obj.children[i].pos.x obj.children[i].pos.y obj.children[i].pos.z to: fileobj
   format ",(-90,-90,-90),(90,90,90),\"%\"\n" obj.name to: fileobj
   WriteBoneChildren obj.children[i]
  )
 )
)

-- write bonesystem
fn WriteBonesys obj =
(
 if obj.parent == undefined do
 (
  format "\nbonesys \"%\"\n{\n" obj.name to: fileobj
  format " r = \"%\",(%,%,%)\n" obj.name obj.pos.x obj.pos.y obj.pos.z to: fileobj
  WriteBoneChildren obj
  format "}\n" to: fileobj
 )
)

-- write mesh
fn WriteMesh obj =
(
 local isskin = false
 local boneVerts = #()
 local i
 local k
 
 if classof(obj.modifiers[1]) == Skin then
 (
  isskin = true
  local myskin = obj.modifiers[1]
  
  -- skin stuff only works if the modifer panel is open, and mesh is selected
  max modify mode
  select obj
  
  -- collect up bone indicies for each vertex
  for i=1 to skinOps.getNumberVertices myskin do
  (
   local maxb = 0 -- best bone so far
   local maxw = 0.0 -- best weight so far
   -- Trespasser only supports singly weighted verticies, so choose bone with greatest influence on vertex
   for k=1 to (skinops.getVertexWeightCount myskin i) do
   (
    if (skinops.getVertexWeight myskin i k) > maxw then
	(
     maxw = (skinops.getVertexWeight myskin i k)
     maxb = (skinops.getVertexWeightBoneID myskin i k)
    )
    append boneVerts maxb -- should be error if not attached to any bone?
   )
   format "\nskin \"%\"\n{\n" obj.name to: fileobj
  )
 ) else (
  format "\nmesh \"%\"\n{\n" obj.name to: fileobj
 )
 
 -- standard material
 if classof(obj.material) == StandardMaterial do
 (
  format " m = \"%\"\n" obj.material.name to: fileobj
 )
 
 -- multimaterial
 if classof(obj.material) == MultiMaterial do
 (
  for i=1 to obj.material.numsubs do
  (
   format " m = \"%\"\n" obj.material[i].name to: fileobj
  )
 )
 
 -- write vertices
 for i=1 to obj.numverts do
 (
  in coordsys local v = (getvert obj i)
  if isskin == true then
  (
   format " v = (%,%,%),%\n" v.x v.y v.z (boneVerts[i]-1) to: fileobj
  ) else (
   format " v = (%,%,%)\n" v.x v.y v.z to: fileobj
  )
 )
 
 -- write texcoords
 if not obj.numtverts == 0 then
 (
  for i=1 to obj.numtverts do
  (
   t = (gettvert obj i)
   format " t = (%,%)\n" t.x t.y to: fileobj
  )
 ) else (
  format " t = (0,0)\n" to: fileobj
 )
 
 -- write normals
 for i=1 to obj.numverts do
 (
  in coordsys local n = (getnormal obj i)
  format " n = (%,%,%)\n" n.x n.y n.z to: fileobj
 )
 
 -- write faces
 for i=1 to obj.numfaces do
 (
 
  -- vertex pointers
  v = (getface obj i)
  if not obj.numtverts == 0 do
  (
   t = (gettvface obj i)
  )
  n = (getface obj i)
  f = (getfacematid obj i)
  format " f = (%,%,%)" (v.x as integer) (v.y as integer) (v.z as integer) to: fileobj
  
  -- texcoord pointers
  if not obj.numtverts == 0 then
  (
   format ",(%,%,%)" (t.x as integer) (t.y as integer) (t.z as integer) to: fileobj
  ) else (
   -- dummy texcoord pointers
   format ",(1,1,1)" to: fileobj
  )
  
  -- normal pointers
  format ",(%,%,%)" (n.x as integer) (n.y as integer) (n.z as integer) to: fileobj
  
  -- material pointers
  if obj.material == undefined then
  (
   -- no material on mesh
   format ",0\n" to: fileobj
  ) else (
   if classof(obj.material) == StandardMaterial do
   (
    -- single material for entire mesh
    format ",1\n" to: fileobj
   )
   if classof(obj.material) == MultiMaterial do
   (
    -- submaterials, per face
    format ",%\n" (getfacematid obj i as integer) to: fileobj
   )
  )
 )
 
 -- end of mesh chunk
 format "}\n" to: fileobj
)

utility TpmExp "TPM Exporter"
(
 group "About"
 (
  label lab1 "TPM Exporter"
  label lab2 "Version 1.2.1"
  label lab3 "by Martijn Buijs, Andres James"
  label lab4 "Copyright © TresCom, 2004"
 )
 group "Export"
 (
  checkbox chkSkipHidden "Skip Hidden Objects" checked: true
  checkbox chkSkipFrozen "Skip Frozen Objects" checked: true
  button cmdExport "Export Scene..." width: 140
 )
 on cmdExport pressed do
 (
  local j
  local b
  
  -- clean up listener
  clearListener
  
  -- save file dialog
  filename = getSaveFileName caption:"Export Scene" types:"Trespasser Models (*.tpm)|*.tpm|*.txt|All Files (*.*)|*.*"
  if filename != undefined then
  (
   fileobj = createfile filename
   
   -- print file header
   format "// Trespasser Model File (.tpm)\n" to: fileobj
   format "\nfileinfo\n{\n" to: fileobj
   format " formatversion = 1.0.1\n" to: fileobj
   format " name = \"%\"\n" maxFileName to: fileobj
   format " version = 1.2.1\n" to: fileobj
   format " source = \"%\"\n" maxFileName to: fileobj
   format " date = %\n" localTime to: fileobj
   format " comments = \"\"\n" to: fileobj
   format "}\n" to: fileobj
   
   -- print used materials
   for j=1 to sceneMaterials.count do

   (
    if classOf(sceneMaterials[j]) == StandardMaterial do
    (
     WriteMaterial sceneMaterials[j]
    )
    if classOf(sceneMaterials[j]) == MultiMaterial do
    (
     for k=1 to sceneMaterials[j].numSubs do
     (
	  mat = sceneMaterials[j].materialList[k]
	  if classof(mat) == StandardMaterial do WriteMaterial mat
	 )
    )
   )

   -- write objects
   for j=1 to $objects.count do
   (
   
    -- skip hidden
	if chkSkipHidden.checked == true then
	(
	 b = not $objects[j].ishidden
	) else (
	 b = true
	)
	
	-- skip frozen
	if b == true do
	(
	 if chkSkipFrozen.checked == true then
	 (
	  b = not $objects[j].isFrozen
	 ) else (
	  b = true
	 )
	)
    
    -- export object?
    if b == true do
	(
	
     -- print bonesys (max 2.5)
     if classof($objects[j]) == Bone do
     (
      WriteBonesys $objects[j]
     )
     
     -- print bonesys (max 3.0 and up)
     if classof($objects[j]) == BoneGeometry do
     (
      local b = $objects[j]
      format "\nbone \"%\"\n{\n" b.name to: fileobj
      format " position = (%,%,%)\n" b.pos.x b.pos.y b.pos.z to: fileobj
      local userProps = getuserpropbuffer $objects[j]
      if userProps != undefined AND userProps != "" do
      (
       format "%" userProps to: fileobj
      )
      format"}\n" to: fileobj
     )
     
     -- print meshes
     if classof($objects[j]) == Editable_mesh do
     (
      WriteMesh $objects[j]
     )
	 
	 -- print instance
     if classof($objects[j]) == Editable_mesh do
     (
      local obj = $objects [j]
      format "\ninstance \"%\" {\n" obj.name to: fileobj
      format " mesh = \"%\"\n" obj.name to: fileobj -- need to deal with meshes with multiple instances
      format " position = (%,%,%)\n" obj.pos.x obj.pos.y obj.pos.z to: fileobj
      local ea = quatToEuler obj.rotation order:1 -- check which order is correct
      -- euler angles seem to be -ve what they should be...probably stuffing up the rotation order too, should check
      format " rotation = (%,%,%)\n" -ea.x -ea.y -ea.z to: fileobj
      format " scale = %\n" obj.scale.x to: fileobj
      format "}\n" to: fileobj
     )
    )
   )
   
   -- success confirmation
   messagebox "Scene exported successfully."
   
   -- clean up "OK" messages in listener
   clearListener
   
  )
 )
)