penrose

program for generating penrose tilings.
Log | Files | Refs | README | LICENSE

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 }