-- TreeMesh Importer
--  Copyright © Martijn Buijs, 2010
--  http://www.bytehazard.com

-------------------------------------------------------

-- clean up
clearlistener()
global tmImp
if tmImp != undefined do
(
 closeRolloutFloater tmImp
)


-- globals
global impscale

struct tmgrpmat (start,count,texname)
struct tmgrp (matnum,mat)


-- skips over BSP node
fn SkipBspNode fp =
(
 -- skip AABB (24 bytes)
 fseek fp 24 #seek_cur
 
 -- read number of faces (4 bytes)
 local facenum = ReadLong fp
 
 -- skip over face index array (4 bytes * facenum)
 fseek fp (facenum * 4) #seek_cur
 
 -- read node A flag (1 byte)
 local flag_a = ReadByte fp
 
 -- skip over node A (1 byte
 if flag_a == 1 do SkipBspNode fp
 
 -- read node B flag (1 byte)
 local flag_b = ReadByte fp
 
 -- skip over node B
 if flag_b == 1 do SkipBspNode fp
)


-- imports mesh file
fn ImportTreeMesh filename =
(
 -- open file
 local fp = fopen filename "rb"
 if fp == undefined do
 (
  messagebox "Could not open file \"" + filename + "\"."
  return false
 )
 
 ---- header -------------------------------------------------------------------
 
 -- skip header (12 bytes)
 fseek fp 12 #seek_cur
 
 -- skip bounds (24 bytes)
 fseek fp 24 #seek_cur
 
 -- skip secondary bounds (24 bytes)
 fseek fp 24 #seek_cur
 
 ---- groups -------------------------------------------------------------------
 
 -- read group info
 local tmgroupnum = 4
 local tmgroup = #()
 for i=1 to tmgroupnum do
 (
  -- read number of materials in this group (4 bytes)
  local matnum = ReadLong fp
  
  -- create group material
  tmgroup[i] = tmgrp()
  tmgroup[i].matnum = matnum
  tmgroup[i].mat = #()
  
  -- read group materials
  for j=1 to matnum do
  (
   tmgroup[i].mat[j] = tmgrpmat()
   
   -- read index start offset (4 bytes)
   tmgroup[i].mat[j].start = ReadLong fp
   
   -- number of triangles (4 bytes)
   tmgroup[i].mat[j].count = ReadLong fp
   
   -- read name
   local namelen = ReadLong fp
   local namestr = ""
   for i=1 to namelen do
   (
    namestr = namestr + (bit.IntAsChar (ReadByte fp))
   )
   tmgroup[i].mat[j].texname = namestr
   
   --format ">>> texture: %\n" namestr
  )
 )
 
 ---- collision geometry -------------------------------------------------------
 
 local colvert = #()
 local colface = #()
 local colfaceid = #()
 
 --format ">>> collider data start @ %\n" (ftell fp)
 
 -- collision flag (4 bytes)
 local colflag = ReadLong fp
 
 -- read collision data
 if colflag != 0 do
 (
  -- skip over version number (4 bytes)
  fseek fp 4 #seek_cur
  
  -- collider vertices (4 bytes)
  local vertnum = ReadLong fp
  
  --format ">>> collider vertices: %\n" vertnum
  --format ">>> collider vertex block start start @ %\n" (ftell fp)
  
  -- read vertices (16 bytes * vertnum)
  for i=1 to vertnum do
  (
   -- read position
   local vx = (ReadFloat fp)*impscale
   local vy = (ReadFloat fp)*impscale
   local vz = (ReadFloat fp)*impscale
   append colvert [-vx,-vz,vy]
   
   -- skip over flags
   fseek fp 4 #seek_cur
  )
  
  --format ">>> collider vertex block end @ %\n" (ftell fp)
  
  -- read facenum (4 bytes)
  local facenum = ReadLong fp
  
  --format ">>> collider faces: %\n" facenum
  --format ">>> collider faces block start start @ %\n" (ftell fp)
  
  -- read faces (8 bytes * facenum)
  for i=1 to facenum do
  (
   local v1 = (ReadShort fp #unsigned)+1
   local v2 = (ReadShort fp #unsigned)+1
   local v3 = (ReadShort fp #unsigned)+1
   local matid = (ReadShort fp #unsigned)+1
   
   append colface [v1,v2,v3]
   append colfaceid matid
  )
  
  --format ">>> collider faces block end @ %\n" (ftell fp)
  
  -- skip over unknown (8 bytes)
  fseek fp 8 #seek_cur
  
  -- skip over unknown data block
  local unk_num = ReadLong fp
  fseek fp (32 * unk_num) #seek_cur
  
  -- skip over BSP tree
  SkipBspNode fp
 )
 
 ---- geometry data ------------------------------------------------------------
 
 -- read vertex num (4 bytes)
 local vertnum = ReadLong fp
 
 --format ">>> vertices: %\n" vertnum
 --format ">>> vertex block start start @ %\n" (ftell fp)
 
 -- read vertices (44 bytes * vertnum)
 local vert = #()
 local norm = #()
 local texc = #()
 for i=1 to vertnum do
 (
  -- read position (12 bytes)
  local vx = (ReadFloat fp)*impscale
  local vy = (ReadFloat fp)*impscale
  local vz = (ReadFloat fp)*impscale
  append vert [-vx,-vz,vy]
  
  -- read normal (12 bytes)
  local nx = ReadFloat fp
  local ny = ReadFloat fp
  local nz = ReadFloat fp
  append norm [-nx,-nz,ny]
  
  -- skip unknown (4 bytes)
  fseek fp 4 #seek_cur
  
  -- read UV (8 bytes)
  local tx = (ReadFloat fp)
  local ty = (1.0 - (ReadFloat fp)) - 1.0
  append texc [tx,ty,0.0]
  
  -- skip over unknown (8 bytes)
  fseek fp 8 #seek_cur
 )
 
 format ">>> vertex block end @ %\n" (ftell fp)
 
 -- read number of indices (4 bytes)
 local indexnum = ReadLong fp
 
 --format ">>> indices: %\n" indexnum
 --format ">>> index block start start @ %\n" (ftell fp)
 
 -- read indices
 local index = #()
 for i=1 to indexnum do
 (
  index[i] = (ReadShort fp #unsigned)
 )
 --format ">>> index block end @ %\n" (ftell fp)
 
 -------------------------------------------------------------------------------
 
 -- close file
 fclose fp
 
 ---- construct material -------------------------------------------------------
 
 -- count materials
 local totalmats = 0
 for i=1 to tmgroupnum do
 (
  totalmats = totalmats + tmgroup[i].matnum
 )
 
 -- quit if there are no materials
 if totalmats == 0 do return false
 
 -- create multi material
 local multimat = MultiMaterial numsubs:totalmats
 multimat.name = getFilenameFile filename
 
 -- build face mat ID list
 local facemat = #()
 local matind = 1
 for i=1 to tmgroupnum do
 (
  for j=1 to tmgroup[i].matnum do
  (
   -- create sub material
   local mat = StandardMaterial();
   multimat[matind] = mat
   mat.name = getFilenameFile tmgroup[i].mat[j].texname
   local bm = BitmapTexture()
   mat.diffuseMap = bm
   mat.showInViewport = true
   bm.filename = (getFilenameFile tmgroup[i].mat[j].texname) + ".dds"
   bm.alphasource = 2
   bm.monoOutput = 0
   
   if i==1 OR i==3 do (
    mat.twosided = true
    
    local bma = BitmapTexture()
    mat.opacitymap = bma
    bma.filename = bm.filename
    bma.alphasource = 0
    bma.monoOutput = 1
   )
   
   -- build face material index array
   for k=1 to tmgroup[i].mat[j].count do
   (
    append facemat matind
   )
   
   matind = matind+1
  )
 )
 
 ---- construct mesh -----------------------------------------------------------
 
 -- construct face array
 local face = #()
 for i=1 to tmgroupnum do
 ( 
  for j=1 to tmgroup[i].matnum do
  (
   local istart = tmgroup[i].mat[j].start
   local fcount = tmgroup[i].mat[j].count
   for f=0 to fcount-1 do
   (
    local i1 = index[1+ istart + (f*3) +0 ]+1
    local i2 = index[1+ istart + (f*3) +1 ]+1
    local i3 = index[1+ istart + (f*3) +2 ]+1
    append face [i3,i2,i1]
   )
  )
 )
 
 -- check if there are faces at all
 local obj = undefined
 if face.count > 0 do
 (
  --format ">>> face.count: %\n" face.count
  --format ">>> facemat.count: %\n" facemat.count
  
  -- create mesh
  local msh = mesh vertices:vert tverts:texc faces:face materialIDS:facemat
  msh.name = getFilenameFile filename
  
  -- MXS bug workaround
  buildTVFaces msh 
  for j=1 to msh.numfaces do
  (
   setTVFace msh j (getFace msh j)
  )
  -- end workaround
  
  -- set material
  msh.mat = multimat
  
  -- finalize
  update msh
  select msh
  obj = msh
 )
 
 ---- construct collision mesh -------------------------------------------------
 
 if colface.count > 0 do
 (
  local msh = mesh vertices:colvert faces:colface materialIDS:colfaceid
  msh.name = (getFilenameFile filename) + "_col"
  msh.renderable = false
  
  -- clear smoothgroups
  for i=1 to msh.numfaces do
  (
   setFaceSmoothGroup msh i 0
  )
  
  -- finalize
  update msh
  if obj != undefined do msh.parent = obj
 )
 
 -------------------------------------------------------------------------------
 
 return true
)


-- GUI
rollout rImport "TreeMesh Importer"
(
 group "Import"
 (
  spinner spnImpScale "Scale " range:[0.001,1000.0,10.0]
  button cmdImport "Import..." width: 140
 )
 on cmdImport pressed do
 (
  
  -- open file dialog
  filename = getOpenFileName caption:"Import File" types:"Tree Mesh (*.tm)|*.tm|All Files (*.*)|*.*"
  if filename == undefined then
  (
   return false
  )
  
  -- copy values
  impscale = spnImpScale.value
  
  -- import
  ImportTreeMesh filename
  
 )
)

rollout rAbout "About"
(
 label lab1 "TreeMesh Importer"
 label lab2 "Version 1.0.0"
 label lab3 "Copyright © Martijn Buijs, 2010"
)

-- floater
tmImp = newRolloutFloater "TreeMesh Importer" 180 155
addRollout rImport tmImp
addRollout rAbout tmImp rolledup:true

-- END OF FILE
