183 lines
7.6 KiB
C++
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());
|
|
}
|