为什么BufferedReader在从wavefront obj文件进行加载时跳过行?

时间:2018-06-10 10:43:41

标签: parsing opengl lwjgl wavefront

我为Java编写了一个简单的wavefront parses,它显示在以下代码部分中:

源代码

public class OBJLoader {

public static GameEntity loadObjModel(String fileName, String texturePath) throws Exception {
    double start = System.nanoTime();

    List<Vector3f> vertices = null;
    List<Vector2f> textures = null;
    List<Vector3f> normals = null;
    List<Integer> indices = null;

    String line;

    float[] vertexPosArray = null;
    float[] texturesArray = null;
    float[] normalsArray = null;
    int[] indicesArray = null;



    try {
        FileReader fr = new FileReader(new File("resources/models/" + fileName + ".obj"));
        BufferedReader br = new BufferedReader(fr);
        vertices = new ArrayList<>();
        textures = new ArrayList<>();
        normals = new ArrayList<>();
        indices = new ArrayList<>();

        //read v, vt and vn
        while((line = br.readLine()) != null) {
            line = br.readLine();

            if (line != null || !line.equals("") || !line.startsWith("#")) {
                String[] splitLine = line.split(" ");

                switch(splitLine[0]) {
                case "v":
                    Vector3f vertex = new Vector3f(Float.parseFloat(splitLine[1]), Float.parseFloat(splitLine[2]), Float.parseFloat(splitLine[3]));
                    vertices.add(vertex);
                    System.out.println("[OBJLoader.loadObjModel]: Vertex " + vertex.toString() + " has been added to vertices from " + fileName);
                    break;
                case "vt":
                    Vector2f texture = new Vector2f(Float.parseFloat(splitLine[1]), Float.parseFloat(splitLine[2]));
                    textures.add(texture);
                    System.out.println("[OBJLoader.loadObjModel]: Texture coordinate [" + texture.x +  ", " + texture.y  + "] has been added to textures from " + fileName);
                    break;
                case "vn":
                    Vector3f normal = new Vector3f(Float.parseFloat(splitLine[1]), Float.parseFloat(splitLine[2]), Float.parseFloat(splitLine[3]));
                    normals.add(normal);
                    System.out.println("OBJLoader.loadObjModel]: Normal " + normal + " has been added to normals from " + fileName);
                    break;
                }
            }
        }

        int numVertices = vertices.size();
        texturesArray = new float[numVertices*2];
        normalsArray = new float[numVertices*3];

        //read f
        while((line = br.readLine()) != null && line.startsWith("f")) {
            String[] split = line.split(" ");

            String[] v1 = split[1].split("/");
            String[] v2 = split[2].split("/");
            String[] v3 = split[3].split("/");

            processVertex(v1, indices, textures, normals, texturesArray, normalsArray);
            processVertex(v2, indices, textures, normals, texturesArray, normalsArray);
            processVertex(v3, indices, textures, normals, texturesArray, normalsArray);

            line = br.readLine();
        }
        br.close();

    } catch (Exception e) {
        System.err.print("[OBJLoader.loadObjModel]: Error loading obj model!");
        e.printStackTrace();
    }

    vertexPosArray = new float[vertices.size()*3];
    indicesArray = new int[indices.size()];

    int i = 0;
    for(Vector3f vertex : vertices) {
        vertexPosArray[i++] = vertex.x;
        vertexPosArray[i++] = vertex.y;
        vertexPosArray[i++] = vertex.z;
    }

    for(int j = 0; j<indices.size(); j++) {
        indicesArray[i] = indices.get(i);
    }

    double end = System.nanoTime();
    double delta = (end - start) / 1000_000;
    System.out.println("[OBJLoader.loadObjModel]: Vertices array of " + fileName + ": ");
    ArrayUtils.displayFloatArray(vertexPosArray);
    System.out.println("[OBJLoader.loadObjModel]: It took " + delta + " milliseconds to load " + fileName);

    return new GameEntity(vertexPosArray, indicesArray, texturesArray, texturePath);
}

/**
 * The input to this method is vertex data as a String array, which is used to determine how to
 * arrange texture coordinate and normal vector data (this data is associated with each vertex position)
 * into the correct order in the texture and normals array
 * @param vertexData
 * @param indices
 * @param textrues
 * @param normals
 * @param textureArray
 * @param normalsArray
 */
private static void processVertex(String[] vertexData, List<Integer> indices, List<Vector2f> textures,
        List<Vector3f> normals, float[] textureArray, float[] normalsArray) {
    int currentVertexPointer = Integer.parseInt(vertexData[0]) - 1;
    indices.add(currentVertexPointer);

    Vector2f currentTex = textures.get(Integer.parseInt(vertexData[1]) - 1);
    textureArray[currentVertexPointer*2] = currentTex.x;
    textureArray[currentVertexPointer*2 + 1] = 1 - currentTex.y;

    Vector3f currentNorm = normals.get(Integer.parseInt(vertexData[2]) - 1);
    normalsArray[currentVertexPointer*3] = currentNorm.x;
    normalsArray[currentVertexPointer*3 + 1] = currentNorm.y;
    normalsArray[currentVertexPointer*3 + 2] = currentNorm.z;
}

}

