-- StdMesh Importer
--  Copyright © Martijn Buijs, 2008
--  http://www.bytehazard.com


-- Misc Functions --------------------------------------

-- splits string by seperator character
fn Split str sep =
(
 local out = #()
 local tmp = ""
 for i=1 to str.count do
 (
  if (str[i] == sep) then
  (
   if tmp.count > 0 do
   (
    append out tmp
    tmp = ""
   )
  ) else (
   tmp = tmp + str[i]
  )
  if (i == str.count) do (
   if tmp.count > 0 do append out tmp
  )
 )
 return out
)

-- returns character position in string
fn InStr str char =
(
 for i=1 to str.count do
 (
  if str[i] == char do return i
 )
 return 0
)

-- returns character position in string from end
fn InStrRev str char =
(
 local i = str.count
 while i > 0 do
 (
  if str[i] == char do return i
  i=i-1
 )
 return 0
)

-- returns part of string
fn Mid str start num =
(
 local tmp = ""
 for i=start to start+num-1 do
 (
  tmp = tmp + str[i]
 )
 return tmp
)

-- returns part of string
fn StrPart str start end =
(
 local tmp = ""
 for i=start to end do
 (
  tmp = tmp + str[i]
 )
 return tmp
)

-- replaces characters
fn ReplaceChars str fnd rpl =
(
 for i=1 to str.count do
 (
  if str[i] == fnd do str[i] = rpl
 )
 return str
)

-- replaces characters
fn StripChars str rem =
(
 local tmp = ""
 for i=1 to str.count do
 (
  if str[i] != rem do tmp = tmp + str[i]
 )
 return tmp
)

-- returns material by name
fn GetMatByName matname =
(
 for i=1 to sceneMaterials.count do
 (
  local mat = sceneMaterials[i]
  if mat.name == matname do return mat
  if (classof mat) == Multimaterial do
  (
   for j=1 to mat.numsubs do
   (
    if mat[j] != undefined do
    (
     if mat[j].name == matname do return mat[j]
    )
   )
  )
 )
 return undefined
)

-------------------------------------------------------

-- clean up
clearlistener()
global smImp
if smImp != undefined do
(
 closeRolloutFloater smImp
)


-- structures
struct stdmeshmat (matname, u2, u3, u4, u5, u6, vertstride, vertnum, indexnum, u7)


-- globals
global stdmat
global impscale


-- returns stdmat by name
fn FindStdMat mname =
(
 for i=1 to stdmat.count do
 (
  if stdmat[i].name == mname do return stdmat[i]
 )
 return undefined
)


