/* /\ +--------------------------------------------------------------+ ____/ \____ /| - zlib/MIT/Unlicenced game framework licensed to freely use, | \ / / | copy, modify and sell without restriction | +--\ ^__^ /--+ | | | ~/ \~ | | - originally created at [http://nugget.fun] | | ~~~~~~~~~~~~ | +--------------------------------------------------------------+ | SPACE ~~~~~ | / | ~~~~~~~ BOX |/ +--------------+ Demonstrates functions from the Box class that map vertices from a square to a circle and from a circle to a square. The functions are based on the equations described at http://squircular.blogspot.com/2015/09/mapping-circle-to-square.html */ #include "Squircle.hpp" int main() { Squircle squircle = Squircle(); squircle.run(); squircle.quit(); return 0; } Squircle::Squircle() { /* subscribe to command events */ get_delegate().subscribe(&Squircle::respond, this); /* testing graphics in GL context */ load_gl_context(); } void Squircle::load_gl_context() { super::load_gl_context(); /* 2D vertices for any texture that is a plane spanning the screen */ std::array plane_vertices = {{ {-1.0f, 1.0f}, {1.0f, 1.0f}, {-1.0f, -1.0f}, {1.0f, 1.0f}, {1.0f, -1.0f}, {-1.0f, -1.0f} }}; std::vector circle_vertices = sb::points_on_circle(get_configuration()["circle-resolution"]); circle_vertices.insert(circle_vertices.begin(), {0.0f, 0.0f}); circle_vertices.insert(circle_vertices.end(), circle_vertices[1]); circle_vertices_count = circle_vertices.size(); /* Generate vertex buffer object to hold both mapped and unmapped data */ GLuint vbo; glGenBuffers(1, &vbo); glBindBuffer(GL_ARRAY_BUFFER, vbo); /* Allocate space for plane and circle and copy plane vertices in VBO */ GLsizeiptr vbo_size = (plane_vertices.size() + circle_vertices.size()) * sizeof(glm::vec2); glBufferData(GL_ARRAY_BUFFER, vbo_size, plane_vertices.data(), GL_STATIC_DRAW); /* Allocate VAO for the plane vertices and connect attributes to the VBO */ glGenVertexArrays(1, &unmapped_vao); glBindVertexArray(unmapped_vao); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr); glEnableVertexAttribArray(0); /* Copy circle vertices into VBO */ GLintptr offset = plane_vertices.size() * sizeof(glm::vec2); glBufferSubData(GL_ARRAY_BUFFER, offset, circle_vertices.size() * sizeof(glm::vec2), circle_vertices.data()); /* Allocate VAO for the circle vertices and connect attributes to the VBO */ glGenVertexArrays(1, &mapped_vao); glBindVertexArray(mapped_vao); glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, reinterpret_cast(offset)); glEnableVertexAttribArray(0); /* Load flat shader program */ GLuint vertex_shader = load_shader("flat.vert", GL_VERTEX_SHADER); GLuint fragment_shader = load_shader("flat.frag", GL_FRAGMENT_SHADER); flat_program = glCreateProgram(); glBindAttribLocation(flat_program, 0, "position"); glAttachShader(flat_program, vertex_shader); glAttachShader(flat_program, fragment_shader); link_shader(flat_program); glUseProgram(flat_program); /* load image */ load_image_index(0); base_texture_shader_location = glGetUniformLocation(flat_program, "base_texture"); mode_uniform_location = glGetUniformLocation(flat_program, "mode"); transformation_uniform_location = glGetUniformLocation(flat_program, "transformation"); log_gl_errors(); } /* Load image at path as an SDL surface, generate texture to load pixel data into, allocate storage, and bind * and edit texture properties. Returns the ID of the generated texture. */ GLuint Squircle::load_file_into_texture(fs::path path) const { GLuint texture_id; std::unique_ptr surface(IMG_Load(path.c_str()), SDL_FreeSurface); std::unique_ptr flipped_surface(rotozoomSurfaceXY(surface.get(), 0, 1, -1, 0), SDL_FreeSurface); glGenTextures(1, &texture_id); glBindTexture(GL_TEXTURE_2D, texture_id); glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGB8, flipped_surface->w, flipped_surface->h); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, flipped_surface->w, flipped_surface->h, GL_RGBA, GL_UNSIGNED_BYTE, flipped_surface->pixels); std::ostringstream message; message << "loaded image into texture id #" << texture_id << " " << flipped_surface->w << " x " << flipped_surface->h; log(message.str()); return texture_id; } void Squircle::load_image_index(int index) { std::vector paths = sb::glob("images/.*"); fs::path path = paths[index % paths.size()]; std::ostringstream message; message << "loading " << path; log(message.str()); image_index = index; base_texture_id = load_file_into_texture(path); } void Squircle::respond(SDL_Event& event) { if (get_delegate().compare(event, "next")) { load_image_index(image_index + 1); } else if (get_delegate().compare(event, "mode")) { mode = mode == Mode::SQUIRCLE ? Mode::UNSQUIRCLE : Mode::SQUIRCLE; } else if (get_delegate().compare(event, "left")) { spin_z = Spin::NEGATIVE; } else if (get_delegate().compare(event, "right")) { spin_z = Spin::POSITIVE; } else if (get_delegate().compare(event, {std::string("left"), std::string("right")}, false, true)) { spin_z = Spin::NONE; } else if (get_delegate().compare(event, "up")) { spin_x = Spin::NEGATIVE; } else if (get_delegate().compare(event, "down")) { spin_x = Spin::POSITIVE; } else if (get_delegate().compare(event, {std::string("up"), std::string("down")}, false, true)) { spin_x = Spin::NONE; } } void Squircle::update() { /* apply rotation to transformation matrix */ float rotation_speed = get_configuration()["rotation-speed"]; transformation = glm::rotate(transformation, spin_z * rotation_speed, {0, 0, 1}); transformation = glm::rotate(transformation, spin_x * rotation_speed, {1, 0, 0}); glUniformMatrix4fv(transformation_uniform_location, 1, false, &transformation[0][0]); /* viewport box will be used to tell GL where to draw */ Box viewport_box = window_box(); glDisable(GL_DEPTH_TEST); /* paint the screen black */ glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* set uniform and activate texture */ glUniform1i(base_texture_shader_location, 0); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, base_texture_id); /* set viewport to left side of screen for unmapped plane */ glViewport(viewport_box.left(), viewport_box.top(), viewport_box.width() / 2, viewport_box.height()); /* draws plane vertices and plane UV */ glBindVertexArray(unmapped_vao); glUniform1i(mode_uniform_location, mode == Mode::UNSQUIRCLE ? 2 : 0); glDrawArrays(GL_TRIANGLES, 0, 6); /* set viewport to right side of screen for mapped plane */ glUseProgram(flat_program); glViewport(viewport_box.cx(), viewport_box.top(), viewport_box.width() / 2, viewport_box.height()); /* draws mapped plane vertices and plane UV */ glBindVertexArray(mapped_vao); glUniform1i(mode_uniform_location, mode == Mode::SQUIRCLE ? 1 : 0); glDrawArrays(GL_TRIANGLE_FAN, 0, circle_vertices_count); SDL_GL_SwapWindow(get_window()); }