【发布时间】:2023-03-15 04:12:01
【问题描述】:
我为 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 变量 line,由 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 开头,则从v 后面的数字中创建一个新的Vector3f 对象(一个3 维向量,其构造函数为Vector3f(float x, float y, float z))。由于 .obj 文件中空格字符的定义方式,这些数字作为 splitLine 字符串数组中的第一个、第二个和第三个索引进行访问。在发送到构造函数之前,它们会从字符串解析为浮点数。然后在最后,将 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
这是 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 语句中是否存在导致跳行的错误?
感谢您抽出宝贵的时间。
【问题讨论】:
标签: parsing opengl lwjgl wavefront