     tx: 0, ty: 0
   (text, ptr) ->
     ptr = ignoreSpace(text, ptr)
-    {num: result.tx, ptr} = parseFloatx(text, ptr)
-    ptr = ignoreSpace(text, ptr)
-    {num: result.ty, ptr} = parseFloatx(text, ptr)
+    {num: result.tx, ptr} = parseFloatEx(text, ptr)
+    ptr = ignoreSpace(text, ptr)
+    {num: result.ty, ptr} = parseFloatEx(text, ptr)
     result.ptr = ignoreLine(text, ptr)
     return result
 
-parseVertex = do ->
+parseNormal = do ->
   result =
     ptr: 0
     nx: 0, ny: 0, nz: 0
   (text, ptr) ->
     ptr = ignoreSpace(text, ptr)
-    {num: result.nx, ptr} = parseFloatx(text, ptr)
-    ptr = ignoreSpace(text, ptr)
-    {num: result.ny, ptr} = parseFloatx(text, ptr)
-    ptr = ignoreSpace(text, ptr)
-    {num: result.nz, ptr} = parseFloatx(text, ptr)
+    {num: result.nx, ptr} = parseFloatEx(text, ptr)
+    ptr = ignoreSpace(text, ptr)
+    {num: result.ny, ptr} = parseFloatEx(text, ptr)
+    ptr = ignoreSpace(text, ptr)
+    {num: result.nz, ptr} = parseFloatEx(text, ptr)
     result.ptr = ignoreLine(text, ptr)
     return result
 
 parseThird = do ->
   result =
     ptr: 0
-    vi: 0, ti: 0, ni: 0
-  return (text, ptr) ->
-    result.vi = null
-    result.ti = null
-    result.ni = null
-    # vi
-    {ptr: ptr, num: result.vi} = parseIntx(text, ptr)
-    # ti
-    if '/' != text[ptr]
-      result.ptr = ptr
-      return result
-    ptr = ptr + 1
-    if '/' != text[ptr]
-      {ptr: ptr, num: result.ti} = parseIntx(text, ptr)
-    # ni
-    if '/' != text[ptr]
-      result.ptr = ptr
-      return result
-    ptr = ptr + 1
-    {ptr: ptr, num: result.ni} = parseIntx(text, ptr)
+    idxText: ""
+  (text, ptr) ->
+    start = ptr
+    while true
+      charCode = text.charCodeAt(ptr)
+      if 0 == (CHAR_INDEX & CHAR_TABLE[charCode])
+        break
+      ptr = ptr + 1
     result.ptr = ptr
