Я работаю над игрой с друзьями, и чтобы облегчить жизнь, я решил, что все 3D-модели будут создаваться с использованием файлов .obj, экспортируемых в такие программы, как 3DSMax, Maya и Blender3D.
Итак, я написал программу для чтения файлов .obj и опробовал ее на простой сцене, некоторые файлы .obj отрисовывались хорошо (например, простой куб), некоторые отображались действительно странно, а затем некоторые вообще не отображались. Я надеялся, что кто-то может указать мне, что я сделал неправильно, следующий код содержит один класс с двумя встроенными классами, один из которых содержит другой встроенный класс. Это может сбить с толку, так что вы можете скопировать и вставить в файл для удобства чтения.
Скрипт читает строку за строкой в файле и, если он начинается с "v" (вершина), он разбивает строку на пробелы и принимает индексы 1, 2 и 3 (xy и z) и сохраняет целочисленные значения в классе. называется Vertex и добавляет его в массив объектов Vertex. объект Vertex действует как вектор, только он содержит два вектора, один для положения и один для нормалей.
Если строка начинается с "vn" (Vertex Normal), она разбивает строку на "" и берет индексы 1 2 и 3 и добавляет затем к объекту Vertex, который до сих пор действует как вектор, а затем Vertex добавляется в другой массив Vertex специально для нормалей.
Теперь вот интересная часть, когда строка начинается с "f" (Face), строка может выглядеть так:
f 1//3 5//3 6//1 2//4
каждый # 1 // # 2, # 1 - это индекс правильной вершины, а # 2 - это индекс для правильной нормали. Таким образом, я беру каждую часть строки, разделенную на «» и разделяющую ее на «//», и извлекаю вершину из массива вершин и вершину из массива нормали, устанавливаю нормали вершин в xy и z нормали, сделал объект Face и добавил его в список граней, в которых только 3 или 4 объекта Vertex.
Надеюсь, это объяснение может сделать файл не таким грязным.
Ну, вот код:
package org.ic3d.utils;
import java.io.*;
import org.ic3d.utils.ObjReader.Face.FaceStateException;
import org.lwjgl.opengl.GL11;
public class ObjReader
{
public ObjReader(String file)
{
try
{
BufferedReader reader = new BufferedReader(new FileReader(new File(file)));
parse(reader);
}
catch(Exception e)
{
System.out.println(file+" Failed To Load! Can't continue.");
System.exit(0);
}
}
/**
* Parse all lines in the BufferedReader to convert the .obj file
*
* @param br - A BufferedReader object pointing to the desired .obj file
* @throws IOException If the obj file can not be read for any reason.
* @throws FaceStateException If the obj file is malformed and vertice are added to a face of different shape (tri - quad)
*/
public void parse(BufferedReader br) throws IOException, FaceStateException
{
String s="";
Vertex[] v1 = new Vertex[15000];
Vertex[] n1 = new Vertex[15000];
while((s = br.readLine())!=null)
{
if(s.startsWith("v"))
{
String[] pv = s.split(" ");
Vertex vert_0x = new Vertex(Float.parseFloat(pv[1]), Float.parseFloat(pv[2]), Float.parseFloat(pv[3]));
v1 = appendVert(v1, vert_0x);
}
if(s.startsWith("vn"))
{
String[] pv = s.split(" ");
Vertex vert_0x = new Vertex(Float.parseFloat(pv[1]), Float.parseFloat(pv[2]), Float.parseFloat(pv[3]));
n1 = appendVert(n1, vert_0x);
}
if(s.startsWith("f"))
{
String[] pv = s.split(" ");
Vertex[] temp = new Vertex[pv.length-1];
for(int i=1;i<pv.length;i++)
{
String[] vn = pv[i].split("//");
Vertex v = v1[Integer.parseInt(vn[0])];
Vertex n = n1[Integer.parseInt(vn[1])];
v.setNormals(n.getX(), n.getY(), n.getZ());
temp = appendVert(temp, v);
}
try
{
Face f = new Face(temp.length==3?Face.GL_FACE_TRI:Face.GL_FACE_QUAD, temp);
faces = appendFace(faces, f);
}
catch(FaceStateException e)
{
throw e;
}
}
}
}
private Vertex[] appendVert(Vertex[] l, Vertex v)
{
for(int i=0;i<l.length;i++)
{
if(l[i]==null)
{
l[i] = v;
return l;
}
}
System.out.println("Vertex[] can only hold "+l.length+" Vertices at one time");
return l;
}
private Face[] appendFace(Face[] l, Face f)
{
for(int i=0;i<l.length;i++)
{
if(l[i]==null)
{
l[i] = f;
return l;
}
}
System.out.println("Face[] can only hold "+faces.length+" Faces at one time");
return l;
}
public void renderTri(Face f, float x, float y, float z)
{
Vertex[] v = f.getVerts();
GL11.glBegin(GL11.GL_TRIANGLES);
Vertex cv = v[0];
GL11.glNormal3f(cv.getNormalX(), cv.getNormalY(), cv.getNormalZ());
GL11.glVertex3f(x+cv.getX(), y+cv.getY(), z+cv.getZ());
cv = v[1];
GL11.glNormal3f(cv.getNormalX(), cv.getNormalY(), cv.getNormalZ());
GL11.glVertex3f(x+cv.getX(), y+cv.getY(), z+cv.getZ());
cv = v[2];
GL11.glNormal3f(cv.getNormalX(), cv.getNormalY(), cv.getNormalZ());
GL11.glVertex3f(x+cv.getX(), y+cv.getY(), z+cv.getZ());
GL11.glEnd();
}
public void renderQuad(Face f, float x, float y, float z)
{
Vertex[] v = f.getVerts();
GL11.glBegin(GL11.GL_QUADS);
Vertex cv = v[0];
GL11.glNormal3f(cv.getNormalX(), cv.getNormalY(), cv.getNormalZ());
GL11.glVertex3f(x+cv.getX(), y+cv.getY(), z+cv.getZ());
cv = v[2];
GL11.glNormal3f(cv.getNormalX(), cv.getNormalY(), cv.getNormalZ());
GL11.glVertex3f(x+cv.getX(), y+cv.getY(), z+cv.getZ());
cv = v[2];
GL11.glNormal3f(cv.getNormalX(), cv.getNormalY(), cv.getNormalZ());
GL11.glVertex3f(x+cv.getX(), y+cv.getY(), z+cv.getZ());
cv = v[3];
GL11.glNormal3f(cv.getNormalX(), cv.getNormalY(), cv.getNormalZ());
GL11.glVertex3f(x+cv.getX(), y+cv.getY(), z+cv.getZ());
GL11.glEnd();
}
public void render(float x, float y, float z)
{
GL11.glPushMatrix();
for(Face f : faces)
{
if(f==null)
{
GL11.glPopMatrix();
return;
}
else
{
switch(f.getType())
{
case(3):
{
renderTri(f, x, y, z);
}
case(4):
{
renderQuad(f, x, y, z);
}
}
}
}
GL11.glPopMatrix();
}
public int listid=0;
public Face[] faces = new Face[15000];
public class Face
{
/**
* Create a new Face object, Faces Hold 3 or 4 Vertex Objects, Polygons not accepted.
* @param shape
* @param verts
* @throws FaceStateException - If the number of vertice in the Vertex[] is not equal to the face type set.
*/
public Face(int shape, Vertex[] vertlist) throws FaceStateException{
int vert_n = GL_FACE_NONE-shape;
if(vertlist.length>vert_n)
{
throw new FaceStateException(vert_n+" Vertice faces can not hold "+verts.length+" vertices");
}
if(vertlist.length<vert_n)
{
throw new FaceStateException(vert_n+" Vertice faces must hold "+vert_n+" vertice, not "+verts.length+" vertices");
}
if(vert_n!=3 && vert_n!=4)
{
throw new FaceStateException("Faces can only be 3 or 4 vertice. Shapes besides QUAD and TRI are not allowed.");
}
type=vert_n;
verts=vertlist;
}
public Vertex[] getVerts()
{
return verts;
}
public int getType()
{
return type;
}
public String getType(int i)
{
if(i==1)
{
return(type==3?"TRI":"QUAD");
}
else
{
return(type==3?"TRIANGLE":"QUAD");
}
}
private Vertex[] verts;
public static final int GL_FACE_QUAD = 3;
public static final int GL_FACE_TRI = 4;
public static final int GL_FACE_NONE = 7;
private int type=7;
public class FaceStateException extends Exception
{
public FaceStateException(String s)
{
super(s);
}
private static final long serialVersionUID = 1L;
}
}
public class Vertex
{
public Vertex(float x, float y, float z)
{
_x=x;
_y=y;
_z=z;
}
public void setNormals(float x, float y, float z)
{
_nx=x;
_ny=y;
_nz=z;
}
public float getX()
{
return _x;
}
public float getY()
{
return _y;
}
public float getZ()
{
return _z;
}
public float getNormalX()
{
return _nx;
}
public float getNormalY()
{
return _ny;
}
public float getNormalZ()
{
return _nz;
}
public float[] getNormalXYZ()
{
return new float[]{_nx, _ny, _nz};
}
public void setXYZ(float x, float y, float z)
{
_x=x;
_y=y;
_z=z;
}
public float[] getXYZ()
{
return new float[]{_x, _y, _z};
}
private float _x;
private float _y;
private float _z;
private float _nx;
private float _ny;
private float _nz;
}
}