diff --git a/.gitignore b/.gitignore index b83b697..9d2adcd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Don't track content of these folders .vscode/* +.VSCodeCounter CMakeFiles/* .vs/* out/* diff --git a/examples/dev_testing/src/main.cpp b/examples/dev_testing/src/main.cpp index 01bb019..58aa5b8 100644 --- a/examples/dev_testing/src/main.cpp +++ b/examples/dev_testing/src/main.cpp @@ -1,4 +1,6 @@ #include "simpleengine/camera.h" +#include "simpleengine/ecs/component/model_componenet.h" +#include "simpleengine/ecs/entity.h" #include "simpleengine/gfx/light.h" #include "simpleengine/gfx/material.h" #include "simpleengine/gfx/model.h" @@ -13,7 +15,7 @@ #include #include -#include +//#include #include #include @@ -141,14 +143,25 @@ int main(int argc, char *argv[]) { se::gfx::Material material(white_texture, 1.f, 0.f, 0.f, 0.f, 0.f); - auto cube = std::make_shared(cube_vertices, cube_indicies, + auto entity = std::make_shared(); + se::gfx::Model model(cube_vertices, cube_indicies, std::optional(material)); + model.calculate_normals(); + + se::ModelComponent model_component(model); + + entity->add_component(model_component); + entity->translate(3.5f, 0.f, 0.f); + + /* auto cube = std::make_shared(cube_vertices, cube_indicies, std::optional(material)); cube->calculate_normals(); - cube->translate(3.5f, 0.f, 0.f); + cube->translate(3.5f, 0.f, 0.f); */ //game.add_event(cube); - /* auto renderer = std::make_shared(game.get_window(), core_shader); - renderer->add_model(white_texture, cube); + auto renderer = std::make_shared(game.get_window(), core_shader); + renderer->submit_entity(entity); + game.add_event(renderer); + /* renderer->add_model(white_texture, cube); game.add_event(renderer); */ /* auto r_event = std::make_shared(renderer); @@ -157,5 +170,9 @@ int main(int argc, char *argv[]) { auto camera = std::make_shared(game.get_window(), core_shader, 70, glm::vec3(0, 0, 0)); game.add_event(camera); - return game.run(); + int res = game.run(); + + renderer->destroy(); + + return res; } \ No newline at end of file diff --git a/include/simpleengine/component.h b/include/simpleengine/ecs/component/component.h similarity index 58% rename from include/simpleengine/component.h rename to include/simpleengine/ecs/component/component.h index 68dbc7a..e2f03cf 100644 --- a/include/simpleengine/component.h +++ b/include/simpleengine/ecs/component/component.h @@ -1,7 +1,7 @@ #pragma once -#include "gfx/model.h" -#include "event/event.h" +#include "../../gfx/model.h" +#include "../../event/event.h" #include #include @@ -12,16 +12,20 @@ namespace simpleengine { * */ class Component : public simpleengine::Event { + private: + static uint32_t incrementing_handle; + uint32_t handle; public: - Component() = default; + Component() { + handle = incrementing_handle++; + } + + uint32_t get_handle() { + return handle; + } virtual void update(const float& delta_time) override { std::cout << "Component update" << std::endl; } - - virtual std::vector> get_renderable_models() { - std::cout << "Got renderables" << std::endl; - return {}; - } }; } \ No newline at end of file diff --git a/include/simpleengine/ecs/component/model_componenet.h b/include/simpleengine/ecs/component/model_componenet.h new file mode 100644 index 0000000..18f1815 --- /dev/null +++ b/include/simpleengine/ecs/component/model_componenet.h @@ -0,0 +1,28 @@ +#pragma once + +#include "component.h" +#include "../../gfx/model.h" +#include "../../gfx/material.h" + +#include +#include + +namespace simpleengine { + /** + * @brief A Model is a object that will be shown on the screen by a renderer. + * + */ + class ModelComponent : public simpleengine::Component { + public: + gfx::Model model; + //gfx::Material material; + + ModelComponent(gfx::Model model) : model(model) { + + } + + virtual void update(const float& delta_time) override { + std::cout << "Model Component update" << std::endl; + } + }; +} \ No newline at end of file diff --git a/include/simpleengine/ecs/entity.h b/include/simpleengine/ecs/entity.h new file mode 100644 index 0000000..8a2c8b0 --- /dev/null +++ b/include/simpleengine/ecs/entity.h @@ -0,0 +1,103 @@ +#pragma once + +#include "component/component.h" +#include "../transformable.h" +#include "../util.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace simpleengine { + /** + * @brief A Model is a object that will be shown on the screen by a renderer. + * + */ + class Entity : public simpleengine::Event, public simpleengine::Transformable { + private: + static uint32_t incrementing_handle; + uint32_t handle; + public: + std::vector> components; + + Entity() : components({}) { + handle = incrementing_handle++; + } + + /* Entity() : components({}) { + std::random_device rd; // obtain a random number from hardware + std::mt19937 gen(rd()); // seed the generator + std::uniform_int_distribution distr(1, std::numeric_limits::max()); + + uint16_t num = distr(gen); + uint32_t pid = simpleengine::util::get_current_pid(); + + handle |= num; + handle |= (pid << 16); + + std::string binary = std::bitset<16>(num).to_string(); + std::cout << "Entity handle: " << binary << std::endl; + + } */ + + Entity(std::vector> components) : Entity() { + this->components = components; + } + + uint32_t get_handle() { + return handle; + } + + virtual void update(const float& delta_time) override { + std::cout << "Update entity" << std::endl; + + for (auto& component : components) { + component->update(delta_time); + } + } + + template + bool has_component() const { + for (const auto& comp : components) { + if (std::dynamic_pointer_cast(comp)) { + return true; + } + } + + return false; + } + + template + std::shared_ptr get_component() const { + for (const auto& comp : components) { + if (std::shared_ptr dyn_comp = std::dynamic_pointer_cast(comp); dyn_comp) { + return dyn_comp; + } + } + + return nullptr; + } + + template + void add_component(std::shared_ptr component) { + static_assert(std::is_base_of_v, "Component class must derive from simpleengine::Component"); + + // Only allow one type of the same component + assert(!has_component()); // TODO: Don't assert, give an error + components.push_back(component); + } + + template + void add_component(T component) { + static_assert(std::is_base_of_v, "Component class must derive from simpleengine::Component"); + + // Only allow one type of the same component + assert(!has_component()); // TODO: Don't assert, give an error + components.push_back(std::make_shared(component)); + } + }; +} \ No newline at end of file diff --git a/include/simpleengine/entity.h b/include/simpleengine/entity.h deleted file mode 100644 index c2d7004..0000000 --- a/include/simpleengine/entity.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include "component.h" -#include "transformable.h" - -#include -#include -#include - -namespace simpleengine { - /** - * @brief A Model is a object that will be shown on the screen by a renderer. - * - */ - class Entity : public simpleengine::Event, public simpleengine::Transformable { - public: - std::vector components; - - Entity(std::vector components = {}) : components(components) { - - } - - virtual void update(const float& delta_time) override { - std::cout << "Update entity" << std::endl; - - for (auto& component : components) { - component.update(delta_time); - } - } - - virtual std::vector> get_renderable_models() { - std::cout << "Got renderables from entity" << std::endl; - - std::vector> models; - for (auto& component : components) { - std::vector> comp_models = component.get_renderable_models(); - - // Move comp_models into models - models.insert(models.end(), std::make_move_iterator(comp_models.begin()), - std::make_move_iterator(comp_models.end())); - } - - return models; - } - }; -} \ No newline at end of file diff --git a/include/simpleengine/gfx/model.h b/include/simpleengine/gfx/model.h index c1e94b1..73ba370 100644 --- a/include/simpleengine/gfx/model.h +++ b/include/simpleengine/gfx/model.h @@ -23,6 +23,7 @@ namespace simpleengine::gfx { std::vector vertices; std::vector indicies; + Model(std::vector vertices, std::vector indicies, Material material); Model(std::vector vertices, std::vector indicies = std::vector(), std::optional material = std::nullopt); virtual void update(const float& delta_time) override; diff --git a/include/simpleengine/gfx/renderer.h b/include/simpleengine/gfx/renderer.h index ea2686c..5f5f4a6 100644 --- a/include/simpleengine/gfx/renderer.h +++ b/include/simpleengine/gfx/renderer.h @@ -1,10 +1,13 @@ #pragma once +#include "../ecs/entity.h" +#include "material.h" #include "texture.h" #include "shader.h" //#include "renderable.h" #include "model.h" +#include #include namespace simpleengine::gfx { @@ -12,35 +15,58 @@ namespace simpleengine::gfx { private: GLFWwindow* window; public: - class RenderingModel { + class RenderingBuffers { public: - RenderingModel(std::shared_ptr model, simpleengine::gfx::Texture texture, gfx::VBO ebo, - gfx::VBO vbo, gfx::VAO vao) : model(model), texture(texture), ebo(ebo), vbo(vbo), vao(vao) { - - } - - std::shared_ptr model; - simpleengine::gfx::Texture texture; - + gfx::Model& model; gfx::VBO ebo; gfx::VBO vbo; gfx::VAO vao; + + RenderingBuffers(gfx::Model& model, gfx::VBO ebo, gfx::VBO vbo, gfx::VAO vao) : model(model), ebo(ebo), vbo(vbo), vao(vao) { + + } + + /* std::vector& vertices; + std::vector& indicies; */ + /// If these buffers were rendered last update. + //bool rendered; + }; + + class RenderingModel { + public: + std::shared_ptr entity; + std::unordered_map rendering_buffers; + + RenderingModel(std::shared_ptr entity) : entity(entity) { + + } + + /** + * @brief Create and delete buffers for new and old components in entity. + * + */ + void update_buffers(); + + /** + * @brief Destroy the buffers + * + */ + void destroy_buffers(); }; gfx::Shader shader; - std::vector rendering_models; + std::unordered_map rendering_models; Renderer(GLFWwindow* window, gfx::Shader shader); Renderer(GLFWwindow* window, GLuint shader_program); - virtual void add_model(simpleengine::gfx::Texture texture, std::shared_ptr model); - virtual void remove_model(std::shared_ptr model); + virtual void submit_entity(std::shared_ptr entity); + virtual bool withdraw_entity(std::shared_ptr entity); virtual void initialize(); + virtual void destroy(); - virtual void update(const float& delta_time) override { - - } + virtual void update(const float& delta_time) override; virtual void render(GLFWwindow* target) override; }; diff --git a/include/simpleengine/gfx/vao.h b/include/simpleengine/gfx/vao.h index d45bd9f..166b277 100644 --- a/include/simpleengine/gfx/vao.h +++ b/include/simpleengine/gfx/vao.h @@ -54,7 +54,6 @@ namespace simpleengine::gfx { GLenum err = glGetError(); if (err != GL_NO_ERROR) { fprintf(stderr, "Ran into opengl error: 0x%x\n", err); - //std::cerr << "Ran into enum error: " } } @@ -91,6 +90,12 @@ namespace simpleengine::gfx { // rarely happens. Modifying other VAOs requires a call to glBindVertexArray anyways so we generally // don't unbind VAOs (nor VBOs) when it's not directly necessary. glBindVertexArray(0); + + // TODO: Handle opengl errors EVERYWHERE + GLenum err = glGetError(); + if (err != GL_NO_ERROR) { + fprintf(stderr, "Ran into opengl error: 0x%x\n", err); + } } void disable_attrib(const VBO& vbo, GLuint index) const { diff --git a/include/simpleengine/util.h b/include/simpleengine/util.h new file mode 100644 index 0000000..f054b3f --- /dev/null +++ b/include/simpleengine/util.h @@ -0,0 +1,22 @@ +#include + +#ifdef _WIN32 +#include "windows.h" +#elif __linux__ +#include +#endif + +namespace simpleengine { + namespace util { +#ifdef _WIN32 + inline uint32_t get_current_pid(LPCTSTR ProcessName) // non-conflicting function name + { + return GetCurrentProcessId(); + } +#elif __linux__ + inline uint32_t get_current_pid() { + return ::getpid(); + } +#endif + } +} \ No newline at end of file diff --git a/src/ecs/component/component.cpp b/src/ecs/component/component.cpp new file mode 100644 index 0000000..02f50b5 --- /dev/null +++ b/src/ecs/component/component.cpp @@ -0,0 +1,3 @@ +#include "ecs/component/component.h" + +uint32_t simpleengine::Component::incrementing_handle = 0; \ No newline at end of file diff --git a/src/ecs/entity.cpp b/src/ecs/entity.cpp new file mode 100644 index 0000000..01c1846 --- /dev/null +++ b/src/ecs/entity.cpp @@ -0,0 +1,3 @@ +#include "ecs/entity.h" + +uint32_t simpleengine::Entity::incrementing_handle = 0; \ No newline at end of file diff --git a/src/gfx/model.cpp b/src/gfx/model.cpp index f1c56f4..355821a 100644 --- a/src/gfx/model.cpp +++ b/src/gfx/model.cpp @@ -1,6 +1,12 @@ #include "gfx/model.h" +#include namespace simpleengine::gfx { + Model::Model(std::vector vertices, std::vector indicies, Material material) : + material(std::make_optional(material)), vertices(vertices), indicies(indicies) { + + } + Model::Model(std::vector vertices, std::vector indicies, std::optional material) : material(material), vertices(vertices), indicies(indicies) { diff --git a/src/gfx/renderer.cpp b/src/gfx/renderer.cpp index 20392d8..837cf73 100644 --- a/src/gfx/renderer.cpp +++ b/src/gfx/renderer.cpp @@ -1,8 +1,67 @@ #include "gfx/renderer.h" +#include "ecs/component/component.h" +#include "ecs/entity.h" +#include "gfx/model.h" +#include "renderable.h" + +#include "ecs/component/model_componenet.h" + +#include namespace simpleengine::gfx { - Renderer::Renderer(GLFWwindow* window, gfx::Shader shader): window(window), shader(shader) { + void Renderer::RenderingModel::update_buffers() { + if (std::shared_ptr comp = entity->get_component()) { + auto iter = rendering_buffers.find(comp->get_handle()); + if (iter == rendering_buffers.end()) { + std::cout << "Creating buffer for ModelComponent (" << comp->get_handle() << ")..." << std::endl; + auto ebo = gfx::VBO::init(GL_ELEMENT_ARRAY_BUFFER, false); + auto vbo = gfx::VBO::init(GL_ARRAY_BUFFER, false); + auto vao = gfx::VAO::init(); + auto& model = comp->model; + + // Create and setup the EBO, VAO, and VBOs. + vao.bind(); + vbo.buffer(model.vertices.data(), 0, sizeof(LitVertex) * model.vertices.size()); + if (!model.indicies.empty()) { + ebo.buffer(model.indicies.data(), 0, sizeof(GLuint) * model.indicies.size()); + } + + // Enable VAO attributes + vao.enable_attrib(vbo, 0, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, position)); + vao.enable_attrib(vbo, 1, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, color)); + vao.enable_attrib(vbo, 2, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, normal)); + vao.enable_attrib(vbo, 3, 2, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, tex_coord)); + vao.enable_attrib(vbo, 4, 1, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, texture_id)); + + RenderingBuffers buffers(comp->model, ebo, vbo, vao); + rendering_buffers.emplace(comp->get_handle(), buffers); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindVertexArray(0); + + std::cout << "Finished creating ModelComponent buffers!" << std::endl; + } else { + std::cout << "Already exists" << std::endl; + } + } + } + + void Renderer::RenderingModel::destroy_buffers() { + std::cout << "Destroying buffers for entity!" << std::endl; + + // Iterate through all buffer lists and destroy each inner buffer. + for (auto& pair : rendering_buffers) { + RenderingBuffers& buffers = pair.second; + + buffers.ebo.destroy(); + buffers.vao.destroy(); + buffers.vbo.destroy(); + } + } + + Renderer::Renderer(GLFWwindow* window, gfx::Shader shader): window(window), shader(shader) { + } Renderer::Renderer(GLFWwindow* window, GLuint shader_program): Renderer(window, @@ -10,78 +69,77 @@ namespace simpleengine::gfx { } - void Renderer::add_model(simpleengine::gfx::Texture texture, std::shared_ptr model) { - auto ebo = gfx::VBO::init(GL_ELEMENT_ARRAY_BUFFER, false); - auto vbo = gfx::VBO::init(GL_ARRAY_BUFFER, false); - auto vao = gfx::VAO::init(); - - // Create and setup the EBO, VAO, and VBOs. - vao.bind(); - vbo.buffer(model->vertices.data(), 0, sizeof(LitVertex) * model->vertices.size()); - if (!model->indicies.empty()) { - ebo.buffer(model->indicies.data(), 0, model->indicies.size() * sizeof(GLuint)); - } - - // Enable VAO attributes - vao.enable_attrib(vbo, 0, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, position)); - vao.enable_attrib(vbo, 1, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, color)); - vao.enable_attrib(vbo, 2, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, normal)); - vao.enable_attrib(vbo, 3, 2, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, tex_coord)); - vao.enable_attrib(vbo, 4, 1, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, texture_id)); - - glBindBuffer(GL_ARRAY_BUFFER, 0); - glBindVertexArray(0); - - // Create the RenderingModel struct and store it in the vector for later. - RenderingModel rendering_model( model, texture, ebo, vbo, vao); - rendering_models.push_back(rendering_model); + void Renderer::submit_entity(std::shared_ptr entity) { + std::cout << "Submitting entity (" << entity->get_handle() << ")..." << std::endl; + auto it = rendering_models.emplace(entity->get_handle(), entity); + it.first->second.update_buffers(); } - void Renderer::remove_model(std::shared_ptr model) { - std::cerr << "Removing model is unimplemented!" << std::endl; + bool Renderer::withdraw_entity(std::shared_ptr entity) { + std::cout << "Withdrawing entity (" << entity->get_handle() << ")..."; + auto it = rendering_models.find(entity->get_handle()); + + if (it != rendering_models.end()) { + it->second.destroy_buffers(); + rendering_models.erase(it); + + return true; + } + + return false; + } + + void Renderer::update(const float& delta_time) { + this->render(nullptr); } void Renderer::initialize() { std::cout << "Base Renderer initialized" << std::endl; } + void Renderer::destroy() { + std::cout << "Destroying renderer..." << std::endl; + + for (auto& [handle, rendering] : rendering_models) { + rendering.destroy_buffers(); + //rendering.entity->destroy(); + } + } + void Renderer::render(GLFWwindow* target) { shader.use(); + + for (auto& [handle, rendering] : rendering_models) { + if (rendering.rendering_buffers.size() > 0) { + std::shared_ptr& entity = rendering.entity; - for (RenderingModel& rendering : rendering_models) { - std::shared_ptr& model = rendering.model; + shader.set_uniform_matrix_4f("transform_matrix", entity->transform_matrix, false); + + for (const auto& pair : rendering.rendering_buffers) { + const RenderingBuffers& buffers = pair.second; + Model& model = buffers.model; + std::optional& material = model.material; - shader.use(); - shader.set_uniform_matrix_4f("transform_matrix", model->transform_matrix, false); - - rendering.vao.bind(); - if (model->indicies.empty()) { - glDrawArrays(GL_TRIANGLES, 0, model->vertices.size()); - } else { - glDrawElements(GL_TRIANGLES, model->indicies.size(), GL_UNSIGNED_INT, 0); + shader.set_uniform_int("u_textures", 0, false); + + if (material.has_value()) { + //Material& material = material + shader.set_uniform_float("u_texture_shine", material->shine, false); + shader.set_uniform_float("u_texture_reflectivity", material->reflectivity, false); + + glActiveTexture(GL_TEXTURE0); + glBindTextureUnit(0, material->texture.get_texture_id()); + material->texture.bind(); + } + + buffers.vao.bind(); + if (model.indicies.empty()) { + glDrawArrays(GL_TRIANGLES, 0, model.vertices.size()); + } else { + glDrawElements(GL_TRIANGLES, model.indicies.size(), GL_UNSIGNED_INT, 0); + } + } } - - /* const Texture& texture = rendering.texture; - - shader.set_uniform_matrix_4f("transform_matrix", model->transform_matrix, false); - - const int tex_id = 0; - - // On a batch renderer, you would set an array. - shader.set_uniform_int("u_textures", tex_id, false); - shader.set_uniform_float("u_texture_shine", 1.f, false); - shader.set_uniform_float("u_texture_reflectivity", 0.f, false); - - glActiveTexture(GL_TEXTURE0 + tex_id); - glBindTextureUnit(tex_id, texture.get_texture_id()); - texture.bind(); - - rendering.vao.bind(); - if (model->indicies.empty()) { - glDrawArrays(GL_TRIANGLES, 0, model->vertices.size()); - } else { - glDrawElements(GL_TRIANGLES, model->indicies.size(), GL_UNSIGNED_INT, 0); - } */ } shader.unuse();