Implement ECS with EnTT and a submission based renderer

This commit is contained in:
SeanOMik 2022-09-23 00:44:07 -04:00
parent 08bcb56a5f
commit 3b51dce796
Signed by: SeanOMik
GPG Key ID: 568F326C7EB33ACB
18 changed files with 502 additions and 401 deletions

10
CMake/GetEnTT.cmake Normal file
View File

@ -0,0 +1,10 @@
# Get the entt header libraries.
include(FetchContent)
FetchContent_Declare(
entt
GIT_REPOSITORY https://github.com/skypjack/entt.git
GIT_TAG v3.10.3
)
FetchContent_MakeAvailable(entt)
message("Downloaded entt library to: ${entt_SOURCE_DIR}")
set(ENTT_INCLUDE_DIR ${entt_SOURCE_DIR}/src)

View File

@ -4,6 +4,7 @@ project(SimpleEngine)
include(cmrc/CMakeRC.cmake) include(cmrc/CMakeRC.cmake)
include(CMake/GetStbLibraries.cmake) include(CMake/GetStbLibraries.cmake)
include(CMake/GetEnTT.cmake)
# Add some CMake options: # Add some CMake options:
option(SIMPLE_ENGINE_BUILD_EXAMPLES "Build example projects" ON) option(SIMPLE_ENGINE_BUILD_EXAMPLES "Build example projects" ON)
@ -47,11 +48,11 @@ target_link_libraries(simpleengine PUBLIC ${OPENGL_LIBRARIES})
target_link_libraries(simpleengine PUBLIC assimp) target_link_libraries(simpleengine PUBLIC assimp)
target_link_libraries(simpleengine PRIVATE simpleengine_resources) target_link_libraries(simpleengine PRIVATE simpleengine_resources)
target_include_directories(simpleengine PUBLIC ${STB_INCLUDE_DIR})
# Include some dependencies' include directories # Include some dependencies' include directories
target_include_directories(simpleengine PUBLIC ${OPENGL_INCLUDE_DIR}) target_include_directories(simpleengine PUBLIC ${OPENGL_INCLUDE_DIR})
target_include_directories(simpleengine PUBLIC ${GLM_INCLUDE_DIRS}) target_include_directories(simpleengine PUBLIC ${GLM_INCLUDE_DIRS})
target_include_directories(simpleengine PUBLIC ${STB_INCLUDE_DIR})
target_include_directories(simpleengine PUBLIC ${ENTT_INCLUDE_DIR})
# Add examples as a target if the user has them enabled # Add examples as a target if the user has them enabled
if (SIMPLE_ENGINE_BUILD_EXAMPLES) if (SIMPLE_ENGINE_BUILD_EXAMPLES)

View File

