Problems rendering .obj file

I’m learning openGL for educational purposes and for the fun of it now and I’m trying to render a 3D model of a bunny.

I tried to render the .obj file but I get unclear picture
MainDisplay class :



package com.dryadengine.gui;

import com.dryadengine.core.Model;
import com.dryadengine.framework.OBJLoader;
import com.dryadengine.framework.ShaderFactory;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.lwjgl.LWJGLException;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import org.lwjgl.util.vector.Matrix4f;

/**
 *
 * @author Roy
 */
public class MainDisplay {

private Model bunny;
private Matrix4f mProjection;
private Matrix4f mView;
private Matrix4f mModel;
private int shaderProgramID;
private int vboID;
private int vPositionID;
private int mProjectionID;
private int mViewID;
private int mModelID;

/**
 * @param args the command line arguments
 */
public static void main(String[] args) {
    MainDisplay md = new MainDisplay();
    md.create();
    md.init();
    md.run();
}

public MainDisplay() {

}

public void create() {
    try {
        Display.setDisplayMode(new DisplayMode(800, 600));
        Display.setTitle("Dryad Engine 1.0.0");
        Display.setFullscreen(false);
        Display.setResizable(true);
        Display.create();
    } catch (LWJGLException ex) {
        Logger.getLogger(MainDisplay.class.getName()).log(Level.SEVERE, null, ex);
        System.exit(-1);
    }
}

public void init() {
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    try {
        shaderProgramID = ShaderFactory.createShaderProgram("vertexShader", "fragmentShader");
        glUseProgram(shaderProgramID);
        bunny = OBJLoader.parseOBJ(new File("src/com/dryadengine/assets/bunny.obj"));
        FloatBuffer vbo = BufferUtils.createFloatBuffer(bunny.getVertices().length);
        vbo.put(bunny.getVertices());
        vbo.flip();
        vboID = glGenBuffers();
        glBindBuffer(GL_ARRAY_BUFFER, vboID);
        glBufferData(GL_ARRAY_BUFFER, vbo, GL_STATIC_DRAW);
        vPositionID = glGetAttribLocation(shaderProgramID, "vPosition");
        glEnableVertexAttribArray(vPositionID);
        mProjection = new Matrix4f();
        float fieldOfView = 60f;
        float aspectRatio = (float)Display.getWidth() / (float)Display.getHeight();
        float nearPlane = 0.1f;
        float farPlane = 100f;
        float yScale = (float)(1.0f / Math.tan((fieldOfView / 2.0f) * Math.PI / 180));//this.coTangent(this.degreesToRadians(fieldOfView / 2f));
        float xScale = yScale / aspectRatio;
        float frustum_length = farPlane - nearPlane;
        mProjection.m00 = xScale;
        mProjection.m11 = yScale;
        mProjection.m22 = -((farPlane + nearPlane) / frustum_length);
        mProjection.m23 = -1;
        mProjection.m32 = -((2 * nearPlane * farPlane) / frustum_length);
        mProjection.m33 = 0;
        mView = new Matrix4f();
        mView.m23 = -5;
        mModel = new Matrix4f();
        mProjectionID = glGetUniformLocation(shaderProgramID, "mProjection");
        mViewID = glGetUniformLocation(shaderProgramID, "mView");
        mModelID = glGetUniformLocation(shaderProgramID, "mModel");
        glEnable(GL_CULL_FACE);
        glCullFace(GL_BACK);
        glEnable(GL_DEPTH_TEST);
    } catch (FileNotFoundException ex) {
        Logger.getLogger(MainDisplay.class.getName()).log(Level.SEVERE, null, ex);
    } catch (IOException ex) {
        Logger.getLogger(MainDisplay.class.getName()).log(Level.SEVERE, null, ex);
    }
}



public void run() {
    while (!Display.isCloseRequested()) {
        if (Display.isVisible()) {
            render();
        }
        if (Display.wasResized()) {
            reshape();
        }
        Display.update();
        Display.sync(60);
    }
    destroy();
}

public void render() {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    FloatBuffer fb1 = BufferUtils.createFloatBuffer(16);
    FloatBuffer fb2 = BufferUtils.createFloatBuffer(16);
    FloatBuffer fb3 = BufferUtils.createFloatBuffer(16);
    fb1.flip();
    fb2.flip();
    fb3.flip();
    mProjection.store(fb1);
    mView.store(fb2);
    mModel.store(fb3);
    glUniformMatrix4(mProjectionID, true, fb1);
    glUniformMatrix4(mViewID, true, fb2);
    glUniformMatrix4(mModelID, true, fb3);
    for (int i = 0; i < bunny.getVertices().length / 3; i += 3) {
        glVertexAttribPointer(vPositionID, 3, GL_FLOAT, false, 0, i);
        glDrawArrays(GL_TRIANGLES, 0, 3);
    }
}

public void reshape() {
    glViewport(0, 0, Display.getWidth(), Display.getHeight());
}

public void dispose() {
    glDeleteProgram(shaderProgramID);
    glUseProgram(0);
    glDeleteBuffers(vboID);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

public void destroy() {
    Display.destroy();
}
}

ShaderFactory class :

package com.dryadengine.framework;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL20.*;

/**
 *
 * @author Roy
 */
public class ShaderFactory {

private static final String COMMON_SHADERS_PATH = "/com/dryadengine/shaders/";
private static final String SHADER_EXTENSION = ".dsf";

/**
 * 
 * @param vertexShaderName
 * @param fragmentShaderName
 * @return a shader program
 * @throws FileNotFoundException
 * @throws IOException 
 */
public static int createShaderProgram(String vertexShaderName, String fragmentShaderName) throws FileNotFoundException, IOException {
    ArrayList<Integer> shaders = new ArrayList();
    shaders.add(ShaderFactory.compileShader(GL_VERTEX_SHADER, getShaderFileCode(COMMON_SHADERS_PATH + vertexShaderName + SHADER_EXTENSION)));
    shaders.add(ShaderFactory.compileShader(GL_FRAGMENT_SHADER, getShaderFileCode(COMMON_SHADERS_PATH + fragmentShaderName + SHADER_EXTENSION)));
    return ShaderFactory.linkProgram(shaders);
}

/**
 * 
 * @param shaderFilePath
 * @return a shader file code
 * @throws FileNotFoundException
 * @throws IOException 
 */
private static String getShaderFileCode(String shaderFilePath) throws FileNotFoundException, IOException {
    StringBuilder shaderCode = new StringBuilder();
    String line;
    try {
        try (BufferedReader br = new BufferedReader(new InputStreamReader(ShaderFactory.class.getResourceAsStream(shaderFilePath)))) {
            while ((line = br.readLine()) != null) {
                shaderCode.append(line).append("\n");
            }
        }
    } catch (FileNotFoundException e) {
        throw new FileNotFoundException(e.getMessage());
    }
    return shaderCode.toString();
}

/**
 * 
 * @param shaderType
 * @param shaderCode
 * @return a compiled shader file id
 */
public static int compileShader(int shaderType, String shaderCode) {
    int shaderID = glCreateShader(shaderType);
    glShaderSource(shaderID, shaderCode);
    glCompileShader(shaderID);
    int status = glGetShaderi(shaderID, GL_COMPILE_STATUS);
    if (status == GL_FALSE) {
        glDeleteShader(shaderID);
        throw new RuntimeException(glGetShaderInfoLog(shaderID, glGetShaderi(shaderID, GL_INFO_LOG_LENGTH)));
    }
    return shaderID;
}

/**
 * Link the vertex shader and the fragment shader to the shader program
 * @param shaders
 * @return a shader program
 */
public static int linkProgram(ArrayList <Integer> shaders) {
    int shaderProgramID = glCreateProgram();
    for (Integer shader : shaders) {
        glAttachShader(shaderProgramID, shader);
    }
    glLinkProgram(shaderProgramID);
    int status = glGetProgrami(shaderProgramID, GL_LINK_STATUS);
    if (status == GL_FALSE) {
        glDeleteProgram(shaderProgramID);
        throw new RuntimeException(glGetShaderInfoLog(shaderProgramID, glGetProgrami(shaderProgramID, GL_INFO_LOG_LENGTH)));
    }
    for (int shader : shaders) {
        glDeleteShader(shader);
    }
    return shaderProgramID;
}

}

OBJLoader class :

package com.dryadengine.framework;

import com.dryadengine.core.Model;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author Roy
 */
public class OBJLoader {

/**
 * Parse .obj file and make a model from it.
 * @param f
 * @return a model object
 * @throws FileNotFoundException
 * @throws IOException 
 */
public static Model parseOBJ(File f) throws FileNotFoundException, IOException {
    BufferedReader br = new BufferedReader(new FileReader(f));
    String line;
    Model m;
    List<Float> vertices = new ArrayList<>();
    List<Float> normals = new ArrayList<>();
    while ((line = br.readLine()) != null) {
        if (line.startsWith("v")) {
            float x = Float.valueOf(line.split(" ")[1]);
            float y = Float.valueOf(line.split(" ")[2]);
            float z = Float.valueOf(line.split(" ")[3]);
            vertices.add(x);
            vertices.add(y);
            vertices.add(z);
        } else if (line.startsWith("vn")) {
            float x = Float.valueOf(line.split(" ")[1]);
            float y = Float.valueOf(line.split(" ")[2]);
            float z = Float.valueOf(line.split(" ")[3]);
            normals.add(x);
            normals.add(y);
            normals.add(z);
        }
    }
    br.close();
    float[] a = new float[vertices.size()];
    float[] b = new float[normals.size()];
    for (int i = 0; i < vertices.size(); i++) {
        a[i] = vertices.get(i);
    }
    for (int i = 0; i < normals.size(); i++) {
        b[i] = normals.get(i);
    }
    m = new Model(a, b);
    return m;
}

}

Model class :

package com.dryadengine.core;

/**
 *
 * @author Roy
 */
public class Model {

private float[] vertices;
private float[] normals;

/**
 * Construct a new model object.
 * @param vertices
 * @param normals 
 */
public Model(float[] vertices, float[] normals) {
    this.vertices = vertices;
    this.normals = normals;
}

/**
 * 
 * @return the model vertices array
 */
public float[] getVertices() {
    return vertices;
}

/**
 * 
 * @return the model normals array
 */
public float[] getNormals() {
    return normals;
}

}

vertex shader code :

#version 330

uniform mat4 mProjection;
uniform mat4 mView;
uniform mat4 mModel;

in vec4 vPosition;

void main()
{
    gl_Position = mProjection * mView * mModel * vPosition;
}

fragment shader code :

#version 330

out vec4 color;

void main()
{
    color = vec4(1.0, 1.0, 1.0, 1.0);
}

Result :

Any ideas why ?

I think you haven’t used the face data to build the vertex data.
Try this video by Oskar Veerhoek it is pretty good for learning how to load OBJs

I saw it but it wasnt clear to me, how keeping the faces will help me ? the vertex are orginized in the .obj file so each 3 vertex creates a triangle why do I need it then ?

It isn’t that simple. You need to use the face data otherwise the vertex data is not in the right order.

[quote=“kululu,post:3,topic:48374”]
They are not. The lines starting with “v” simply declare where a vertex is. Lines starting with “f” are the important ones that combine 3 or more of those vertices into a face.
The numbers specified on the “f” line are the indices of the veritices used. A vertex can be used in multiple faces (which is always the case for a continuous model - adjacent faces share a lot of vertices).

So parsing the “f” lines is crucial for an OBJ loader to work.

[quote=“Bone008,post:5,topic:48374”]

changed the code now I see only black screen…
Model class :

package com.dryadengine.core;

/**
 *
 * @author Roy
 */
public class Model {
    
