PDA

View Full Version : Custom 3D object class not working



syntax_error
06-20-2016, 06:36 AM
I have been learning OpenGL for a while now, and I decided to try to make my own "game engine".
My first step is to create a class for a 3D object.
The constructor assigns its arguments to class variables (for example, the object's position).
There are also three other functions: void init() - initializes OpenGL stuff, void update() - updates the model, view and projection matrices, void draw() - draws the object.
But it doesn't work. The program doesn't draw anything and I can't find the error.
Code (it doesn't let me post links):

main.cpp

#define GLEW_STATIC
#define GLM_FORCE_RADIANS
#include <glew.h>
#include <GLFW\glfw3.h>
#include <glm\glm.hpp>
#include <glm\gtc\matrix_transform.hpp>
#include <glm\gtc\type_ptr.hpp>
#include <SOIL.h>

#include <iostream>
#include <fstream>
#include <string>
#include <chrono>

#include "shaders.h"
#include "object.h"

using namespace std;

float vertices [] =
{
// cube
-0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,

-0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,

-0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,

0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,

-0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,

-0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f,
0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f
};

int main()
{
glfwInit();

glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

GLFWwindow* window = glfwCreateWindow(1024, 768, "opengl tutorial", nullptr, nullptr);

if (window == nullptr)
{
cout << "Failed to create window" << endl;
glfwTerminate();
system("pause");
return -1;
}

glfwMakeContextCurrent(window);

cout << "Address of window: " << window << endl;

glewExperimental = GL_TRUE;
glewInit();

glEnable(GL_DEPTH_TEST);
glEnable(GL_MULTISAMPLE);

GLuint vs = glCreateShader(GL_VERTEX_SHADER);
string vsStr = loadShader("vertex.glsl");
const char* vsSource = vsStr.c_str();
glShaderSource(vs, 1, &vsSource, nullptr);
glCompileShader(vs);

char vsLog[512];
glGetShaderInfoLog(vs, 512, nullptr, vsLog);
cout << "Vertex shader output: " << endl;
cout << vsLog << endl;

GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
string fsStr = loadShader("fragment.glsl");
const char* fsSource = fsStr.c_str();
glShaderSource(fs, 1, &fsSource, nullptr);
glCompileShader(fs);

char fsLog[512];
glGetShaderInfoLog(fs, 512, nullptr, fsLog);
cout << "Fragment shader output: " << endl;
cout << fsLog << endl;

GLuint prog = glCreateProgram();
glAttachShader(prog, vs);
glAttachShader(prog, fs);
glLinkProgram(prog);

float rot = 0.0f;

glm::mat4 model;
model = glm::mat4(1.0f) * glm::rotate(model, glm::radians(rot), glm::vec3(0.0f, 1.0f, 0.0f));

glm::mat4 view;
view = glm::lookAt(glm::vec3(0.0f, 1.2f, 3.5f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));

glm::mat4 proj;
proj = glm::perspective(glm::radians(45.0f), 1024.0f / 768.0f, 1.0f, 10.0f);

object cube(vertices, 36, "box-texture.png", prog, glm::vec3(0.0f, 0.0f, 0.0f), view, proj);
cube.init();

while (!glfwWindowShouldClose(window))
{
if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);

int currentL = glfwGetKey(window, GLFW_KEY_LEFT);
int currentR = glfwGetKey(window, GLFW_KEY_RIGHT);
if (currentL == GLFW_PRESS)
{
rot += 1.0f;
}
else if (currentR == GLFW_PRESS)
{
rot -= 1.0f;
}

glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

cube.update();
cube.draw();

glfwSwapBuffers(window);
glfwPollEvents();
}

glDeleteShader(vs);
glDeleteShader(fs);

glfwTerminate();

return 0;
}

object.h

#pragma once

#include <glew.h>
#include <GLFW\glfw3.h>
#include <glm\gtc\matrix_transform.hpp>
#include <glm\gtc\type_ptr.hpp>
#include <SOIL.h>

#include "shaders.h"
#include <iostream>

using namespace std;

// 3D object
class object
{
public:
GLuint vbo;
GLuint vao;
GLuint texture;
GLuint prog; // shader program to use
glm::vec3 position;
float* vertices;
int count; // number of vertices
const char* texName; // texture file name
glm::mat4 model; //
glm::mat4 view; // matrices
glm::mat4 proj; //
GLint posAttrib; //
GLint colAttrib; // attributes
GLint texAttrib; //
GLint uniModel; //
GLint uniView; // uniforms
GLint uniProj; //

object(float* _vertices, int _count, const char* _texName, GLuint _prog, glm::vec3 _pos, glm::mat4 _view, glm::mat4 _proj)
{
vertices = _vertices;
prog = _prog;
position = _pos;
count = _count;
texName = _texName;
model = glm::mat4(1.0f) * glm::translate(model, position);
view = _view;
proj = _proj;
}

void init()
{
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

glUseProgram(prog);

posAttrib = glGetAttribLocation(prog, "pos"); // position
glEnableVertexAttribArray(posAttrib);
glVertexAttribPointer(posAttrib, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), nullptr);

colAttrib = glGetAttribLocation(prog, "vColor"); // color
glEnableVertexAttribArray(colAttrib);
glVertexAttribPointer(colAttrib, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*) (3 * sizeof(float)));

texAttrib = glGetAttribLocation(prog, "tex_coord"); // tex coords
glEnableVertexAttribArray(texAttrib);
glVertexAttribPointer(texAttrib, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*) (6 * sizeof(float)));

