/* +------------------------------------------------------+ ____/ \____ /| - Open source game framework licensed to freely use, | \ / / | copy, modify and sell without restriction | +--\ ^__^ /--+ | | | ~/ \~ | | - created for | | ~~~~~~~~~~~~ | +------------------------------------------------------+ | SPACE ~~~~~ | / | ~~~~~~~ BOX |/ +--------------+ Display a rotating box with a space texture and gradient background using SDL, OpenGL, and [SPACE BOX]. */ #include #include #include #include #include #include #include #include #include #include #include #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; }