    private float[] vertices;
    private float[] normals;
    private Face[] faces;
    
    /**
     * Construct a new model object.
     * @param vertices
     * @param normals
     * @param faces
     */
    public Model(float[] vertices, float[] normals, Face[] faces) {
        this.vertices = vertices;
        this.normals = normals;
        this.faces = faces;
    }
    
    /**
     * 
     * @return the model vertices array
     */
    public float[] getVertices() {
        return vertices;
    }
    
    /**
     * 
     * @return the model normals array
     */
    public float[] getNormals() {
        return normals;
    }
    
    /**
     * 
     * @return the model faces array 
     */
    public Face[] getFaces() {
        return faces;
    }
    
}

Face class :

package com.dryadengine.core;

/**
 *
 * @author Roy
 */
public class Face {
    
    private int[] vertexIndices;
    private float[] normals;
    
    public Face() {
        vertexIndices = new int[3];
        normals = new float[3];
    }
    
    /**
     * 
     * @return the face vertex indices array
     */
    public int[] getVertexIndices() {
        return vertexIndices;
    }
    
    /**
     * 
     * @return the face normals array
     */
    public float[] getNormals() {
        return normals;
    }
    
}

OBJLoader class :

package com.dryadengine.framework;

import com.dryadengine.core.Face;
import com.dryadengine.core.Model;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author Roy
 */
public class OBJLoader {
    
