为什么 BufferedReader 在尝试从波前 obj 文件加载时跳过行?

Why is BufferedReader skipping lines when trrying to load from a wavefront obj file?

我已经为 Java 编写了一个简单的波前分析,显示在以下代码部分:

源代码

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循环结束(BufferedReader实例读取的字符串变量行在文件末尾获取值null)。

while 循环是这样的:

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

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

没有必要解释剩余的源代码,因为问题已经出现在这里。

问题

上面的文件解析器似乎只读取了一半的数据(它 "jumps" 每两行并且无法处理它)。

这是显示已处理以 v 开头的行的输出(即 Vector3f 由其数据组成):

[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

这是 obj 文件中 v 的数据:

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

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

感谢您抽出宝贵时间。

问题是这样引起的:

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

您已经在使用 br.readLine() 读取文件的一行,一旦您在循环中再次读取该行,就会再次读取该行。只需删除第二个 line = br.readLine(); 就可以了。

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