这是我试图阅读的wavefront obj文件(它代表一个多维数据集):

# Blender v2.78 (sub 0) OBJ File: 'cube.blend'
# www.blender.org
o Cube
v 1.000000 -1.000000 -1.000000
v 1.000000 -1.000000 1.000000
v -1.000000 -1.000000 1.000000
v -1.000000 -1.000000 -1.000000
v 1.000000 1.000000 -0.999999
v 0.999999 1.000000 1.000001
v -1.000000 1.000000 1.000000
v -1.000000 1.000000 -1.000000
vt 0.2766 0.2633
vt 0.5000 0.4867
vt 0.2766 0.4867
vt 0.7234 0.4867
vt 0.9467 0.2633
vt 0.9467 0.4867
vt 0.0533 0.4867
vt 0.0533 0.2633
vt 0.2766 0.0400
vt 0.5000 0.2633
vt 0.0533 0.7100
vt 0.7234 0.2633
vt 0.0533 0.0400
vt 0.2766 0.7100
vn 0.0000 -1.0000 0.0000
vn 0.0000 1.0000 0.0000
vn 1.0000 -0.0000 0.0000
vn 0.0000 -0.0000 1.0000
vn -1.0000 -0.0000 -0.0000
vn 0.0000 0.0000 -1.0000
s off
f 2/1/1 4/2/1 1/3/1
f 8/4/2 6/5/2 5/6/2
f 5/7/3 2/1/3 1/3/3
f 6/8/4 3/9/4 2/1/4
f 3/10/5 8/4/5 4/2/5
f 1/3/6 8/11/6 5/7/6
f 2/1/1 3/10/1 4/2/1
f 8/4/2 7/12/2 6/5/2
f 5/7/3 6/8/3 2/1/3
f 6/8/4 7/13/4 3/9/4
f 3/10/5 7/12/5 8/4/5
f 1/3/6 4/14/6 8/11/6

应该如何运作

这个obj解析器应该读取obj文件的内容,该文件由resources / models / MODELFILENAME.obj文件路径访问。

首先,它根据文件路径创建一个新的 FileReader ,然后使用该FileReader实例创建一个 BufferedReader实例,用于读取每一行的指定的文件。

然后是while循环。如果到达文件的末尾,则while循环结束(String变量行,由BufferedReader实例读取,在文件末尾获取值为null)。

while循环是这样的:

  • 阅读新行line = br.readLine();
  • 检查该行是否为空,不为空或不以标签开头:if (line != null || !line.equals("") || !line.startsWith("#"))
  • 将读取行拆分为空格:String[] splitLine = line.split(" ");
  • 根据splitLine中的第一个字符串(vvtvnf)执行相应的代码:{{1} }

如果该行以switch(splitLine[0])开头,则在v后面的数字中创建一个新的Vector3f对象(一个三维向量,其构造函数为Vector3f(float x, float y, float z))。由于.obj文件中定义了空格字符的方式,这些数字作为splitLine String数组中的第一个,第二个和第三个索引进行访问。在被发送到构造函数之前,它们将从字符串解析为浮点数。然后在结束时,将Vector3f的字符串表示打印到控制台

类似地,对纹理坐标重复该过程(只是创建Vector3f而不是创建Vector2f)和法线向量。

解释剩余的源代码是没有意义的,因为这里已经出现了问题。

问题

上面的文件解析器似乎只读取了一半的数据(它每隔一行“跳转”并且无法处理它。)

这是输出,显示以v开头的哪些行(即Vector3f由他们的数据构成):

v

这是obj文件中[OBJLoader.loadObjModel]: Vertex 1.0 -1.0 -1.0 has been added to vertices from cube [OBJLoader.loadObjModel]: Vertex -1.0 -1.0 1.0 has been added to vertices from cube [OBJLoader.loadObjModel]: Vertex 1.0 1.0 -0.999999 has been added to vertices from cube [OBJLoader.loadObjModel]: Vertex -1.0 1.0 1.0 has been added to vertices from cube 的数据:

v

如果比较数据,很快就会发现只有4行被处理而不是8行。第一行被读取,第二行被跳过。读取第三行,跳过第四行。所以它继续。为什么会出现问题? switch语句中是否有任何错误导致跳过行?

感谢您抽出时间。

1 个答案:

答案 0 :(得分:2)

问题是由此引起的:

while((line = br.readLine()) != null) {
    line = br.readLine();

您已经使用br.readLine()读取文件的一行,并在循环中再次读取该行时再次读取该行。只需删除第二个line = br.readLine();即可。您应该没问题。

另外,您不需要检查if (line != null ||,因为已经检查了循环条件。