    /**
     * Parse .obj file and make a model from it.
     * @param f
     * @return a model object
     * @throws FileNotFoundException
     * @throws IOException 
     */
    public static Model parseOBJ(File f) throws FileNotFoundException, IOException {
        BufferedReader br = new BufferedReader(new FileReader(f));
        String line;
        Model m;
        List<Float> vertices = new ArrayList<>();
        List<Float> normals = new ArrayList<>();
        List<Face> faces = new ArrayList<>();
        Face face = new Face();
        while ((line = br.readLine()) != null) {
            if (line.startsWith("v")) {
                float x = Float.valueOf(line.split(" ")[1]);
                float y = Float.valueOf(line.split(" ")[2]);
                float z = Float.valueOf(line.split(" ")[3]);
                vertices.add(x);
                vertices.add(y);
                vertices.add(z);
            } else if (line.startsWith("vn")) {
                float x = Float.valueOf(line.split(" ")[1]);
                float y = Float.valueOf(line.split(" ")[2]);
                float z = Float.valueOf(line.split(" ")[3]);
                normals.add(x);
                normals.add(y);
                normals.add(z);
            } else if (line.startsWith("f")) {
                int index1 = Integer.valueOf(line.split(" ")[1].split("//")[0]);
                int index2 = Integer.valueOf(line.split(" ")[2].split("//")[0]);
                int index3 = Integer.valueOf(line.split(" ")[3].split("//")[0]);
                float x = Float.valueOf(line.split(" ")[1].split("//")[1]);
                float y = Float.valueOf(line.split(" ")[2].split("//")[1]);
                float z = Float.valueOf(line.split(" ")[3].split("//")[1]);
                face.getVertexIndices()[0] = index1;
                face.getVertexIndices()[1] = index2;
                face.getVertexIndices()[2] = index3;
                face.getNormals()[0] = x;
                face.getNormals()[1] = y;
                face.getNormals()[2] = z;
                faces.add(face);
            }
        }
        br.close();
        float[] a = new float[vertices.size()];
        float[] b = new float[normals.size()];
        Face[] c = new Face[faces.size()];
        for (int i = 0; i < vertices.size(); i++) {
            a[i] = vertices.get(i);
        }
        for (int i = 0; i < normals.size(); i++) {
            b[i] = normals.get(i);
        }
        for (int i = 0; i < faces.size(); i++) {
            c[i] = faces.get(i);
        }
        m = new Model(a, b, c);
        return m;
    }
    
}

MainDisplay class :

package com.dryadengine.gui;

import com.dryadengine.core.Model;
import com.dryadengine.framework.OBJLoader;
import com.dryadengine.framework.ShaderFactory;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.lwjgl.LWJGLException;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import org.lwjgl.util.vector.Matrix4f;

/**
 *
 * @author Roy
 */
public class MainDisplay {