-    return result
-
-parseFace = do ->
-  result =
-    ptr: 0
-    vi1: 0, ti1: 0, ni1: 0
-    vi2: 0, ti2: 0, ni2: 0
-    vi3: 0, ti3: 0, ni3: 0
-  return (text, ptr) ->
-    {ptr, vi: result.vi1, ti: result.ti1, ni: result.ni1} = parseThird(text, ptr)
-    ptr = ignoreSpace(text, ptr)
-    {ptr, vi: result.vi2, ti: result.ti2, ni: result.ni2} = parseThird(text, ptr)
-    ptr = ignoreSpace(text, ptr)
-    {ptr, vi: result.vi3, ti: result.ti3, ni: result.ni3} = parseThird(text, ptr)
-    ptr = ignoreLine(text, ptr)
-    result.ptr = ptr
-    return result
+    result.idxText = text[start...ptr]
+    if '' == result.idxText
+      throw new Error()
+    return result
+
+writeIndex = (ctx, idxText) ->
+  numArray = idxText.split('/')
+  # init once
+  if not ctx.initFlag
+    ctx.initFlag = true
+    ctx.totalStride = 0
+    if '' != numArray[0]
+      ctx.vertexStride = VERTEX_STRIDE
+      ctx.totalStride = ctx.totalStride + VERTEX_STRIDE
+    if '' != numArray[1]
+      ctx.textureStride = TEXTURE_STRIDE
+      ctx.totalStride = ctx.totalStride + TEXTURE_STRIDE
+    if '' != numArray[2]
+      ctx.normalStride = NORMAL_STRIDE
+      ctx.totalStride = ctx.totalStride + NORMAL_STRIDE
+    ctx.vtxBuf = new Array(ctx.totalStride * 512)
+    ctx.idxBuf = new Array(4 * 512)
+  # 重复的idx不再写入
+  idx = ctx.idxMap[idxText]
+  if 'number' == typeof(idx)
+    ctx.idxBuf[ctx.idxBufLen++] = idx
+    return
+  # 解析新的idx
+  ptr = 0
+  offset = 0
+  # 填充vertex
+  if ctx.vertexStride
+    {ptr: ptr, num: vi} = parseIntEx(idxText, ptr)
+    vi = vi - 1
+    if vi > ctx.vertexLen
+      throw new Error()
+    ctx.vtxBuf[ctx.totalStride * ctx.vtxBufLen + offset++] = ctx.vertex[VERTEX_STRIDE * vi + 0]
+    ctx.vtxBuf[ctx.totalStride * ctx.vtxBufLen + offset++] = ctx.vertex[VERTEX_STRIDE * vi + 1]
+    ctx.vtxBuf[ctx.totalStride * ctx.vtxBufLen + offset++] = ctx.vertex[VERTEX_STRIDE * vi + 2]
+  # 填充texture
+  if ctx.textureStride
+    if '/' != idxText[ptr]
+      throw new Error()
+    ptr = ptr + 1
+    {ptr: ptr, num: ti} = parseIntEx(idxText, ptr)
+    ti = ti - 1
+    if ti > ctx.textureLen
+      throw new Error()
+    ctx.vtxBuf[ctx.totalStride * ctx.vtxBufLen + offset++] = ctx.texture[TEXTURE_STRIDE * ti + 0]
+    ctx.vtxBuf[ctx.totalStride * ctx.vtxBufLen + offset++] = ctx.texture[TEXTURE_STRIDE * ti + 1]
+  # 填充normal
+  if ctx.normalStride
+    if '/' != idxText[ptr]
+      throw new Error()
+    ptr = ptr + 1
+    if '/' == idxText[ptr]
+      ptr = ptr + 1
+    {ptr: ptr, num: ni} = parseIntEx(idxText, ptr)
+    ni = ni - 1
+    if ni > ctx.normalLen
+      throw new Error()
+    ctx.vtxBuf[ctx.totalStride * ctx.vtxBufLen + offset++] = ctx.vertex[NORMAL_STRIDE * ni + 0]
+    ctx.vtxBuf[ctx.totalStride * ctx.vtxBufLen + offset++] = ctx.vertex[NORMAL_STRIDE * ni + 1]
+    ctx.vtxBuf[ctx.totalStride * ctx.vtxBufLen + offset++] = ctx.vertex[NORMAL_STRIDE * ni + 2]
+  # 填充index
+  if ptr != idxText.length
+    throw new Error()
+  ctx.idxBuf[ctx.idxBufLen++] = ctx.vtxBufLen
+  ctx.idxMap[idxText] = ctx.vtxBufLen
+  ctx.vtxBufLen = ctx.vtxBufLen + 1
+  return
 
 parseObj = (text) ->
-  vertex = new Array(512 * 3)
-  vertexLen = 0
-  texture = new Array(512 * 2)
-  textureLen = 0
-  normal = new Array(512 * 3)
-  normalLen = 0
-
-  initBufFormat = false
-  vertexFlag = false
-  textureFlag = false
-  normalFlag = false
-
-  vtxBuf = null
-  vtxBufLen = 0
-  vtxBufPtr = 0
-  vtxStride = 0
-  idxBuf = null
-  idxBufLen = 0
-  idxMap = Object.create(null)
-
+  ctx = createContext()
   ptr = 0
