Я написал простой синтаксический анализ волнового фронта для 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, из-за которых строки пропускаются?
Спасибо за потраченное время.