-- imports mesh file
fn ImportStdMesh filename =
(
 -- open file
 local fp = fopen filename "rb"
 if fp == undefined do
 (
  messagebox "Could not open file \"" + filename + "\"."
  return false
 )
 
 -- get mesh name
 local meshname = getFilenameFile filename
 
 -- file info (8 bytes)
 local u1 = ReadLong fp
 local u2 = ReadLong fp
 
 -- skip bounds (24 bytes)
 fseek fp 24 #seek_cur
 
 -- qflag (1 byte)
 local qflag
 if u1 == 10 do qflag = ReadByte fp
 
 -- cols
 local colnum = ReadLong fp
 for i=1 to colnum do
 (
  local size = ReadLong fp
  --fseek fp size #seek_cur
  --exit
  
  local skiploc = (ftell fp) + size
  
  -- skip 8 bytes
  fseek fp 8 #seek_cur
  
  -- vertices
  local vertnum = ReadLong fp
  local vert = #()
  for j=1 to vertnum do
  (
   local vx = (ReadFloat fp)*impscale
   local vy = (ReadFloat fp)*impscale
   local vz = (ReadFloat fp)*impscale
   fseek fp 4 #seek_cur
   append vert [-vx,-vz,vy]
  )
  
  -- faces
  local facenum = ReadLong fp
  local face = #()
  local faceid = #()
  local mlist = #()
  for j=1 to facenum do
  (
   local v1 = (ReadShort fp)+1
   local v2 = (ReadShort fp)+1
   local v3 = (ReadShort fp)+1
   
   -- determine material ID
   local fid = 0
   local m = (ReadShort fp)
   for k=1 to mlist.count do
   (
    if mlist[k] == m do
    (
     fid = k
     exit
    )
   )
   if fid == 0 do
   (
    append mlist m
    fid = mlist.count
   )
   
   append face [v1, v2, v3]
   append faceid fid
  )
  
  -- create mesh
  local msh = mesh vertices:vert faces:face materialIDS:faceid
  msh.name = (meshname + "_col" + (i as string))
  msh.renderable = false
  
  -- clear smoothgroups
  for j=1 to msh.numfaces do
  (
   setFaceSmoothGroup msh j 0
  )
  
  -- create multimat
  local multimat = MultiMaterial numsubs:mlist.count
  multimat.name = (meshname + "_col" + (i as string))
  for j=1 to mlist.count do
  (
   multimat[j] = StandardMaterial()
   multimat[j].name = ("colmat" + (j as string))
   local c = (color 255 127 127)
   c.h = j * (255.0 / mlist.count)
   multimat[j].diffusecolor = c
  )
  msh.mat = multimat
  
  -- done
  update msh
  
  -- skip to next col
  fseek fp skiploc #seek_set
 )
 
 -- meshes
 local msh
 local oldmsh
 local meshnum = ReadLong fp
 for i=1 to meshnum do
 (
  -- material data
  local matnum = ReadLong fp
  local mat = #()
  for j=1 to matnum do
  (
   mat[j] = stdmeshmat()
   
   -- material name
   local matnamelen = ReadLong fp
   local matname = ""
   for k=1 to matnamelen do
   (
    matname = matname + (bit.IntAsChar (ReadByte fp))
   )
   mat[j].matname = matname
   
   -- geometry info
   mat[j].u2 = ReadLong fp
   mat[j].u3 = ReadLong fp
   mat[j].u4 = ReadLong fp
   mat[j].u5 = ReadLong fp
   mat[j].u6 = ReadLong fp
   mat[j].vertstride = ReadLong fp
   mat[j].vertnum = ReadLong fp
   mat[j].indexnum = ReadLong fp
   mat[j].u7 = ReadLong fp
  )
  
  -- create multi material
  local multimat = MultiMaterial numsubs:matnum
  multimat.name = meshname + "_Material_lod" + (i as string)
  for j=1 to matnum do
  (
   local submat = FindStdMat mat[j].matname
   if submat == undefined then
   (
    multimat[j] = StandardMaterial()
    multimat[j].name = mat[j].matname
    multimat[j].diffusecolor = (color 255 127 0 1)
   ) else (
    multimat[j] = submat
   )
   multimat[j].showInViewport = true
  )
  
  -- geometry data
  local vert = #()
  local norm = #()
  local tex1 = #()
  local tex2 = #()
  local face = #()
  local fmat = #()
  local offset = 0
  for j=1 to matnum do
  (
   -- vertex data
   for k=1 to mat[j].vertnum do
   (
    -- vertex
    vx = (ReadFloat fp)*impscale
    vy = (ReadFloat fp)*impscale
    vz = (ReadFloat fp)*impscale
    append vert [-vx,-vz,vy]
    
    -- normal
    nx = ReadFloat fp
    ny = ReadFloat fp
    nz = ReadFloat fp
    append norm [-nx,-nz,ny]
    
    -- texcoord 1
    tx = (ReadFloat fp)
    ty = (1.0 - (ReadFloat fp)) - 1.0
    append tex1 [tx,ty,0.0]
    
    -- texcoord 2
    if mat[j].vertstride > 32 do
    (
     tx = (ReadFloat fp)
     ty = (1.0 - (ReadFloat fp))
     append tex2 [tx,ty,0.0]
    )
   )
   
   -- index data
   for k=1 to (mat[j].indexnum/3) do
   (
    v3 = (ReadShort fp)+1
    v2 = (ReadShort fp)+1
    v1 = (ReadShort fp)+1
    append face [offset+v1, offset+v2, offset+v3]
    append fmat j
   )
   
   -- increment vertex offset
   offset = offset+mat[j].vertnum
  )
  
  -- check if there are faces at all
  if face.count > 0 do
  (
   
   -- create mesh
   oldmsh = msh
   msh = mesh vertices:vert tverts:tex1 faces:face materialIDS:fmat
   
   -- MXS bug workaround
   buildTVFaces msh 
   for j=1 to msh.numfaces do
   (
    setTVFace msh j (getFace msh j)
   )
   -- end workaround
   
   -- lightmap UVs
   if tex2.count > 0 do
   (
    
    format ">>> geom[%]" i
    
    -- MXS bug workaround
    local modf = uvwmap()
    modf.mapchannel = 2
    addModifier msh modf
    collapseStack msh
    -- end workaround
    
    meshop.setMapSupport msh 2 true
    meshop.setNumMapVerts msh 2 tex2.count
    for j=1 to tex2.count do
    (
     meshop.setMapVert msh 2 j tex2[j] 
    )
    meshop.buildMapFaces msh 2
    for j=1 to msh.numfaces do
    (
     meshop.setMapFace msh 2 j (getFace msh j)
    )
   )
   
   -- set material
   msh.mat = multimat
   
   -- clean up
   -- todo: run auto edge < 5 degrees
   -- todo: set single smooth group
   
   if i==1 then (
    msh.name = meshname
    select msh
   ) else (
    msh.name = meshname + "_lod" + ((i-1) as string)
    msh.parent = oldmsh
   )
   
   -- done
   update msh
   
  )
 )
 
 -- close file
 fclose fp
 
 return true
)