-  switch text[ptr]
-    when 'v'
-      ptr = ptr + 1
-      if 't' == text[ptr]
-        ptr = ptr + 1
-        ptr = ignoreSpace(text, ptr)
-        {ptr, tx, ty} = parseTexture(text, ptr)
-        texture[textureLen++] = tx
-        texture[textureLen++] = ty
-      else if 'n' == text[ptr]
-        ptr = ptr + 1
-        ptr = ignoreSpace(text, ptr)
-        {ptr, nx, ny, nz} = parseVertex(text, ptr)
-        normal[normalLen++] = nx
-        normal[normalLen++] = ny
-        normal[normalLen++] = nz
+  textLen = text.length
+  while ptr < textLen
+    switch text[ptr]
+      when 'v'
+        ptr = ptr + 1
+        if 't' == text[ptr]
+          ptr = ptr + 1
+          {ptr, tx, ty} = parseTexture(text, ptr)
+          ctx.texture[TEXTURE_STRIDE * ctx.textureLen + 0] = tx
+          ctx.texture[TEXTURE_STRIDE * ctx.textureLen + 1] = ty
+          ctx.textureLen = ctx.textureLen + 1
+        else if 'n' == text[ptr]
+          ptr = ptr + 1
+          {ptr, nx, ny, nz} = parseNormal(text, ptr)
+          ctx.normal[NORMAL_STRIDE * ctx.normalLen + 0] = nx
+          ctx.normal[NORMAL_STRIDE * ctx.normalLen + 1] = ny
+          ctx.normal[NORMAL_STRIDE * ctx.normalLen + 2] = nz
+          ctx.normalLen = ctx.normalLen + 1
+        else
+          {ptr, vx, vy, vz} = parseVertex(text, ptr)
+          ctx.vertex[VERTEX_STRIDE * ctx.vertexLen + 0] = vx
+          ctx.vertex[VERTEX_STRIDE * ctx.vertexLen + 1] = vy
+          ctx.vertex[VERTEX_STRIDE * ctx.vertexLen + 2] = vz
+          ctx.vertexLen = ctx.vertexLen + 1
+      when 'f'
+        ptr = ptr + 1
+        ptr = ignoreSpace(text, ptr)
+        {ptr, idxText} = parseThird(text, ptr)
+        writeIndex(ctx, idxText)
+        ptr = ignoreSpace(text, ptr)
+        {ptr, idxText} = parseThird(text, ptr)
+        writeIndex(ctx, idxText)
+        ptr = ignoreSpace(text, ptr)
+        {ptr, idxText} = parseThird(text, ptr)
+        writeIndex(ctx, idxText)
+        ptr = ignoreLine(text, ptr)
+      when '#'
+        ptr = ignoreLine(text, ptr)
+      when 'o'
+        ptr = ptr + 1
+        ptr = ignoreSpace(text, ptr)
+        ptr = ignoreLine(text, ptr)
+      when 's'
+        ptr = ptr + 1
+        ptr = ignoreSpace(text, ptr)
+        ptr = ignoreLine(text, ptr)
+      when 'm'
+        if 'mtllib' != text[ptr...(ptr+6)]
+          throw new Error()
+        ptr = ptr + 6
+        ptr = ignoreSpace(text, ptr)
+        ptr = ignoreLine(text, ptr)
+      when 'u'
+        if 'usemtl' != text[ptr...(ptr+6)]
+          throw new Error()
+        ptr = ptr + 6
+        ptr = ignoreSpace(text, ptr)
+        ptr = ignoreLine(text, ptr)
+      when '\r', '\n'
+        ptr = ignoreLine(text, ptr)
+      when ' ', '\t'
+        ptr = ignoreSpace(text, ptr)
+        ptr = ignoreLine(text, ptr)
       else
