/* Simplified OpenGL 4.5 demo * Seth Long, Fall 2020 * Simple demo, C++ version * Note operator overloading for matrix multiplication! * * Linux build: g++ simple_demo.cpp -lglfw -lGLEW -lGL -lGLU */ /* Set this to Windows for Windows */ #define UNIX #include #include #include #ifdef UNIX #include #endif #include // glad on Windows #include #include #include #include #define TINYOBJLOADER_IMPLEMENTATION #include "tiny_obj_loader.h" #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" /* Data Structures */ #include #include #include using namespace std; using namespace glm; const float height = 800; const float width = 1280; bool shutdown = false; void GLAPIENTRY debug_callback(GLenum, GLenum type, GLuint, GLenum severity, GLsizei, const GLchar* message, const void*){ if(severity != GL_DEBUG_SEVERITY_HIGH && severity != GL_DEBUG_SEVERITY_MEDIUM ) return; fprintf( stderr, "GL ERROR MESSAGE: %s type = 0x%x, severity = 0x%x, message = %s\n", ( type == GL_DEBUG_TYPE_ERROR ? "** GL ERROR **" : "" ), type, severity, message ); } /* Remember to free the result! */ char* get_source(const char *filename){ FILE *source_file = fopen(filename, "r"); fseek(source_file, 0, SEEK_END); long length = ftell(source_file); char *source = (char*)malloc(length + 1); rewind(source_file); fread(source, 1, length, source_file); fclose(source_file); source[length] = 0; return source; } class GameObject { public: /* Identifiers for OpenGL resources */ unsigned int v_attrib; unsigned int tc_attrib; unsigned int vp_uniform; unsigned int program; unsigned int vbuf; unsigned int ebuf; unsigned int tex; unsigned int element_count; unsigned int models_buffer; /* Properties not related to OpenGL */ vector models; float spin_rate = 0.01; void add_location(vec3 location); bool init(const char* object_file, const char* texture_file); void draw(mat4 vp); void movement(float ms); // takes the number of milliseconds since the last run }; void GameObject::add_location(vec3 location){ models.push_back(translate(mat4(1.0), location)); } void GameObject::movement(float ms){ for(auto &m : models) m = rotate(m, spin_rate, vec3(0, 1, 0)); } vector game_objects; struct vertex { float x, y, z, u, v; }; bool operator==(const vertex& a, const vertex& o){ return a.x == o.x && a.y == o.y && a.z == o.z && a.u == o.u && a.v == o.v; } namespace std { template<> struct hash { size_t operator()(vertex const& v) const { size_t vals[] = {(size_t)(v.x * 100000), (size_t)(v.y * 10000), (size_t)(v.z * 1000), (size_t)(v.u * 100), (size_t)v.v}; size_t result = vals[0]; for(auto v : vals) result = result ^ v; return result; } }; } bool GameObject::init(const char* object_file, const char* texture_file){ tinyobj::attrib_t attrib; vector shapes; vector materials; string warn, err; if(!tinyobj::LoadObj(&attrib, &shapes, &materials, &err, object_file)){ printf("Loading failed for: %s\n%s\n", object_file, err.c_str()); return false; } vector vertices; vector elements; unordered_map unique_vertices; for(const auto& shape : shapes){ for(const auto& index : shape.mesh.indices){ vertex new_vertex; new_vertex.x = attrib.vertices[3 * index.vertex_index + 0]; // x coordinate new_vertex.z = attrib.vertices[3 * index.vertex_index + 1]; // y coordinate new_vertex.y = attrib.vertices[3 * index.vertex_index + 2]; // z coordinate new_vertex.u = attrib.texcoords[2 * index.texcoord_index + 0]; // u coordinate new_vertex.v = 1.0f - attrib.texcoords[2 * index.texcoord_index + 1]; // v coordinate if(unique_vertices.count(new_vertex) == 0){ unique_vertices[new_vertex] = (unsigned int)vertices.size(); vertices.push_back(new_vertex); } elements.push_back(unique_vertices[new_vertex]); } } element_count = elements.size(); printf("Element count: %lu\n", elements.size()); // Format: xyzuv glGenBuffers(1, &vbuf); glBindBuffer(GL_SHADER_STORAGE_BUFFER, vbuf); glBufferData(GL_SHADER_STORAGE_BUFFER, 20 * vertices.size(), vertices.data(), GL_STATIC_DRAW); glGenBuffers(1, &ebuf); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebuf); glBufferData(GL_ELEMENT_ARRAY_BUFFER, 4 * elements.size(), elements.data(), GL_STATIC_DRAW); /* Setup texture here */ int img_width, img_height; unsigned char* image = stbi_load(texture_file, &img_width, &img_height, 0, STBI_rgb); if(!image){ printf("image failed to load: %s\n", texture_file); return false; } else { printf("Image size is %d by %d\n", img_width, img_height); } glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, img_width, img_height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); free(image); glGenBuffers(1, &models_buffer); char *source = get_source("vertex_shader.glsl"); puts(source); unsigned int vs_reference = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vs_reference, 1, (const char**)&source, 0); glCompileShader(vs_reference); glGetShaderInfoLog(vs_reference, 2048, NULL, source); puts(source); free(source); int compile_ok; glGetShaderiv(vs_reference, GL_COMPILE_STATUS, &compile_ok); if(!compile_ok){ printf("Vertex shader failed to compile\n"); return false; } source = get_source("fragment_shader.glsl"); unsigned int fs_reference = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fs_reference, 1, (const char**)&source, 0); glCompileShader(fs_reference); glGetShaderInfoLog(fs_reference, 2048, NULL, source); puts(source); glGetShaderiv(fs_reference, GL_COMPILE_STATUS, &compile_ok); if(!compile_ok){ printf("Fragment shader failed to compile\n"); return false; } free(source); program = glCreateProgram(); glAttachShader(program, vs_reference); glAttachShader(program, fs_reference); glLinkProgram(program); int link_ok; glGetProgramiv(program, GL_LINK_STATUS, &link_ok); glGetProgramInfoLog(program, 2048, NULL, source); puts(source); if(!link_ok){ printf("Link failure\n"); return false; } v_attrib = glGetAttribLocation(program, "in_vertex"); tc_attrib = glGetAttribLocation(program, "in_texcoord"); vp_uniform = glGetUniformLocation(program, "vp"); return true; } void GameObject::draw(mat4 vp){ static int count; count++; glUseProgram(program); glEnableVertexAttribArray(v_attrib); glBindBuffer(GL_ARRAY_BUFFER, vbuf); glVertexAttribPointer(v_attrib, 3, GL_FLOAT, GL_FALSE, 20, 0); glEnableVertexAttribArray(tc_attrib); glBindBuffer(GL_ARRAY_BUFFER, vbuf); glVertexAttribPointer(tc_attrib, 2, GL_FLOAT, GL_FALSE, 20, (const void*)12); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebuf); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, tex); glBindBuffer(GL_SHADER_STORAGE_BUFFER, models_buffer); glBufferData(GL_SHADER_STORAGE_BUFFER, models.size() * sizeof(mat4), models.data(), GL_DYNAMIC_DRAW); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, models_buffer); /* This is for a specific object again */ glUniformMatrix4fv(vp_uniform, 1, 0, value_ptr(vp)); glDrawElementsInstanced(GL_TRIANGLES, element_count, GL_UNSIGNED_INT, 0, models.size()); } void movement_function() { /* We might need a shutdown procedure that ends this loop */ while(!shutdown) { for(auto o : game_objects) o->movement(10); #ifdef UNIX usleep(10000); #else sleep(10); #endif } } int main(int argc, char ** argv){ glfwInit(); GLFWwindow* window = glfwCreateWindow(width, height, "Simple OpenGL 4.0+ Demo", 0, 0); glfwMakeContextCurrent(window); glewInit(); // During init, enable debug output glEnable(GL_DEBUG_OUTPUT); glDebugMessageCallback(debug_callback, 0); /* Level Loading Area */ /* GameObject *cylinder = new GameObject; if(!cylinder->init("cylinder.obj", "brick.jpg")){ printf("Object failed to load, giving up!\n"); return 1; } for(int i = -8; i < 8; i += 3) cylinder->add_location(vec3(i, 0, 0)); game_objects.push_back(cylinder); GameObject *cube = new GameObject; cube->spin_rate = 0.05f; if(!cube->init("cube.obj", "brick.jpg")) return 1; for(int i = -8; i < 8; i += 3) cube->add_location(vec3(i, 3, 0)); game_objects.push_back(cube); */ GameObject *chalet = new GameObject; chalet->init("chalet.obj", "chalet.jpg"); chalet->add_location(vec3(0, 0, 0)); game_objects.push_back(chalet); /* Done with level loading */ int count = 0; /* Ready to start! Time to start movement */ thread movement_thread(movement_function); glEnable(GL_DEPTH_TEST); while(!glfwWindowShouldClose(window)){ /* Not for a particular object */ count++; glfwPollEvents(); glClearColor(1.0, 1.0, 1.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); glClear(GL_DEPTH_BUFFER_BIT); /* This sets up the camera location, view angle, etc */ mat4 view = lookAt(vec3(0.0f, 0.0f, 5.0f), vec3(0, 0, 0), vec3(0, 1, 0)); mat4 projection = perspective(radians(45.0f), width/height, 0.1f, 1000.0f); mat4 vp = projection * view; /* Call draw and give it vp */ for(auto o : game_objects) o->draw(vp); /* Not for a particular object */ glfwSwapBuffers(window); // usleep(10000); } shutdown = true; movement_thread.join(); glfwDestroyWindow(window); glfwTerminate(); return 0; }