Main Menu

Deformed mesh

Started by JebKer, January 01, 2016, 19:14:54

Previous topic - Next topic

JebKer

Hello, I am loading a wavefront .obj into a vao with a simple obj loader. The program does not crash but fails to properly construct the mesh. The provided image should display a gui square with a brick texture and a green cube in 3d space. As you can see, the constructed mesh doensn't resemble a square and cube. I double checked the .obj files with blender and they appear to be correct. I triangulate the faces in blender and then draw the mesh with GL_TRIANGLES. I have no idea why it is so messed up surely It can't be the indices order because it's drawing vertices I didn't even specify.

quew8

You haven't given us any code so we really can't help you. I suggest posting this "simple obj loader".

JebKer

I haven't posted any code because I have no idea what is responsible for this, but I do know that my obj loader does load the values properly. I have also tried several loaders from github and they all produce the same result, for the simple gui mesh my loader get's the following values:
Vertices (-1.0, -1.0, 0.0), (0.0, 1.0, 0.0), (1.0, 1.0, 0.0), (1.0, 1.0, 0.0), (0.0, -1.0, 0.0), (-1.0, -1.0, 0.0)
Normals (0.0, 0.0, -1.0), (0.0, 0.0, -1.0), (0.0, 0.0, -1.0), (0.0, 0.0, -1.0), (0.0, 0.0, -1.0), (0.0, 0.0, -1.0)
Tangents (1.0, 0.0, 0.0), (1.0, 0.0, 0.0), (1.0, 0.0, 0.0), (1.0, 0.0, 0.0), (1.0, 0.0, 0.0), (1.0, 0.0, 0.0)
TexCoords (0.0, 0.0), (0.0, 1.0), (1.0, 1.0), (1.0, 1.0), (1.0, 0.0), (0.0, 0.0)
Indices 0, 1, 2, 3, 4, 5


Here's the code that draws the vao
        GL30.glBindVertexArray(getMeshData().getVertexArrayObject());
        GL11.glDrawElements(GL11.GL_TRIANGLES, getMeshData().getDrawCount(), GL11.GL_UNSIGNED_INT, 0);
        GL30.glBindVertexArray(0);



Here's the raw loading part of the obj loader but I don't think the problem is loading.

        File file = new File(path);
        RawModel rawModel = new RawModel();
        try
        {
            BufferedReader reader = new BufferedReader(new FileReader(file));

            String line;
            while((line = reader.readLine()) != null)
            {
                if(line.startsWith("v ")) // Vertex
                {
                    float x = Float.valueOf(line.split(" ")[1]);
                    float y = Float.valueOf(line.split(" ")[2]);
                    float z = Float.valueOf(line.split(" ")[3]);

                    rawModel.vertices.add(new Vector3f(x, y, z));
                }
                else if(line.startsWith("vt ")) // TexCoord
                {
                    float u = Float.valueOf(line.split(" ")[1]);
                    float v = Float.valueOf(line.split(" ")[2]);

                    rawModel.texCoords.add(new Vector2f(u, v));
                }
                else if(line.startsWith("vn ")) // Normal
                {
                    float x = Float.valueOf(line.split(" ")[1]);
                    float y = Float.valueOf(line.split(" ")[2]);
                    float z = Float.valueOf(line.split(" ")[3]);

                    rawModel.normals.add(new Vector3f(x, y, z));
                }
                else if(line.startsWith("f ")) // Face
                {
                    String[] part = line.split(" ");
                    if(part.length != 4) System.err.println("Model " + path + " must be triangulated!");

                    Face face = new Face();

                    for(int i = 1; i < part.length; i++)
                    {
                        face.vertexIndex[i - 1] = Integer.valueOf(part[i].split("/")[0]);
                        face.texCoordIndex[i - 1] = Integer.valueOf(part[i].split("/")[1]);
                        face.normalIndex[i - 1] = Integer.valueOf(part[i].split("/")[2]);
                    }

                    rawModel.faces.add(face);
                }
            }
        }
        catch(IOException e) { e.printStackTrace(); }

        return rawModel;


Then I convert this raw model to a more gpu friendly format with this code

        Model model = new Model();
        
        for(Face face : rawModel.faces)
        {
            model.addVertex(rawModel.vertices.get(face.vertexIndex[0] - 1));
            model.addVertex(rawModel.vertices.get(face.vertexIndex[1] - 1));
            model.addVertex(rawModel.vertices.get(face.vertexIndex[2] - 1));

            model.addTexCoord(rawModel.texCoords.get(face.texCoordIndex[0] - 1));
            model.addTexCoord(rawModel.texCoords.get(face.texCoordIndex[1] - 1));
            model.addTexCoord(rawModel.texCoords.get(face.texCoordIndex[2] - 1));

            model.addNormal(rawModel.normals.get(face.normalIndex[0] - 1));
            model.addNormal(rawModel.normals.get(face.normalIndex[1] - 1));
            model.addNormal(rawModel.normals.get(face.normalIndex[2] - 1));
        }

        for(int i = 0; i < model.getVertices().size(); i += 3)
        {
            model.addFace(i, i + 1, i + 2);
        }

        model = model.create();

        return model;



Model.create() just checks the model and generates the tangents.

If I should post more code just tell me what part because the codebase is quite big.

Kai

