Почему BufferedReader пропускает строки при попытке загрузки из obj-файла wavefront?

Я написал простой синтаксический анализ волнового фронта для 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;
}

}

Это 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 (это либо v, vt, vn, либо f): switch(splitLine[0])

Если строка начинается с v, то создайте новый объект Vector3f (трехмерный вектор, его конструктор Vector3f(float x, float y, float z)) из чисел, стоящих за v. Доступ к этим числам осуществляется как к первому, второму и третьему индексу в массиве строк splitLine из-за того, как символы пробела определены в файле .obj. Перед отправкой в ​​конструктор они анализируются из строк в числа с плавающей запятой. Затем в конце выведите строковое представление Vector3f на консоль.

Точно так же процесс повторяется для координат текстуры (только вместо создания Vector3f создается Vector2f) и векторов нормалей.

Объяснять оставшийся исходный код нет смысла, так как проблема возникает уже здесь.

Эта проблема

Приведенный выше анализатор файлов, кажется, читает только половину данных (он «перескакивает» через каждую вторую строку и не может ее обработать).

Это выходные данные, которые показывают, какие строки, начиная с 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

Это данные для v в файле obj:

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, из-за которых строки пропускаются?

Спасибо за потраченное время.


person Fish_In_A_Suit    schedule 10.06.2018    source источник


Ответы (1)


Проблема вызвана этим:

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

Вы уже читаете одну строку файла с br.readLine() и второй раз, как только снова читаете строку в цикле. Просто удалите второй line = br.readLine();, и все будет в порядке.

Кроме того, вам не нужно проверять if (line != null ||, так как это уже проверено как условие цикла.

person Holger    schedule 10.06.2018
comment
Вау, спасибо за такой быстрый ответ. Очень ценю это. Попробую, как только вернусь домой :D - person Fish_In_A_Suit; 10.06.2018