    private Model bunny;
    private FloatBuffer vbo;
    private Matrix4f mProjection;
    private Matrix4f mView;
    private Matrix4f mModel;
    private int shaderProgramID;
    private int vboID;
    private int vPositionID;
    private int mProjectionID;
    private int mViewID;
    private int mModelID;
    
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        MainDisplay md = new MainDisplay();
        md.create();
        md.init();
        md.run();
    }
    
    public MainDisplay() {
        
    }
    
    public void create() {
        try {
            Display.setDisplayMode(new DisplayMode(800, 600));
            Display.setTitle("Dryad Engine 1.0.0");
            Display.setFullscreen(false);
            Display.setResizable(true);
            Display.create();
        } catch (LWJGLException ex) {
            Logger.getLogger(MainDisplay.class.getName()).log(Level.SEVERE, null, ex);
            System.exit(-1);
        }
    }
    
    public void init() {
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        try {
            shaderProgramID = ShaderFactory.createShaderProgram("vertexShader", "fragmentShader");
            glUseProgram(shaderProgramID);
            bunny = OBJLoader.parseOBJ(new File("src/com/dryadengine/assets/bunny.obj"));
            vbo = BufferUtils.createFloatBuffer(bunny.getFaces().length * 3);
            float[] vertexData = new float[bunny.getFaces().length * 3];
            for (int i = 0; i < bunny.getFaces().length; i++) {
                vertexData[i * 3] = bunny.getVertices()[bunny.getFaces()[i].getVertexIndices()[0]];
                vertexData[i * 3 + 1] = bunny.getVertices()[bunny.getFaces()[i].getVertexIndices()[1]];
                vertexData[i  * 3 + 2] = bunny.getVertices()[bunny.getFaces()[i].getVertexIndices()[2]];
            }
            vbo.put(vertexData);
            vbo.flip();
            vboID = glGenBuffers();
            glBindBuffer(GL_ARRAY_BUFFER, vboID);
            glBufferData(GL_ARRAY_BUFFER, vbo, GL_STATIC_DRAW);
            vPositionID = glGetAttribLocation(shaderProgramID, "vPosition");
            glEnableVertexAttribArray(vPositionID);
            mProjection = new Matrix4f();
            float fieldOfView = 60f;
            float aspectRatio = (float)Display.getWidth() / (float)Display.getHeight();
            float nearPlane = 0.1f;
            float farPlane = 100f;
            float yScale = (float)(1.0f / Math.tan((fieldOfView / 2.0f) * Math.PI / 180));//this.coTangent(this.degreesToRadians(fieldOfView / 2f));
            float xScale = yScale / aspectRatio;
            float frustum_length = farPlane - nearPlane;
            mProjection.m00 = xScale;
            mProjection.m11 = yScale;
            mProjection.m22 = -((farPlane + nearPlane) / frustum_length);
            mProjection.m23 = -1;
            mProjection.m32 = -((2 * nearPlane * farPlane) / frustum_length);
            mProjection.m33 = 0;
            mView = new Matrix4f();
            mView.m23 = 500;
            mModel = new Matrix4f();
            mProjectionID = glGetUniformLocation(shaderProgramID, "mProjection");
            mViewID = glGetUniformLocation(shaderProgramID, "mView");
            mModelID = glGetUniformLocation(shaderProgramID, "mModel");
            glEnable(GL_CULL_FACE);
            glCullFace(GL_BACK);
            glEnable(GL_DEPTH_TEST);
        } catch (FileNotFoundException ex) {
            Logger.getLogger(MainDisplay.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(MainDisplay.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    public void run() {
        while (!Display.isCloseRequested()) {
            if (Display.isVisible()) {
                render();
            }
            if (Display.wasResized()) {
                reshape();
            }
            Display.update();
            Display.sync(60);
        }
        destroy();
    }
    
    public void render() {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        FloatBuffer fb1 = BufferUtils.createFloatBuffer(16);
        FloatBuffer fb2 = BufferUtils.createFloatBuffer(16);
        FloatBuffer fb3 = BufferUtils.createFloatBuffer(16);
        mProjection.store(fb1);
        mView.store(fb2);
        mModel.store(fb3);
        fb1.flip();
        fb2.flip();
        fb3.flip();
        glUniformMatrix4(mProjectionID, false, fb1);
        glUniformMatrix4(mViewID, false, fb2);
        glUniformMatrix4(mModelID, false, fb3);
        glVertexAttribPointer (vPositionID, 3, GL_FLOAT, false, 0, 0);
        glDrawArrays(GL_TRIANGLES, 0, bunny.getFaces().length * 3);
    }
    
    public void reshape() {
        glViewport(0, 0, Display.getWidth(), Display.getHeight());
    }
    
    public void dispose() {
        glDeleteProgram(shaderProgramID);
        glUseProgram(0);
        glDeleteBuffers(vboID);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }
    
    public void destroy() {
        Display.destroy();
    }
}

I pasted only classes that I’ve changed

One thing to keep in mind is that the indices are 1-based, as opposed to 0-based arrays/lists in java. So you have to subtract 1 from the value you read from the file to get the correct index.
(The first vertex stored in your array is at index 0 but is referenced by a 1.

I would generally put the vertex lookup logic into the loader class. So your Face class stores the vertex data directly. That way you can debug more easily if your vertices are assembled correctly.
It might be just the being off by 1 that already fixes your problem, though.

So what do you say should I just make the Model class hold an array of faces that stores directly the normals and vertices values instead of their indices and remove the normals and vertices arrays from the Model class ?
Also thanks for spotting that bug, unfortunately the output is still a black screen… what am I missing here?

Yeah, that’s more or less the way I did it. It’s not necessary, but I found that you can then use it for rendering more easily.

Unfortunately, I’m lacking the experience with modern OpenGL code to spot any errors in the rendering part, so I can’t help you with the black screen. But first, try to validate if the Model you loaded contains valid vertex data to see if the error is still in the loader or in the rendering.
So after loading the model, maybe dump out the first 100 faces and their position/normal data and see if it makes any sense (or use the debugger of your IDE to inspect the arrays).

EDIT :
Changed OBJLoader, Model, Face and MainDisplay classes, printed the vertices of the 10 first faces of the model and it seems to be correct still black screen, I assume the problem now is in MainDisplay class but I can’t find it…

MainDisplay class :

package com.dryadengine.gui;

import com.dryadengine.core.Model;
import com.dryadengine.framework.OBJLoader;
import com.dryadengine.framework.ShaderFactory;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.FloatBuffer;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.lwjgl.LWJGLException;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL15.*;
import static org.lwjgl.opengl.GL20.*;
import org.lwjgl.util.vector.Matrix4f;

/**
 *
 * @author Roy
 */
public class MainDisplay {

    private Model bunny;
    private FloatBuffer vbo;
    private Matrix4f mProjection;
    private Matrix4f mView;
    private Matrix4f mModel;
    private int shaderProgramID;
    private int vboID;
    private int vPositionID;
    private int mProjectionID;
    private int mViewID;
    private int mModelID;
    
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        MainDisplay md = new MainDisplay();
        md.create();
        md.init();
        md.run();
    }
    
    public MainDisplay() {
        
    }
    
    public void create() {
        try {
            Display.setDisplayMode(new DisplayMode(800, 600));
            Display.setTitle("Dryad Engine 1.0.0");
            Display.setFullscreen(false);
            Display.setResizable(true);
            Display.create();
        } catch (LWJGLException ex) {
            Logger.getLogger(MainDisplay.class.getName()).log(Level.SEVERE, null, ex);
            System.exit(-1);
        }
    }
    
    public void init() {
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        try {
            shaderProgramID = ShaderFactory.createShaderProgram("vertexShader", "fragmentShader");
            glUseProgram(shaderProgramID);
            bunny = OBJLoader.parseOBJ(new File("src/com/dryadengine/assets/bunny.obj"));
            vbo = BufferUtils.createFloatBuffer(bunny.getFaces().size() * 3 * 3);
            float[] vertexData = new float[bunny.getFaces().size() * 3 * 3];
//            System.out.println("PRINTING 10 first faces info : ");
//            for (int i = 0; i < 10; i++) {
//                System.out.println("FACE #" +(i+1));
//                for (int j = 0; j < 3; j++) {
//                    System.out.println("VERTEX #" +(j + 1));
//                    System.out.println(bunny.getFaces().get(i).getVertices()[j].x + ", " +bunny.getFaces().get(i).getVertices()[j].y+ ", " +bunny.getFaces().get(i).getVertices()[j].z);
//                }
//            }
            int offset = -1;
            for (int i = 0; i < bunny.getFaces().size(); i++) {
                for (int j = 0; j < 3; j++) {
                    offset++;
                    vertexData[offset] = bunny.getFaces().get(i).getVertices()[j].x;
                    offset++;
                    vertexData[offset] = bunny.getFaces().get(i).getVertices()[j].y;
                    offset++;
                    vertexData[offset] = bunny.getFaces().get(i).getVertices()[j].z;
                }
            }
            vbo.put(vertexData);
            vbo.flip();
            vboID = glGenBuffers();
            glBindBuffer(GL_ARRAY_BUFFER, vboID);
            glBufferData(GL_ARRAY_BUFFER, vbo, GL_STATIC_DRAW);
            vPositionID = glGetAttribLocation(shaderProgramID, "vPosition");
            glEnableVertexAttribArray(vPositionID);
            mProjection = new Matrix4f();
            float fieldOfView = 60f;
            float aspectRatio = (float)Display.getWidth() / (float)Display.getHeight();
            float nearPlane = 0.1f;
            float farPlane = 100f;
            float yScale = (float)(1.0f / Math.tan((fieldOfView / 2.0f) * Math.PI / 180));//this.coTangent(this.degreesToRadians(fieldOfView / 2f));
            float xScale = yScale / aspectRatio;
            float frustum_length = farPlane - nearPlane;
            mProjection.m00 = xScale;
            mProjection.m11 = yScale;
            mProjection.m22 = -((farPlane + nearPlane) / frustum_length);
            mProjection.m23 = -1;
            mProjection.m32 = -((2 * nearPlane * farPlane) / frustum_length);
            mProjection.m33 = 0;
            mView = new Matrix4f();
            mView.m23 = -5;
            mModel = new Matrix4f();
            mProjectionID = glGetUniformLocation(shaderProgramID, "mProjection");
            mViewID = glGetUniformLocation(shaderProgramID, "mView");
            mModelID = glGetUniformLocation(shaderProgramID, "mModel");
            glEnable(GL_CULL_FACE);
            glCullFace(GL_BACK);
            glEnable(GL_DEPTH_TEST);
        } catch (FileNotFoundException ex) {
            Logger.getLogger(MainDisplay.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(MainDisplay.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
    
    public void run() {
        while (!Display.isCloseRequested()) {
            if (Display.isVisible()) {
                render();
            }
            if (Display.wasResized()) {
                reshape();
            }
            Display.update();
            Display.sync(60);
        }
        destroy();
    }
    
    public void render() {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        FloatBuffer fb1 = BufferUtils.createFloatBuffer(16);
        FloatBuffer fb2 = BufferUtils.createFloatBuffer(16);
        FloatBuffer fb3 = BufferUtils.createFloatBuffer(16);
        mProjection.store(fb1);
        mView.store(fb2);
        mModel.store(fb3);
        fb1.flip();
        fb2.flip();
        fb3.flip();
        glUniformMatrix4(mProjectionID, false, fb1);
        glUniformMatrix4(mViewID, false, fb2);
        glUniformMatrix4(mModelID, false, fb3);
        glVertexAttribPointer (vPositionID, 3, GL_FLOAT, false, 0, 0);
        glDrawArrays(GL_TRIANGLES, 0, bunny.getFaces().size() * 3);
    }
    
    public void reshape() {
        glViewport(0, 0, Display.getWidth(), Display.getHeight());
    }
    
    public void dispose() {
        glDeleteProgram(shaderProgramID);
        glUseProgram(0);
        glDeleteBuffers(vboID);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }
    
    public void destroy() {
        Display.destroy();
    }
}

OBJLoader class :

package com.dryadengine.framework;

import com.dryadengine.core.Face;
import com.dryadengine.core.Model;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.lwjgl.util.vector.Vector3f;

/**
 *
 * @author Roy
 */
public class OBJLoader {
    
    /**
     * Parse .obj file and make a model from it.
     * @param f
     * @return a model object
     * @throws FileNotFoundException
     * @throws IOException 
     */
    public static Model parseOBJ(File f) throws FileNotFoundException, IOException {
        BufferedReader br = new BufferedReader(new FileReader(f));
        String line;
        Model m;
        List<Vector3f> vertices = new ArrayList<>();
        List<Vector3f> normals = new ArrayList<>();
        List<Face> faces = new ArrayList<>();
        while ((line = br.readLine()) != null) {
            if (line.startsWith("v ")) {
                float x = Float.valueOf(line.split(" ")[1]);
                float y = Float.valueOf(line.split(" ")[2]);
                float z = Float.valueOf(line.split(" ")[3]);
                vertices.add(new Vector3f(x, y, z));
            } else if (line.startsWith("vn ")) {
                float x = Float.valueOf(line.split(" ")[1]);
                float y = Float.valueOf(line.split(" ")[2]);
                float z = Float.valueOf(line.split(" ")[3]);
                normals.add(new Vector3f(x, y, z));
            } else if (line.startsWith("f ")) {
                Vector3f[] a = new Vector3f[3];
                Vector3f[] b = new Vector3f[3];
                a[0] = vertices.get(Integer.valueOf(line.split(" ")[1].split("//")[0]) - 1);
                a[1] = vertices.get(Integer.valueOf(line.split(" ")[2].split("//")[0]) - 1);
                a[2] = vertices.get(Integer.valueOf(line.split(" ")[3].split("//")[0]) - 1);
                b[0] = normals.get(Integer.valueOf(line.split(" ")[1].split("//")[1]) - 1);
                b[1] = normals.get(Integer.valueOf(line.split(" ")[2].split("//")[1]) - 1);
                b[2] = normals.get(Integer.valueOf(line.split(" ")[3].split("//")[1]) - 1);
                faces.add(new Face(a, b));
            }
        }
        br.close();
        m = new Model(faces);
        return m;
    }
    
}

Model class :

package com.dryadengine.core;

import java.util.ArrayList;
import java.util.List;

/**
 *
 * @author Roy
 */
public class Model {
    
    private List<Face> faces;
    
    /**
     * Construct a new model object.
     * @param faces
     */
    public Model(List<Face> faces) {
        this.faces = faces;
    }
    
    /**
     * 
     * @return the model faces array 
     */
    public List<Face> getFaces() {
        return faces;
    }
    
}

Face class :

package com.dryadengine.core;

import org.lwjgl.util.vector.Vector3f;

/**
 *
 * @author Roy
 */
public class Face {
    
    private Vector3f[] vertices;
    private Vector3f[] normals;
    
    public Face() {
        vertices = new Vector3f[3];
        normals = new Vector3f[3];
    }
    
    public Face(Vector3f[] vertices, Vector3f[] normals) {
        this();
        this.vertices = vertices;
        this.normals = normals;
    }
    
    /**
     * 
     * @return the face vertices
     */
    public Vector3f[] getVertices() {
        return vertices;
    }
    
    /**
     * 
     * @return the face normals
     */
    public Vector3f[] getNormals() {
        return normals;
    }
    
    /**
     * Sets the vertices
     * @param vertices 
     */
    public void setVertices(Vector3f[] vertices) {
        this.vertices = vertices;
    }
    
    /**
     * Sets the normals
     * @param normals 
     */
    public void setNormals(Vector3f[] normals) {
        this.normals = normals;
    }
    
}

BUMP, anyone could help me solve my problem ???

Have you tried to “reconstruct” the code in OpenGL 1.1 Immidiate Mode.
If it works then, there might be a problem with your shaders/shaderfactory(or perhaps you projection matrix, etc?).
I have not worked to much with any of the versions above OpenGL 1.1, so I can’t help to much on that part :frowning:

But if you have a Source package I can take a look at, I can try to debug your code for you :slight_smile:

EDIT:
I have made a simple engine EzEngine.
You can take a look at that and see if you can use some in your own engine/library :slight_smile:
It’s primarily for my own personal use, but i upload all the code changes when they happen.

Thanks for the source of your engine I’ll sure look on how you constructed things, my problem isn’t in my shaders and matrices I’ve tried to pass only the vertices without multipling them by the matrices and the only thing I saw was a straight line so I suspect its not a shader / matrix problem its something about the copying / reading from the VBO I suspect / maybe something with glDrawArrays(…) method ? I’m not sure couldn’t find out yet whats the problem ???

Just a thought, but can you move your model or move your view/camera around? You might be inside the model if it is the bunny.obj. try rendering in wire mesh mode.

glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );

I find it is easy to use a cube model first so you can make sure all the faces are the right way.
Also if you don’t multiply by the modelviewprojection matrix ect the model will not be in the right space…

still cant see even with wire frame mode and I mutliply the position by the vertices and still nothing