uniModel = glGetUniformLocation(prog, "model");
uniView = glGetUniformLocation(prog, "view");
uniProj = glGetUniformLocation(prog, "proj");

int w, h;
unsigned char* texData = SOIL_load_image(texName, &w, &h, nullptr, SOIL_LOAD_RGB);
if (texData == nullptr)
{
cout << "The texture is nullptr" << endl;
system("pause");
}
else
{
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, texData);
SOIL_free_image_data(texData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);

glUniformMatrix4fv(uniModel, 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(uniView, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(uniProj, 1, GL_FALSE, glm::value_ptr(proj));
}
}

void update()
{
glUseProgram(prog);
model = glm::mat4(1.0f) * glm::translate(model, position);

glUniformMatrix4fv(uniModel, 1, GL_FALSE, glm::value_ptr(model));
glUniformMatrix4fv(uniView, 1, GL_FALSE, glm::value_ptr(view));
glUniformMatrix4fv(uniProj, 1, GL_FALSE, glm::value_ptr(proj));
}

void draw()
{
// binding stuff before drawing (in case more objects are drawn)
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBindTexture(GL_TEXTURE_2D, texture);

glDrawArrays(GL_TRIANGLES, 0, count);
}

~object()
{
glDeleteBuffers(1, &vbo);
glDeleteVertexArrays(1, &vao);
glDeleteTextures(1, &texture);
}
};


You can notice that I'm loading shaders with my own function from another file, but that has nothing to do with this.

Thanks

john_connor
06-20-2016, 08:56 AM
i just want to point out, that using for each object a new vertex array / buffer isnt the "best" solution
you should decouple models (contains much data) from actual objects (only instances with a model matrix and further object infos)

why dont you start with a simple triangle tutorial and if you got it working, expand your code with additional features like texture / normals / light etc

you didnt check for the shaders link status / errors, what does glGetError() say ?

syntax_error
06-20-2016, 10:39 AM
Thanks for your quick answer.


i just want to point out, that using for each object a new vertex array / buffer isnt the "best" solution
you should decouple models (contains much data) from actual objects (only instances with a model matrix and further object infos)
I removed it and put the VAO back in main().


why dont you start with a simple triangle tutorial and if you got it working, expand your code with additional features like texture / normals / light etc
Now I removed the texture part and just tried to render a triangle with red, green and blue vertices (I changed the offsets in glVertexAttribPointer accordingly).


you didnt check for the shaders link status / errors, what does glGetError() say ?
Shaders don't give any errors, but glGetError() returns GL_INVALID_ENUM. I guess that it means that some function doesn't like a GLenum type argument?

