spacebox/demo/box/BoxDemo.cpp

240 lines
8.4 KiB
C++

/* +------------------------------------------------------+
____/ \____ /| - Open source game framework licensed to freely use, |
\ / / | copy, modify and sell without restriction |
+--\ ^__^ /--+ | |
| ~/ \~ | | - created for <https://foam.shampoo.ooo> |
| ~~~~~~~~~~~~ | +------------------------------------------------------+
| SPACE ~~~~~ | /
| ~~~~~~~ BOX |/
+--------------+
Display a rotating box with a space texture and gradient background using SDL, OpenGL, and [SPACE BOX].
*/
#include <iostream>
#include <stdio.h>
#include <math.h>
#include <vector>
#include <array>
#include <list>
#include <cstdlib>
#include <algorithm>
#include <string>
#include <functional>
#include <fstream>
#include "glm/glm.hpp"
#include "Game.hpp"
#include "GLObject.hpp"
#include "VBO.hpp"
#include "Model.hpp"
struct BoxDemo : Game
{
private:
sb::Model box;
sb::Plane background;
bool right_active = false, down_active = false, left_active = false, up_active = false, is_writing_audio = true;
SDL_Event event;
sb::Texture fake;
GLuint mvp_uniform_id, flat_program, world_program;
glm::mat4 projection, view, model = glm::mat4(1.0f), mvp;
sb::VAO vao;
sb::VBO vbo;
sb::Timer timer;
public:
BoxDemo()
{
Mix_Music *music = Mix_LoadMUS("resource/Field.mp3");
Mix_PlayMusic(music, -1);
load_gl_context();
delegate().subscribe(&BoxDemo::respond, this);
delegate().subscribe(&BoxDemo::respond, this, SDL_MOUSEBUTTONDOWN);
timer.on();
}
void load_gl_context()
{
Game::load_gl_context();
/* Generate a vertex array object ID, bind it as current (requirement of OpenGL) */
vao.generate();
vao.bind();
/* Generate ID for the vertex buffer object that will hold all vertex data. Using one buffer for all attributes, data
* will be copied in one after the other. */
vbo.generate();
vbo.bind();
/*
v0-v1-v2 (front)
v2-v3-v0
v0-v3-v4 (right)
v4-v5-v0
v0-v5-v6 (top)
v6-v1-v0
v1-v6-v7 (left)
v7-v2-v1
v7-v4-v3 (bottom)
v3-v2-v7
v4-v7-v6 (back)
v6-v5-v4
*/
box = sb::Model({
{"position", {
{1, 1, 1}, {-1, 1, 1}, {-1,-1, 1},
{-1,-1, 1}, {1,-1, 1}, {1, 1, 1},
{1, 1, 1}, {1,-1, 1}, {1,-1,-1},
{1,-1,-1}, {1, 1,-1}, {1, 1, 1},
{1, 1, 1}, {1, 1,-1}, {-1, 1,-1},
{-1, 1,-1}, {-1, 1, 1}, {1, 1, 1},
{-1, 1, 1}, {-1, 1,-1}, {-1,-1,-1},
{-1,-1,-1}, {-1,-1, 1}, {-1, 1, 1},
{-1,-1,-1}, {1,-1,-1}, {1,-1, 1},
{1,-1, 1}, {-1,-1, 1}, {-1,-1,-1},
{1,-1,-1}, {-1,-1,-1}, {-1, 1,-1},
{-1, 1,-1}, {1, 1,-1}, {1,-1,-1}
}},
{"uv", {
{1, 1}, {0, 1}, {0, 0},
{0, 0}, {1, 0}, {1, 1},
{0, 1}, {0, 0}, {1, 0},
{1, 0}, {1, 1}, {0, 1},
{0, 1}, {0, 0}, {1, 0},
{1, 0}, {1, 1}, {0, 1},
{1, 1}, {0, 1}, {0, 0},
{0, 0}, {1, 0}, {1, 1},
{0, 0}, {1, 0}, {1, 1},
{1, 1}, {0, 1}, {0, 0},
{0, 0}, {1, 0}, {1, 1},
{1, 1}, {0, 1}, {0, 0}
}}
});
box.texture(sb::Texture());
box.texture(0).load("resource/tile.png");
/* Create a 1x1 white texture */
glm::vec2 fake_size = {1, 1};
unsigned char fake_color[4] = {255, 255, 255, 255};
fake.generate(fake_size);
fake.load(fake_color, fake_size);
/* Add color attributes and a full white texture */
background.attributes({
{0.2f, 0.6f, 0.8f}, {0.2f, 0.6f, 0.8f}, {1.0f, 1.0f, 0.0f},
{0.2f, 0.6f, 0.8f}, {1.0f, 1.0f, 0.0f}, {1.0f, 1.0f, 0.0f}
}, "color");
background.texture(fake);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
/* Create a projection matrix */
glm::ivec2 resolution = get_display().window_size();
float aspect = resolution.x / (float) resolution.y;
projection = glm::perspective(glm::radians(40.0f) / aspect, aspect, 0.1f, 100.0f);
view = glm::lookAt(glm::vec3{4.0f, 3.0f, 3.0f}, glm::vec3{0.0f, 0.65f, 0.0f}, glm::vec3{0.0f, 1.0f, 0.0f});
/* Load shader programs, associate model attributes with shader variables, read uniform IDs */
GLuint triangle_vertex_shader = load_shader("shaders/triangle.vert", GL_VERTEX_SHADER);
GLuint fragment_shader = load_shader("shaders/all.frag", GL_FRAGMENT_SHADER);
GLuint flat_vertex_shader = load_shader("shaders/flat.vert", GL_VERTEX_SHADER);
world_program = glCreateProgram();
glAttachShader(world_program, triangle_vertex_shader);
glAttachShader(world_program, fragment_shader);
link_shader(world_program);
box.attributes("position")->bind("in_Position", world_program);
box.attributes("uv")->bind("vertexUV", world_program);
flat_program = glCreateProgram();
glAttachShader(flat_program, flat_vertex_shader);
glAttachShader(flat_program, fragment_shader);
link_shader(flat_program);
background.attributes("position")->bind("in_Position", flat_program);
background.attributes("uv")->bind("vertexUV", flat_program);
background.attributes("color")->bind("in_Color", flat_program);
mvp_uniform_id = glGetUniformLocation(world_program, "MVP");
/* Fill VBO with attribute data */
vbo.allocate(background.size() + box.size(), GL_STATIC_DRAW);
vbo.add(*sb::Plane::position);
vbo.add(*sb::Plane::uv);
vbo.add(*background.attributes("color"));
vbo.add(*box.attributes("position"));
vbo.add(*box.attributes("uv"));
sb::Log::gl_errors("after filling VBO");
for (GLuint program : {flat_program, world_program})
{
/* Set the active texture unit to #0, Get the texture uniform from the shader and set it use texture #0. See
* https://www.khronos.org/opengl/wiki/Sampler_(GLSL)#Binding_textures_to_samplers */
glUseProgram(program);
glActiveTexture(GL_TEXTURE0);
glUniform1i(glGetUniformLocation(program, "myTextureSampler"), 0);
}
glDepthFunc(GL_LESS);
}
void respond(SDL_Event& event)
{
if (delegate().compare(event, "play-sound") || event.type == SDL_MOUSEBUTTONDOWN)
{
Mix_Chunk* music = Mix_LoadWAV("resource/Ag.ogg");
Mix_PlayChannel(-1, music, 0);
}
}
void update(float timestamp)
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
/* Keep timer up to date */
timer.update(timestamp);
/* Draw background */
glUseProgram(flat_program);
box.disable();
background.bind();
background.enable();
glDrawArrays(GL_TRIANGLES, 0, background.attributes("position")->count());
/* Rotate projection matrix */
model = glm::rotate(model, timer.delta(0.25f), glm::vec3(0.0f, 1.0f, 0.0f));
mvp = projection * view * model;
glUseProgram(world_program);
/* Pass the MVP matrix to the triangle vertex shader using the memory address of the top left value */
glUniformMatrix4fv(mvp_uniform_id, 1, GL_FALSE, &mvp[0][0]);
/* Set the color attribute to white so the texture will not be modified */
glVertexAttrib3f(glGetAttribLocation(world_program, "in_Color"), 1, 1, 1);
/* Draw the box */
glEnable(GL_DEPTH_TEST);
background.disable();
box.bind();
box.enable();
glDrawArrays(GL_TRIANGLES, 0, box.attributes("position")->count());
SDL_GL_SwapWindow(window());
}
};
int main()
{
BoxDemo demo = BoxDemo();
demo.run();
demo.quit();
return 0;
}