penrose.cpp (6623B)
1 /* 2 * © 2020 Michael Percival <m@michaelpercival.xyz> 3 * See LICENSE file for copyright and license details. 4 */ 5 6 #include <GL/glew.h> 7 #include <GLFW/glfw3.h> 8 9 #include <glm/glm.hpp> 10 #include <glm/gtx/rotate_vector.hpp> 11 12 #include <array> 13 #include <random> 14 #include <string> 15 #include <vector> 16 17 #include <png.h> 18 19 #include "shader.hpp" 20 #include "png_writer.hpp" 21 22 static const uint32_t window_w = 1920 * 4; 23 static const uint32_t window_h = 1080 * 4; 24 static const uint32_t depth = 10; //recursion depth 25 static const bool p2 = false; //tiling type (p2, p3) 26 static const float line_w = 2.0f; //line width 27 static const std::string file_name = "penrose.png"; 28 29 static const float phi = 1.0 / ((1.0 + sqrt(5.0)) / 2); 30 31 class triangle { 32 public: 33 bool t_123; 34 std::array<uint32_t, 3> indices; 35 std::vector<triangle*> sub_triangles; 36 37 triangle(bool t_123, std::array<uint32_t, 3> indices) { 38 this->t_123 = t_123; 39 this->indices = indices; 40 } 41 }; 42 43 void split(triangle& p, std::vector<glm::vec2>& points, std::array<std::vector<uint32_t>, 5>& indices, uint32_t depth) { 44 uint32_t s = points.size(); 45 std::array<uint32_t, 3>& i = p.indices; 46 47 if (depth > 0) { 48 if (p.t_123 ^ !p2) { 49 points.push_back(glm::vec2(((1.0f - phi) * points[i[0]]) + (phi * points[i[2]]))); 50 points.push_back(glm::vec2(((1.0f - phi) * points[i[p2]]) + (phi * points[i[!p2]]))); 51 52 triangle t1(p2, std::array<uint32_t, 3>({ i[(!p2) + 1], p2 ? i[2] : s, p2 ? s : i[1] })); 53 triangle t2(true, std::array<uint32_t, 3>({ p2 ? i[1] : s, s + 1, p2 ? s : i[1] })); 54 triangle t3(false, std::array<uint32_t, 3>({ s, s + 1, i[0] })); 55 56 p.sub_triangles = { &t1, &t2, &t3 }; 57 } 58 else { 59 points.push_back(glm::vec2(((1.0f - phi) * points[i[p2 * 2]]) + (phi * points[i[!p2]]))); 60 61 triangle t1(true, std::array<uint32_t, 3>({ i[2], s, i[1] })); 62 triangle t2(false, std::array<uint32_t, 3>({ i[(!p2) + 1], s, i[0] })); 63 64 p.sub_triangles = { &t1, &t2 }; 65 } 66 67 for (auto& t : p.sub_triangles) { 68 if (depth == 1) { 69 for (uint32_t k = 0; k < 3; ++k) { 70 if (k != (t->t_123 ^ !p2 ? 2 : 1)) { 71 indices[indices.size() - 1].push_back(t->indices[k]); 72 indices[indices.size() - 1].push_back(t->indices[((k + 1) % 3)]); 73 } 74 } 75 76 indices[t->t_123 + (p.t_123 ? 0 : 2)].insert(indices[t->t_123 + (p.t_123 ? 0 : 2)].end(), t->indices.begin(), t->indices.end()); 77 } 78 79 split(*t, points, indices, depth - 1); 80 } 81 } 82 83 return; 84 } 85 86 int main() { 87 static std::default_random_engine e(std::random_device{}()); 88 static std::uniform_real_distribution<> d(0, 1); 89 90 std::vector<glm::vec3> colours = { glm::vec3(d(e), d(e), d(e)), glm::vec3(d(e), d(e), d(e)), 91 glm::vec3(d(e), d(e), d(e)), glm::vec3(d(e), d(e), d(e)), 92 glm::vec3(d(e), d(e), d(e)), glm::vec3(d(e), d(e), d(e)) }; 93 94 uint32_t poly = 10; 95 float poly_angle = glm::radians(360.0f / poly); 96 97 std::vector<glm::vec2> points = { glm::vec2(0.0f, 0.0f), glm::vec2(0.0f, 1.0f) }; 98 std::array<std::vector<uint32_t>, 5> indices; 99 100 for (uint32_t i = 1; i < poly; ++i) { 101 glm::vec2 next = glm::rotate(points[i], poly_angle); 102 points.push_back(next); 103 } 104 105 for (auto& p : points) { 106 p = glm::rotate(p, poly_angle); 107 p.x = (p.x / window_w) * window_h; 108 } 109 110 for (uint32_t i = 0; i < poly; i++) { 111 std::array<uint32_t, 2> p = { (i % (poly + 1)) + 1, ((i + 1) % poly) + 1 }; 112 113 triangle t(true, std::array<uint32_t, 3>({ 0, p[i & 1], p[!(i & 1)] })); 114 115 split(t, points, indices, depth); 116 } 117 118 if(!glfwInit()) 119 { 120 return -1; 121 } 122 123 glfwWindowHint(GLFW_SAMPLES, 4); 124 glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 125 glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 126 glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 127 128 GLFWwindow* window = glfwCreateWindow(window_w, window_h, "penrose", NULL, NULL); 129 130 if(window == NULL) { 131 glfwTerminate(); 132 return -1; 133 } 134 135 glfwMakeContextCurrent(window); 136 glewExperimental=true; 137 138 if (glewInit() != GLEW_OK) { 139 return -1; 140 } 141 142 glfwSetInputMode(window, GLFW_STICKY_KEYS, GL_TRUE); 143 144 uint32_t VAOs[5], VBO, EBOs[5]; 145 146 glGenVertexArrays(5, VAOs); 147 glGenBuffers(1, &VBO); 148 glGenBuffers(5, EBOs); 149 glLineWidth(line_w); 150 151 for (uint32_t i = 0; i < indices.size(); ++i) { 152 glBindVertexArray(VAOs[i]); 153 154 glBindBuffer(GL_ARRAY_BUFFER, VBO); 155 glBufferData(GL_ARRAY_BUFFER, points.size() * 4 * 2, &points[0], GL_STATIC_DRAW); 156 157 glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBOs[i]); 158 glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices[i].size() * 4, &indices[i][0], GL_STATIC_DRAW); 159 160 glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0); 161 glEnableVertexAttribArray(0); 162 } 163 164 uint32_t programID = Shader::loadShaders("vertex.vert", "fragment.frag"); 165 GLint paint = glGetUniformLocation(programID, "paint"); 166 167 while (glfwGetKey(window, GLFW_KEY_ESCAPE) != GLFW_PRESS && glfwWindowShouldClose(window) == 0 && paint != -1) { 168 glViewport(-1.0 * (window_w / 2.5), -1.0 * (window_h / 2.5), window_w, window_h); 169 glClearColor(colours.back().x, colours.back().y, colours.back().z, 1.0f); 170 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 171 172 glUseProgram(programID); 173 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 174 175 for (uint32_t i = 0; i < indices.size(); ++i) { 176 glPolygonMode(GL_FRONT_AND_BACK, i < indices.size() - 1 ? GL_FILL : GL_LINE); 177 glUniform3fv(paint, 1, &colours[i][0]); 178 glBindVertexArray(VAOs[i]); 179 glDrawElements(i < indices.size() - 1 ? GL_TRIANGLES : GL_LINES, indices[i].size(), GL_UNSIGNED_INT, 0); 180 } 181 182 glfwSwapBuffers(window); 183 glfwPollEvents(); 184 } 185 186 int frame_w, frame_h; 187 glfwGetFramebufferSize(window, &frame_w, &frame_h); 188 189 png_bytep* row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * frame_h); 190 191 for (int y = 0; y < frame_h; ++y) { 192 row_pointers[y] = (png_byte*) malloc((4 * sizeof(png_byte)) * frame_w); 193 glReadPixels(0, y, frame_w, 1, GL_RGBA, GL_UNSIGNED_BYTE, row_pointers[y]); 194 } 195 196 PngWriter::write_png_file(file_name, frame_w, frame_h, row_pointers); 197 198 return 0; 199 }