pepy/src/Pepy.cpp

384 lines
15 KiB
C++

/* Pepy by @ohsqueezy [ohsqueezy.itch.io] */
#include "Pepy.hpp"
/* Launch the Pepy instance's mainloop */
int main()
{
Pepy pepy = Pepy();
pepy.run();
pepy.quit();
return 0;
}
/* Initialize a Pepy instance */
Pepy::Pepy()
{
/* subscribe to command events */
get_delegate().subscribe(&Pepy::respond, this);
get_delegate().subscribe(&Pepy::respond, this, SDL_MOUSEBUTTONDOWN);
get_delegate().subscribe(&Pepy::respond, this, SDL_MOUSEBUTTONUP);
/* create a glowing ring */
int point_count = configuration()["sim"]["wall-count"].get<int>() + 1;
std::vector<glm::vec2> outer_points = sb::points_on_circle(point_count, 0.75f, {0.0f, 0.0f});
std::vector<glm::vec2> inner_points = sb::points_on_circle(point_count, 0.65f, {0.0f, 0.0f});
float inner_saturation = 0.1f;
float inner_value = 1.0f;
float outer_saturation = 1.0f;
float outer_value = 0.1f;
int next;
for (int ii = 0; ii <= point_count; ii++)
{
next = (ii + 1) % point_count;
cuckoo["position"]->add(outer_points[ii]);
cuckoo["position"]->add(outer_points[next]);
cuckoo["position"]->add(inner_points[ii]);
cuckoo["position"]->add(inner_points[ii]);
cuckoo["position"]->add(inner_points[next]);
cuckoo["position"]->add(outer_points[next]);
cuckoo["color"]->add(glm::rgbColor(glm::vec3(ii / static_cast<float>(point_count) * 255.0f, outer_saturation, outer_value)));
cuckoo["color"]->add(glm::rgbColor(glm::vec3(next / static_cast<float>(point_count) * 255.0f, outer_saturation, outer_value)));
cuckoo["color"]->add(glm::rgbColor(glm::vec3(ii / static_cast<float>(point_count) * 255.0f, inner_saturation, inner_value)));
cuckoo["color"]->add(glm::rgbColor(glm::vec3(ii / static_cast<float>(point_count) * 255.0f, inner_saturation, inner_value)));
cuckoo["color"]->add(glm::rgbColor(glm::vec3(next / static_cast<float>(point_count) * 255.0f, inner_saturation, inner_value)));
cuckoo["color"]->add(glm::rgbColor(glm::vec3(next / static_cast<float>(point_count) * 255.0f, outer_saturation, outer_value)));
}
/* create balls */
float size = configuration()["sim"]["ball-scale"];
auto spawn_points = sb::points_on_circle(5, configuration()["sim"]["spawn-radius"]);
for (auto spawn_point : spawn_points)
{
Plane ball;
ball.transformation(glm::translate(glm::vec3{spawn_point.x, spawn_point.y, 1.0f}) * glm::scale(glm::vec3{size, size, 1.0f}));
wad.push_back(std::pair{ball, glm::vec2{0.0f, 0.0f}});
}
background.transformation(glm::scale(glm::vec3{5.0f, 5.0f, 1.0f}));
/* load Open GL */
load_gl_context();
/* load wad textures */
sb::Texture wad_texture {"resource/wad.png"};
wad_texture.load();
/* Apply the wad texture to each wad */
for (size_t wad_ii = 0; wad_ii < wad.size(); wad_ii++)
{
wad[wad_ii].first.texture(wad_texture);
}
sb::Texture texture {"resource/space.png"};
texture.load();
background.texture(texture);
}
/* Create GL context via super class and load vertices, UV data, and shaders */
void Pepy::load_gl_context()
{
super::load_gl_context();
cuckoo_vao.generate();
wad_vao.generate();
cuckoo_vao.bind();
/* Generate ID for the vertex buffer object that will hold vertex data. Because there is one buffer, data
* will be copied in one after the other, offset to after the previous location. */
vbo.generate();
vbo.bind();
vbo.allocate(cuckoo.size() + wad[0].first.size(), GL_STATIC_DRAW);
GLuint vertex_shader = load_shader("src/shader.vert", GL_VERTEX_SHADER);
GLuint fragment_shader = load_shader("src/shader.frag", GL_FRAGMENT_SHADER);
shader = glCreateProgram();
glAttachShader(shader, vertex_shader);
glAttachShader(shader, fragment_shader);
sb::Log::gl_errors("after attaching shaders");
cuckoo.attributes("position")->bind(0, shader, "vertex_position");
cuckoo.attributes("color")->bind(1, shader, "vertex_color");
vbo.add(*cuckoo.attributes("position"));
vbo.add(*cuckoo.attributes("color"));
wad_vao.bind();
wad[0].first.attributes("position")->bind(0, shader, "vertex_position");
wad[0].first.attributes("uv")->bind(2, shader, "vertex_uv");
vbo.add(*wad[0].first.attributes("position"));
vbo.add(*wad[0].first.attributes("uv"));
sb::Log::gl_errors("after VBO allocation");
link_shader(shader);
uniform["blend"] = glGetUniformLocation(shader, "blend_min_hsv");
uniform["orthographic_projection"] = glGetUniformLocation(shader, "orthographic_projection");
uniform["model_transformation"] = glGetUniformLocation(shader, "model_transformation");
uniform["base_texture"] = glGetUniformLocation(shader, "base_texture");
uniform["textured"] = glGetUniformLocation(shader, "textured");
uniform["scroll"] = glGetUniformLocation(shader, "scroll");
uniform["time"] = glGetUniformLocation(shader, "time");
sb::Log::gl_errors("after uniforms");
/* enable alpha rendering */
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
sb::Log::gl_errors("at end of load context");
}
void Pepy::respond(SDL_Event& event)
{
/* Will check if reset should be triggered */
bool reset = false;
/* Check for any direction when waiting to reset */
if (stopped && delegate.compare(event, {"up", "down", "left", "right"}))
{
reset = true;
}
if (event.type == SDL_MOUSEBUTTONDOWN)
{
if (shaking)
{
grabbed = true;
}
/* Mouse button will trigger reset when stopped */
else if (stopped)
{
reset = true;
}
}
else if (event.type == SDL_MOUSEBUTTONUP && shaking)
{
grabbed = false;
}
/* Reset to countdown */
if (reset)
{
float size = configuration()["sim"]["ball-scale"];
auto spawn_points = sb::points_on_circle(5, configuration()["sim"]["spawn-radius"]);
int ii = 0;
for (auto& ball : wad)
{
ball.first.transformation(glm::translate(glm::vec3{spawn_points[ii].x, spawn_points[ii++].y, 1.0f}) * glm::scale(glm::vec3{size, size, 1.0f}));
}
stopped = false;
shaking = true;
}
}
/* Update state and draw the screen */
void Pepy::update()
{
/* number of seconds running */
time_seconds = SDL_GetTicks() / 1000.0f;
if (shaking)
{
countdown -= last_frame_length / 1000.0f;
if (countdown < 0.0f)
{
shaking = false;
flying = true;
countdown = 10.0f;
}
}
/* move cuckoo with mouse */
if (grabbed && shaking)
{
/* get mouse coordinates in NDC */
SDL_GetMouseState(&mouse_pixel.x, &mouse_pixel.y);
glm::vec2 mouse_ndc {
static_cast<float>(mouse_pixel.x) / window_box().width() * 2.0f - 1.0f,
(1.0f - static_cast<float>(mouse_pixel.y) / window_box().height()) * 2.0f - 1.0f
};
cuckoo_offset.x += weight(mouse_ndc.x / 20.0f) * std::max(std::min(std::abs(1.0f - cuckoo_offset.x), 1.0f), 0.1f);
cuckoo_offset.y += weight(mouse_ndc.y / 20.0f) * std::max(std::min(std::abs(1.0f - cuckoo_offset.y), 1.0f), 0.1f);
}
else
{
/* move cuckoo with keys */
const std::uint8_t* state = SDL_GetKeyboardState(nullptr);
float motion = weight(1 / 25.0f), diagonal = motion * std::sin(glm::pi<float>() * 0.25f);
if (state[SDL_SCANCODE_UP])
{
if (state[SDL_SCANCODE_RIGHT])
{
cuckoo_offset.x += diagonal;
cuckoo_offset.y += diagonal;
}
else if (state[SDL_SCANCODE_LEFT])
{
cuckoo_offset.x -= diagonal;
cuckoo_offset.y += diagonal;
}
else
{
cuckoo_offset.y += motion;
}
}
else if (state[SDL_SCANCODE_RIGHT])
{
if (state[SDL_SCANCODE_DOWN])
{
cuckoo_offset.x += diagonal;
cuckoo_offset.y -= diagonal;
}
else
{
cuckoo_offset.x += motion;
}
}
else if (state[SDL_SCANCODE_DOWN])
{
if (state[SDL_SCANCODE_LEFT])
{
cuckoo_offset.x -= diagonal;
cuckoo_offset.y -= diagonal;
}
else
{
cuckoo_offset.y -= motion;
}
}
else if (state[SDL_SCANCODE_LEFT])
{
cuckoo_offset.x -= motion;
}
}
cuckoo_offset.x -= weight(glm::sign(cuckoo_offset).x * return_speed);
cuckoo_offset.y -= weight(glm::sign(cuckoo_offset).y * return_speed);
cuckoo.transformation(glm::translate(cuckoo_offset));
hue_offset += weight(0.002f);
glm::vec2 x_range = {-1.0f, 1.0f};
glm::vec2 y_range = {-1.0f, 1.0f};
/* move wad */
bool all_stopped = true;
for (auto& ball : wad)
{
const glm::vec2& ball_center = {ball.first.transformation()[3].x, ball.first.transformation()[3].y};
const glm::vec2& cuckoo_center = {cuckoo.transformation()[3].x, cuckoo.transformation()[3].y};
float distance = glm::distance(ball_center, cuckoo_center);
if (shaking)
{
if (distance > 0.65f)
{
float angle = glm::atan(cuckoo_center.y - ball_center.y, cuckoo_center.x - ball_center.x);
ball.second.y += 0.0005f;
ball.second.x = angle + glm::half_pi<float>();
}
}
glm::vec2 step {glm::sin(ball.second.x) * ball.second.y, -glm::cos(ball.second.x) * ball.second.y};
ball.first.transformation(glm::translate(glm::vec3{step.x, step.y, 0.0f}) * ball.first.transformation());
if (ball.second.y > 0)
{
float friction;
if (shaking)
{
friction = 0.00005f;
}
else
{
friction = 0.0001f;
}
ball.second.y = std::max(0.0f, ball.second.y - friction);
}
if (ball_center.x < x_range[0])
{
x_range[0] = ball_center.x - 0.1f;
}
else if (ball_center.x > x_range[1])
{
x_range[1] = ball_center.x + 0.1f;
}
if (ball_center.y < y_range[0])
{
y_range[0] = ball_center.y - 0.1f;
}
else if (ball_center.y > y_range[1])
{
y_range[1] = ball_center.y + 0.1f;
}
if (ball.second.y > 0.0f)
{
all_stopped = false;
}
}
if (all_stopped && flying)
{
flying = false;
stopped = true;
glm::vec2 sum {0.0f, 0.0f};
std::vector<glm::vec2> centers;
for (auto& ball : wad)
{
const glm::vec2& ball_center = {ball.first.transformation()[3].x, ball.first.transformation()[3].y};
centers.push_back(ball_center);
sum += ball_center;
}
glm::vec2 centroid = sum / wad.size();
angle_sort sorter {centroid, centers[0]};
std::sort(centers.begin(), centers.end(), sorter);
float area = 0.0f;
for (std::size_t center_ii = 0; center_ii < centers.size() - 1; center_ii++)
{
area += centers[center_ii].x * centers[center_ii + 1].y - centers[center_ii + 1].x * centers[center_ii].y;
}
area += centers[centers.size() - 1].x * centers[0].y - centers[0].x * centers[centers.size() - 1].y;
area = std::abs(area) / 2.0f;
std::cout << "Your score (size) is " << area << ". Press arrow key to play again." << std::endl;
}
/* paint over screen */
glClearColor(0, 0, 0, 1);
sb::Log::gl_errors("after setting clear color");
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
sb::Log::gl_errors("after clearing screen");
glUseProgram(shader);
sb::Log::gl_errors("after using program");
/* set orthographic project for viewing entire scene at normalized screen ratio */
aspect_ratio = window_box().aspect(true);
if (window_box().width() > window_box().height())
{
orthographic_projection = glm::ortho(aspect_ratio * x_range[0], aspect_ratio * x_range[1], y_range[0], y_range[1]);
}
else
{
orthographic_projection = glm::ortho(x_range[0], x_range[1], aspect_ratio * y_range[0], aspect_ratio * y_range[1]);
}
glUniformMatrix4fv(uniform["orthographic_projection"], 1, GL_FALSE, &orthographic_projection[0][0]);
sb::Log::gl_errors("after setting orthographic projection");
/* draw background */
wad_vao.bind();
glUniform3f(uniform["blend"], hue_offset, 0.0f, 1.0f);
glUniform1i(uniform["textured"], true);
glUniform1i(uniform["scroll"], true);
glUniform1f(uniform["time"], time_seconds);
glUniform1i(uniform["base_texture"], 0);
glActiveTexture(GL_TEXTURE0);
background.texture().bind();
glUniformMatrix4fv(uniform["model_transformation"], 1, GL_FALSE, reinterpret_cast<const GLfloat*>(&background.transformation()[0][0]));
background.enable();
glDrawArrays(GL_TRIANGLES, 0, background.attributes("position")->count());
background.disable();
/* draw wad */
wad_vao.bind();
glUniform3f(uniform["blend"], 0.0f, 0.0f, 1.0f);
glUniform1i(uniform["textured"], true);
glUniform1i(uniform["scroll"], false);
sb::Log::gl_errors("after setting blending and textured flag");
for (std::pair<Plane, glm::vec2>& ball : wad)
{
glUniform1i(uniform["base_texture"], 0);
sb::Log::gl_errors("after setting texture uniform");
glActiveTexture(GL_TEXTURE0);
sb::Log::gl_errors("after activating texture");
ball.first.texture().bind();
sb::Log::gl_errors("after binding wad");
glUniformMatrix4fv(uniform["model_transformation"], 1, GL_FALSE, reinterpret_cast<const GLfloat*>(&ball.first.transformation()[0][0]));
ball.first.enable();
glDrawArrays(GL_TRIANGLES, 0, ball.first.attributes("position")->count());
ball.first.disable();
sb::Log::gl_errors("after drawing wad");
}
/* draw cuckoo */
if (shaking || stopped)
{
cuckoo_vao.bind();
glUniform3f(uniform["blend"], hue_offset, 0.5f, 1.0f);
glUniform1i(uniform["textured"], false);
glUniformMatrix4fv(uniform["model_transformation"], 1, GL_FALSE, reinterpret_cast<const GLfloat*>(&cuckoo.transformation()[0][0]));
cuckoo.enable();
glDrawArrays(GL_TRIANGLES, 0, cuckoo.attributes("position")->count());
cuckoo.disable();
}
SDL_GL_SwapWindow(window());
sb::Log::gl_errors("at end of update");
if (shaking)
{
std::cout << countdown << std::endl;
}
}