-- imports shader file
fn ImportRs filename =
(
 -- clear stdmat list
 stdmat = #()
 
 -- open file
 local fs = openfile filename
 if fs == undefined do
 (
  messagebox "Shader file \"" + filename + "\" not found."
  return false
 )
 
 -- read lines
 local inblock = false
 local mat = undefined
 while not (eof fs) do
 (
  -- read single line
  local ln = readline fs
  
  -- remove evil tabs
  ln = (StripChars ln "\t")
  
  -- only process if line not empty
  if ln.count > 0 do
  (
   str = (Split ln " ")
   case str[1] of
   (
    "subshader":
    (
     mat = StandardMaterial()
     mat.name = StripChars str[2] "\""
     --format ">>> subshader %\n" mat.name
     append stdmat mat
    )
    "{":( inblock=true )
    "}":( inblock=false )
    default:
    (
     -- ignore everything outside block
     if inblock do
     (
      case str[1] of
      (
       "lighting":()
       "lightingSpecular":()
       "materialDiffuse":()
       "texture":
       (
        local tmp = StripChars (StripChars str[2] "\"") ";"
        local s = (InStrRev tmp "/")+1
        local e = tmp.count
        local texname = StrPart tmp s e
        mat.diffusemap = BitmapTexture()
        mat.diffusemap.filename = texname + ".dds"
       
        --format ">>>  texture %\n" texname
       )
      )
     )
    )
    
   )
   
  )
 )
 
 -- close file
 close fs
 
 -- success
 return true
)


-- GUI
rollout rImport "StandardMesh Importer"
(
 group "Import"
 (
  spinner spnImpScale "Scale " range:[0.001,1000.0,10.0]
  checkbox chkImpShader "Import Shader" checked:true
  button cmdImport "Import..." width: 140
 )
 on cmdImport pressed do
 (
  
  -- open file dialog
  filename = getOpenFileName caption:"Import File" types:"Standard Mesh (*.sm)|*.sm|All Files (*.*)|*.*"
  if filename == undefined then
  (
   return false
  )
  
  -- copy values
  impscale = spnImpScale.value
  
  -- import shader file
  if chkImpShader.checked do
  (
   local fsfile = (getFilenamePath filename) + (getFilenameFile filename) + ".rs"
   ImportRs fsfile
  )
  
  -- import
  ImportStdMesh filename
  
 )
)

rollout rAbout "About"
(
 label lab1 "StandardMesh Importer"
 label lab2 "Version 1.1.1"
 label lab3 "Copyright © Martijn Buijs, 2008"
)

-- floater
smImp = newRolloutFloater "StdMesh Importer" 180 155
addRollout rImport smImp
addRollout rAbout smImp rolledup:true

-- END OF FILE
