I've been working on my first opengl, glfw, and glm project (in cmake) while following tutorials online, and I wanted to start organizing my code into specific classes for performing certain tasks (like a model class for organizing shaders and verticies); however, once I organized my code, I ran into two problems.
First, after I finally got rid of all syntax errors in my code, I came across the compilation error 'class' type redefinition
twice for two separate classes (one being a static class). I have #pragma once
at the very top of my only header file (zachos.h), so I have no idea what the issue is here. Why does my code (see below) throw this error? The exact error messages as listed are:
zachos.cpp(94): error C2011: 'zachos::Model': 'class' type redefinition
zachos.h(53): note: see declaration of 'zachos::Model'
zachos.cpp(173): error C2011: 'zachos::Mainframe': 'class' type redefinition
zachos.h(84): note: see declaration of 'zachos::Mainframe'
Second, I needed to initialize an static member object from inside a static function of that class. I defined the object within my header file (zachos.h) using static Model mModel;
, then redefined it inside of my code file (zachos.cpp), and finally I created the class inside the static function using a copy operator Model model(gVertexBufferData, gColorBufferData); mModel = model;
. I have serious doubts about this working (since the copy operator only does a shallow copy), and I don't know how to use pointers to define mModel within the function (I believe I have to use delete
in some manner). Is there a problem with this form of object initialization, and is there any better way to do it?
Code (all files are in the same directory and referenced by CMake, I can provide the CMakeLists.txt file too if needed):
zachos.h:
#pragma once
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <sstream>
#include <iostream>
#include <fstream>
#include <cstdlib>
#include <ctime>
/*
Namespace that contains all basic codestuff for zachos.
*/
namespace zachos {
// Constant data
static const std::string DATA_DEF("#version 330 core\nlayout(location = 0) in vec3 vertexPosition_modelspace;\nlayout(location = 1) in vec3 vertexColor;\nout vec3 fragmentColor;\n\nuniform mat4 MVP;\n\nvoid main()\n{\n\tgl_Position = MVP * vec4(vertexPosition_modelspace, 1);\n\t\n\tfragmentColor = vertexColor;\n}\n\n#version 330 core\nin vec3 fragmentColor;\nout vec3 color;\n\nvoid main()\n{\n\tcolor = fragmentColor;\n}");
std::vector<GLfloat> gVertexBufferData = {
-1.0f, -1.0f, -1.0f, // triangle 1 : begin
-1.0f, -1.0f, 1.0f,
-1.0f, 1.0f, 1.0f, // triangle 1 : end
1.0f, 1.0f, -1.0f, // triangle 2 : begin
-1.0f, -1.0f, -1.0f,
-1.0f, 1.0f, -1.0f, // triangle 2 : end
1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, -1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, 1.0f,
-1.0f, -1.0f, -1.0f,
-1.0f, 1.0f, 1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, -1.0f, -1.0f,
1.0f, 1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
1.0f, 1.0f, 1.0f,
1.0f, -1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, -1.0f,
1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, -1.0f,
-1.0f, 1.0f, 1.0f,
1.0f, 1.0f, 1.0f,
-1.0f, 1.0f, 1.0f,
1.0f, -1.0f, 1.0f
};
std::vector<GLfloat> gColorBufferData(12 * 3 * 3, 0);
/*
Enum of all possible error codes for the program to return on its own
*/
enum ErrorCodes {
SUCCESS,
GLFW_INIT_FAIL,
GLFW_WINDOW_CREATION_FAIL,
GLAD_INIT_FAIL,
WINDOW_FAIL
};
/*
Error callback function for glfw. Nothing special.
*/
void errorCallback(int error, const char* description);
/*
Load shaders function for convenience. Used especially by the Model class.
*/
GLuint loadShaders(std::string shaders);
/*
Class for creating models; it holds a VBO, CBO, and programID.
*/
class Model {
public:
std::vector<GLfloat> mVertexBufferData;
std::vector<GLfloat> mColorBufferData;
GLuint VBO;
GLuint CBO;
GLuint programID;
glm::mat4 model;
/*
Default Constructor. Must contain all vertex and color data from the get go.
*/
Model(std::vector<GLfloat> vertexBufferData, std::vector<GLfloat> colorBufferData);
/*
Model update() function, called every "tick"
*/
virtual void update();
/*
Model draw() function, called to draw the model
*/
virtual void draw();
};
/*
Wrapper class of the main window for handling events and perspective.
*/
class Mainframe {
public:
static GLFWwindow* window;
static glm::mat4 projection;
static glm::mat4 view;
static Model mModel;
static int i;
// Event functions
static void framebufferSizeCallback(GLFWwindow* window, int width, int height);
static void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods);
/*
Class initialization function.
*/
static int init();
/*
Frame by frame drawing function.
*/
static int draw();
/*
Get whether the window should close or not
*/
static bool shouldClose();
};
}
zachos.cpp:
#include "zachos.h"
namespace zachos {
void errorCallback(int error, const char* description) {
fprintf(stderr, "GLFW error %d: %s\n", error, description);
}
GLuint loadShaders(std::string shaders) {
// Create the shaders
GLuint vertexShaderID = glCreateShader(GL_VERTEX_SHADER);
GLuint fragmentShaderID = glCreateShader(GL_FRAGMENT_SHADER);
// Read the shader code
std::string shaderCode;
std::string vertexShaderCode;
std::string fragmentShaderCode;
//std::ifstream shaderStream(file_path, std::ios::in);
if (/*shaderStream.is_open()*/ true) {
//std::stringstream sstr;
//sstr << shaderStream.rdbuf();
//shaderCode = sstr.str();
shaderCode = shaders;
//shaderStream.close();
size_t val = shaderCode.find("#version", 8);
vertexShaderCode = shaderCode.substr(0, val);
fragmentShaderCode = shaderCode.substr(val);
}
else {
//printf("Impossible to open \"%s\". Are you in the right directory? Don't forget to read the FAQ!\n", file_path);
getchar();
return 0;
}
GLint result = GL_FALSE;
int infoLogLength;
// Compile Vertex Shader
char const * vertexSourcePointer = vertexShaderCode.c_str();
glShaderSource(vertexShaderID, 1, &vertexSourcePointer, nullptr);
glCompileShader(vertexShaderID);
// Check Vertex Shader
glGetShaderiv(vertexShaderID, GL_COMPILE_STATUS, &result);
glGetShaderiv(vertexShaderID, GL_INFO_LOG_LENGTH, &infoLogLength);
if (infoLogLength > 0) {
std::vector<char> vertexShaderErrorMessage(infoLogLength + 1);
glGetShaderInfoLog(vertexShaderID, infoLogLength, nullptr, &vertexShaderErrorMessage[0]);
printf("%s\n", &vertexShaderErrorMessage[0]);
}
// Compile Fragment Shader
char const * fragmentSourcePointer = fragmentShaderCode.c_str();
glShaderSource(fragmentShaderID, 1, &fragmentSourcePointer, nullptr);
glCompileShader(fragmentShaderID);
// Check Fragment Shader
glGetShaderiv(fragmentShaderID, GL_COMPILE_STATUS, &result);
glGetShaderiv(fragmentShaderID, GL_INFO_LOG_LENGTH, &infoLogLength);
if (infoLogLength > 0) {
std::vector<char> fragmentShaderErrorMessage(infoLogLength + 1);
glGetShaderInfoLog(fragmentShaderID, infoLogLength, nullptr, &fragmentShaderErrorMessage[0]);
printf("%s\n", &fragmentShaderErrorMessage[0]);
}
// Link the program
printf("Linking program\n");
GLuint programID = glCreateProgram();
glAttachShader(programID, vertexShaderID);
glAttachShader(programID, fragmentShaderID);
glLinkProgram(programID);
// Check the program
glGetProgramiv(programID, GL_LINK_STATUS, &result);
glGetProgramiv(programID, GL_INFO_LOG_LENGTH, &infoLogLength);
if (infoLogLength > 0) {
std::vector<char> programErrorMessage(infoLogLength + 1);
glGetProgramInfoLog(programID, infoLogLength, nullptr, &programErrorMessage[0]);
printf("%s\n", &programErrorMessage[0]);
}
glDetachShader(programID, vertexShaderID);
glDetachShader(programID, fragmentShaderID);
glDeleteShader(vertexShaderID);
glDeleteShader(fragmentShaderID);
return programID;
}
class Model {
public:
std::vector<GLfloat> mVertexBufferData;
std::vector<GLfloat> mColorBufferData;
GLuint VBO;
GLuint CBO;
GLuint programID;
glm::mat4 model = glm::mat4(1.0f);
Model(std::vector<GLfloat> vertexBufferData, std::vector<GLfloat> colorBufferData) {
mVertexBufferData = vertexBufferData;
mColorBufferData = colorBufferData;
// Generate 1 buffer, put the resulting identifier in vertexbuffer
glGenBuffers(1, &VBO);
// The following commands will talk about our 'vertexbuffer' buffer
glBindBuffer(GL_ARRAY_BUFFER, VBO);
// Give our vertices to OpenGL.
glBufferData(GL_ARRAY_BUFFER, sizeof(mVertexBufferData), &mVertexBufferData.front(), GL_STATIC_DRAW);
glGenBuffers(1, &CBO);
glBindBuffer(GL_ARRAY_BUFFER, CBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(mColorBufferData), &mColorBufferData.front(), GL_STATIC_DRAW);
// Create and compile our GLSL program from the shaders
GLuint programID = loadShaders(zachos::DATA_DEF);
glUseProgram(programID);
}
virtual void update() {
for (int v = 0; v < 12 * 3; v++) {
mColorBufferData[3 * v + 0] = (float)std::rand() / RAND_MAX;
mColorBufferData[3 * v + 1] = (float)std::rand() / RAND_MAX;
mColorBufferData[3 * v + 2] = (float)std::rand() / RAND_MAX;
}
glBufferData(GL_ARRAY_BUFFER, sizeof(mColorBufferData), &mColorBufferData.front(), GL_STATIC_DRAW);
}
virtual void draw() {
glEnableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glVertexAttribPointer(
0, // attribute 0. No particular reason for 0, but must match the layout in the shader.
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, CBO);
glVertexAttribPointer(
1, // attribute. No particular reason for 1, but must match the layout in the shader.
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void*)0 // array buffer offset
);
// Setup some 3D stuff
glm::mat4 mvp = Mainframe::projection * Mainframe::view * model;
GLuint MatrixID = glGetUniformLocation(programID, "MVP");
glUniformMatrix4fv(MatrixID, 1, GL_FALSE, &mvp[0][0]);
// Draw the array
glDrawArrays(GL_TRIANGLES, 0, mVertexBufferData.size() / 3);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
}
};
class Mainframe {
public:
static GLFWwindow* window;
static glm::mat4 projection;
static glm::mat4 view;
static Model mModel;
static int i;
static void framebufferSizeCallback(GLFWwindow* window, int width, int height) {
glViewport(0, 0, width, height);
glm::perspective(glm::radians(45.0f), (float)width / (float)height, 0.1f, 100.0f);
}
static void keyCallback(GLFWwindow* window, int key, int scancode, int action, int mods) {
switch (key) {
case GLFW_KEY_ESCAPE:
break;
}
}
static int init() {
// Initialize GLFW
int glfwInitRes = glfwInit();
if (!glfwInitRes) {
fprintf(stderr, "Unable to initialize GLFW\n");
return GLFW_INIT_FAIL;
}
// Set GLFW flags. Not exactly sure if these are needed.
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
// Attempt to create the main window.
window = glfwCreateWindow(1280, 720, "InitGL", nullptr, nullptr);
if (!window) {
fprintf(stderr, "Unable to create GLFW window\n");
glfwTerminate();
return GLFW_WINDOW_CREATION_FAIL;
}
glfwMakeContextCurrent(window);
// Initialize GLAD and OpenGL
int gladInitRes = gladLoadGL();
if (!gladInitRes) {
fprintf(stderr, "Unable to initialize glad\n");
glfwTerminate();
return GLAD_INIT_FAIL;
}
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
glfwSetFramebufferSizeCallback(window, framebufferSizeCallback);
glfwSetKeyCallback(window, keyCallback);
// Create da CUBANGLÉ
GLuint VertexArrayID;
glGenVertexArrays(1, &VertexArrayID);
glBindVertexArray(VertexArrayID);
for (int v = 0; v < 12 * 3; v++) {
gColorBufferData[3 * v + 0] = (float)std::rand() / RAND_MAX;
gColorBufferData[3 * v + 1] = (float)std::rand() / RAND_MAX;
gColorBufferData[3 * v + 2] = (float)std::rand() / RAND_MAX;
}
Model model(gVertexBufferData, gColorBufferData);
mModel = model;
// Set the clear color
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
projection = glm::perspective(glm::radians(45.0f), (float)1280 / (float)720, 0.1f, 100.0f);
view = glm::lookAt(
glm::vec3(4, 3, 3), // Camera is at (4,3,3), in World Space
glm::vec3(0, 0, 0), // and looks at the origin
glm::vec3(0, 1, 0) // Head is up (set to 0,-1,0 to look upside-down)
);
i = 60;
return SUCCESS;
}
static int draw() {
if (i <= 0) {
view = glm::lookAt(
glm::vec3(3 + 2 * (float)std::rand() / RAND_MAX, 3 + 2 * (float)std::rand() / RAND_MAX, 3 + 2 * (float)std::rand() / RAND_MAX), // Camera is at (), in World Space
glm::vec3(0, 0, 0), // and looks at the origin
glm::vec3(0, 1, 0) // Head is up (set to 0,-1,0 to look upside-down)
);
mModel.update();
i = 60;
}
i--;
mModel.draw();
glfwSwapBuffers(window);
}
static bool shouldClose() {
return glfwWindowShouldClose(window);
}
};
}
main.cpp:
#include "zachos.h"
/*
I'm pretty sure you know what this function is.
*/
int main(int argc, char* argv[]) {
// Seed the random number generator
std::srand(static_cast<unsigned int>(std::time(nullptr)));
// Implement Mainframe for shorthand
using zachos::Mainframe;
// set GLFW error callback function
glfwSetErrorCallback(zachos::errorCallback);
// Run the window initiazation function
int value = Mainframe::init();
if (!value) {
// Return if failed
return value;
}
// Main loop
while (!Mainframe::shouldClose()) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glfwPollEvents();
// Actually does the hard work
Mainframe::draw();
}
glfwTerminate();
return zachos::SUCCESS;
}
I am also aware of other possible issues present in this code. You can mention them as well if you wish, but my main question revolves around these two main issues.
You appear to declare the classes 'Model' and 'Mainframe' twice, once in zachos.h and once in zachos.cpp. You should declare the class in the header with
class Model {
public:
virtual void update();
...
}
And then define the class in the cpp with
void Model::update() {
...
}
The header tells the compiler the structure and contents of the class, and the cpp provides what the functions actually do.
For your second point, I'm not sure what you're trying the achieve, so don't know if there's a better way to do it.