@ -1,8 +1,9 @@
#include "entt/entity/fwd.hpp"
#include "simpleengine/camera.h" #include "simpleengine/camera.h"
#include "simpleengine/ecs/component/mesh_component.h" #include "simpleengine/ecs/component/mesh_component.h"
#include <simpleengine/ecs/component/model_component.h> #include <simpleengine/ecs/component/model_component.h>
#include "simpleengine/ecs/component/transform_component.h"
#include "simpleengine/ecs/entity.h" #include "simpleengine/ecs/entity.h"
#include "simpleengine/entity_manager.h"
#include "simpleengine/gfx/light.h" #include "simpleengine/gfx/light.h"
#include "simpleengine/gfx/material.h" #include "simpleengine/gfx/material.h"
#include "simpleengine/gfx/mesh.h" #include "simpleengine/gfx/mesh.h"
@ -20,6 +21,8 @@
#include <simpleengine/gfx/shaders/core_3d_shader.h> #include <simpleengine/gfx/shaders/core_3d_shader.h>
#include <simpleengine/gfx/model.h> #include <simpleengine/gfx/model.h>
#include <simpleengine/scene.h>
//#include <simpleengine/scene.h> //#include <simpleengine/scene.h>
#include <glm/ext/matrix_clip_space.hpp> #include <glm/ext/matrix_clip_space.hpp>
@ -170,25 +173,29 @@ int main(int argc, char *argv[]) {
textures.emplace(white_texture.type, std::vector<se::gfx::Texture>{ white_texture }); textures.emplace(white_texture.type, std::vector<se::gfx::Texture>{ white_texture });
se::gfx::Material white_material(textures, 1.f, 0.f, 0.f, 0.f, 0.f); se::gfx::Material white_material(textures, 1.f, 0.f, 0.f, 0.f, 0.f);
// Create a renderer
auto renderer = std::make_shared<se::gfx::Renderer>(game.get_window(), core_shader);
game.add_renderable(renderer);
// Create a Scene and give it the renderer
auto scene = std::make_shared<se::Scene>(renderer);
game.add_event(scene);
// Create an Entity in the Scene and add components to it.
se::ecs::Entity entity = scene->create_entity();
entity.add_component<se::ModelComponent>("examples/dev_testing/resources/dragon.obj");
auto& transform_comp = entity.add_component<se::TransformComponent>();
transform_comp.translate(12.f, -4.f, 0.f);
// Create the entity and add the model component to it. // Create the entity and add the model component to it.
/* auto entity = std::make_shared<simpleengine::Entity>(); /* auto entity = std::make_shared<simpleengine::Entity>();
entity->add_component<se::MeshComponent>(cube_vertices, cube_indicies, white_material, true); entity->add_component<se::MeshComponent>(cube_vertices, cube_indicies, white_material, true);
entity->translate(3.5f, 0.f, 0.f); */ entity->translate(3.5f, 0.f, 0.f); */
auto entity = std::make_shared<simpleengine::Entity>(); /* auto entity = std::make_shared<simpleengine::Entity>();
entity->add_component<se::ModelComponent>("examples/dev_testing/resources/dragon.obj"); entity->add_component<se::ModelComponent>("examples/dev_testing/resources/dragon.obj");
entity->translate(12.f, -4.f, 0.f); entity->translate(12.f, -4.f, 0.f); */
// Create a renderer and submit the entity into it.
auto renderer = std::make_shared<se::gfx::Renderer>(game.get_window(), core_shader);
renderer->enable_debug();
renderer->submit_entity(entity);
game.add_renderable(renderer);
// Create an EntityManager, and submit the entity into it.
auto ecs_manager = std::make_shared<se::EntityManager>();
ecs_manager->submit_entity(entity);
game.add_event(ecs_manager);
auto camera = std::make_shared<se::Camera>(game.get_window(), core_shader, 70, glm::vec3(0, 0, 0)); auto camera = std::make_shared<se::Camera>(game.get_window(), core_shader, 70, glm::vec3(0, 0, 0));
game.add_event(camera); game.add_event(camera);

View File

@ -1,31 +0,0 @@
#pragma once
#include "../../gfx/mesh.h"
#include "../../event/event.h"
#include <iostream>
#include <vector>
namespace simpleengine {
/**
* @brief A Model is a object that will be shown on the screen by a renderer.
*
*/
class Component : public simpleengine::Event {
private:
static uint32_t incrementing_handle;
uint32_t handle;
public:
Component() {
handle = incrementing_handle++;
}
uint32_t get_handle() {
return handle;
}
virtual void update(const float& delta_time) override {
std::cout << "Component update" << std::endl;
}
};
}

View File

@ -1,6 +1,5 @@
#pragma once #pragma once
#include "component.h"
#include "../../gfx/mesh.h" #include "../../gfx/mesh.h"
#include "../../gfx/material.h" #include "../../gfx/material.h"
@ -9,37 +8,32 @@
namespace simpleengine { namespace simpleengine {
/** /**
* @brief A Model is a object that will be shown on the screen by a renderer. * @brief A component that contains a Mesh that will be rendered.
* *
*/ */
class MeshComponent : public simpleengine::Component { class MeshComponent {
public: public:
gfx::Mesh model; gfx::Mesh mesh;
//gfx::Material material;
MeshComponent(gfx::Mesh model) : model(model) { MeshComponent(gfx::Mesh mesh) : mesh(mesh) {
} }
MeshComponent(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, gfx::Material material, MeshComponent(std::vector<LitVertex> vertices, std::vector<GLuint> indicies, gfx::Material material,
bool calculate_normals = false): model(vertices, indicies, material) { bool calculate_normals = false): mesh(vertices, indicies, material) {
if (calculate_normals) { if (calculate_normals) {
model.calculate_normals(); mesh.calculate_normals();
} }
} }
MeshComponent(std::vector<LitVertex> vertices, std::vector<GLuint> indicies = std::vector<GLuint>(), MeshComponent(std::vector<LitVertex> vertices, std::vector<GLuint> indicies = std::vector<GLuint>(),
std::optional<gfx::Material> material = std::nullopt, bool calculate_normals = false) : std::optional<gfx::Material> material = std::nullopt, bool calculate_normals = false) :
model(vertices, indicies, material) { mesh(vertices, indicies, material) {
if (calculate_normals) { if (calculate_normals) {
model.calculate_normals(); mesh.calculate_normals();
} }
} }
virtual void update(const float& delta_time) override {
}
}; };
} }

View File

@ -1,6 +1,5 @@
#pragma once #pragma once
#include "component.h"
#include "../../gfx/model.h" #include "../../gfx/model.h"
#include <iostream> #include <iostream>
@ -8,10 +7,10 @@
namespace simpleengine { namespace simpleengine {
/** /**
* @brief A Model is a object that will be shown on the screen by a renderer. * @brief A component that contains a Model that will be rendered.
* *
*/ */
class ModelComponent : public simpleengine::Component { class ModelComponent {
public: public:
gfx::Model model; gfx::Model model;
@ -22,9 +21,5 @@ namespace simpleengine {
ModelComponent(std::string model_file_path) : model(model_file_path) { ModelComponent(std::string model_file_path) : model(model_file_path) {
} }
virtual void update(const float& delta_time) override {
}
}; };
} }

View File

@ -0,0 +1,99 @@
#pragma once
#include <glm/ext/matrix_transform.hpp>
#include <glm/glm.hpp>
namespace simpleengine {
/**
* @brief A component that contains a Mesh that will be rendered.
*
*/
class TransformComponent {
public:
glm::mat4 transform_matrix;
TransformComponent() : transform_matrix(glm::mat4(1.f)) {
}
TransformComponent(glm::mat4 transform_matrix) : transform_matrix(transform_matrix) {
}
friend TransformComponent operator+(TransformComponent lhs, const TransformComponent& rhs) {
lhs.transform_matrix += rhs.transform_matrix;
return lhs;
}
friend TransformComponent operator-(TransformComponent lhs, const TransformComponent& rhs) {
lhs.transform_matrix -= rhs.transform_matrix;
return lhs;
}
friend TransformComponent operator*(TransformComponent lhs, const TransformComponent& rhs) {
lhs.transform_matrix *= rhs.transform_matrix;
return lhs;
}
friend TransformComponent operator/(TransformComponent lhs, const TransformComponent& rhs) {
lhs.transform_matrix /= rhs.transform_matrix;
return lhs;
}
virtual void combine_transform(const glm::mat4& transform_matrix) {
this->transform_matrix *= transform_matrix;
}
virtual void combine_transform(const TransformComponent& transformable) {
transform_matrix = transformable.transform_matrix;
}
virtual void translate(float x, float y, float z) {
transform_matrix = glm::translate(transform_matrix, glm::vec3(x, y, z));
}
/* virtual void translate(const glm::vec3& vec) {
transform_matrix = glm::translate(transform_matrix, vec);
} */
virtual glm::mat4 rotation_matrix(float degrees, glm::vec3 rotation_axis) const {
return glm::rotate(transform_matrix, glm::radians(degrees), rotation_axis);
}
virtual glm::mat4 rotation_x_matrix(float degrees) const {
return rotation_matrix(degrees, glm::vec3(1, 0, 0));
}
virtual glm::mat4 rotation_y_matrix(float degrees) const {
return rotation_matrix(degrees, glm::vec3(0, 1, 0));
}
virtual glm::mat4 rotation_z_matrix(float degrees) const {
return rotation_matrix(degrees, glm::vec3(0, 0, 1));
}
virtual void rotate(float degrees, glm::vec3 rotation_axis) {
transform_matrix = rotation_matrix(degrees, rotation_axis);
}
virtual void rotate_x(float degrees) {
transform_matrix = rotation_x_matrix(degrees);
}
virtual void rotate_y(float degrees) {
transform_matrix = rotation_y_matrix(degrees);
}
virtual void rotate_z(float degrees) {
transform_matrix = rotation_z_matrix(degrees);
}
virtual void scale(glm::vec3 scalar_vec) {
transform_matrix = glm::scale(transform_matrix, scalar_vec);
}
virtual void scale(float scalar) {
transform_matrix = glm::scale(transform_matrix, glm::vec3(scalar, scalar, scalar));
}
};
}

View File

@ -1,116 +1,266 @@
#pragma once #pragma once
#include "component/component.h" #include "entt/entity/fwd.hpp"
#include "../transformable.h" #include <cstddef>
#include "../util.h" #include <entt/entt.hpp>
#include <utility>
#include <iterator> namespace simpleengine::ecs {
#include <vector> class Entity {
#include <iostream>
#include <chrono>
#include <random>
#include <bitset>
#include <type_traits>
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 {
// TODO: Don't extend from Event, create own destroy function
private: private:
static uint32_t incrementing_handle; entt::registry& registry;
uint32_t handle; entt::entity inner;
public: public:
std::vector<std::shared_ptr<Component>> components; /* Entity(entt::entity entity) : inner(entity) {
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<uint16_t> distr(1, std::numeric_limits<std::uint16_t>::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<std::shared_ptr<Component>> components) : Entity() { Entity(entt::registry& registry, entt::entity entity) : registry(registry), inner(entity) {
this->components = components;
} }
uint32_t get_handle() { /**
return handle; * @brief Checks if an identifier refers to a valid entity.
*
* @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#a1795b1221d728f806319a685930f520d
*
* @return true if the identifier is valid
* @return false otherwise
*/
bool is_valid() const {
return registry.valid(inner);
} }
virtual void update(const float& delta_time) override { /**
for (auto& component : components) { * @brief Returns the actual version for an identifier.
component->update(delta_time); *
} * @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#a199babc787d6baa6f7ccce761228a5f6
*
rotate_y(delta_time * 10); // TODO: Remove * @return entt::registry::version_type The version for the given identifier if valid, the tombstone version otherwise.
*/
entt::registry::version_type current_version() const {
return registry.current(inner);
} }
template<typename T> /**
bool has_component() const { * @brief Releases an identifier.
for (const auto& comp : components) { *
if (std::dynamic_pointer_cast<T>(comp)) { * The version is updated and the identifier can be recycled at any time.
return true; *
} * @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#a3d9cb2368384b0952cb54848e777359e
} *
* @return entt::registry::version_type The version of the recycled entity.
return false; */
entt::registry::version_type release() {
return registry.release(inner);
} }
template<typename T> /**
std::shared_ptr<T> get_component() const { * @brief Releases an identifier.
for (const auto& comp : components) { *
if (std::shared_ptr<T> dyn_comp = std::dynamic_pointer_cast<T>(comp); dyn_comp) { * The suggested version or the valid version closest to the suggested one is used instead of the implicitly generated version.
return dyn_comp; *
} * @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#af9c919867fc93a7e1a2d0762ac3a9877
} *
* @param version A desired version upon destruction.
return nullptr; * @return entt::registry::version_type The version actually assigned to the entity.
*/
entt::registry::version_type release(entt::registry::version_type version) {
return registry.release(inner, version);
} }
template<typename T> /**
void add_component(std::shared_ptr<T> component) { * @brief Destroys an entity and releases its identifier.
static_assert(std::is_base_of_v<Component, T>, "Component class must derive from simpleengine::Component"); *
* @warning Adding or removing components to an entity that is being destroyed can result in undefined behavior. Attempting to use an invalid entity results in undefined behavior.
// Only allow one type of the same component *
assert(!has_component<T>()); // TODO: Don't assert, give an error * @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#a7b2c0368d508a6af2d094a9fc592a4a0
components.push_back(component); *
* @return entt::registry::version_type The version of the recycled entity.
*/
entt::registry::version_type destroy() {
return registry.destroy(inner);
} }
template<typename T> /**
void add_component(T component) { * @brief Destroys an entity and releases its identifier.
static_assert(std::is_base_of_v<Component, T>, "Component class must derive from simpleengine::Component"); *
* The suggested version or the valid version closest to the suggested one is used instead of the implicitly generated version.
// Only allow one type of the same component *
assert(!has_component<T>()); // TODO: Don't assert, give an error * @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#adc175a0d3bcf83a133c63890c674ceb3
components.push_back(std::make_shared<T>(component)); *
* @param version A desired version upon destruction.
* @return entt::registry::version_type The version actually assigned to the entity.
*/
entt::registry::version_type destroy(entt::registry::version_type version) {
return registry.destroy(inner, version);
} }
template<typename T, typename ...Args> /**
std::shared_ptr<T> add_component(Args&&... args) { * @brief Assigns the given component to an entity.
static_assert(std::is_base_of_v<Component, T>, "Component class must derive from simpleengine::Component"); *
* The component must have a proper constructor or be of aggregate type.
*
* @warning Attempting to use an invalid entity or to assign a component to an entity that already owns it results in undefined behavior.
*
* @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#a4c9c0532972adc7a930a50888a97efdf
*
* @tparam Component Type of component to create.
* @tparam Args Types of arguments to use to construct the component.
*
* @param args Parameters to use to initialize the component.
*
* @return decltype(auto) A reference to the newly created component.
*/
template<typename Component, typename... Args>
decltype(auto) add_component(Args&&... args) {
return registry.emplace<Component>(inner, std::forward<Args>(args)...);
}
// Only allow one type of the same component /**
assert(!has_component<T>()); // TODO: Don't assert, give an error * @brief Assigns or replaces the given component for an entity.
auto comp = std::make_shared<T>(std::forward<Args>(args)...); *
components.push_back(comp); * The component must have a proper constructor or be of aggregate type.
*
* @warning Attempting to use an invalid entity or to assign a component to an entity that already owns it results in undefined behavior.
*
* @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#a3bbaebbb9365eef262453e42a8f7dc4b
*
* @tparam Component Type of component to assign or replace.
* @tparam Args Types of arguments to use to construct the component.
*
* @param args Parameters to use to initialize the component.
*
* @return decltype(auto) A reference to the newly created component.
*/
template<typename Component, typename... Args>
decltype(auto) add_or_replace_component(Args&&... args) {
return registry.emplace_or_replace<Component>(inner, std::forward<Args>(args)...);
}
return comp; /**
* @brief Replaces the given component for an entity.
*
* The component must have a proper constructor or be of aggregate type.
*
* @warning Attempting to use an invalid entity or to replace a component of an entity that doesn't own it results in undefined behavior.
*
* @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#a0501ff5a96a3421f3eec427bf5d62380
*
* @tparam Component Type of component to replace.
* @tparam Args Types of arguments to use to construct the component.
*
* @param args Parameters to use to initialize the component.
*
* @return decltype(auto) A reference to the component being replaced.
*/
template<typename Component, typename... Args>
decltype(auto) replace_component(Args&&... args) {
return registry.replace<Component>(inner, std::forward<Args>(args)...);
}
/**
* @brief Patches the given component for an entity.
*
* The signature of the function should be equivalent to the following:
* \code{.cpp}
* void(Component &);
* \endcode
*
* @note Empty types aren't explicitly instantiated and therefore they are never returned. However, this function can be used to trigger an update signal for them.
*
* @warning Attempting to use an invalid entity or to patch a component of an entity that doesn't own it results in undefined behavior.
*
* @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#a22454253689ff77e41326f849bfea976
*
* @tparam Component Type of component to patch.
* @tparam Func Types of the function objects to invoke.
*
* @param func Valid function objects.
*
* @return decltype(auto) A reference to the patched component.
*/
template<typename Component, typename... Func>
decltype(auto) patch(Func&&... func) {
return registry.patch<Component>(inner, std::forward<Func>(func)...);
}
/**
* @brief Removes the given components from an entity.
*
* @warning Attempting to use an invalid entity results in undefined behavior.
*
* @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#aa66ca71c5eb6c2e1b3bde6ff86a268c7
*
* @tparam Component Type of component to remove.
* @tparam Other Other types of components to remove.
*
* @return size_t The number of components actually removed.
*/
template<typename Component, typename... Other>
size_t remove_components() {
return registry.remove<Component, Other...>(inner);
}
/**
* @brief Erases the given components from an entity.
*
* @warning Attempting to use an invalid entity or to erase a component from an entity that doesn't own it results in undefined behavior.
*
* @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#ad7a51ade7ff181d7aa0b35915d0a4f2b
*
* @tparam Component Types of components to erase.
* @tparam Other Other types of components to erase.
*/
template<typename Component, typename... Other>
void erase() {
return registry.remove<Component, Other...>(inner);
}
/**
* @brief Checks if an entity has all the given components.
*
* @warning Attempting to use an invalid entity results in undefined behavior.
*
* @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#a40072eca607846f43f47d0e3f11dd196
*
* @tparam Component Components for which to perform the check.
*
* @return true if the entity has all the components
* @return false otherwise
*/
template<typename... Component>
bool has_all_of() const {
return registry.all_of<Component...>(inner);
}
/**
* @brief Checks if an entity has at least one of the given components.
*
* @warning Attempting to use an invalid entity results in undefined behavior.
*
* @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#a08f819276fd2d8b2b2b6d01357f61a42
*
* @tparam Component Components for which to perform the check.
*
* @return true if the entity has at least one of the given components
* @return false otherwise
*/
template<typename... Component>
bool has_any_of() const {
return registry.any_of<Component...>(inner);
}
/**
* @brief Checks if an entity has components assigned.
*
* @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#a10f5e61d4cabab9e9c1e87edc30de551
*
* @return true if the entity has no components assigned
* @return false otherwise
*/
bool has_any_components() const {
return registry.orphan(inner);
} }
}; };
} }

