@Override
  public LoaderOBJ parse() throws ParsingException {
    super.parse();
    BufferedReader buffer = null;
    if (mFile == null) {
      InputStream fileIn = mResources.openRawResource(mResourceId);
      buffer = new BufferedReader(new InputStreamReader(fileIn));
    } else {
      try {
        buffer = new BufferedReader(new FileReader(mFile));
      } catch (FileNotFoundException e) {
        RajLog.e("[" + getClass().getCanonicalName() + "] Could not find file.");
        e.printStackTrace();
      }
    }
    String line;
    ObjIndexData currObjIndexData = new ObjIndexData(new Object3D());
    ArrayList<ObjIndexData> objIndices = new ArrayList<ObjIndexData>();

    ArrayList<Float> vertices = new ArrayList<Float>();
    ArrayList<Float> texCoords = new ArrayList<Float>();
    ArrayList<Float> normals = new ArrayList<Float>();
    MaterialLib matLib = new MaterialLib();

    try {
      while ((line = buffer.readLine()) != null) {
        // Skip comments and empty lines.
        if (line.length() == 0 || line.charAt(0) == '#') continue;
        StringTokenizer parts = new StringTokenizer(line, " ");
        int numTokens = parts.countTokens();

        if (numTokens == 0) continue;
        String type = parts.nextToken();

        if (type.equals(VERTEX)) {
          vertices.add(Float.parseFloat(parts.nextToken()));
          vertices.add(Float.parseFloat(parts.nextToken()));
          vertices.add(Float.parseFloat(parts.nextToken()));
        } else if (type.equals(FACE)) {
          boolean isQuad = numTokens == 5;
          int[] quadvids = new int[4];
          int[] quadtids = new int[4];
          int[] quadnids = new int[4];

          boolean emptyVt = line.indexOf("//") > -1;
          if (emptyVt) line = line.replace("//", "/");

          parts = new StringTokenizer(line);

          parts.nextToken();
          StringTokenizer subParts = new StringTokenizer(parts.nextToken(), "/");
          int partLength = subParts.countTokens();

          boolean hasuv = partLength >= 2 && !emptyVt;
          boolean hasn = partLength == 3 || (partLength == 2 && emptyVt);
          int idx;

          for (int i = 1; i < numTokens; i++) {
            if (i > 1) subParts = new StringTokenizer(parts.nextToken(), "/");
            idx = Integer.parseInt(subParts.nextToken());

            if (idx < 0) idx = (vertices.size() / 3) + idx;
            else idx -= 1;
            if (!isQuad) currObjIndexData.vertexIndices.add(idx);
            else quadvids[i - 1] = idx;
            if (hasuv) {
              idx = Integer.parseInt(subParts.nextToken());
              if (idx < 0) idx = (texCoords.size() / 2) + idx;
              else idx -= 1;
              if (!isQuad) currObjIndexData.texCoordIndices.add(idx);
              else quadtids[i - 1] = idx;
            }
            if (hasn) {
              idx = Integer.parseInt(subParts.nextToken());
              if (idx < 0) idx = (normals.size() / 3) + idx;
              else idx -= 1;
              if (!isQuad) currObjIndexData.normalIndices.add(idx);
              else quadnids[i - 1] = idx;
            }
          }

          if (isQuad) {
            int[] indices = new int[] {0, 1, 2, 0, 2, 3};

            for (int i = 0; i < 6; ++i) {
              int index = indices[i];
              currObjIndexData.vertexIndices.add(quadvids[index]);
              currObjIndexData.texCoordIndices.add(quadtids[index]);
              currObjIndexData.normalIndices.add(quadnids[index]);
            }
          }
        } else if (type.equals(TEXCOORD)) {
          texCoords.add(Float.parseFloat(parts.nextToken()));
          texCoords.add(1f - Float.parseFloat(parts.nextToken()));
        } else if (type.equals(NORMAL)) {
          normals.add(Float.parseFloat(parts.nextToken()));
          normals.add(Float.parseFloat(parts.nextToken()));
          normals.add(Float.parseFloat(parts.nextToken()));
        } else if (type.equals(OBJECT) || type.equals(GROUP)) {
          String objName =
              parts.hasMoreTokens() ? parts.nextToken() : "Object" + (int) (Math.random() * 10000);

          if (type.equals(OBJECT)) {
            RajLog.i("Parsing object: " + objName);
            if (currObjIndexData.targetObj.getName() != null)
              currObjIndexData = new ObjIndexData(new Object3D(objName));
            else currObjIndexData.targetObj.setName(objName);
            objIndices.add(currObjIndexData);
          } else if (type.equals(GROUP)) {
            RajLog.i("Parsing group: " + objName);
            Object3D group = mRootObject.getChildByName(objName);
            if (group == null) {
              group = new Object3D(objName);
              mRootObject.addChild(group);
            }
            group.addChild(currObjIndexData.targetObj);
          }
        } else if (type.equals(MATERIAL_LIB)) {
          if (!parts.hasMoreTokens()) continue;
          String materialLibPath = parts.nextToken().replace(".", "_");
          Log.d(Wallpaper.TAG, "Found Material Lib: " + materialLibPath);
          if (mFile != null) matLib.parse(materialLibPath, null, null);
          else
            matLib.parse(
                materialLibPath,
                mResources.getResourceTypeName(mResourceId),
                mResources.getResourcePackageName(mResourceId));
        } else if (type.equals(USE_MATERIAL)) {
          currObjIndexData.materialName = parts.nextToken();
        }
      }
      buffer.close();

      if (objIndices.size() == 0) {
        objIndices.add(currObjIndexData);
      }
    } catch (IOException e) {
      throw new ParsingException(e);
    }

    int numObjects = objIndices.size();

    for (int j = 0; j < numObjects; ++j) {
      ObjIndexData oid = objIndices.get(j);

      int i;
      float[] aVertices = new float[oid.vertexIndices.size() * 3];
      float[] aTexCoords = new float[oid.texCoordIndices.size() * 2];
      float[] aNormals = new float[oid.normalIndices.size() * 3];
      float[] aColors = new float[oid.colorIndices.size() * 4];
      int[] aIndices = new int[oid.vertexIndices.size()];

      for (i = 0; i < oid.vertexIndices.size(); ++i) {
        int faceIndex = oid.vertexIndices.get(i) * 3;
        int vertexIndex = i * 3;
        try {
          aVertices[vertexIndex] = vertices.get(faceIndex);
          aVertices[vertexIndex + 1] = vertices.get(faceIndex + 1);
          aVertices[vertexIndex + 2] = vertices.get(faceIndex + 2);
          aIndices[i] = i;
        } catch (ArrayIndexOutOfBoundsException e) {
          Log.d("Rajawali", "ERREUR!! " + vertexIndex + ", " + faceIndex);
        }
      }
      if (texCoords != null && texCoords.size() > 0) {
        for (i = 0; i < oid.texCoordIndices.size(); ++i) {
          int texCoordIndex = oid.texCoordIndices.get(i) * 2;
          int ti = i * 2;
          aTexCoords[ti] = texCoords.get(texCoordIndex);
          aTexCoords[ti + 1] = texCoords.get(texCoordIndex + 1);
        }
      }
      for (i = 0; i < oid.colorIndices.size(); ++i) {
        int colorIndex = oid.colorIndices.get(i) * 4;
        int ti = i * 4;
        aTexCoords[ti] = texCoords.get(colorIndex);
        aTexCoords[ti + 1] = texCoords.get(colorIndex + 1);
        aTexCoords[ti + 2] = texCoords.get(colorIndex + 2);
        aTexCoords[ti + 3] = texCoords.get(colorIndex + 3);
      }
      for (i = 0; i < oid.normalIndices.size(); ++i) {
        int normalIndex = oid.normalIndices.get(i) * 3;
        int ni = i * 3;
        if (normals.size() == 0) {
          RajLog.e(
              "["
                  + getClass().getName()
                  + "] There are no normals specified for this model. Please re-export with normals.");
          throw new ParsingException(
              "["
                  + getClass().getName()
                  + "] There are no normals specified for this model. Please re-export with normals.");
        }
        aNormals[ni] = normals.get(normalIndex);
        aNormals[ni + 1] = normals.get(normalIndex + 1);
        aNormals[ni + 2] = normals.get(normalIndex + 2);
      }

      oid.targetObj.setData(aVertices, aNormals, aTexCoords, aColors, aIndices);
      try {
        matLib.setMaterial(oid.targetObj, oid.materialName);
      } catch (TextureException tme) {
        throw new ParsingException(tme);
      }
      if (oid.targetObj.getParent() == null) mRootObject.addChild(oid.targetObj);
    }

    if (mRootObject.getNumChildren() == 1 && !mRootObject.getChildAt(0).isContainer())
      mRootObject = mRootObject.getChildAt(0);

    return this;
  }
    protected void initScene() {
      DirectionalLight light = new DirectionalLight(0, -.6f, .4f);
      light.setPower(1);
      getCurrentScene().addLight(light);

      // -- create sky sphere
      mSphere = new Sphere(400, 8, 8);
      Material sphereMaterial = new Material();
      try {
        sphereMaterial.addTexture(new Texture("skySphere", R.drawable.skysphere));
      } catch (TextureException e1) {
        e1.printStackTrace();
      }
      mSphere.setMaterial(sphereMaterial);
      mSphere.setDoubleSided(true);
      addChild(mSphere);

      try {
        // -- load gzipped serialized object
        ObjectInputStream ois;
        GZIPInputStream zis =
            new GZIPInputStream(mContext.getResources().openRawResource(R.raw.raptor));
        ois = new ObjectInputStream(zis);
        mRaptor = new Object3D((SerializedObject3D) ois.readObject());
        Material raptorMaterial = new Material();
        raptorMaterial.setDiffuseMethod(new DiffuseMethod.Lambert());
        raptorMaterial.enableLighting(true);
        raptorMaterial.addTexture(new Texture("raptorTex", R.drawable.raptor_texture));
        mRaptor.setMaterial(raptorMaterial);
        mRaptor.setScale(.5f);
        addChild(mRaptor);
      } catch (Exception e) {
        e.printStackTrace();
      }

      // -- create a bunch of cubes that will serve as orientation helpers

      mCubes = new Object3D[30];

      mRootCube = new Cube(1);
      Material rootCubeMaterial = new Material();
      rootCubeMaterial.setDiffuseMethod(new DiffuseMethod.Lambert());
      rootCubeMaterial.enableLighting(true);
      try {
        rootCubeMaterial.addTexture(new Texture("camouflage", R.drawable.camouflage));
      } catch (TextureException e) {
        e.printStackTrace();
      }
      mRootCube.setMaterial(rootCubeMaterial);
      mRootCube.setY(-1f);
      // -- similar objects with the same material, optimize
      mRootCube.setRenderChildrenAsBatch(true);
      addChild(mRootCube);
      mCubes[0] = mRootCube;

      for (int i = 1; i < mCubes.length; ++i) {
        Object3D cube = mRootCube.clone(true);
        cube.setY(-1f);
        cube.setZ(i * 30);
        mRootCube.addChild(cube);
        mCubes[i] = cube;
      }

      // -- create a chase camera
      // the first parameter is the camera offset
      // the second parameter is the interpolation factor
      ChaseCamera chaseCamera = new ChaseCamera(new Vector3(0, 3, 16), .1f);
      // -- tell the camera which object to chase
      chaseCamera.setObjectToChase(mRaptor);
      // -- set the far plane to 1000 so that we actually see the sky sphere
      chaseCamera.setFarPlane(1000);
      replaceAndSwitchCamera(chaseCamera, 0);
    }