LWJGL .OBJ 文件读取器有时会导致文件变形或不渲染文件

发布于 2024-12-20 16:33:37 字数 9416 浏览 3 评论 0原文

我正在和一些朋友一起开发一款游戏,为了让生活更轻松,我决定使用 .obj 文件制作所有 3D 模型,并在 3DSMax、Maya 和 Blender3D 等程序中导出。

因此,我编写了一个 .obj 文件阅读器,并在一个简单的场景中进行了尝试,一些 .obj 文件渲染得很好(就像一个简单的立方体),有些渲染得很奇怪,然后有些根本没有渲染。我希望有人能指出我做错了什么,下面的代码包含一个类,有2个嵌入类,其中一个包含另一个嵌入类。它可能会令人困惑,因此您可以根据需要复制并粘贴到文件中以便于阅读。

该脚本逐行读取文件中的行,如果它以“v”(顶点)开头,则会按空格分割行,并采用索引 1 2 和 3(xy 和 z)并将整数值存储在名为 Vertex 的类中,并将其添加到 Vertex 对象数组中。 Vertex 对象的作用就像一个向量,只是它包含两个向量,一个用于位置,另一个用于法线。

如果该线以“vn”(顶点法线)开头,它将将该线分割为“”,并获取索引 1 2 和 3,然后添加到一个 Vertex 对象,到目前为止,该对象的作用就像一个向量,然后添加该 Vertex到专门用于法线的不同顶点数组。

现在这是有趣的部分,当该行以“f”(面)开头时,该行可能如下所示:

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;
    }
}

I am working on a game with some friends, and to make life easier I decided all out 3D models are going to be made using .obj files, exported in programs like 3DSMax, Maya, and Blender3D.

So I have written a .obj file reader and tried it out in a simple scene, some .obj files rendered good (like a simple cube), some rendered really strangely, and then some didn't render at all. I was hoping someone could point out to me what I did wrong, the following code contains one class, with 2 embedded classed, one of which contains another embedded class. It may get confusing, so you can copy and paste into a file for easier reading if needed.

The script reads line by line in the file and if it starts with "v" (vertex) it splits the line by space, and takes index 1 2 and 3 (x y and z) and stores the integer values in a class called Vertex, and adds it to an array of Vertex objects. a Vertex object acts just like a vector, only it contains two vectors, one for position, and one for the normals.

If the line starts with "vn" (Vertex Normal) it splits the line by " " and takes the indexes 1 2 and 3 and adds then to a Vertex object, which acts just like a vector so far, and then that Vertex is added to a different Vertex array specifically for normals.

Now here is the interesting part, when the line starts with "f" (Face) the line might look like:

f 1//3 5//3 6//1 2//4

each #1//#2, #1 is the index of the correct vertex, and #2 is the index for the proper normal. So I take each part of the line, splitted by " " and split it by "//" and took the vertex from the vertices array and the vertex from the normals array, set the vertices normals to the x y and z of the normal, made a Face object, and added it to a list of faces, which faces just hold 3 or 4 Vertex objects.

Hopefully this explaination might make the file not seem as messy.

Well, here is the code:

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;
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

一杯敬自由 2024-12-27 16:33:37

我相信这是相当简单的修复,实际上不需要 GL11.glEnd() ,您可以使用 begin 启动另一个渲染类型,甚至不需要先结束它。尝试将 GL11.glEnd() 剪切到整个渲染脚本的末尾,看看是否有帮助:

public void render()
{
    Object1.render( 5, 0, 5);
    Object1.render(-5, 0,-5);
    Object2.render( 5, 0,-5);
    Object2.render(-5, 0, 5);
    GL11.glEnd();
}

I believe it's fairly simple fix, GL11.glEnd() is really not needed, you can start another render type using begin, without even ending it first. Try cutting the GL11.glEnd() to the end of your entire render script, see if that helps any:

public void render()
{
    Object1.render( 5, 0, 5);
    Object1.render(-5, 0,-5);
    Object2.render( 5, 0,-5);
    Object2.render(-5, 0, 5);
    GL11.glEnd();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文