Do you allocate the ByteBuffer/FloatBuffer holding the vertex positions, before uploading them to the VBO, via LWJGL's BufferUtils or do you allocate them manually with the static ByteBuffer/FloatBuffer.allocateDirect()?
If the latter, then did you probably forget to set the correct endianness on those buffers with .order(ByteOrder.nativeOrder()) ?

JebKer

I always create my buffers with LWJGL's BufferUtils.create...
In fact, I use this helper method to create the buffer that I then upload.

        public static FloatBuffer createFlippedBuffer3f(ArrayList<Vector3f> data)
	{
		FloatBuffer buffer = createFloatBuffer(data.size() * 3);
		for(Vector3f vector : data)
		{
			buffer.put(vector.x);
			buffer.put(vector.y);
			buffer.put(vector.z);
		}

		buffer.flip();

		return buffer;
	}


and similar methods for Vector2f and Integers.

I really have no idea what needs to go wrong in order for a simple cube to look like a whatever this resembles. The result even is concave in some places. I might add a texture to the cube and set up some lights and post pictures of it if the first picture wasn't clear enugh.

quew8

So seeing that the texture on the square is fine, I'm assuming that your pointer's are all set up correctly there which makes me think that is is something wrong with a transformation causing that. Try getting rid of any transformations in your shader so it just passes the position straight through to the fragment and see if that works.

However with the cube, it seems like a dodgy transformation wouldn't cause it to be that weird but I assume it is a colour thing using different pointers so it might be those pointers being weird as well.

JebKer

Well, my vertex shader doesn't transform that much, here is the code

#version 330
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 texCoord;
layout (location = 2) in vec3 normal;
layout (location = 3) in vec3 tangent;

out vec2 texCoord0;
out mat3 tbnMatrix;
out vec3 worldPos0;

uniform mat4 T_model;
uniform mat4 T_MVP;

void main()
{
	gl_Position = T_MVP * vec4(position, 1.0);
	
	texCoord0 = texCoord;
	
	vec3 n = normalize((T_model * vec4(normal, 0.0)).xyz);
	vec3 t = normalize((T_model * vec4(tangent, 0.0)).xyz);
	t = normalize(t - dot(t, n) * n);
	vec3 biTangent = cross(t, n);
	tbnMatrix = mat3(t, biTangent, n);
	
	worldPos0 = (T_model * vec4(position, 1.0)).xyz;
}


You can ignore the tbn Matrix as it isn't used in the fragment shader yet.
When I change the worldPos0 to worldPos0 = position, nothing seems to change.

Here's a new image I took with lighting and texturing. It's interesting to see that the normals appear to be the one provided with the .obj because faces that should be lit are not.

quew8

To be clear. Is this a problem that came up when you started importing wavefront models? If you try drawing a cube by manually typing in vertices, does the problem still occur?

JebKer

Thing is, I just checked the FloatBuffer that goes into the vbo and it's values are, as expected, the ones of my .obj so the correct values are being uploaded. I tried drawing a cube with glBegin and glVertex3f and the cube appears to be correct. Strange.

quew8

When you say glVertex3f(), I presume you mean glVertexAttrib3f()?

In that case it does sound like a pointer issue.

Try printing the output of this method just before you call glDrawXXX(). That way you can check exactly what state the pointers are in.
public static String getPointerInfo() {
	IntBuffer ib = BufferUtils.createIntBuffer(1);
	glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, ib);
	int n = ib.get(0);
	glGetIntegerv(GL_ELEMENT_ARRAY_BUFFER_BINDING, ib);
    String s = "Element Buffer Binding " + ib.get(0);
	for(int i = 0; i < n; i++) {
        glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_ENABLED, ib);
		boolean enabled = ib.get(0) != 0;
		if(enabled) {
			glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_SIZE, ib);
			int size = ib.get(0);
			glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_STRIDE, ib);
			int stride = ib.get(0);
			glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_TYPE, ib);
			String type;
			switch(ib.get(0)) {
				case GL_BYTE: type = "GL_BYTE"; break;
				case GL_UNSIGNED_BYTE: type = "GL_UNSIGNED_BYTE"; break;
				case GL_SHORT: type = "GL_SHORT"; break;
				case GL_UNSIGNED_SHORT: type = "GL_UNSIGNED_SHORT"; break;
				case GL_INT: type = "GL_INT"; break;
				case GL_UNSIGNED_INT: type = "GL_UNSIGNED_INT"; break;
				case GL_FLOAT: type = "GL_FLOAT"; break;
				case GL_DOUBLE: type = "GL_DOUBLE"; break;
				default: type = "UNKNOWN ENUM " + ib.get(0);
			}
			glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_NORMALIZED, ib);
			boolean normalized = ib.get(0) != 0;
			glGetVertexAttribiv(i, GL_VERTEX_ATTRIB_ARRAY_BUFFER_BINDING, ib);
			int binding = ib.get(0);
			s += "Vertex Pointer " + i + " Enabled " + enabled + ", Size " + size + 
					", Stride " + stride + ", Type " + type + ", Normalized " + 
					normalized + ", Binding " + binding;
				s += "\n" + getVertexAttribPointerString(i);
		}
	}
	return s;
}


The other thing I haven't mentioned before because you seemed on top of such things but does catch out a lot of beginners importing Wavefront files is the indexing.
Obviously Wavefront will use different indices for position, texCoord, normal etc. data but OpenGL requires they have the same index.