/* Simplified OpenGL 4.5 demo * Seth Long, Fall 2020 * This is a *very* short demo which displays a triangle * Many of these functions can fail, and return error values * I doubt it can be done much shorter without leaving vertices in only main memory or some such * If moving to C++, use GLM instead of cglm g++ load_object.cpp -lGL -lglfw -lGLEW */ #define MAIN #include "imgui.h" #include "examples/imgui_impl_glfw.h" #include "examples/imgui_impl_opengl3.h" #include "engine.h" StoneBlock sb; Chalet village; float landscape_grid[20][20]; void adjust_center(int xl, int xh, int zl, int zh){ // TODO: Fix static 10 if(xl + 1 >= xh || zl + 1 >= zh) return; int cx = (xl + xh) / 2; int cz = (zl + zh) / 2; float corner_average = (landscape_grid[xl][zl] + landscape_grid[xh][zl] + landscape_grid[xl][zh] + landscape_grid[xh][zh]) / 4.0f; landscape_grid[cx][cz] = corner_average + ((xh - xl + zh - zl) / 2) * ((random() % 1000)/2000.0f); landscape_grid[cx][zl] = (landscape_grid[xl][zl] + landscape_grid[xh][zl]) / 2; landscape_grid[cx][zh] = (landscape_grid[xl][zh] + landscape_grid[xh][zh]) / 2; landscape_grid[xl][cz] = (landscape_grid[xl][zl] + landscape_grid[xl][zh]) / 2; landscape_grid[xh][cz] = (landscape_grid[xh][zl] + landscape_grid[xh][zh]) / 2; adjust_center(xl, cx, zl, cz); adjust_center(cx, xh, zl, cz); adjust_center(xl, cx, cz, zh); adjust_center(cx, xh, cz, zh); } void make_landscape(GameObject *go, int x_size, int z_size, float x_corner, float z_corner){ int colheight = 2; memset(landscape_grid, 0, sizeof(landscape_grid)); adjust_center(0, x_size-1, 0, z_size-1); // set up further iterations for(int i = 0; i < x_size; i++) for(int j = 0; j < z_size; j++) for(int k = 0; k < colheight; k++) go->locations.push_back(vec4(i + x_corner, landscape_grid[i][j] + k, j + z_corner, 0)); } void load_level(){ /* for(float x = -1.0; x < 15; x += 0.5){ for(float y = -1.0; y < x; y += 0.5){ sb.locations.push_back(vec4(x, y, 3.0, 0)); sb.locations.push_back(vec4(x, y, 3.5, 0)); } } for(float x = 15.0; x < 25.0; x+=0.5) { sb.locations.push_back(vec4(x, 14.0, 2.0, 0)); sb.locations.push_back(vec4(x, 14.0, 2.5, 0)); sb.locations.push_back(vec4(x, 14.0, 3.0, 0)); sb.locations.push_back(vec4(x, 14.0, 3.5, 0)); sb.locations.push_back(vec4(x, 14.0, 4.0, 0)); } HelpingCat *hc = new HelpingCat(); objects.push_back(hc); hc->add(24.0, 15.0, 3.0); hc->add(5.0, 0.0, 0.0); */ village.locations.push_back(vec4(0, -1, 0, 0)); village.locations.push_back(vec4(-5, -1, 0, 0)); //objects.push_back(&sb); objects.push_back(&village); /* MonsterBox *monsters = new MonsterBox(); monsters->locations.push_back(vec4(0, 0, -5, 0)); monsters->locations.push_back(vec4(3, 0, -5, 0)); monsters->locations.push_back(vec4(0, 20, 0, 0)); for(int z = -5; z < 5; z++) monsters->locations.push_back(vec4(-8, 10, z*4, 0)); objects.push_back(monsters); StoneBlock *lb = new StoneBlock(); lb->texfile = "rock.jpg"; lb->cube_size = 2.0f; objects.push_back(lb); make_landscape(lb, 20, 20, 0, 10); */ /* GenericObject *spheres = new GenericObject("sphere", "ball.obj", "brick.jpg"); spheres->locations.push_back(vec4(-10, 1, 5, 0)); spheres->locations.push_back(vec4(-10, 3, 5, 0)); objects.push_back(spheres); */ projectiles = new Projectile(); objects.push_back(projectiles); /* fragments = new Fragment(); objects.push_back(fragments); */ for(auto o : objects) o->init(); } GLuint make_shader(const char *filename, GLenum shaderType){ int fd = open(filename, O_RDONLY); size_t readlen = read(fd, general_buffer, GBLEN); close(fd); if(readlen == GBLEN){ printf(RED("Buffer Length of %d bytes Inadequate for File %s\n").c_str(), GBLEN, filename); return 0; } general_buffer[readlen] = 0; printf(DGREEN("Read shader in file %s (%d bytes)\n").c_str(), filename, readlen); puts(general_buffer); unsigned int s_reference = glCreateShader(shaderType); glShaderSource(s_reference, 1, (const char**)&general_buffer, 0); glCompileShader(s_reference); glGetShaderInfoLog(s_reference, GBLEN, NULL, general_buffer); puts(general_buffer); GLint compile_ok; glGetShaderiv(s_reference, GL_COMPILE_STATUS, &compile_ok); if(compile_ok){ puts(GREEN("Compile Success").c_str()); return s_reference; } puts(RED("Compile Failed\n").c_str()); return 0; } void fire(){ projectiles->addnew_radial(player_x, player_y, player_z, 1, player_heading, player_elevation); } const float activate_distance = 0.8; void activate(){ float activate_x = player_x + activate_distance * cos(player_heading); float activate_z = player_z + activate_distance * sin(player_heading); float activate_y = player_y + activate_distance * sin(player_elevation); for(auto go : objects){ go->activate(activate_x, activate_y, activate_z); } } void cap_release_cursor(){ cursor_free = !cursor_free; if(cursor_free){ glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); } else { glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); } } void handle_mousemove(GLFWwindow* window, double xpos, double ypos){ static double old_xpos = width/2, old_ypos = height/2; // Mouse Movement if(!cursor_free){ double xmove = old_xpos - xpos; double ymove = old_ypos - ypos; player_heading -= xmove / 100.0; player_elevation += ymove / 100.0; if(player_elevation > 2.0) player_elevation = 2.0; if(player_elevation < -2.0) player_elevation = -2.0; old_ypos = ypos; old_xpos = xpos; } } void handle_keystrokes(GLFWwindow* window, int key, int scancode, int action, int mods){ if(action == 2) return; if(key == GLFW_KEY_W) key_status.w = action; else if(key == GLFW_KEY_S) key_status.s = action; else if(key == GLFW_KEY_A) key_status.a = action; else if(key == GLFW_KEY_D) key_status.d = action; else if(key == GLFW_KEY_ESCAPE && action == 0) cap_release_cursor(); else if(key == GLFW_KEY_F1 && action == 0) draw_menu = !draw_menu; else if(key == GLFW_KEY_SPACE && action == 0) player_yvel = 5.0; else if(key == GLFW_KEY_T && action == 0) fire(); else if(key == GLFW_KEY_E && action == 0) activate(); } float speed = 0.1; void handle_mousebutton(GLFWwindow* window, int button, int action, int mods){ if(button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS && !cursor_free) fire(); } double loop_time; void movement(){ double old_time; while(!shutdown){ double now = glfwGetTime(); loop_time = now - old_time; old_time = now; float old_x = player_x, old_z = player_z; // Movement if(key_status.w){ player_x += speed * cos(player_heading); player_z += speed * sin(player_heading); } if(key_status.s) { player_x -= speed * cos(player_heading); player_z -= speed * sin(player_heading); } if(key_status.a){ player_x += speed * sin(player_heading); player_z -= speed * cos(player_heading); } if(key_status.d){ player_x -= speed * sin(player_heading); player_z += speed * cos(player_heading); } for(auto go : objects){ if(!go->solid) continue; int contact = go->in_contact(player_x, player_y, player_z); if(contact != -1){ bool block_x = true, block_z = true; if(-1 == go->in_contact(player_x, player_y, old_z)) block_x = false; if(-1 == go->in_contact(old_x, player_y, player_z)) block_z = false; if(block_x) player_x = old_x; if(block_z) player_z = old_z; } } player_yvel -= gravity; float old_y = player_y; player_y += player_yvel/100.0; float efloor = -1.0; for(auto go : objects){ if(!go->solid) continue; // This is for the ceiling int contact = go->in_contact(player_x, player_y, player_z); if(contact != -1){ player_y = old_y; player_yvel = 0; } // This part is for the floor // Find the maximum effective floor float sb_floor = go->find_floor(); if(sb_floor > efloor) efloor = sb_floor; } if(player_y <= efloor + 1.0f) { player_y = efloor + 1.0f; player_yvel = 0.0f; } for(auto go : objects){ go->movement(); if(go->dangerous && !invincible){ if(-1 != go->in_contact(player_x, player_y, player_z)) up_direction = vec3(1, 0, 0); } } int sleep_usecs = 10000 - (loop_time/1000000.0); if(sleep_usecs > 10) usleep(sleep_usecs); // usleep expects microseconds, loop_time is in seconds } } void collisions(){ while(!shutdown){ // New Main Collision loop // Note: Only dangerous objects are considered here! for(auto dangerous : objects){ if(!dangerous->dangerous) continue; for(auto hit_item : objects){ if(hit_item == dangerous) continue; for(size_t i = 0; i < dangerous->locations.size(); i++){ if(hit_item->dangerous_at(dangerous->locations[i])) dangerous->contact_at(i); } } } } } void resize(GLFWwindow* window, int new_width, int new_height){ width = new_width; height = new_height; printf("Window resized, now %f by %f\n", width, height); glViewport(0, 0, width, height); } int main(int argc, char ** argv){ general_buffer = (char*)malloc(GBLEN); glfwInit(); window = glfwCreateWindow(width, height, "Simple OpenGL 4.0+ Demo", 0, 0); glfwMakeContextCurrent(window); glfwSetFramebufferSizeCallback(window, resize); glewInit(); tinyobj::attrib_t attrib; std::vector shapes; std::vector material; std::string warn, err; tinyobj::LoadObj(&attrib, &shapes, &material, &warn, &err, "torus.obj"); // Initialization part unsigned int vbuf; glGenBuffers(1, &vbuf); glBindBuffer(GL_SHADER_STORAGE_BUFFER, vbuf); glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(float) * attrib.vertices.size(), attrib.vertices.data(), GL_STATIC_DRAW); std::cout << "Loaded " << attrib.vertices.size() << " Vertices\n"; float floor[] = {0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0}; unsigned int floorbuf; glGenBuffers(1, &floorbuf); glBindBuffer(GL_SHADER_STORAGE_BUFFER, floorbuf); glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(floor), floor, GL_STATIC_DRAW); unsigned int ibuf; std::vector indices; for(const auto &e : shapes[0].mesh.indices) indices.push_back(e.vertex_index); glGenBuffers(1, &ibuf); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short) * indices.size(), indices.data(), GL_STATIC_DRAW); std::cout << "Loaded " << indices.size() << " Indexes\n"; int texwidth, texheight; unsigned int tex; unsigned char *image_data = SOIL_load_image("brick_2.png", &texwidth, &texheight, 0, SOIL_LOAD_RGB); printf("Loaded texture %d by %d\n", texwidth, texheight); 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, texwidth, texheight, 0, GL_RGB, GL_UNSIGNED_BYTE, image_data); unsigned int vs_reference = make_shader("vertex_shader.glsl", GL_VERTEX_SHADER); unsigned int fs_reference = make_shader("fragment_shader.glsl", GL_FRAGMENT_SHADER); if(!(vs_reference && fs_reference)) return 0; unsigned int floor_vertex = make_shader("vertex_floor.glsl", GL_VERTEX_SHADER); unsigned int floor_frag = make_shader("fragment_floor.glsl", GL_FRAGMENT_SHADER); if(!(vs_reference && fs_reference)) return 0; unsigned int program = glCreateProgram(); glAttachShader(program, vs_reference); glAttachShader(program, fs_reference); glLinkProgram(program); GLint link_ok; glGetProgramiv(program, GL_LINK_STATUS, &link_ok); if(!link_ok){ glGetProgramInfoLog(program, GBLEN, NULL, general_buffer); puts(general_buffer); puts(RED("Link Failed").c_str()); return 0; } unsigned int floor_program = glCreateProgram(); glAttachShader(floor_program, floor_vertex); glAttachShader(floor_program, floor_frag); glLinkProgram(floor_program); glGetProgramiv(floor_program, GL_LINK_STATUS, &link_ok); if(!link_ok){ glGetProgramInfoLog(floor_program, GBLEN, NULL, general_buffer); puts(general_buffer); puts(RED("Link Failed").c_str()); return 0; } unsigned int v_attrib = glGetAttribLocation(program, "in_vertex"); unsigned int mvp_uniform = glGetUniformLocation(program, "mvp"); unsigned int v_attrib_floor = glGetAttribLocation(floor_program, "in_vertex"); unsigned int mvp_uniform_floor = glGetUniformLocation(floor_program, "mvp"); unsigned int tex_uniform = glGetUniformLocation(floor_program, "tex"); load_level(); thread run_movement(movement); thread run_collisions(collisions); IMGUI_CHECKVERSION(); using namespace ImGui; CreateContext(); ImGuiIO& io = GetIO(); (void)io; StyleColorsDark(); ImGui_ImplGlfw_InitForOpenGL(window, true); ImGui_ImplOpenGL3_Init("#version 450"); glEnable(GL_DEPTH_TEST); glfwSetKeyCallback(window, handle_keystrokes); glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); glfwSetMouseButtonCallback(window, handle_mousebutton); glfwSetCursorPosCallback(window, handle_mousemove); while(!glfwWindowShouldClose(window)){ glfwPollEvents(); glClearColor(0, 0.2, 0, 1.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); double time = glfwGetTime(); glm::mat4 model = glm::rotate(glm::mat4(1.0f), (float)time, vec3(1, 0, 0)); glm::vec3 lookat; lookat.x = player_x + cos(player_elevation) * cos(player_heading); lookat.y = player_y + sin(player_elevation); lookat.z = player_z + cos(player_elevation) * sin(player_heading); glm::mat4 view = glm::lookAt(glm::vec3(player_x, player_y, player_z), lookat, up_direction); glm::mat4 projection = glm::perspective(45.0f, width/height, .01f, 1000.0f); glm::mat4 mvp = projection * view * model; mat4 vp = projection * view; // Draw the object we loaded /* glUseProgram(program); glUniformMatrix4fv(mvp_uniform, 1, 0, glm::value_ptr(mvp)); glEnableVertexAttribArray(v_attrib); glBindBuffer(GL_ARRAY_BUFFER, vbuf); glVertexAttribPointer(v_attrib, 3, GL_FLOAT, GL_FALSE, 0, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibuf); glDrawElementsInstanced(GL_TRIANGLES, indices.size(), GL_UNSIGNED_SHORT, 0, 20); */ // Draw the floor glUseProgram(floor_program); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, tex); glUniform1i(tex_uniform, 0); glUniformMatrix4fv(mvp_uniform_floor, 1, 0, glm::value_ptr(vp)); glEnableVertexAttribArray(v_attrib_floor); glBindBuffer(GL_ARRAY_BUFFER, floorbuf); glVertexAttribPointer(v_attrib_floor, 3, GL_FLOAT, GL_FALSE, 0, 0); glDrawArraysInstanced(GL_TRIANGLES, 0, 18, 400); for(GameObject* go : objects) go->draw(vp); if(draw_menu){ ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplGlfw_NewFrame(); NewFrame(); Begin("OurWindow"); Text("FPS: %.1f FPS", GetIO().Framerate); Text("Loop Time: %.4lf", loop_time); Text("X = %f, Y = %f, Z = %f", player_x, player_y, player_z); Checkbox("Invincible", &invincible); SliderFloat("Speed", &speed, 0.0f, 1.0f); End(); Render(); ImGui_ImplOpenGL3_RenderDrawData(GetDrawData()); } glfwSwapBuffers(window); } shutdown = true; run_movement.join(); run_collisions.join(); glDeleteProgram(program); glfwDestroyWindow(window); glfwTerminate(); free(general_buffer); }