/**
     * vertexチャンクの読み込み
     *
     * @param num チャンクにあるデータ数
     * @param br 読み込みストリーム
     * @param scale モデルの倍率
     * @return 頂点配列
     * @throws Exception
     */
    private KGLPoint[] readVertex(int num, multiInput br, float scale) throws Exception {
      KGLPoint[] ret = null;
      String line = null;
      String[] s;
      int cnt;
      ret = new KGLPoint[num];
      cnt = 0;
      try {
        while ((line = br.readLine()) != null) {
          if (line.length() <= 0) continue;
          line = line.trim();
          if (line.equals("}")) break;
          s = line.split(" ", 3);
          ret[cnt] =
              KGLPoint.createXYZ(
                  Float.parseFloat(s[0]) * scale,
                  Float.parseFloat(s[1]) * scale,
                  Float.parseFloat(s[2]) * scale);
          cnt++;
        }
      } catch (Exception e) {
        Log.e("KGLMetaseq", "MQOファイル フォーマットエラー(Object>vertex)[" + line + "]");
        throw e;
      }

      if (cnt != num) return null;
      return ret;
    }
 /**
  * BVertexチャンクの読み込み
  *
  * @param br 読み込みストリーム
  * @return 頂点配列
  * @param scale モデルの倍率
  * @throws Exception
  */
 private KGLPoint[] readBvertex(multiInput br, float scale) throws Exception {
   KGLPoint[] ret = null;
   String line = null;
   String[] s;
   int p;
   int pe;
   int datasize = 0;
   try {
     while ((line = br.readLine().trim()) != null) {
       if (line.length() <= 0) continue;
       s = line.split(" ");
       if (s[0].equals("Vector")) {
         if (s.length != 3) {
           line = null;
           break;
         }
         p = s[2].indexOf("[");
         pe = s[2].indexOf("]");
         datasize = Integer.parseInt(s[2].substring(p + 1, pe));
         break;
       }
     }
   } catch (Exception e) {
     Log.e("KGLMetaseq", "MQOファイル フォーマットエラー(Object>Bvertex)[" + line + "]");
     throw new KGLException(e);
   }
   if (line == null) {
     return null;
   }
   if (datasize == 0) return null;
   byte[] bbuf = new byte[datasize];
   if (datasize != br.read(bbuf)) return null;
   ByteBuffer bb;
   bb = ByteBuffer.wrap(bbuf);
   bb.order(ByteOrder.LITTLE_ENDIAN); // MQOファイルのエンディアンはIntel形式
   FloatBuffer fb = bb.asFloatBuffer();
   ret = new KGLPoint[fb.limit() / 3];
   fb.position(0);
   float[] wf = new float[3];
   for (int i = 0; i < ret.length; i++) {
     fb.get(wf);
     ret[i] = KGLPoint.create(wf).scale(scale);
   }
   while ((line = br.readLine().trim()) != null) {
     if (line.equals("}")) break;
   }
   return ret;
 }
 /**
  * 頂点法線を求める
  *
  * @param mqoObj 読み込んだMQOデータ
  * @return 頂点法線
  */
 protected KGLPoint[] vNormal(objects mqoObj) {
   KGLPoint[] ret = null;
   KGLPoint sn = null;
   // 頂点に接している面の法線を頂点法線に足し込んでいく
   ret = new KGLPoint[mqoObj.vertex.length];
   for (int f = 0; f < mqoObj.face.length; f++) {
     sn = calcNormal(mqoObj.vertex, mqoObj.face[f].V[0], mqoObj.face[f].V[1], mqoObj.face[f].V[2]);
     if (sn == null) continue;
     for (int i = 0; i < 3; i++) {
       if (ret[mqoObj.face[f].V[i]] == null) {
         ret[mqoObj.face[f].V[i]] = KGLPoint.createXYZ(0, 0, 0);
       }
       ret[mqoObj.face[f].V[i]].add(sn);
     }
   }
   // 正規化(長さを求めて、ソレで割って0~1の値にする!)
   for (int v = 0; v < ret.length; v++) {
     if (ret[v] == null) continue;
     ret[v].normalize();
   }
   return ret;
 }
 /**
  * 法線を求める
  *
  * @param V 頂点配列
  * @param A 頂点の位置
  * @param B 頂点の位置
  * @param C 頂点の位置
  * @return 法線ベクトル
  */
 protected KGLPoint calcNormal(KGLPoint[] V, int A, int B, int C) {
   KGLPoint ret = null;
   KGLPoint BA = null;
   KGLPoint BC = null;
   // ベクトルB->A
   BA = KGLPoint.vector(V[B], V[A]);
   // ベクトルB->C
   BC = KGLPoint.vector(V[B], V[C]);
   // 法線の計算
   ret =
       KGLPoint.createXYZ(
           BA.Y() * BC.Z() - BA.Z() * BC.Y(),
           BA.Z() * BC.X() - BA.X() * BC.Z(),
           BA.X() * BC.Y() - BA.Y() * BC.X());
   ret.normalize(); // 正規化
   return ret;
 }
  /**
   * 描画用マテリアル情報をMQOデータから作成
   *
   * @param mqomat MQOファイルから読み込んだマテリアル情報
   * @param i_mqomat MQOファイルのマテリアル番号
   * @param mqoObjs MQOファイルのオブジェクト情報
   * @param vn 頂点法線配列
   * @return 描画用マテリアル情報
   */
  private GLMaterial makeMats(
      GL10 gl, material mqomat, int i_mqomat, objects mqoObjs, KGLPoint[] vn) {
    GLMaterial ret = new GLMaterial();
    ArrayList<KGLPoint> apv = new ArrayList<KGLPoint>();
    ArrayList<KGLPoint> apn = new ArrayList<KGLPoint>();
    ArrayList<KGLPoint> apuv = new ArrayList<KGLPoint>();
    ArrayList<KGLPoint> apc = new ArrayList<KGLPoint>();
    KGLPoint wpoint = null;
    boolean uvValid = false;
    boolean colValid = false;
    KGLPoint fn;
    float s;
    for (int f = 0; f < mqoObjs.face.length; f++) {
      if (mqoObjs.face[f].M == null) {
        continue;
      }
      if (mqoObjs.face[f].M != i_mqomat) continue;

      fn =
          calcNormal(
              mqoObjs.vertex, mqoObjs.face[f].V[0], mqoObjs.face[f].V[1], mqoObjs.face[f].V[2]);
      for (int v = 0; v < 3; v++) {
        apv.add(mqoObjs.vertex[mqoObjs.face[f].V[v]]);
        // apv.add(new KGLPoint(mqoObjs.vertex[mqoObjs.face[f].V[v]])) ;
        s =
            (float)
                Math.acos(
                    fn.X() * vn[mqoObjs.face[f].V[v]].X()
                        + fn.Y() * vn[mqoObjs.face[f].V[v]].Y()
                        + fn.Z() * vn[mqoObjs.face[f].V[v]].Z());
        if (mqoObjs.data.facet < s) {
          apn.add(fn);
        } else {
          apn.add(vn[mqoObjs.face[f].V[v]]);
        }
        wpoint = new KGLPoint(2);
        if (mqoObjs.face[f].UV == null) {
          wpoint.set_UV(0, 0);
        } else {
          wpoint.set_UV(mqoObjs.face[f].UV[v * 2 + 0], mqoObjs.face[f].UV[v * 2 + 1]);
          uvValid = true;
        }
        apuv.add(wpoint);
        wpoint = new KGLPoint(4);
        if (mqoObjs.face[f].COL == null) {
          if (mqomat.data.col == null) {
            wpoint.set_COLOR(1.0f, 1.0f, 1.0f, 1.0f);
          } else {
            wpoint.set_COLOR(
                mqomat.data.col[0], mqomat.data.col[1], mqomat.data.col[2], mqomat.data.col[3]);
          }
        } else {
          wpoint.set_COLOR(
              mqoObjs.face[f].COL[v * 4 + 0],
              mqoObjs.face[f].COL[v * 4 + 1],
              mqoObjs.face[f].COL[v * 4 + 2],
              mqoObjs.face[f].COL[v * 4 + 3]);
          colValid = true;
        }
        apc.add(wpoint);
      }
    }
    ret.texID = texPool.getGLTexture(gl, mqomat.data.tex, mqomat.data.aplane, false);
    // @@@ reload 用
    if (ret.texID != 0) {
      ret.texName = mqomat.data.tex;
      ret.alphaTexName = mqomat.data.aplane;
    } else {
      ret.texName = null;
      ret.alphaTexName = null;
    }

    if (apv.size() == 0) return null;
    uvValid &= (ret.texID != 0);
    ret.name = mqomat.name;
    // uvValid = false ;
    KGLPoint[] wfv = null;
    KGLPoint[] wfn = null;
    KGLPoint[] wft = null;
    KGLPoint[] wfc = null;
    wfv = apv.toArray(new KGLPoint[0]);
    wfn = apn.toArray(new KGLPoint[0]);
    wft = apuv.toArray(new KGLPoint[0]);
    wfc = apc.toArray(new KGLPoint[0]);
    ret.vertex_num = wfv.length;

    // @@@ interleaveFormat は無いので分ける
    ret.uvValid = uvValid;
    ret.colValid = colValid;

    ret.vertexBuffer = ByteBuffer.allocateDirect(ret.vertex_num * 3 * 4);
    ret.vertexBuffer.order(ByteOrder.nativeOrder());
    ret.vertexBuffer.position(0);

    ret.normalBuffer = ByteBuffer.allocateDirect(ret.vertex_num * 3 * 4);
    ret.normalBuffer.order(ByteOrder.nativeOrder());
    ret.normalBuffer.position(0);

    if (uvValid) {
      ret.uvBuffer = ByteBuffer.allocateDirect(ret.vertex_num * 2 * 4);
      ret.uvBuffer.order(ByteOrder.nativeOrder());
      ret.uvBuffer.position(0);
    }
    if (colValid) {
      ret.colBuffer = ByteBuffer.allocateDirect(ret.vertex_num * 4 * 4);
      ret.colBuffer.order(ByteOrder.nativeOrder());
      ret.colBuffer.position(0);
    }

    // Log.i("KGLMetaseq", "vertex_num: "+ ret.vertex_num);

    for (int v = 0; v < ret.vertex_num; v++) {
      ret.vertexBuffer.putFloat(wfv[v].X());
      ret.vertexBuffer.putFloat(wfv[v].Y());
      ret.vertexBuffer.putFloat(wfv[v].Z());

      ret.normalBuffer.putFloat(wfn[v].X());
      ret.normalBuffer.putFloat(wfn[v].Y());
      ret.normalBuffer.putFloat(wfn[v].Z());

      if (uvValid) {
        ret.uvBuffer.putFloat(wft[v].U());
        ret.uvBuffer.putFloat(wft[v].V());
      }
      if (colValid) {
        ret.colBuffer.putFloat(wfc[v].R());
        ret.colBuffer.putFloat(wfc[v].G());
        ret.colBuffer.putFloat(wfc[v].B());
        ret.colBuffer.putFloat(wfc[v].A());
      }
    }
    if (mqomat.data.col != null) {
      ret.color = new float[mqomat.data.col.length];
      for (int c = 0; c < mqomat.data.col.length; c++) {
        ret.color[c] = mqomat.data.col[c];
      }
      if (mqomat.data.dif != null) {
        ret.dif = new float[mqomat.data.col.length];
        for (int c = 0; c < mqomat.data.col.length; c++) {
          ret.dif[c] = mqomat.data.dif * mqomat.data.col[c];
        }
        // KEICHECK difでアルファ値を1未満にすると透明度が変化する?
        ret.dif[3] = mqomat.data.col[3];
      }
      if (mqomat.data.amb != null) {
        ret.amb = new float[mqomat.data.col.length];
        for (int c = 0; c < mqomat.data.col.length; c++) {
          ret.amb[c] = mqomat.data.amb * mqomat.data.col[c];
        }
      }
      if (mqomat.data.emi != null) {
        ret.emi = new float[mqomat.data.col.length];
        for (int c = 0; c < mqomat.data.col.length; c++) {
          ret.emi[c] = mqomat.data.emi * mqomat.data.col[c];
        }
      }
      if (mqomat.data.spc != null) {
        ret.spc = new float[mqomat.data.col.length];
        for (int c = 0; c < mqomat.data.col.length; c++) {
          ret.spc[c] = mqomat.data.spc * mqomat.data.col[c];
        }
      }
    }
    if (mqomat.data.pow != null) {
      ret.power = new float[1];
      ret.power[0] = mqomat.data.pow;
    }
    ret.shadeMode_IsSmooth = true; // defaultはtrue
    if (mqoObjs.data.shading == 0) ret.shadeMode_IsSmooth = false;

    return ret;
  }