spacebox/demo/squircle/Squircle.cpp

183 lines
7.6 KiB
C++

/*
/\ +--------------------------------------------------------------+
____/ \____ /| - 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<glm::vec2, 6> 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<glm::vec2> 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<GLvoid*>(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<SDL_Surface, decltype(&SDL_FreeSurface)> surface(IMG_Load(path.c_str()), SDL_FreeSurface);
std::unique_ptr<SDL_Surface, decltype(&SDL_FreeSurface)> 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<fs::path> 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());
}