#define GLM_ENABLE_EXPERIMENTAL #include #include #include #include #include struct Vertex { vec3 pos; vec2 texCoord; bool operator==(const Vertex& other) const { return pos == other.pos && texCoord == other.texCoord; } } __attribute__((packed)); namespace std { template<> struct hash { size_t operator()(Vertex const& vertex) const { return ((hash()(vertex.pos) ^ (hash()(vertex.texCoord) << 1))); } }; } struct GameObject { unsigned int vbuf, ebuf, tbuf, lbuf, vtbuf; unsigned int vertex_shader; unsigned int fragment_shader; unsigned int program; unsigned int vertex_attrib; unsigned int tex_attrib; unsigned int mvp_uniform; unsigned int tex_uniform; unsigned int element_count; unsigned int tex; float radius; vector locations; bool init_from_object(char *objfilename) { tinyobj::attrib_t attrib; std::vector shapes; std::vector materials; std::string warn, err; vector vertices; vector indices; if(!tinyobj::LoadObj(&attrib, &shapes, &materials, &warn, &err, objfilename)) throw std::runtime_error(err); unordered_map uniqueVertices = {}; for(const auto& shape : shapes){ for(const auto& index : shape.mesh.indices){ Vertex vertex = {}; vertex.pos = { attrib.vertices[3 * index.vertex_index + 0], attrib.vertices[3 * index.vertex_index + 1], attrib.vertices[3 * index.vertex_index + 2] }; vertex.texCoord = { attrib.texcoords[2 * index.texcoord_index + 0], attrib.texcoords[2 * index.texcoord_index + 1] }; if(uniqueVertices.count(vertex) == 0){ uniqueVertices[vertex] = (uint32_t)vertices.size(); vertices.push_back(vertex); } indices.push_back(uniqueVertices[vertex]); } } float *non_padded = (float*)malloc(5 * sizeof(float) * vertices.size()); for(size_t i = 0; i < vertices.size(); i++){ non_padded[i*5 + 0] = vertices[i].pos.x; non_padded[i*5 + 1] = vertices[i].pos.y; non_padded[i*5 + 2] = vertices[i].pos.z; non_padded[i*5 + 3] = vertices[i].texCoord.x; non_padded[i*5 + 4] = vertices[i].texCoord.y; } glGenBuffers(1, &vtbuf); glBindBuffer(GL_SHADER_STORAGE_BUFFER, vtbuf); glBufferData(GL_SHADER_STORAGE_BUFFER, 5 * sizeof(float) * vertices.size(), non_padded, GL_STATIC_DRAW); free(non_padded); std::cout << "Loaded " << vertices.size() << " Vertices\n"; glGenBuffers(1, &ebuf); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebuf); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * indices.size(), indices.data(), GL_STATIC_DRAW); element_count = indices.size(); std::cout << "Loaded " << indices.size() << " Indexes\n"; return true; } bool in_contact_radial_location(float x, float y, float z, int idx){ // pythagorean theorom to find if it's in the given radius } virtual bool in_contact_location(float, float, float, int) { return false; } virtual bool init() = 0; // drawing: set up and call glDrawElementsInstanced void draw_object(const mat4 &vp) { glUseProgram(program); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, tex); glUniform1i(tex_uniform, 0); glUniformMatrix4fv(mvp_uniform, 1, 0, glm::value_ptr(vp)); glEnableVertexAttribArray(vertex_attrib); glBindBuffer(GL_ARRAY_BUFFER, vtbuf); glVertexAttribPointer(vertex_attrib, 3, GL_FLOAT, GL_FALSE, 5, 0); glEnableVertexAttribArray(tex_attrib); glBindBuffer(GL_ARRAY_BUFFER, vtbuf); glVertexAttribPointer(tex_attrib, 2, GL_FLOAT, GL_FALSE, 5, (void*)2); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebuf); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, lbuf); glDrawElementsInstanced(GL_TRIANGLES, element_count, GL_UNSIGNED_INT, 0, locations.size()); // glDrawElementsInstanced(GL_TRIANGLES, 30, GL_UNSIGNED_INT, 0, locations.size()); } bool setup_standard_shaders(){ vertex_shader = make_shader("cube_vertex.glsl", GL_VERTEX_SHADER); fragment_shader = make_shader("cube_fragment.glsl", GL_FRAGMENT_SHADER); if(!(vertex_shader && fragment_shader)) return 0; program = glCreateProgram(); glAttachShader(program, vertex_shader); glAttachShader(program, fragment_shader); glLinkProgram(program); GLint link_ok; glGetProgramiv(program, GL_LINK_STATUS, &link_ok); if(!link_ok){ glGetProgramInfoLog(program, GBLEN, NULL, general_buffer); puts(general_buffer); printf(RED("Link Failed\n").c_str()); return false; } vertex_attrib = glGetAttribLocation(program, "in_vertex"); tex_attrib = glGetAttribLocation(program, "in_texcoord"); mvp_uniform = glGetUniformLocation(program, "mvp"); tex_uniform = glGetUniformLocation(program, "tex"); return true; } bool setup_texture(char *filename){ int texwidth, texheight; unsigned char *image_data = SOIL_load_image(filename, &texwidth, &texheight, 0, SOIL_LOAD_RGB); printf("Loaded texture %d by %d\n", texwidth, texheight); if(!image_data) return false; 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); free(image_data); return true; } bool setup_locations(){ glGenBuffers(1, &lbuf); update_locations(); return true; } bool update_locations(){ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, lbuf); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(vec4) * locations.size(), locations.data(), GL_STATIC_DRAW); printf("Added %d locations\n", locations.size()); return true; } }; struct Chalet : public GameObject { bool init() override { init_from_object("chalet.obj"); setup_standard_shaders(); setup_texture("chalet.jpg"); setup_locations(); return true; } void draw(mat4 vp){ draw_object(vp); } }; struct StoneBlock : public GameObject { // initialization: Texture and vertices into memory bool init(){ GLfloat cube_vertices[] = { // front -0.25, -0.25, 0.25, 0.25, -0.25, 0.25, 0.25, 0.25, 0.25, -0.25, 0.25, 0.25, // top -0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, 0.25, -0.25, -0.25, 0.25, -0.25, // back 0.25, -0.25, -0.25, -0.25, -0.25, -0.25, -0.25, 0.25, -0.25, 0.25, 0.25, -0.25, // bottom -0.25, -0.25, -0.25, 0.25, -0.25, -0.25, 0.25, -0.25, 0.25, -0.25, -0.25, 0.25, // left -0.25, -0.25, -0.25, -0.25, -0.25, 0.25, -0.25, 0.25, 0.25, -0.25, 0.25, -0.25, // right 0.25, -0.25, 0.25, 0.25, -0.25, -0.25, 0.25, 0.25, -0.25, 0.25, 0.25, 0.25, }; glGenBuffers(1, &vbuf); glBindBuffer(GL_SHADER_STORAGE_BUFFER, vbuf); glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(cube_vertices), cube_vertices, GL_STATIC_DRAW); GLushort cube_elements[] = { // front 0, 1, 2, 2, 3, 0, // top 4, 5, 6, 6, 7, 4, // back 8, 9, 10, 10, 11, 8, // bottom 12, 13, 14, 14, 15, 12, // left 16, 17, 18, 18, 19, 16, // right 20, 21, 22, 22, 23, 20, }; element_count = sizeof(cube_elements) / sizeof(GLushort); glGenBuffers(1, &ebuf); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebuf); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(cube_elements), cube_elements, GL_STATIC_DRAW); GLfloat cube_texcoords[2*4*6] = { // front 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, }; for (int i = 1; i < 6; i++) memcpy(&cube_texcoords[i*4*2], &cube_texcoords[0], 2*4*sizeof(GLfloat)); glGenBuffers(1, &tbuf); glBindBuffer(GL_ARRAY_BUFFER, tbuf); glBufferData(GL_ARRAY_BUFFER, sizeof(cube_texcoords), cube_texcoords, GL_STATIC_DRAW); setup_locations(); setup_texture("brick.jpg"); setup_standard_shaders(); return true; } bool in_contact_location(float x, float y, float z, int i){ const float margin = 0.1; if(abs(x - locations[i].x) < 0.25 + margin && abs(y - locations[i].y) < 0.25 + margin && abs(z - locations[i].z) < 0.25 + margin){ return i; } } int in_contact(float x, float y, float z){ for(int i = 0; i < locations.size(); i++){ if(in_contact_location(x, y, z, i)) return i; } return -1; } void draw(const mat4 &vp) { glUseProgram(program); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, tex); glUniform1i(tex_uniform, 0); glUniformMatrix4fv(mvp_uniform, 1, 0, glm::value_ptr(vp)); glEnableVertexAttribArray(vertex_attrib); glBindBuffer(GL_ARRAY_BUFFER, vbuf); glVertexAttribPointer(vertex_attrib, 3, GL_FLOAT, GL_FALSE, 0, 0); glEnableVertexAttribArray(tex_attrib); glBindBuffer(GL_ARRAY_BUFFER, tbuf); glVertexAttribPointer(tex_attrib, 2, GL_FLOAT, GL_FALSE, 0, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebuf); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, lbuf); glDrawElementsInstanced(GL_TRIANGLES, element_count, GL_UNSIGNED_SHORT, 0, locations.size()); } // movement: for falling calculations // Returns the effective floor level float movement(){ float efloor = -1000; for(vec3 l : locations){ if(abs(player_x - l.x) < 0.25 && abs(player_z - l.z) < 0.25){ if(l.y < player_y + 2.0 && l.y > efloor) efloor = l.y + 1; } } return efloor; } };