View File

@ -1,25 +0,0 @@
#pragma once
#include "ecs/entity.h"
#include "event/event.h"
#include <memory>
#include <unordered_map>
#include <vector>
namespace simpleengine {
class EntityManager : public simpleengine::Event {
public:
std::unordered_map<uint32_t, std::shared_ptr<simpleengine::Entity>> entities;
EntityManager();
virtual void submit_entity(std::shared_ptr<simpleengine::Entity> entity);
virtual bool withdraw_entity(std::shared_ptr<simpleengine::Entity> entity);
virtual void initialize();
virtual void destroy() override;
virtual void update(const float& delta_time) override;
};
}

View File

@ -23,6 +23,7 @@ namespace simpleengine::gfx {
std::vector<LitVertex> vertices; std::vector<LitVertex> vertices;
std::vector<GLuint> indicies; std::vector<GLuint> indicies;
bool are_buffers_created = false;
gfx::VBO ebo; gfx::VBO ebo;
gfx::VBO vbo; gfx::VBO vbo;
gfx::VAO vao; gfx::VAO vao;

View File

@ -1,45 +1,29 @@
#pragma once #pragma once
#include "../ecs/entity.h"
#include "material.h"
#include "texture.h"
#include "shader.h" #include "shader.h"
//#include "renderable.h" #include "simpleengine/gfx/mesh.h"
#include "mesh.h"
#include "model.h"
#include <unordered_map> #include <unordered_map>
#include <vector> #include <vector>
#include <queue>
namespace simpleengine::gfx { namespace simpleengine::gfx {
class RenderingJob {
public:
gfx::Mesh& rendering_mesh;
glm::mat4 transform_mat;
RenderingJob(gfx::Mesh& mesh, glm::mat4 position) : rendering_mesh(mesh), transform_mat(position) {
}
};
class Renderer : public simpleengine::Renderable { class Renderer : public simpleengine::Renderable {
private: private:
GLFWwindow* window; GLFWwindow* window;
public: public:
class RenderingModel { std::queue<RenderingJob> rendering_queue;
public:
std::shared_ptr<simpleengine::Entity> entity;
std::unordered_map<uint32_t, gfx::Mesh&> component_models;
RenderingModel(std::shared_ptr<simpleengine::Entity> 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();
};
std::unordered_map<uint32_t, RenderingModel> rendering_models;
gfx::Shader shader; gfx::Shader shader;
Renderer(GLFWwindow* window, gfx::Shader shader); Renderer(GLFWwindow* window, gfx::Shader shader);
@ -47,8 +31,8 @@ namespace simpleengine::gfx {
void enable_debug(); void enable_debug();
virtual void submit_entity(std::shared_ptr<simpleengine::Entity> entity); virtual void queue_job(RenderingJob job);
virtual bool withdraw_entity(std::shared_ptr<simpleengine::Entity> entity); virtual void create_job_buffers(RenderingJob& job);
virtual void initialize(); virtual void initialize();
virtual void destroy() override; virtual void destroy() override;

View File

@ -1,44 +1,32 @@
#pragma once #pragma once
#include "entt/entity/fwd.hpp"
#include "gfx/mesh.h" #include "gfx/mesh.h"
#include "entity.h"
#include "event/event.h" #include "event/event.h"
#include "renderable.h" #include "renderable.h"
#include "simpleengine/gfx/renderer.h"
#include <memory> #include <memory>
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include <vector> #include <vector>
#include <entt/entt.hpp>
namespace simpleengine { namespace simpleengine {
namespace ecs {
class Entity;
}
class Scene : public simpleengine::Event { class Scene : public simpleengine::Event {
protected:
entt::registry registry;
std::shared_ptr<gfx::Renderer> renderer;
public: public:
/** Scene(std::shared_ptr<gfx::Renderer> renderer);
* @brief A list of entities in this scene.
*
*/
std::vector<std::shared_ptr<Entity>> entities;
/** ecs::Entity create_entity();
* @brief Models that don't belong to an entity.
*
*/
std::vector<std::shared_ptr<gfx::Model>> stray_models;
Scene() = default; virtual void update(const float& delta_time) override;
void add_entity(std::shared_ptr<Entity> entity) {
entities.push_back(entity);
}
void add_stray_model(std::shared_ptr<gfx::Model> stray) {
stray_models.push_back(stray);
}
virtual void update(const float& delta_time) override {
for (auto& entity : entities) {
entity->update(delta_time);
}
}
}; };
} }

View File

@ -1,3 +0,0 @@
#include "ecs/component/component.h"
uint32_t simpleengine::Component::incrementing_handle = 0;

View File

@ -1,3 +0,0 @@
#include "ecs/entity.h"
uint32_t simpleengine::Entity::incrementing_handle = 0;

View File

@ -1,40 +0,0 @@
#include "entity_manager.h"
simpleengine::EntityManager::EntityManager() {
}
void simpleengine::EntityManager::submit_entity(std::shared_ptr<simpleengine::Entity> entity) {
entities.emplace(entity->get_handle(), entity);
}
bool simpleengine::EntityManager::withdraw_entity(std::shared_ptr<simpleengine::Entity> entity) {
auto it = entities.find(entity->get_handle());
if (it != entities.end()) {
it->second->destroy();
entities.erase(it);
return true;
}
return false;
}
void simpleengine::EntityManager::initialize() {
}
void simpleengine::EntityManager::destroy() {
std::cout << "Destroy entity manager!" << std::endl;
for (auto& [handle, entity] : entities) {
entity->destroy();
}
}
void simpleengine::EntityManager::update(const float& delta_time) {
for (auto& [handle, entity] : entities) {
entity->update(delta_time);
}
}

View File

@ -38,7 +38,7 @@ namespace simpleengine::gfx {
// process all the node's meshes (if any) // process all the node's meshes (if any)
for (int i = 0; i < node->mNumMeshes; i++) { for (int i = 0; i < node->mNumMeshes; i++) {
aiMesh *mesh = scene->mMeshes[node->mMeshes[i]]; aiMesh *mesh = scene->mMeshes[node->mMeshes[i]];
meshes.push_back(process_mesh(mesh, scene)); meshes.push_back(process_mesh(mesh, scene));
} }
// then do the same for each of its children // then do the same for each of its children

View File

@ -1,6 +1,4 @@
#include "gfx/renderer.h" #include "gfx/renderer.h"
#include "ecs/component/component.h"
#include "ecs/entity.h"
#include "gfx/mesh.h" #include "gfx/mesh.h"
#include "gfx/vao.h" #include "gfx/vao.h"
#include "renderable.h" #include "renderable.h"
@ -12,16 +10,7 @@
#include <assimp/material.h> #include <assimp/material.h>
namespace simpleengine::gfx { namespace simpleengine::gfx {
void create_mesh_buffers(std::shared_ptr<simpleengine::Component> comp, simpleengine::gfx::Mesh& mesh); void create_mesh_buffers(simpleengine::gfx::Mesh& mesh);
void Renderer::RenderingModel::destroy_buffers() {
std::cout << "Destroying entity models..." << std::endl;
// Iterate through all buffer lists and destroy each inner buffer.
for (auto& pair : component_models) {
pair.second.destroy();
}
}
Renderer::Renderer(GLFWwindow* window, gfx::Shader shader): window(window), shader(shader) { Renderer::Renderer(GLFWwindow* window, gfx::Shader shader): window(window), shader(shader) {
@ -45,24 +34,40 @@ namespace simpleengine::gfx {
glDebugMessageCallback(debug_message_callback, 0); glDebugMessageCallback(debug_message_callback, 0);
} }
void Renderer::submit_entity(std::shared_ptr<simpleengine::Entity> entity) { void Renderer::queue_job(RenderingJob job) {
std::cout << "Submitting entity (" << entity->get_handle() << ")..." << std::endl; RenderingJob& emplace = rendering_queue.emplace(job);
auto it = rendering_models.emplace(entity->get_handle(), entity); create_job_buffers(emplace);
it.first->second.update_buffers();
} }
bool Renderer::withdraw_entity(std::shared_ptr<simpleengine::Entity> entity) { void Renderer::create_job_buffers(RenderingJob& job) {
std::cout << "Withdrawing entity (" << entity->get_handle() << ")..."; Mesh& rendering_mesh = job.rendering_mesh;
auto it = rendering_models.find(entity->get_handle());
if (it != rendering_models.end()) { if (!rendering_mesh.are_buffers_created) {
it->second.destroy_buffers(); gfx::VBO& vbo = rendering_mesh.vbo;
rendering_models.erase(it); gfx::VBO& ebo = rendering_mesh.ebo;
gfx::VAO& vao = rendering_mesh.vao;
return true; vao.bind();
vbo.buffer(rendering_mesh.vertices.data(), 0, sizeof(LitVertex) * rendering_mesh.vertices.size());
if (!rendering_mesh.indicies.empty()) {
ebo.buffer(rendering_mesh.indicies.data(), 0, rendering_mesh.indicies.size() * sizeof(GLuint));
}
// Enable VAO attributes
vao.enable_attrib(vbo, 0, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, position), false);
vao.enable_attrib(vbo, 1, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, color), false);
vao.enable_attrib(vbo, 2, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, normal), false);
vao.enable_attrib(vbo, 3, 2, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, tex_coord), false);
vao.enable_attrib(vbo, 4, 1, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, texture_id), false);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
rendering_mesh.are_buffers_created = true;
std::cout << "Created render job buffers" << std::endl;
} }
return false;
} }
void Renderer::update(const float& delta_time) { void Renderer::update(const float& delta_time) {
@ -76,113 +81,53 @@ namespace simpleengine::gfx {
void Renderer::destroy() { void Renderer::destroy() {
std::cout << "Destroying renderer..." << std::endl; std::cout << "Destroying renderer..." << std::endl;
for (auto& [handle, rendering] : rendering_models) { /* for (auto& [handle, rendering] : rendering_models) {
rendering.destroy_buffers(); rendering.destroy_buffers();
} } */
} }
void Renderer::render() { void Renderer::render() {
shader.use(); shader.use();
for (auto& [handle, rendering] : rendering_models) { while (!rendering_queue.empty()) {
if (rendering.component_models.size() >= 0) { // Get the job from the queue, we'll remove it after we render.
std::shared_ptr<Entity>& entity = rendering.entity; RenderingJob& job = rendering_queue.front();
Mesh& mesh = job.rendering_mesh;
shader.set_uniform_matrix_4f("transform_matrix", entity->transform_matrix, false); shader.set_uniform_matrix_4f("transform_matrix", job.transform_mat, false);
for (const auto& pair : rendering.component_models) { std::optional<Material>& material = mesh.material;
Mesh& model = pair.second;
std::optional<Material>& material = model.material;
shader.set_uniform_int("u_textures", 0, false); shader.set_uniform_int("u_textures", 0, false);
if (material.has_value()) { if (material.has_value()) {
shader.set_uniform_float("u_texture_shine", material->shine, false); shader.set_uniform_float("u_texture_shine", material->shine, false);
shader.set_uniform_float("u_texture_reflectivity", material->reflectivity, false); shader.set_uniform_float("u_texture_reflectivity", material->reflectivity, false);
int texture_count = 0; int texture_count = 0;
auto diffuse_maps = material->textures.find(aiTextureType_DIFFUSE); auto diffuse_maps = material->textures.find(aiTextureType_DIFFUSE);
for (const auto& texture : diffuse_maps->second) { for (const auto& texture : diffuse_maps->second) {
// We can only bind to 16 textures at a time (indexes are 0-15) // We can only bind to 16 textures at a time (indexes are 0-15)
if (texture_count >= 16) break; if (texture_count >= 16) break;
glActiveTexture(GL_TEXTURE0 + texture_count); glActiveTexture(GL_TEXTURE0 + texture_count);
glBindTextureUnit(texture_count, texture.get_texture_id()); glBindTextureUnit(texture_count, texture.get_texture_id());
texture_count++; texture_count++;
}
}
model.vao.bind();
if (model.indicies.empty()) {
glDrawArrays(GL_TRIANGLES, 0, model.vertices.size());
} else {
glDrawElements(GL_TRIANGLES, model.indicies.size(), GL_UNSIGNED_INT, 0);
}
} }
} }
mesh.vao.bind();
if (mesh.indicies.empty()) {
glDrawArrays(GL_TRIANGLES, 0, mesh.vertices.size());
} else {
glDrawElements(GL_TRIANGLES, mesh.indicies.size(), GL_UNSIGNED_INT, 0);
}
// Now we'll remove the job from the queue.
rendering_queue.pop();
} }
shader.unuse(); shader.unuse();
} }
void Renderer::RenderingModel::update_buffers() {
if (entity->has_component<simpleengine::MeshComponent>()) {
std::shared_ptr<MeshComponent> comp = entity->get_component<simpleengine::MeshComponent>();
auto iter = component_models.find(comp->get_handle());
if (iter == component_models.end()) {
std::cout << "Enabling buffer attributes for MeshComponent (" << comp->get_handle() << ")..." << std::endl;
//iter->second = comp->model;
gfx::Mesh& mesh = comp->model;
create_mesh_buffers(comp, mesh);
component_models.emplace(comp->get_handle(), mesh);
std::cout << "Enabled all buffer attributes for MeshComponent" << std::endl;
} else {
std::cout << "Already exists" << std::endl;
}
} else if (entity->has_component<simpleengine::ModelComponent>()) {
std::shared_ptr<ModelComponent> comp = entity->get_component<simpleengine::ModelComponent>();
auto iter = component_models.find(comp->get_handle());
if (iter == component_models.end()) {
std::cout << "Enabling buffer attributes for ModelComponent (" << comp->get_handle() << ")..." << std::endl;
// Store all the model's meshes
for (auto& mesh : comp->model.meshes) {
create_mesh_buffers(comp, mesh);
component_models.emplace(comp->get_handle(), mesh);
}
std::cout << "Enabled all buffer attributes for ModelComponent" << std::endl;
} else {
std::cout << "Already exists" << std::endl;
}
}
}
void create_mesh_buffers(std::shared_ptr<Component> comp, gfx::Mesh& mesh) {
gfx::VBO& vbo = mesh.vbo;
gfx::VBO& ebo = mesh.ebo;
gfx::VAO& vao = mesh.vao;
vao.bind();
vbo.buffer(mesh.vertices.data(), 0, sizeof(LitVertex) * mesh.vertices.size());
if (!mesh.indicies.empty()) {
ebo.buffer(mesh.indicies.data(), 0, mesh.indicies.size() * sizeof(GLuint));
}
// Enable VAO attributes
vao.enable_attrib(vbo, 0, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, position), false);
vao.enable_attrib(vbo, 1, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, color), false);
vao.enable_attrib(vbo, 2, 3, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, normal), false);
vao.enable_attrib(vbo, 3, 2, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, tex_coord), false);
vao.enable_attrib(vbo, 4, 1, GL_FLOAT, sizeof(LitVertex), offsetof(LitVertex, texture_id), false);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
} }

29
src/scene.cpp Normal file
View File

@ -0,0 +1,29 @@
#include "scene.h"
#include "ecs/component/mesh_component.h"
#include "ecs/component/model_component.h"
#include "ecs/component/transform_component.h"
#include "ecs/entity.h"
#include "gfx/renderer.h"
namespace simpleengine {
Scene::Scene(std::shared_ptr<gfx::Renderer> renderer) : renderer(renderer) {
}
ecs::Entity Scene::create_entity() {
return ecs::Entity(registry, registry.create());
}
void Scene::update(const float& delta_time) {
// Is there a way these can be grouped?
registry.view<const TransformComponent, ModelComponent>().each([this](const TransformComponent& transform, ModelComponent& model_component) {
for (auto& mesh : model_component.model.meshes) {
renderer->queue_job(gfx::RenderingJob(mesh, transform.transform_matrix));
}
});
registry.view<const TransformComponent, MeshComponent>().each([this](const TransformComponent& transform, MeshComponent& mesh_component) {
renderer->queue_job(gfx::RenderingJob(mesh_component.mesh, transform.transform_matrix));
});
}
}