Back to project page ScalAR.
The source code is released under:
GNU General Public License
If you think the Android project ScalAR listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.
/** Copyright (C) 2010 Tobias Domhan/*from w ww . j a va 2s . c o m*/ This file is part of AndObjViewer. AndObjViewer is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. AndObjViewer is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with AndObjViewer. If not, see <http://www.gnu.org/licenses/>. */ package edu.dhbw.andobjviewer.parser; import java.io.BufferedReader; import java.io.IOException; import java.util.ArrayList; import java.util.Iterator; import java.util.Vector; import edu.dhbw.andobjviewer.models.Group; import edu.dhbw.andobjviewer.models.Model; import edu.dhbw.andobjviewer.util.BaseFileUtil; /** * Simple obj parser. * Does not support the full obj specification. * It supports: * - vertices * - vertice normals * - texture coordinates * - basic materials * - faces(faces may not omit the face normal) * - limited texture support, through the map_Kd statement (no options allowed, only image files allowed) * @author tobi * */ public class ObjParser { private final int VERTEX_DIMENSIONS = 3; private final int TEXTURE_COORD_DIMENSIONS = 2; private BaseFileUtil fileUtil; public ObjParser(BaseFileUtil fileUtil) { this.fileUtil = fileUtil; } /** * parses an wavefront obj model file * @param modelName name of the model * @param is stream of the file to parse * @return the parsed model * @throws IOException * @throws ParseException */ public Model parse(String modelName, BufferedReader is) throws IOException, ParseException { //global vertices/normals ArrayList<float[]> vertices = new ArrayList<float[]>(1000); ArrayList<float[]> normals = new ArrayList<float[]>(1000); ArrayList<float[]> texcoords = new ArrayList<float[]>(); Model model = new Model(); Group curGroup = new Group(); MtlParser mtlParser = new MtlParser(model,fileUtil); SimpleTokenizer spaceTokenizer = new SimpleTokenizer(); SimpleTokenizer slashTokenizer = new SimpleTokenizer(); slashTokenizer.setDelimiter("/"); String line; int lineNum = 1; for (line = is.readLine(); line != null; line = is.readLine(), lineNum++) { if (line.length() > 0) { if (line.startsWith("#")) { //ignore comments } else if (line.startsWith("v ")) { //add new vertex to vector String endOfLine = line.substring(2); spaceTokenizer.setStr(endOfLine); vertices.add(new float[]{ Float.parseFloat(spaceTokenizer.next()), Float.parseFloat(spaceTokenizer.next()), Float.parseFloat(spaceTokenizer.next())}); } else if (line.startsWith("vt ")) { //add new texture vertex to vector String endOfLine = line.substring(3); spaceTokenizer.setStr(endOfLine); texcoords.add(new float[]{ Float.parseFloat(spaceTokenizer.next()), Float.parseFloat(spaceTokenizer.next())}); } else if (line.startsWith("f ")) { //add face to group String endOfLine = line.substring(2); spaceTokenizer.setStr(endOfLine); int faces = spaceTokenizer.delimOccurCount()+1; if(faces != 3) { throw new ParseException(modelName, lineNum, "only triangle faces are supported"); } for (int i = 0; i < 3; i++) {//only triangles supported String face = spaceTokenizer.next(); slashTokenizer.setStr(face); int vertexCount = slashTokenizer.delimOccurCount()+1; int vertexID=0; int textureID=-1; int normalID=0; if(vertexCount == 2) { //vertex reference vertexID = Integer.parseInt(slashTokenizer.next())-1; //normal reference normalID = Integer.parseInt(slashTokenizer.next())-1; throw new ParseException(modelName, lineNum, "vertex normal needed."); } else if(vertexCount == 3) { //vertex reference vertexID = Integer.parseInt(slashTokenizer.next())-1; String texCoord = slashTokenizer.next(); if(!texCoord.equals("")) { //might be omitted //texture coord reference textureID = Integer.parseInt(texCoord)-1; } //normal reference normalID = Integer.parseInt(slashTokenizer.next())-1; } else { throw new ParseException(modelName, lineNum, "a faces needs reference a vertex, a normal vertex and optionally a texture coordinate per vertex."); } float[] vec; try { vec = vertices.get(vertexID); } catch (IndexOutOfBoundsException ex) { throw new ParseException(modelName, lineNum, "non existing vertex referenced."); } if(vec==null) throw new ParseException(modelName, lineNum, "non existing vertex referenced."); for (int j = 0; j < VERTEX_DIMENSIONS; j++) curGroup.groupVertices.add(vec[j]); if(textureID != -1) { //in case there is a texture on the face try { vec = texcoords.get(textureID); } catch (IndexOutOfBoundsException ex) { throw new ParseException(modelName, lineNum, "non existing texture coord referenced."); } if(vec==null) throw new ParseException(modelName, lineNum, "non existing texture coordinate referenced."); for (int j = 0; j < TEXTURE_COORD_DIMENSIONS; j++) curGroup.groupTexcoords.add(vec[j]); } try { vec = normals.get(normalID); } catch (IndexOutOfBoundsException ex) { throw new ParseException(modelName, lineNum, "non existing normal vertex referenced."); } if(vec==null) throw new ParseException(modelName, lineNum, "non existing normal vertex referenced."); for (int j = 0; j < VERTEX_DIMENSIONS; j++) curGroup.groupNormals.add(vec[j]); } } else if (line.startsWith("vn ")) { //add new vertex normal to vector String endOfLine = line.substring(3); spaceTokenizer.setStr(endOfLine); normals.add(new float[]{ Float.parseFloat(spaceTokenizer.next()), Float.parseFloat(spaceTokenizer.next()), Float.parseFloat(spaceTokenizer.next())}); } else if (line.startsWith("mtllib ")) { //parse material file //get ID of the mtl file String filename = line.substring(7); String[] files = Util.splitBySpace(filename); for (int i = 0; i < files.length; i++) { BufferedReader mtlFile = fileUtil.getReaderFromName(files[i]); if(mtlFile != null) mtlParser.parse(model, mtlFile); } } else if(line.startsWith("usemtl ")) { //material changed -> new group if(curGroup.groupVertices.size()>0) { model.addGroup(curGroup); curGroup = new Group(); } //the rest of the line contains the name of the new material curGroup.setMaterialName(line.substring(7)); } else if(line.startsWith("g ")) { //new group definition if(curGroup.groupVertices.size()>0) { model.addGroup(curGroup); curGroup = new Group(); //group name will be ignored so far...is there any use? } } } } if(curGroup.groupVertices.size()>0) { model.addGroup(curGroup); } Iterator<Group> groupIt = model.getGroups().iterator(); while (groupIt.hasNext()) { Group group = (Group) groupIt.next(); group.setMaterial(model.getMaterial(group.getMaterialName())); } return model; } }