OBJ Loader IndexOutOfBounds

Я пишу 3D Engine, и у моего OBJ LoaderClass, похоже, есть проблемы с более сложными моделями.

Я получаю IndexOutOfBoundsException, и я не могу понять, почему. Значение текстур ArrayList в индексе 3522, кажется, вызывает это исключение, но почему?

Вот мой OBJ Loader Class

        package graphics.renderEngine;

    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.util.ArrayList;
    import java.util.List;

    import org.joml.Vector2f;
    import org.joml.Vector3f;

    import graphics.models.RawModel;

    public class OBJLoader 
    {

        public static RawModel loadObjModel(String fileName, Loader loader)
        {
            FileReader fr = null;
            try 
            {
                fr = new FileReader(new File("Ressources/Models/"+fileName+".obj"));
            } 
            catch (FileNotFoundException e) 
            {
                System.err.println("Could not load File!");
                e.printStackTrace();
            }
            BufferedReader reader = new BufferedReader(fr);
            String line;
            List<Vector3f> vertices = new ArrayList<Vector3f>();
            List<Vector2f> textures = new ArrayList<Vector2f>();
            List<Vector3f> normals = new ArrayList<Vector3f>();
            List<Integer> indices = new ArrayList<Integer>();
            float[] verticesArray = null;
            float[] normalsArray = null;
            float[] texturesArray = null;
            int[] indicesArray = null;

            try
            {
                while(true)
                {
                    line = reader.readLine();
                    String[] currentLine = line.split(" ");
                    if(line.startsWith("v "))
                    {
                        Vector3f vertex = new Vector3f(Float.parseFloat(currentLine[1]),Float.parseFloat(currentLine[2]), Float.parseFloat(currentLine[3]));
                        vertices.add(vertex);
                    }
                    else if(line.startsWith("vt "))
                    {
                        Vector2f texture = new Vector2f(Float.parseFloat(currentLine[1]), Float.parseFloat(currentLine[2]));
                        textures.add(texture);
                    }
                    else if(line.startsWith("vn "))
                    {
                        Vector3f normal = new Vector3f(Float.parseFloat(currentLine[1]),Float.parseFloat(currentLine[2]), Float.parseFloat(currentLine[3]));
                        normals.add(normal);
                    }
                    else if(line.startsWith("f "))
                    {
                        texturesArray = new float[vertices.size()*2];
                        normalsArray = new float[vertices.size()*3];
                        break;
                    }
                }

                while(line != null)
                {
                    if(!line.startsWith("f "))
                    {
                        line = reader.readLine();
                        continue;
                    }
                    String[] currentLine = line.split(" ");
                    String[] vertex1 = currentLine[1].split("/");
                    String[] vertex2 = currentLine[2].split("/");
                    String[] vertex3 = currentLine[3].split("/");

                    processVertex(vertex1, indices, textures, normals, texturesArray, normalsArray);
                    processVertex(vertex2, indices, textures, normals, texturesArray, normalsArray);
                    processVertex(vertex3, indices, textures, normals, texturesArray, normalsArray);
                    line = reader.readLine();
                }
                reader.close();

            }
            catch(Exception e)
            {
                e.printStackTrace();
            }

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

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

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

            }
            return 
                    loader.loadToVAO
                    (verticesArray, 
                            texturesArray, 
                            normalsArray,
                            indicesArray);

        }

        private static void processVertex(String[] vertexData, List<Integer> indices, List<Vector2f> textures, List<Vector3f> normals, float[] textureArray, float[] normalsArray)
        {
            System.out.println(textures.get(3522));
            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-файл модели, которую я пытаюсь загрузить.

Когда я каждый раз считываю значения текстуры через:

System.out.println(textures.get(Integer.parseInt(vertexData[1])-1));

Последние Векторы, которые я получаю перед Исключениями:

( 4.260E-1  1.275E-1)
( 4.650E-1  1.664E-1)
( 4.706E-1  1.621E-1)
( 4.650E-1  1.664E-1)
( 4.925E-1  2.140E-1)
( 1.340E-1  8.170E-2)
( 1.947E-1  4.650E-2)
( 1.902E-1  3.560E-2)

Который после просмотра файла OBJ, за исключением двух последних, я не могу найти в последовательности.

Вот исключение, которое я получаю

java.lang.IndexOutOfBoundsException: Index: 3522, Size: 3522
    at java.util.ArrayList.rangeCheck(Unknown Source)
    at java.util.ArrayList.get(Unknown Source)
    at graphics.renderEngine.OBJLoader.processVertex(OBJLoader.java:122)
    at graphics.renderEngine.OBJLoader.loadObjModel(OBJLoader.java:82)
    at main.Main.init(Main.java:150)
    at main.Main.<init>(Main.java:82)
    at main.Main.main(Main.java:75)

Я просто озадачен, я понятия не имею, почему я получаю это исключение, заранее спасибо за любую подсказку

3 ответа

Фактическая проблема заключается в том, что вы перестаете читать объявления 'v', 'vt' и 'vn', как только видите первое объявление 'f' (face). Вы не можете сделать это, потому что спецификация Wavefront OBJ не указывает, что объявления 'v*' должны предшествовать объявлениям 'f'. И на самом деле, в вашем файле примера они перемешаны. Это означает, что когда вы прекратите читать объявления 'v*' в вашем первом цикле, вы в конечном итоге найдете объявления 'f', которые ссылаются на другие объявления 'v*', которые вы не читали, поэтому вы получите исключение IndexOutOfBounds. Вы должны реструктурировать свои циклы так, чтобы чтение "v *", а также "f" объявлений было возможно в любое время.

Размер списка - 3522, значения индекса которого находятся в диапазоне от 0 до 3521. Но вы пытаетесь использовать несуществующий индекс 3522 для доступа к элементу списка, отсюда и исключение. Необходимо иметь условие, чтобы проверить, что индекс меньше размера, прежде чем получить доступ к элементу списка.

Похоже, что вы следуете учебнику https://www.youtube.com/user/ThinMatrix

Учебник в порядке; это работает отлично. (Я использовал это раньше)

Эта проблема

Проблема в том, что его OBJLoader специфичен для формата файла OBJ, который он выбрал, и не является универсальным загрузчиком OBJ.

Его формат следующий: v/vt/vn/f/EOF

В то время как ваш больше похож на: v/vt/vn/f/v/vt/vn/f/.../EOF

Код, который вы опубликовали, предполагает, что после того, как он найдет первую строку с 'f', данных вершин больше не будет, а потом будут только данные лица.

Поэтому код не получает все данные вершин, поэтому, когда вы пытаетесь проиндексировать эти данные, его там нет.

Исправление

Есть два возможных решения этого:

  1. Переконфигурируйте ваш код для работы в более общем смысле, позволяя ему принимать все данные из файла и затем загружать его в буферы.
  2. Измените структуру вашего файла OBJ так, чтобы он правильно лежал в формате, указанном ThinMatrix. Возможно, самый простой способ - это скопировать / вставить, хотя вы можете создать программу, которая сделает это за вас.

Я предполагаю, что, поскольку вы следуете учебнику по этому вопросу, у вас мало опыта с ним, и самым простым способом для вас будет решение № 2.

Проверьте
return loader.loadToVAO(verticesArray,textureArray,indexArray);

здесь есть небольшая орфографическая ошибка, посмотрите написание textureArray, и вы определили texturesArray, где есть s .

Другие вопросы по тегам