EDIT* I rearranged the initialization list, as suggested by much_a_chos, so that the Window object initializes before the Game object, ensuring that glew is initialized first. However, this did not work:
//Rearranged initialization list
class TempCore
{
public:
TempCore(Game* g) :
win(new Window(800, 800, "EngineTry", false)), gamew(g) {}
~TempCore() { if(gamew) delete gamew; }
...
};
And here is the code I changed in the Mesh constructor when the above didn't work:
Mesh::Mesh( Vertex* vertices, unsigned int numVerts )
{
m_drawCount = numVerts;
glewExperimental = GL_TRUE;
if(glewInit() != GLEW_OK){
exit(-150); //application stops and exits here with the code -150
}
glGenVertexArrays(1, &m_vertexArrayObject);
glBindVertexArray(m_vertexArrayObject);
...
}
What happens when I compile and run is surprising. The program exits at the if(glewInit() != GLEW_OK)
I copied from the Window constructor. For some reason glew initializes properly in the Window constructor (which is called before the Game constructor), but it fails to initialize when called the second time in the Mesh constructor. I assume its bad practice to call on glewInit()
more than once in a program, but I don't think it should fail if I actually did so. Does anybody know what might be happening? Am I making a mistake in calling glewInit()
more than once?
*END OF EDIT
I've been following a 3D Game Engine Development tutorial and I've encountered a weird bug in my code, which I will demonstrate below. I'm attempting to make my own game-engine purely for educational reasons. I'm using Code-blocks 13.12 as my IDE and mingw-w64 v4.0 as my compiler. I'm also using SDL2, glew, Assimp and boost as my third-party libraries.
I apologize in advance for the numerous code extracts, but I put in what I thought what was necessary to understand the context of the error.
I have a Core
class for my game-engine that holds the main loop and updates and renders accordingly, calling the Game
class update()
and render()
methods in the process as well. The Game class is intended as the holder for all the assets in the game, and will be the base class for any games made using the engine, thus, it contains mesh, texture and camera references. The Game class update()
, render()
and input()
methods are all virtual as the Game
class is meant to be derived.
My problem is: when I initialize the Game member variable in the Core
class, I get a SIGSEGV (i.e. segmentation fault) in the Mesh
object's constructor at the glGenVertexArrays
call.
However, when I move my Game
object out of the Core
class and straight into the main method (so I changed it from being a class member to a simple scoped variable in the main method), along with the necessary parts from the Core
class, then its runs perfectly and renders my rudimentary triangle example. This is a bug I've never come across and I would really appreciate any help I can get.
Below is an extract of my morphed code that ran perfectly and rendered the triangle:
int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow)
{
Window win(800, 800, "EngineTry", false); //Creates an SDL implemented window with a GL_context
Game* gamew = new Game;
const double frameTime = 1.0 / 500; //500 = maximum fps
double lastTime = FTime::getTime(); //gets current time in milliseconds
double unprocessedTime = 0.0;
int frames = 0;
double frameCounter = 0;
while(win.isRunning()){
bool _render = false;
double startTime = FTime::getTime();
double passedTime = startTime - lastTime;
lastTime = startTime;
unprocessedTime += passedTime / (double)FTime::SECOND;
frameCounter += passedTime;
while(unprocessedTime > frameTime){
if(!win.isRunning())
exit(0);
_render = true;
unprocessedTime -= frameTime;
FTime::delta = frameTime;
gamew->input();
Input::update();
gamew->update();
if(frameCounter >= FTime::SECOND)
{
std::cout << "FPS: " << frames << std::endl;
frames = 0;
frameCounter = 0;
}
}
if(_render){
RenderUtil::clearScreen(); //simple wrapper to the glClear function
gamew->render();
win.Update();
frames++;
}else{
Sleep(1);
}
}
delete gamew;
return 0;
}
Here is an extract of my modified Core
class that doesn't work (throws the sigsegv in the Mesh
constructor)
class TempCore
{
public:
TempCore(Game* g) :
gamew(g), win(800, 800, "EngineTry", false) {}
~TempCore() { if(gamew) delete gamew; }
void start();
private:
Window win;
Game* gamew;
};
int WINAPI WinMain (HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow)
{
TempCore m_core(new Game());
m_core.start();
return 0;
}
void TempCore::start()
{
const double frameTime = 1.0 / 500;
double lastTime = FTime::getTime();
double unprocessedTime = 0.0;
int frames = 0;
double frameCounter = 0;
while(win.isRunning()){
bool _render = false;
double startTime = FTime::getTime();
double passedTime = startTime - lastTime;
lastTime = startTime;
unprocessedTime += passedTime / (double)FTime::SECOND;
frameCounter += passedTime;
while(unprocessedTime > frameTime){
if(!win.isRunning())
exit(0);
_render = true;
unprocessedTime -= frameTime;
FTime::delta = frameTime;
gamew->input();
Input::update();
gamew->update();
if(frameCounter >= FTime::SECOND){
//double totalTime = ((1000.0 * frameCounter)/((double)frames));
//double totalMeasuredTime = 0.0;
std::cout << "Frames: " << frames << std::endl;
//m_frames_per_second = frames;
frames = 0;
frameCounter = 0;
}
}
if(_render){
RenderUtil::clearScreen();
gamew->render();
win.Update();
frames++;
}else{
Sleep(1);
}
}
}
Mesh constructor where the sigsegv occurs in the above TestCore
implementation:
Mesh::Mesh( Vertex* vertices, unsigned int numVerts )
{
m_drawCount = numVerts;
glGenVertexArrays(1, &m_vertexArrayObject); //sigsegv occurs here
glBindVertexArray(m_vertexArrayObject);
std::vector<glm::vec3> positions;
std::vector<glm::vec2> texCoords;
positions.reserve(numVerts);
texCoords.reserve(numVerts);
for(unsigned i = 0; i < numVerts; i++){
positions.push_back(vertices[i].pos);
texCoords.push_back(vertices[i].texCoord);
}
glGenBuffers(NUM_BUFFERS, m_vertexArrayBuffers);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexArrayBuffers[POSITION_VB]);
glBufferData(GL_ARRAY_BUFFER, numVerts*sizeof(positions[0]), &positions[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexArrayBuffers[TEXCOORD_VB]);
glBufferData(GL_ARRAY_BUFFER, numVerts*sizeof(texCoords[0]), &texCoords[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0);
glBindVertexArray(0);
}
The Game
constructor that initializes the Mesh
object:
Vertex vertices[] = { Vertex(-0.5f, -0.5f, 0, 0, 0),
Vertex(0, 0.5f, 0, 0.5f, 1.0f),
Vertex(0.5f, -0.5f, 0, 1.0f, 0)};
//Vertex is basically a struct with a glm::vec3 for position and a glm::vec2 for texture coordinate
Game::Game() :
m_mesh(vertices, sizeof(vertices)/sizeof(vertices[0])),
m_shader("res\\shaders\\basic_shader"),
m_texture("res\\textures\\mist_tree.jpg")
{
}
The Window
class constructor that initializes glew
:
Window::Window(int width, int height, const std::string& title, bool full_screen) :
m_fullscreen(full_screen)
{
SDL_Init(SDL_INIT_EVERYTHING);
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BUFFER_SIZE, 32);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
//SDL_Window* in private of class declaration
m_window = SDL_CreateWindow(title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, width, height, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
//SDL_GLContext in private of class declaration
m_glContext = SDL_GL_CreateContext(m_window);
std::cout << "GL Version: " << glGetString(GL_VERSION) << std::endl;
glewExperimental = GL_TRUE;
if(glewInit() != GLEW_OK || !glVersionAbove(3.0)){
std::cerr << "Glew failed to initialize...\n";
exit(-150);
}
}