commit 492acd0c26172d7757c79ff60e996036d1c32660 Author: Relintai Date: Mon Dec 18 17:06:11 2023 +0100 Initial commit. Did not add some dependencies as they will be dropped. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..08a7299 --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ + +bin/ +obj/ + +*.layout +*.depend + + +# These are tmeporary: +libs/ +*.bmp + + + + diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..1f16081 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,19 @@ +Copyright (c) 2023-present Péter Magyar. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..72d8791 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# SFW - Simple Framework + diff --git a/application.cpp b/application.cpp new file mode 100644 index 0000000..70258af --- /dev/null +++ b/application.cpp @@ -0,0 +1,121 @@ +#include "application.h" + +#include + +void Application::event(const SDL_Event &ev) { + if (ev.type == SDL_QUIT) { + running = false; + } + + scene->event(ev); +} + +void Application::update(float delta) { + scene->update(delta); +} + +void Application::render() { + scene->render(); + + SDL_GL_SwapWindow(window); +} + +void Application::main_loop() { + std::chrono::high_resolution_clock::time_point start = std::chrono::high_resolution_clock::now(); + + //handle input + SDL_Event current_event; + + while (SDL_PollEvent(¤t_event)) { + event(current_event); + } + + //update_world + update(frame_delta); + + //render + render(); + + std::chrono::high_resolution_clock::time_point end = std::chrono::high_resolution_clock::now(); + + std::chrono::duration elapsed_seconds = end - start; + + double t = elapsed_seconds.count(); + + double tfps = 1.0 / static_cast(target_fps); + double remaining = tfps - t; + + if (remaining > 0) { + Uint32 fms = static_cast(remaining * 1000.0); + + frame_delta = tfps; + + SDL_Delay(fms); + } else { + frame_delta = t; + } +} + +Application::Application() { + _instance = this; + + running = true; + target_fps = 60; + + scene = NULL; + + frame_delta = 0; + + SDL_SetMainReady(); + + int error = SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER); + + if (error) { + SDL_Log("SDL_Init fail: %s\n", SDL_GetError()); + + running = false; + return; + } + +#if defined(_WIN64) || defined(_WIN32) + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); +#else + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); +#endif + + window = SDL_CreateWindow("SDL + OpenGL", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 640, 480, SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE); + + if (!window) { + SDL_Log("SDL_CreateWindow Failed! %s\n", SDL_GetError()); + + running = false; + + return; + } + + context = SDL_GL_CreateContext(window); + + #if defined(_WIN64) || defined(_WIN32) + gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress); + #endif // defined + + printf("%s\n", glGetString(GL_VERSION)); +} +Application::~Application() { + SDL_DestroyWindow(window); + + window = NULL; + + SDL_Quit(); +} + +Application *Application::get_singleton() { + return _instance; +} + +Application * Application::_instance = NULL; + diff --git a/application.h b/application.h new file mode 100644 index 0000000..4a06987 --- /dev/null +++ b/application.h @@ -0,0 +1,38 @@ +#ifndef APPLICATION_H +#define APPLICATION_H + +#include + +#include + +#include "opengl.h" +#include "scene.h" + +class Application { +public: + bool running; + int target_fps; + + virtual void event(const SDL_Event &ev); + virtual void update(float delta); + virtual void render(); + + void main_loop(); + + Application(); + virtual ~Application(); + + Scene *scene; + + SDL_Window *window; + SDL_GLContext context; + + static Application *get_singleton(); + + double frame_delta; + +protected: + static Application * _instance; +}; + +#endif // APPLICATION_H diff --git a/camera.cpp b/camera.cpp new file mode 100644 index 0000000..cb25b7d --- /dev/null +++ b/camera.cpp @@ -0,0 +1,73 @@ +#include "camera.h" + +#include "./libs/glm/gtc/matrix_transform.hpp" + + +void Camera::bind() { + make_current(); + + model_view_matrix = glm::mat4(1); +} + +void Camera::make_current() { + current_camera = this; +} + +Camera::Camera() { + width = 2; + height = 2; + fov = glm::radians(45.0); + + position = glm::vec3(0, 0, 0); + rotation = glm::vec3(0, 0, 0); + scale = glm::vec3(1, 1, 1); +} +Camera::~Camera() { +} + +Camera* Camera::current_camera = NULL; + +void OrthographicCamera::bind() { + Camera::bind(); + + projection_matrix = glm::ortho(-(width / 2.0), + width / 2.0, + -(height / 2.0), + height / 2.0); + + projection_matrix = glm::translate(projection_matrix, -position); + + projection_matrix = glm::rotate(projection_matrix, rotation.x, glm::vec3(1, 0, 0)); + projection_matrix = glm::rotate(projection_matrix, rotation.y, glm::vec3(0, 1, 0)); + projection_matrix = glm::rotate(projection_matrix, rotation.z, glm::vec3(0, 0, 1)); + + projection_matrix = glm::scale(projection_matrix, scale); +} + +OrthographicCamera::OrthographicCamera() : Camera() { +} +OrthographicCamera::~OrthographicCamera() { +} + +void PerspectiveCamera::bind() { + Camera::bind(); + + projection_matrix = glm::perspectiveFov(fov, width, height, near, far); + + projection_matrix = glm::translate(projection_matrix, -position); + + projection_matrix = glm::rotate(projection_matrix, rotation.x, glm::vec3(1, 0, 0)); + projection_matrix = glm::rotate(projection_matrix, rotation.y, glm::vec3(0, 1, 0)); + projection_matrix = glm::rotate(projection_matrix, rotation.z, glm::vec3(0, 0, 1)); + + projection_matrix = glm::scale(projection_matrix, scale); +} + +PerspectiveCamera::PerspectiveCamera() : Camera() { + near = 0.1; + far = 10; +} + +PerspectiveCamera::~PerspectiveCamera() { +} + diff --git a/camera.h b/camera.h new file mode 100644 index 0000000..3c25872 --- /dev/null +++ b/camera.h @@ -0,0 +1,51 @@ +#ifndef CAMERA_H +#define CAMERA_H + +#include "opengl.h" + +#include "./libs/glm/vec3.hpp" +#include "./libs/glm/matrix.hpp" + +class Camera { +public: + virtual void bind(); + + void make_current(); + + Camera(); + virtual ~Camera(); + + float width; + float height; + float fov; + + glm::vec3 position; + glm::vec3 rotation; + glm::vec3 scale; + + static Camera* current_camera; + + glm::mat4x4 projection_matrix; + glm::mat4x4 model_view_matrix; +}; + +class OrthographicCamera : public Camera { +public: + void bind(); + + OrthographicCamera(); + ~OrthographicCamera(); +}; + +class PerspectiveCamera : public Camera { +public: + float near; + float far; + + void bind(); + + PerspectiveCamera(); + ~PerspectiveCamera(); +}; + +#endif // CAMERA_H diff --git a/colored_material.h b/colored_material.h new file mode 100644 index 0000000..6ea0a4b --- /dev/null +++ b/colored_material.h @@ -0,0 +1,73 @@ +#ifndef COLORED_MATERIAL_H +#define COLORED_MATERIAL_H + +#include "material.h" + +#include "./libs/glm/vec4.hpp" +#include "./libs/glm/gtc/type_ptr.hpp" + +#include "camera.h" + +class ColoredMaterial : public Material { +public: + int get_material_id() { + return 1; + } + + void bind_uniforms() { + glUniformMatrix4fv(projection_matrix_location, 1, GL_FALSE, glm::value_ptr(Camera::current_camera->projection_matrix)); + glUniformMatrix4fv(model_view_matrix_location, 1, GL_FALSE, glm::value_ptr(Camera::current_camera->model_view_matrix)); + + glUniform4f(tri_color_uniform_location, color.r, color.g, color.b, color.a); + } + + void setup_uniforms() { + projection_matrix_location = get_uniform("u_proj_matrix"); + model_view_matrix_location = get_uniform("u_model_view_matrix"); + + tri_color_uniform_location = get_uniform("fragment_color"); + } + + const GLchar **get_vertex_shader_source() { + static const GLchar *vertex_shader_source[] = { + "uniform mat4 u_proj_matrix;\n" + "uniform mat4 u_model_view_matrix;\n" + "\n" + "attribute vec4 a_position;\n" + "\n" + "void main() {\n" + " gl_Position = u_proj_matrix * u_model_view_matrix * a_position;\n" + "}" + }; + + return vertex_shader_source; + } + + const GLchar **get_fragment_shader_source() { + static const GLchar *fragment_shader_source[] = { + "precision mediump float;\n" + "\n" + "uniform vec4 fragment_color;\n" + "\n" + "void main() {\n" + " gl_FragColor = fragment_color;\n" + "}" + }; + + return fragment_shader_source; + } + + ColoredMaterial() : Material() { + color = glm::vec4(1, 1, 1, 1); + } + + GLint projection_matrix_location; + GLint model_view_matrix_location; + + GLint tri_color_uniform_location; + glm::vec4 color; +}; + + + +#endif // COLORED_MATERIAL_H diff --git a/compile_linux.sh b/compile_linux.sh new file mode 100755 index 0000000..2cd9c50 --- /dev/null +++ b/compile_linux.sh @@ -0,0 +1,7 @@ + + +#clang++ main.cpp -Wall -o3 -o ./bin/game -Ilibs/SDL2-linux/include -Llibs/SDL2-linux/lib -lSDL2 -lSDL2main + +#g++ main.cpp -Wall -o3 -o ./bin/game -Ilibs/SDL2-linux/include -Llibs/SDL2-linux/lib -lSDL2 -lSDL2main + +g++ main.cpp -Wall -o3 -o ./game $(pkg-config --cflags --libs sdl2 glew) \ No newline at end of file diff --git a/compile_mingw_x64.bat b/compile_mingw_x64.bat new file mode 100755 index 0000000..3045e66 --- /dev/null +++ b/compile_mingw_x64.bat @@ -0,0 +1,5 @@ +rem -g -> debug symbols +rem -o[0-3] -> optimization +rem -Wall -> all warning + +g++ main.cpp ./libs/glad/src/glad.c -Wall -o3 -o ./game.exe -Ilibs/SDL2-mingw/include -Llibs/SDL2-mingw/lib/x64 -Ilibs/glad/include -lSDL2 -lSDL2main -lOpengl32 \ No newline at end of file diff --git a/compile_mingw_x86.bat b/compile_mingw_x86.bat new file mode 100755 index 0000000..8e76c55 --- /dev/null +++ b/compile_mingw_x86.bat @@ -0,0 +1,5 @@ +rem -g -> debug symbols +rem -o[0-3] -> optimization +rem -Wall -> all warning + +g++ main.cpp ./libs/glad/src/glad.c -Wall -o3 -o ./game.exe -Ilibs/SDL2-mingw/include -Llibs/SDL2-mingw/lib/x86 -Ilibs/glad/include -lSDL2 -lSDL2main -lOpengl32 \ No newline at end of file diff --git a/game_application.h b/game_application.h new file mode 100644 index 0000000..5e7fff4 --- /dev/null +++ b/game_application.h @@ -0,0 +1,20 @@ +#ifndef GAME_APPLICATION_H +#define GAME_APPLICATION_H + +#include "application.h" + +#include "game_scene.h" + +class GameApplication : public Application { +public: + GameApplication() : Application() { + scene = new GameScene(); + } + + ~GameApplication() { + delete scene; + } +}; + + +#endif // GAME_APPLICATION_H diff --git a/game_scene.cpp b/game_scene.cpp new file mode 100644 index 0000000..b9bdabb --- /dev/null +++ b/game_scene.cpp @@ -0,0 +1,141 @@ +#include "game_scene.h" + +#include "application.h" + +void GameScene::event(const SDL_Event &ev) { + switch (ev.type) { + case SDL_WINDOWEVENT: { + switch (ev.window.event) { + case SDL_WINDOWEVENT_SIZE_CHANGED: { + int width = ev.window.data1; + int height = ev.window.data2; + + float ar = static_cast(width) / static_cast(height); + + camera->width = camera->height * ar; + + glViewport(0, 0, width, height); + + } break; + } + } break; + case SDL_KEYDOWN: { + + if (ev.key.keysym.scancode == SDL_SCANCODE_A) { + left = true; + } else if (ev.key.keysym.scancode == SDL_SCANCODE_W) { + up = true; + } else if (ev.key.keysym.scancode == SDL_SCANCODE_S) { + down = true; + } else if (ev.key.keysym.scancode == SDL_SCANCODE_D) { + right = true; + } + + + } break; + case SDL_KEYUP: { + + if (ev.key.keysym.scancode == SDL_SCANCODE_A) { + left = false; + } else if (ev.key.keysym.scancode == SDL_SCANCODE_W) { + up = false; + } else if (ev.key.keysym.scancode == SDL_SCANCODE_S) { + down = false; + } else if (ev.key.keysym.scancode == SDL_SCANCODE_D) { + right = false; + } + + } break; + } +} +void GameScene::update(float delta) { + if (up) { + sprite->position.y += delta * 3.0; + } + + if (down) { + sprite->position.y -= delta * 3.0; + } + + if (left) { + sprite->position.x -= delta * 3.0; + } + + if (right) { + sprite->position.x += delta * 3.0; + } + + if (sprite->position.x < 1.5) { + sprite->position.x = 1.5; + } + + if (sprite->position.x > 14.5) { + sprite->position.x = 14.5; + } + + if (sprite->position.y < 1.5) { + sprite->position.y = 1.5; + } + + if (sprite->position.y > 14.5) { + sprite->position.y = 14.5; + } +} +void GameScene::render() { + glClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + + camera->bind(); + + sprite->render(); +} + +GameScene::GameScene() { + left = false; + right = false; + up = false; + down = false; + + camera = new OrthographicCamera(); + camera->width = 16; + camera->height = 16; + camera->position.x = 8; + camera->position.y = 8; + //camera->position.z = -2; + + int w; + int h; + SDL_GetWindowSize(Application::get_singleton()->window, &w, &h); + + float ar = static_cast(w) / static_cast(h); + camera->width = camera->height * ar; + + texture = new Texture(); + texture->load_image("download.bmp"); + + material = new TextureMaterial(); + material->texture = texture; + + sprite = new Sprite(); + sprite->mesh_instance->material = material; + sprite->height = 1; + sprite->width = 1; + sprite->position.x = 8; + sprite->position.y = 8; + + float region_x = 7; + float region_y = 7; + + sprite->region_x = region_x * (1.0 / 16.0); + sprite->region_y = region_y * (1.0 / 16.0); + sprite->region_width = 1.0 / 16.0; + sprite->region_height = 1.0 / 16.0; + + sprite->update_mesh(); +} + +GameScene::~GameScene() { + delete camera; + delete sprite; + delete material; +} diff --git a/game_scene.h b/game_scene.h new file mode 100644 index 0000000..d6285e2 --- /dev/null +++ b/game_scene.h @@ -0,0 +1,35 @@ +#ifndef GAME_SCENE_H +#define GAME_SCENE_H + +#include "scene.h" + +#include "colored_material.h" +#include "camera.h" +#include "mesh.h" +#include "mesh_instance.h" +#include "sprite.h" +#include "texture_material.h" +#include "texture.h" + +class GameScene : public Scene { +public: + virtual void event(const SDL_Event &ev); + virtual void update(float delta); + virtual void render(); + + GameScene(); + ~GameScene(); + + bool left; + bool right; + bool up; + bool down; + + OrthographicCamera *camera; + Sprite *sprite; + Texture *texture; + TextureMaterial *material; +}; + + +#endif // GAME_SCENE_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..cb2155f --- /dev/null +++ b/main.cpp @@ -0,0 +1,37 @@ +#ifdef __EMSCRIPTEN__ +#include +#endif // __EMSCRIPTEN__ + +#include "application.h" + +#include "game_application.h" + +//érdekesség +//https://gist.github.com/reduz/9635c731f0592d7e526367c6063b8f8f +//https://gist.github.com/reduz/9b9d1278848237fd9a9a8b6cc77c8270 +//https://github.com/reduz/larvita3 + +Application *application = NULL; + +void handle_frame() { + application->main_loop(); +} + +int main(int argc, char** argv) +{ + application = new GameApplication(); + + #ifdef __EMSCRIPTEN__ + emscripten_set_main_loop(handle_frame, 0, 1); + #else + + while (application->running) { + application->main_loop(); + } + + delete application; + + #endif // __EMSCRIPTEN__ + + return 0; +} diff --git a/material.cpp b/material.cpp new file mode 100644 index 0000000..a3de816 --- /dev/null +++ b/material.cpp @@ -0,0 +1,63 @@ +#include "material.h" + +#include + +void Material::bind() { + //csak main thread! + + if (!shader) { + shader = ShaderCache::get_singleton()->get_shader(get_material_id()); + + if (!shader) { + shader = new Shader(); + + shader->set_vertex_shader_source(get_vertex_shader_source()); + shader->set_fragment_shader_source(get_fragment_shader_source()); + + shader->compile(); + + ShaderCache::get_singleton()->add_shader(get_material_id(), shader); + } + + setup_uniforms(); + } + + if (current_material && current_material != this) { + current_material->unbind(); + + setup_state(); + } + + current_material = this; + + shader->bind(); + + bind_uniforms(); +} + +void Material::unbind() { +} +void Material::bind_uniforms() { +} +void Material::setup_uniforms() { +} +void Material::setup_state() { +} + +GLint Material::get_uniform(const char* name) { + GLint uniform = glGetUniformLocation(shader->program, name); + + if (uniform == -1) { + printf("%s is not a valid glsl program variable!\n", name); + } + + return uniform; +} + +Material::Material() { + shader = NULL; +} +Material::~Material() { +} + +Material *Material::current_material = NULL; diff --git a/material.h b/material.h new file mode 100644 index 0000000..04c1780 --- /dev/null +++ b/material.h @@ -0,0 +1,31 @@ +#ifndef MATERIAL_H +#define MATERIAL_H + +#include "opengl.h" +#include "shader.h" + +class Material { +public: + void bind(); + + virtual void unbind(); + virtual int get_material_id() = 0; + virtual void bind_uniforms(); + virtual void setup_uniforms(); + virtual void setup_state(); + virtual const GLchar **get_vertex_shader_source() = 0; + virtual const GLchar **get_fragment_shader_source() = 0; + + GLint get_uniform(const char* name); + + Material(); + virtual ~Material(); + +protected: + static Material *current_material; + + Shader * shader; +}; + + +#endif // MATERIAL_H diff --git a/mesh.cpp b/mesh.cpp new file mode 100644 index 0000000..e73dde4 --- /dev/null +++ b/mesh.cpp @@ -0,0 +1,187 @@ +#include "mesh.h" + +#include "shader.h" + +void Mesh::add_vertex2(float x, float y) { + vertices.push_back(x); + vertices.push_back(y); +} +void Mesh::add_vertex3(float x, float y, float z) { + vertices.push_back(x); + vertices.push_back(y); + vertices.push_back(z); +} + +void Mesh::add_normal(float x, float y, float z) { + normals.push_back(x); + normals.push_back(y); + normals.push_back(z); +} +void Mesh::add_color(float r, float g, float b, float a) { + colors.push_back(r); + colors.push_back(g); + colors.push_back(b); + colors.push_back(a); +} + +void Mesh::add_uv(float u, float v) { + uvs.push_back(u); + uvs.push_back(v); +} + +void Mesh::add_index(uint32_t index) { + indices.push_back(index); +} +void Mesh::add_triangle(uint32_t i1, uint32_t i2, uint32_t i3) { + indices.push_back(i1); + indices.push_back(i2); + indices.push_back(i3); +} + +void Mesh::clear() { + vertices.clear(); + normals.clear(); + colors.clear(); + uvs.clear(); + indices.clear(); +} + +void Mesh::upload() { + if (vertices.size() == 0) { + return; + } + + if (!VBO) { + glGenBuffers(1, &VBO); + } + + vertices_vbo_size = sizeof(float) * vertices.size(); + normals_vbo_size = sizeof(float) * normals.size(); + colors_vbo_size = sizeof(float) * colors.size(); + uvs_vbo_size = sizeof(float) * uvs.size(); + indices_vbo_size = sizeof(float) * indices.size(); + + glBindBuffer(GL_ARRAY_BUFFER, VBO); + glBufferData(GL_ARRAY_BUFFER, vertices_vbo_size + normals_vbo_size + colors_vbo_size + uvs_vbo_size, NULL, GL_STATIC_DRAW); + + glBufferSubData(GL_ARRAY_BUFFER, 0, vertices_vbo_size, &vertices[0]); + + if (normals_vbo_size > 0) { + glBufferSubData(GL_ARRAY_BUFFER, vertices_vbo_size, normals_vbo_size, &normals[0]); + } + + if (colors_vbo_size > 0) { + glBufferSubData(GL_ARRAY_BUFFER, vertices_vbo_size + normals_vbo_size, colors_vbo_size, &colors[0]); + } + + if (uvs_vbo_size > 0) { + glBufferSubData(GL_ARRAY_BUFFER, vertices_vbo_size + normals_vbo_size + colors_vbo_size, uvs_vbo_size, &uvs[0]); + } + + if (indices_vbo_size > 0) { + if (!IBO) { + glGenBuffers(1, &IBO); + } + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices_vbo_size, &indices[0], GL_STATIC_DRAW); + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); +} +void Mesh::destroy() { + if (VBO) { + glDeleteBuffers(1, &VBO); + VBO = 0; + } + + if (IBO) { + glDeleteBuffers(1, &IBO); + IBO = 0; + } +} +void Mesh::render() { + if (vertices.size() == 0) { + return; + } + + if (!Shader::current_shader) { + return; + } + + glBindBuffer(GL_ARRAY_BUFFER, VBO); + + glVertexAttribPointer(Shader::ATTRIBUTE_POSITION, vertex_dimesions, GL_FLOAT, GL_FALSE, 0, 0); + glEnableVertexAttribArray(Shader::ATTRIBUTE_POSITION); + + if (normals_vbo_size > 0) { + glVertexAttribPointer(Shader::ATTRIBUTE_NORMAL, 3, GL_FLOAT, GL_FALSE, 0, (void*)(vertices_vbo_size)); + glEnableVertexAttribArray(Shader::ATTRIBUTE_NORMAL); + } + + if (colors_vbo_size > 0) { + glVertexAttribPointer(Shader::ATTRIBUTE_COLOR, 4, GL_FLOAT, GL_FALSE, 0, (void*)(vertices_vbo_size + normals_vbo_size)); + glEnableVertexAttribArray(Shader::ATTRIBUTE_COLOR); + } + + if (uvs_vbo_size > 0) { + glVertexAttribPointer(Shader::ATTRIBUTE_UV, 2, GL_FLOAT, GL_FALSE, 0, (void*)(vertices_vbo_size + normals_vbo_size + colors_vbo_size)); + glEnableVertexAttribArray(Shader::ATTRIBUTE_UV); + } + + if (indices_vbo_size > 0) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IBO); + + glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, (GLvoid*)0); + } else { + glDrawArrays(GL_TRIANGLES, 0, vertices.size()); + } + + glDisableVertexAttribArray(Shader::ATTRIBUTE_POSITION); + + if (normals_vbo_size > 0) { + glDisableVertexAttribArray(Shader::ATTRIBUTE_NORMAL); + } + + if (colors_vbo_size > 0) { + glDisableVertexAttribArray(Shader::ATTRIBUTE_COLOR); + } + + if (uvs_vbo_size > 0) { + glDisableVertexAttribArray(Shader::ATTRIBUTE_UV); + } + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); +} + +Mesh::Mesh() { + VBO = 0; + IBO = 0; + + vertex_dimesions = 3; + + vertices_vbo_size = 0; + normals_vbo_size = 0; + colors_vbo_size = 0; + uvs_vbo_size = 0; + indices_vbo_size = 0; +} +Mesh::Mesh(int vert_dim) { + VBO = 0; + IBO = 0; + + vertex_dimesions = vert_dim; + + vertices_vbo_size = 0; + normals_vbo_size = 0; + colors_vbo_size = 0; + uvs_vbo_size = 0; + indices_vbo_size = 0; +} +Mesh::~Mesh() { + destroy(); + clear(); +} diff --git a/mesh.h b/mesh.h new file mode 100644 index 0000000..a3bbfbf --- /dev/null +++ b/mesh.h @@ -0,0 +1,51 @@ +#ifndef MESH_H +#define MESH_H + +#include +#include + +#include "opengl.h" + +class Mesh { +public: + void add_vertex2(float x, float y); + void add_vertex3(float x, float y, float z); + + void add_normal(float x, float y, float z); + void add_color(float r = 1, float g = 1, float b = 1, float a = 1); + + void add_uv(float u, float v); + + void add_index(uint32_t index); + void add_triangle(uint32_t i1, uint32_t i2, uint32_t i3); + + void clear(); + + void upload(); + void destroy(); + void render(); + + Mesh(); + Mesh(int vert_dim); + virtual ~Mesh(); + + GLuint VBO; + GLuint IBO; + + int vertex_dimesions; + + std::vector vertices; + std::vector normals; + std::vector colors; + std::vector uvs; + std::vector indices; + +protected: + uint32_t vertices_vbo_size; + uint32_t normals_vbo_size; + uint32_t colors_vbo_size; + uint32_t uvs_vbo_size; + uint32_t indices_vbo_size; +}; + +#endif // MESH_H diff --git a/mesh_instance.cpp b/mesh_instance.cpp new file mode 100644 index 0000000..c8bdf70 --- /dev/null +++ b/mesh_instance.cpp @@ -0,0 +1,53 @@ +#include "mesh_instance.h" + +#include "camera.h" + +#include "./libs/glm/vec3.hpp" +#include "./libs/glm/matrix.hpp" +#include "./libs/glm/gtc/matrix_transform.hpp" + + +void MeshInstance::render() { + if (!mesh) { + return; + } + + glm::mat4 mat_orig = Camera::current_camera->model_view_matrix; + + Camera::current_camera->model_view_matrix = glm::translate(Camera::current_camera->model_view_matrix, position); + + Camera::current_camera->model_view_matrix = glm::rotate(Camera::current_camera->model_view_matrix, rotation.x, glm::vec3(1, 0, 0)); + Camera::current_camera->model_view_matrix = glm::rotate(Camera::current_camera->model_view_matrix, rotation.y, glm::vec3(0, 1, 0)); + Camera::current_camera->model_view_matrix = glm::rotate(Camera::current_camera->model_view_matrix, rotation.z, glm::vec3(0, 0, 1)); + + Camera::current_camera->model_view_matrix = glm::scale(Camera::current_camera->model_view_matrix, scale); + + + if (material) { + material->bind(); + } + + mesh->render(); + + for (uint32_t i = 0; i < children.size(); ++i) { + MeshInstance * c = children[i]; + + if (c) { + c->render(); + } + } + + Camera::current_camera->model_view_matrix = mat_orig; +} + +MeshInstance::MeshInstance() { + material = NULL; + mesh = NULL; + + position = glm::vec3(0, 0, 0); + rotation = glm::vec3(0, 0, 0); + scale = glm::vec3(1, 1, 1); +} +MeshInstance::~MeshInstance() { + children.clear(); +} diff --git a/mesh_instance.h b/mesh_instance.h new file mode 100644 index 0000000..a9e5741 --- /dev/null +++ b/mesh_instance.h @@ -0,0 +1,29 @@ +#ifndef MESH_INSTACE_H +#define MESH_INSTACE_H + +#include + +#include "opengl.h" +#include "material.h" +#include "mesh.h" + +#include "./libs/glm/vec3.hpp" + +class MeshInstance { +public: + void render(); + + MeshInstance(); + ~MeshInstance(); + + Material *material; + Mesh *mesh; + + glm::vec3 position; + glm::vec3 rotation; + glm::vec3 scale; + + std::vector children; +}; + +#endif // MESH_INSTACE_H diff --git a/object_2d.cpp b/object_2d.cpp new file mode 100644 index 0000000..f1e5dcf --- /dev/null +++ b/object_2d.cpp @@ -0,0 +1,11 @@ +#include "object_2d.h" + + +Object2D::Object2D() { + position = glm::vec2(0, 0); + rotation = 0; + scale = glm::vec2(1, 1); +} + +Object2D::~Object2D() { +} diff --git a/object_2d.h b/object_2d.h new file mode 100644 index 0000000..27c3685 --- /dev/null +++ b/object_2d.h @@ -0,0 +1,17 @@ +#ifndef OBJECT_2D_H +#define OBJECT_2D_H + +#include "./libs/glm/vec2.hpp" + +class Object2D { +public: + Object2D(); + virtual ~Object2D(); + + glm::vec2 position; + float rotation; + glm::vec2 scale; +}; + + +#endif // OBJECT_2D_h diff --git a/opengl.h b/opengl.h new file mode 100644 index 0000000..efb1e6d --- /dev/null +++ b/opengl.h @@ -0,0 +1,18 @@ +#ifndef OPENGL_H +#define OPENGL_H + +#if defined(__unix__) && !defined(__ANDROID__) +#define GL_GLEXT_PROTOTYPES +#include +#endif // __unix__ + +#if __ANDROID__ +#define GL_GLEXT_PROTOTYPES +#include +#endif + +#if defined(_WIN64) || defined(_WIN32) +#include +#endif // defined + +#endif // OPENGL_H diff --git a/scene.cpp b/scene.cpp new file mode 100644 index 0000000..e1be32c --- /dev/null +++ b/scene.cpp @@ -0,0 +1,7 @@ +#include "scene.h" + +Scene::Scene() { +} + +Scene::~Scene() { +} diff --git a/scene.h b/scene.h new file mode 100644 index 0000000..2994c3d --- /dev/null +++ b/scene.h @@ -0,0 +1,16 @@ +#ifndef SCENE_H +#define SCENE_H + +#include + +class Scene { +public: + virtual void event(const SDL_Event &ev) = 0; + virtual void update(float delta) = 0; + virtual void render() = 0; + + Scene(); + virtual ~Scene(); +}; + +#endif // APPLICATION_H diff --git a/sdl_linux.cbp b/sdl_linux.cbp new file mode 100644 index 0000000..4b550e3 --- /dev/null +++ b/sdl_linux.cbp @@ -0,0 +1,71 @@ + + + + + + diff --git a/sdl_opengl_win_x64.cbp b/sdl_opengl_win_x64.cbp new file mode 100755 index 0000000..ea93fa7 --- /dev/null +++ b/sdl_opengl_win_x64.cbp @@ -0,0 +1,86 @@ + + + + + + diff --git a/sdl_windows.cbp b/sdl_windows.cbp new file mode 100644 index 0000000..beb5b9b --- /dev/null +++ b/sdl_windows.cbp @@ -0,0 +1,84 @@ + + + + + + diff --git a/shader.cpp b/shader.cpp new file mode 100644 index 0000000..7d30cc4 --- /dev/null +++ b/shader.cpp @@ -0,0 +1,171 @@ +#include "shader.h" + +#include +#include + +bool Shader::bind() { + if (current_shader != this) { + glUseProgram(program); + + current_shader = this; + + return true; + } + + return false; +} + +void Shader::unbind() { + if (current_shader == this) { + glUseProgram(0); + + current_shader = NULL; + } +} + +void Shader::compile() { + if (!program) { + program = glCreateProgram(); + } + + if (!vertex_shader) { + vertex_shader = glCreateShader(GL_VERTEX_SHADER); + } + + if (!fragment_shader) { + fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + } + + const GLchar **vertex_shader_source = get_vertex_shader_source(); + + glShaderSource(vertex_shader, 1, vertex_shader_source, NULL); + glCompileShader(vertex_shader); + + GLint shader_compiled = GL_FALSE; + glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &shader_compiled); + if (shader_compiled != GL_TRUE) { + print_shader_errors(vertex_shader, "compiling Vertex Shader"); + return; + } + + glAttachShader(program, vertex_shader); + + + const GLchar **fragment_shader_source = get_fragment_shader_source(); + + glShaderSource(fragment_shader, 1, fragment_shader_source, NULL); + glCompileShader(fragment_shader); + + shader_compiled = GL_FALSE; + glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &shader_compiled); + if (shader_compiled != GL_TRUE) { + print_shader_errors(fragment_shader, "compiling Fragment Shader"); + return; + } + + glAttachShader(program, fragment_shader); + + glBindAttribLocation(program, ATTRIBUTE_POSITION, "a_position"); + glBindAttribLocation(program, ATTRIBUTE_NORMAL, "a_normal"); + glBindAttribLocation(program, ATTRIBUTE_COLOR, "a_color"); + glBindAttribLocation(program, ATTRIBUTE_UV, "a_uv"); + + glLinkProgram(program); + + GLint program_compiled = GL_FALSE; + glGetProgramiv(program, GL_LINK_STATUS, &program_compiled); + if (program_compiled != GL_TRUE) { + print_program_errors(program); + return; + } +} +void Shader::destroy() { + glDeleteShader(vertex_shader); + glDeleteShader(fragment_shader); + glDeleteProgram(program); +} + +const GLchar **Shader::get_vertex_shader_source() { + return vertex_shader_source; +} +void Shader::set_vertex_shader_source(const GLchar **source) { + vertex_shader_source = source; +} + +const GLchar **Shader::get_fragment_shader_source() { + return fragment_shader_source; +} +void Shader::set_fragment_shader_source(const GLchar **source) { + fragment_shader_source = source; +} + +void Shader::print_shader_errors(const GLuint p_program, const char *name) { + int max_length = 5000; + std::vector error_log(max_length); + + glGetShaderInfoLog(p_program, max_length, &max_length, &error_log[0]); + + printf("Error %s!\n", name); + printf("%s!\n", &error_log[0]); +} + +void Shader::print_program_errors(const GLuint p_program) { + if (glIsProgram(program)) { + int info_length = 0; + int max_length = 5000; + + glGetProgramiv(p_program, GL_INFO_LOG_LENGTH, &info_length); + + char *info_log = new char[max_length]; + + glGetProgramInfoLog(p_program, max_length, &info_length, info_log); + + if (info_length > 0) { + printf("%s\n", info_log); + } + + delete[] info_log; + } else { + printf("print_program_errors: Not a program!\n"); + } +} + +Shader::Shader() { + vertex_shader = 0; + fragment_shader = 0; + program = 0; + + vertex_shader_source = NULL; + fragment_shader_source = NULL; +} +Shader::~Shader() { + destroy(); +} + +Shader *Shader::current_shader = NULL; + +//Meyers singleton +//thread safe +ShaderCache *ShaderCache::get_singleton() { + static ShaderCache instance; + + return &instance; +} + +Shader *ShaderCache::get_shader(const int id) { + return shaders[id]; +} +void ShaderCache::add_shader(const int id, Shader *shader) { + shaders[id] = shader; +} + +ShaderCache::ShaderCache() { +} +ShaderCache::~ShaderCache() { + for (const std::pair n : shaders) { + delete n.second; + } + + shaders.clear(); +} + diff --git a/shader.h b/shader.h new file mode 100644 index 0000000..a41ca8f --- /dev/null +++ b/shader.h @@ -0,0 +1,60 @@ +#ifndef SHADER_H +#define SHADER_H + +#include "opengl.h" +#include + +class Shader { +public: + enum VertexAttributes { + ATTRIBUTE_POSITION = 0, + ATTRIBUTE_NORMAL, + ATTRIBUTE_COLOR, + ATTRIBUTE_UV, + }; + + bool bind(); + void unbind(); + + void compile(); + void destroy(); + + const GLchar **get_vertex_shader_source(); + void set_vertex_shader_source(const GLchar **source); + + const GLchar **get_fragment_shader_source(); + void set_fragment_shader_source(const GLchar **source); + + void print_shader_errors(const GLuint p_program, const char *name); + void print_program_errors(const GLuint p_program); + + Shader(); + ~Shader(); + + GLuint vertex_shader; + GLuint fragment_shader; + GLuint program; + + static Shader * current_shader; + +protected: + const GLchar **vertex_shader_source; + const GLchar **fragment_shader_source; +}; + +class ShaderCache { +public: + static ShaderCache *get_singleton(); + + Shader *get_shader(const int id); + void add_shader(const int id, Shader *shader); + + ShaderCache(); + ~ShaderCache(); + +protected: + std::unordered_map shaders; +}; + + +#endif // SHADER_H diff --git a/sprite.cpp b/sprite.cpp new file mode 100644 index 0000000..00882d1 --- /dev/null +++ b/sprite.cpp @@ -0,0 +1,61 @@ +#include "sprite.h" + +void Sprite::render() { + mesh_instance->position.x = position.x; + mesh_instance->position.y = position.y; + + mesh_instance->rotation.z = rotation; + + mesh_instance->scale.x = scale.x; + mesh_instance->scale.y = scale.y; + + mesh_instance->render(); +} + +void Sprite::update_mesh() { + Mesh *mesh = mesh_instance->mesh; + + mesh->clear(); + + float w2 = width / 2.0; + float h2 = height / 2.0; + + mesh->add_uv(region_x, region_y); + mesh->add_vertex2(-w2, h2); + + mesh->add_uv(region_x + region_width, region_y + region_height); + mesh->add_vertex2(w2, -h2); + + mesh->add_uv(region_x, region_y + region_height); + mesh->add_vertex2(-w2, -h2); + + mesh->add_uv(region_x + region_width, region_y); + mesh->add_vertex2(w2, h2); + + mesh->add_triangle(1, 0, 2); + mesh->add_triangle(0, 1, 3); + + mesh->upload(); +} + +Sprite::Sprite() : Object2D() { + mesh_instance = new MeshInstance(); + mesh_instance->mesh = new Mesh(2); + + width = 1; + height = 1; + + region_x = 0; + region_y = 0; + region_width = 1; + region_height = 1; +} + +Sprite::~Sprite() { + delete mesh_instance->mesh; + delete mesh_instance; +} + + + + diff --git a/sprite.h b/sprite.h new file mode 100644 index 0000000..f6f6c68 --- /dev/null +++ b/sprite.h @@ -0,0 +1,28 @@ +#ifndef SPRITE_H +#define SPRITE_H + +#include "object_2d.h" + +#include "mesh_instance.h" + +class Sprite : public Object2D { +public: + void render(); + void update_mesh(); + + Sprite(); + ~Sprite(); + + MeshInstance *mesh_instance; + + float width; + float height; + + float region_x; + float region_y; + float region_width; + float region_height; +}; + + +#endif // SPRITE_H diff --git a/texture.cpp b/texture.cpp new file mode 100644 index 0000000..697f44e --- /dev/null +++ b/texture.cpp @@ -0,0 +1,51 @@ +#include "texture.h" + +#include + +void Texture::load_image(const char * file_name, const int format, const int internal_components) { + if (image) { + SDL_FreeSurface(image); + image = NULL; + glDeleteTextures(1, &texture); + } + + SDL_Surface *img = SDL_LoadBMP(file_name); + + if (!img) { + printf("Couldn't load %s.\n", file_name); + } else { + image = SDL_ConvertSurfaceFormat(img, SDL_PIXELFORMAT_RGBA32, 0); + SDL_FreeSurface(img); + + glGenTextures(1, &texture); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D(GL_TEXTURE_2D, 0, format, image->w, image->h, 0, internal_components, GL_UNSIGNED_BYTE, image->pixels); + apply_filter(); + } +} + +void Texture::apply_filter() { + GLint params = GL_NEAREST; + + if (filter == TEXTURE_FILTER_LINEAR) { + params = GL_LINEAR; + } + + glBindTexture(GL_TEXTURE_2D, texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, params); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, params); +} + +Texture::Texture() { + filter = TEXTURE_FILTER_NEAREST; + texture = 0; + image = NULL; +} + +Texture::~Texture() { + if (image) { + SDL_FreeSurface(image); + + glDeleteTextures(1, &texture); + } +} diff --git a/texture.h b/texture.h new file mode 100644 index 0000000..35572f1 --- /dev/null +++ b/texture.h @@ -0,0 +1,25 @@ +#ifndef TEXTURE_H +#define TEXTURE_H + +#include "opengl.h" +#include + +class Texture { +public: + enum TextureFilter { + TEXTURE_FILTER_NEAREST = 0, + TEXTURE_FILTER_LINEAR, + }; + + void load_image(const char * file_name, const int format = GL_RGBA, const int internal_components = GL_RGBA); + void apply_filter(); + + TextureFilter filter; + GLuint texture; + SDL_Surface *image; + + Texture(); + virtual ~Texture(); +}; + +#endif // TEXTURE_H diff --git a/texture_material.h b/texture_material.h new file mode 100644 index 0000000..304909a --- /dev/null +++ b/texture_material.h @@ -0,0 +1,94 @@ +#ifndef TEXTURE_MATERIAL_H +#define TEXTURE_MATERIAL_H + +#include "material.h" +#include "texture.h" + +#include "./libs/glm/vec4.hpp" +#include "./libs/glm/gtc/type_ptr.hpp" + +#include "camera.h" + +class TextureMaterial : public Material { +public: + int get_material_id() { + return 3; + } + + void bind_uniforms() { + glUniformMatrix4fv(projection_matrix_location, 1, GL_FALSE, glm::value_ptr(Camera::current_camera->projection_matrix)); + glUniformMatrix4fv(model_view_matrix_location, 1, GL_FALSE, glm::value_ptr(Camera::current_camera->model_view_matrix)); + + if (texture) { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture->texture); + glUniform1i(texture_location, 0); + } + } + + void setup_uniforms() { + projection_matrix_location = get_uniform("u_proj_matrix"); + model_view_matrix_location = get_uniform("u_model_view_matrix"); + + texture_location = get_uniform("u_texture"); + } + + void unbind() { + glDisable(GL_TEXTURE_2D); + } + + void setup_state() { + glEnable(GL_TEXTURE_2D); + } + + const GLchar **get_vertex_shader_source() { + static const GLchar *vertex_shader_source[] = { + "uniform mat4 u_proj_matrix;\n" + "uniform mat4 u_model_view_matrix;\n" + "\n" + "attribute vec4 a_position;\n" + "attribute vec2 a_uv;\n" + "\n" + "varying vec2 v_uv;\n" + "\n" + "void main() {\n" + " v_uv = a_uv;\n" + " gl_Position = u_proj_matrix * u_model_view_matrix * a_position;\n" + "}" + }; + + return vertex_shader_source; + } + + const GLchar **get_fragment_shader_source() { + static const GLchar *fragment_shader_source[] = { + "precision mediump float;\n" + "\n" + "uniform sampler2D u_texture;\n" + "\n" + "varying vec2 v_uv;\n" + "\n" + "void main() {\n" + " gl_FragColor = texture2D(u_texture, v_uv);\n" + "}" + }; + + return fragment_shader_source; + } + + TextureMaterial() : Material() { + texture_location = 0; + texture = NULL; + } + + GLint projection_matrix_location; + GLint model_view_matrix_location; + + GLint texture_location; + + Texture *texture; +}; + + + +#endif // COLORED_MATERIAL_H diff --git a/transparent_texture_material.h b/transparent_texture_material.h new file mode 100644 index 0000000..3690555 --- /dev/null +++ b/transparent_texture_material.h @@ -0,0 +1,100 @@ +#ifndef TRANSPARENT_TEXTURE_MATERIAL_H +#define TRANSPARENT_TEXTURE_MATERIAL_H + +#include "material.h" +#include "texture.h" + +#include "./libs/glm/vec4.hpp" +#include "./libs/glm/gtc/type_ptr.hpp" + +#include "camera.h" + +class TransparentTextureMaterial : public Material { +public: + int get_material_id() { + return 4; + } + + void bind_uniforms() { + glUniformMatrix4fv(projection_matrix_location, 1, GL_FALSE, glm::value_ptr(Camera::current_camera->projection_matrix)); + glUniformMatrix4fv(model_view_matrix_location, 1, GL_FALSE, glm::value_ptr(Camera::current_camera->model_view_matrix)); + + if (texture) { + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture->texture); + glUniform1i(texture_location, 0); + } + } + + void setup_uniforms() { + projection_matrix_location = get_uniform("u_proj_matrix"); + model_view_matrix_location = get_uniform("u_model_view_matrix"); + + texture_location = get_uniform("u_texture"); + } + + void unbind() { + glDisable(GL_TEXTURE_2D); + } + + void setup_state() { + glEnable(GL_TEXTURE_2D); + } + + const GLchar **get_vertex_shader_source() { + static const GLchar *vertex_shader_source[] = { + "uniform mat4 u_proj_matrix;\n" + "uniform mat4 u_model_view_matrix;\n" + "\n" + "attribute vec4 a_position;\n" + "attribute vec2 a_uv;\n" + "\n" + "varying vec2 v_uv;\n" + "\n" + "void main() {\n" + " v_uv = a_uv;\n" + " gl_Position = u_proj_matrix * u_model_view_matrix * a_position;\n" + "}" + }; + + return vertex_shader_source; + } + + const GLchar **get_fragment_shader_source() { + static const GLchar *fragment_shader_source[] = { + "precision mediump float;\n" + "\n" + "uniform sampler2D u_texture;\n" + "\n" + "varying vec2 v_uv;\n" + "\n" + "void main() {\n" + " vec4 col = texture2D(u_texture, v_uv);\n" + "\n" + " if (col.a < 0.1) {\n" + " discard;\n" + " }\n" + "\n" + " gl_FragColor = col;\n" + "}" + }; + + return fragment_shader_source; + } + + TransparentTextureMaterial() : Material() { + texture_location = 0; + texture = NULL; + } + + GLint projection_matrix_location; + GLint model_view_matrix_location; + + GLint texture_location; + + Texture *texture; +}; + + + +#endif // COLORED_MATERIAL_H