-        ptr = ignoreSpace(text, ptr)
-        {ptr, vx, vy, vz} = parseVertex(text, ptr)
-        vertex[vertexLen++] = vx
-        vertex[vertexLen++] = vy
-        vertex[vertexLen++] = vz
-    when 'f'
-      ptr = ptr + 1
-      ptr = ignoreSpace(text, ptr)
-      {ptr, vi1, ti1, ni1, vi2, ti2, ni2, vi3, ti3, ni3} = parseFace(text, ptr)
-      
-    when 'o'
-      ptr = ptr + 1
-      ptr = ignoreSpace(text, ptr)
-      ptr = ignoreLine(text, ptr)
-    when 's'
-      ptr = ptr + 1
-      ptr = ignoreSpace(text, ptr)
-      ptr = ignoreLine(text, ptr)
-    when 'm'
-      if 'mtllib' != text[ptr...(ptr+6)]
         throw new Error()
-      ptr = ptr + 6
-      ptr = ignoreSpace(text, ptr)
-      ptr = ignoreLine(text, ptr)
-    when 'u'
-      if 'usemtl' != text[ptr...(ptr+6)]
-        throw new Error()
-      ptr = ptr + 6
-      ptr = ignoreSpace(text, ptr)
-      ptr = ignoreLine(text, ptr)
+  ctx.vtxBuf.length = ctx.vtxBufLen * ctx.totalStride
+  ctx.idxBuf.length = ctx.idxBufLen
+  return {
+    vtxBuf: new Float32Array(ctx.vtxBuf)
+    idxBuf: new Uint32Array(ctx.idxBuf)
+    vertexStride: ctx.vertexStride
+    textureStride: ctx.textureStride
+    normalStride: ctx.normalStride
+  }
+
+objFile = """
+# ......
+
+v  -0.5000 0.0000 0.5000
+v  -0.5000 0.0000 0.3750
+v  -0.3750 0.0000 0.3750
+v  -0.3750 0.0000 0.5000
+
+vn 0.0000 -1.0000 -0.0000
+vn 0.0000 -1.0000 -0.0000
+vn 0.0000 -1.0000 -0.0000
+vn 0.0000 -1.0000 -0.0000
+
+vt 1.0000 0.0000 0.0000
+vt 1.0000 0.1250 0.0000
+vt 0.8750 0.1250 0.0000
+vt 0.8750 0.0000 0.0000
+
+f 1/1/1 2/2/1 3/3/1
+f 3/3/1 4/4/1 1/1/1
+"""
+
+debugger
+res = parseObj(objFile)
+console.log(res)

    ptr = ptr + 1
  return ptr

parseIntx = do ->
  result =
    ptr: 0
    num: 0
  return (text, ptr) ->
    start = ptr
    charCode = text.charCodeAt(ptr)
    if charCode >= CHAR_SIZE or 0 == CHAR_INT | CHAR_TABLE[charCode]
      throw new Error()
    ptr = ptr + 1
    while true
      charCode = text.charCodeAt(ptr)
      if charCode >= CHAR_SIZE or 0 == CHAR_INT | CHAR_TABLE[charCode]
        break
      ptr = ptr + 1
    result.ptr = ptr
    result.num = parseInt(text[start...ptr])
    return result

parseFloatx = do ->
  result =
    ptr: 0
    num: 0
  return (text, ptr) ->
    start = ptr
    charCode = text.charCodeAt(ptr)
    if charCode >= CHAR_SIZE or 0 == CHAR_FLOAT | CHAR_TABLE[charCode]
      throw new Error()
    ptr = ptr + 1
    while true
      charCode = text.charCodeAt(ptr)
      if charCode >= CHAR_SIZE or 0 == CHAR_FLOAT | CHAR_TABLE[charCode]
        break
      ptr = ptr + 1
    result.ptr = ptr
    result.num = parseInt(text[start...ptr])
    return result

parseVertex = do ->

    vx: 0, vy: 0, vz: 0
  (text, ptr) ->
    ptr = ignoreSpace(text, ptr)
    {num: result.vx, ptr} = parseFloatEx(text, ptr)
    ptr = ignoreSpace(text, ptr)
    {num: result.vy, ptr} = parseFloatEx(text, ptr)
    ptr = ignoreSpace(text, ptr)
    {num: result.vz, ptr} = parseFloatEx(text, ptr)
    result.ptr = ignoreLine(text, ptr)
    return result