Cornix
06-20-2016, 10:43 AM
glGetError() returns GL_INVALID_ENUM. I guess that it means that some function doesn't like a GLenum type argument?

Yes exactly. Some argument which you pass to some method is wrong. To find out which it is you have to call glGetError() after every opengl call. When you found out which method produces the error you can look up in the reference pages what INVALID_ENUM means for that particular opengl method.

syntax_error
06-20-2016, 11:07 AM
Now I have no idea what is going on.
I made a function for processing the glGetError error:


void error(GLenum e)
{
switch (e)
{
case GL_INVALID_ENUM:
cout << "Error: GL_INVALID_ENUM" << endl;
system("pause");
glfwTerminate();
break;
case GL_INVALID_VALUE:
cout << "Error: GL_INVALID_VALUE" << endl;
system("pause");
glfwTerminate();
break;
case GL_INVALID_OPERATION:
cout << "Error: GL_INVALID_OPERATION" << endl;
system("pause");
glfwTerminate();
break;
case GL_INVALID_FRAMEBUFFER_OPERATION:
cout << "Error: GL_INVALID_FRAMEBUFFER_OPERATION" << endl;
system("pause");
glfwTerminate();
break;
case GL_OUT_OF_MEMORY:
cout << "Error: GL_OUT_OF_MEMORY" << endl;
system("pause");
glfwTerminate();
break;
case GL_NO_ERROR:
cout << "No error reported" << endl;
break;
}
}


And I called it after the first four or five OpenGL function calls.
Here's the strange part.
I called it first after my first OpenGL call (that is glEnable(GL_DEPTH_TEST)). It returns GL_INVALID_ENUM. But the other calls all return GL_INVALID_OPERATION.
Then I comment out the first glGetError call so the second one becomes the first. Now that one returns GL_INVALID_ENUM and all others return GL_INVALID_OPERATION.
If I comment out the second so the third call is the first, the same happens.
What is going on?!

Cornix
06-20-2016, 11:24 AM
Errors are buffered. The INVALID_ENUM is generated at some point in your program and kept until you call glGetError() for the first time. It doesnt matter if you call it immediately after the call that generated the error or at some later point in time. Its all explained within the reference pages: https://www.opengl.org/sdk/docs/man/html/glGetError.xhtml

john_connor
06-20-2016, 01:02 PM
just tested that code: i get a access violation if that part remains in main()


//glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
//glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
//glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
//glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
//glfwWindowHint(GLFW_SAMPLES, 4);
//glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

if i comment that part out, it works

another thing i want to point out:
its one thing to get a VAO working aand pointers correct, its another to develop a complete game engine :)


glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
std::cout << "just a test for the array size: " << sizeof(vertices) << std::endl;

that little test says: you are just filling 8 bytes (= size of a x64 pointer) into your buffer
i would recommend to use c++ containers such as <vector> and pass it to that "object" by &reference

to make a simple model renderer shouldnt be that difficult, te question is only: how good it'll be
http://www.cplusplus.com/articles/28hv0pDG/
http://www.cplusplus.com/articles/1w6AC542/

syntax_error
06-21-2016, 03:21 AM
Yep, the fact that I'm using a float pointer is a problem.
I just tried to define the vertices in a float array as a global variable and it works.
Now the problem is that OpenGL doesn't like vectors.
Apparently there is a trick to convert a vector to an array:


float* arr = &vec[0];

But I still get a pointer so it doesn't work.
I tried to do this:


int count; // this is a class-level variable, number of vertices
....
float v[8 * count]; // 3 coords, 3 colors, 2 texcoords; ERROR: expression did not evaluate to a constant
for (int i = 0; i < (8 * count); i++)
{
v[i] = vec[i];
}


But it doesn't work.
I also tried to define 8 * count as a const int variable, but although that variable is const, it still won't let me use it as array size.
I'm out of ideas.

syntax_error
06-23-2016, 03:39 AM
I solved it. I just had to put sizeof(vertices) * count * 8 as size argument for glBufferData, as there are count vertices and each vertex has 3 coords, 3 color components and 2 texcoords (3+3+2=8)