Merge pull request #1 from SeanOMik/feature/ecs-and-submission-rendering

Feature/ecs and submission rendering
This commit is contained in:
SeanOMik 2022-09-27 14:43:56 -04:00 committed by GitHub
commit 1ea28a8790
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 730 additions and 493 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,5 +1,2 @@
*.blend *
*.obj !shaders
*.png
*.bin
*.gltf

View File

@ -1,8 +1,9 @@
#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/component/rotating_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"
@ -10,7 +11,6 @@
#include "simpleengine/gfx/renderer.h" #include "simpleengine/gfx/renderer.h"
#include "simpleengine/gfx/texture.h" #include "simpleengine/gfx/texture.h"
#include "simpleengine/vector.h" #include "simpleengine/vector.h"
#include <GLFW/glfw3.h>
#include <simpleengine/gfx/shader.h> #include <simpleengine/gfx/shader.h>
#include <simpleengine/renderable.h> #include <simpleengine/renderable.h>
#include <simpleengine/event/event.h> #include <simpleengine/event/event.h>
@ -19,8 +19,7 @@
#include <simpleengine/vertex.h> #include <simpleengine/vertex.h>
#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 <glm/ext/matrix_clip_space.hpp> #include <glm/ext/matrix_clip_space.hpp>
#include <glm/fwd.hpp> #include <glm/fwd.hpp>
@ -166,29 +165,45 @@ int main(int argc, char *argv[]) {
5, 6, 12, 12, 6, 13 5, 6, 12, 12, 6, 13
}; };
std::unordered_map<aiTextureType, std::vector<se::gfx::Texture>> textures; std::unordered_map<aiTextureType, std::vector<std::shared_ptr<se::gfx::Texture>>> textures;
textures.emplace(white_texture.type, std::vector<se::gfx::Texture>{ white_texture }); textures.emplace(white_texture.type, std::vector<std::shared_ptr<se::gfx::Texture>>{ std::make_shared<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");
//entity.add_component<se::ModelComponent>("examples/dev_testing/resources/stall.obj");
// Backpack model required vertically flipped texture coords.
auto& model_comp = entity.add_component<se::ModelComponent>("examples/dev_testing/resources/backpack/backpack.obj");
model_comp.model.vertically_flip_tex_coords();
//entity.add_component<se::ModelComponent>("examples/dev_testing/resources/scientist/scientist.fbx");
//entity.add_component<se::ModelComponent>("examples/dev_testing/resources/paradigm/paradigm.fbx");
//entity.add_component<se::RotatingComponent>();
auto& transform_comp = entity.add_component<se::TransformComponent>();
transform_comp.translate(15.f, -8.f, 0.f);
/* transform_comp.scale(0.1f);
transform_comp.rotate_z(-90.f);*/
transform_comp.rotate_x(-90.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;
@ -19,11 +18,8 @@ namespace simpleengine {
} }
ModelComponent(std::string model_file_path) : model(model_file_path) { ModelComponent(std::string model_file_path, int model_processing_flags = gfx::ModelProcessingFlags::MdlProcFlag_NONE,
int assimp_flags = 0) : model(model_file_path, model_processing_flags, assimp_flags) {
}
virtual void update(const float& delta_time) override {
} }
}; };

View File

@ -0,0 +1,31 @@
#pragma once
#include <glm/glm.hpp>
namespace simpleengine {
/**
* @brief A component that will rotate the transform every frame.
*
*/
class RotatingComponent {
public:
float rate;
glm::vec3 rotation_axis;
RotatingComponent() : rate(10.f), rotation_axis(glm::vec3(0, 1, 0)) {
}
RotatingComponent(float rate) : rate(rate), rotation_axis(glm::vec3(0, 1, 0)) {
}
RotatingComponent(glm::vec3 rotation_axis) : rotation_axis(rotation_axis) {
}
RotatingComponent(float rate, glm::vec3 rotation_axis) : rate(rate), rotation_axis(rotation_axis) {
}
};
}

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
*
* @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);
} }
rotate_y(delta_time * 10); // TODO: Remove /**
* @brief Releases an identifier.
*
* The version is updated and the identifier can be recycled at any time.
*
* @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#a3d9cb2368384b0952cb54848e777359e
*
* @return entt::registry::version_type The version of the recycled entity.
*/
entt::registry::version_type release() {
return registry.release(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 suggested version or the valid version closest to the suggested one is used instead of the implicitly generated version.
return true; *
} * @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#af9c919867fc93a7e1a2d0762ac3a9877
*
* @param version A desired version upon destruction.
* @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);
} }
return false; /**
* @brief Destroys an entity and releases its identifier.
*
* @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.
*
* @link https://skypjack.github.io/entt/classentt_1_1basic__registry.html#a7b2c0368d508a6af2d094a9fc592a4a0
*
* @return entt::registry::version_type The version of the recycled entity.
*/
entt::registry::version_type destroy() {
return registry.destroy(inner);
} }
template<typename T> /**
std::shared_ptr<T> get_component() const { * @brief Destroys an entity and releases its 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#adc175a0d3bcf83a133c63890c674ceb3
*
* @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);
} }
return nullptr; /**
* @brief Assigns the given component to an entity.
*
* 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)...);
} }
template<typename T> /**
void add_component(std::shared_ptr<T> component) { * @brief Assigns or replaces the given component for 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.
// Only allow one type of the same component *
assert(!has_component<T>()); // TODO: Don't assert, give an error * @warning Attempting to use an invalid entity or to assign a component to an entity that already owns it results in undefined behavior.
components.push_back(component); *
* @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)...);
} }
template<typename T> /**
void add_component(T component) { * @brief Replaces the given component for 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.
// Only allow one type of the same component *
assert(!has_component<T>()); // TODO: Don't assert, give an error * @warning Attempting to use an invalid entity or to replace a component of an entity that doesn't own it results in undefined behavior.
components.push_back(std::make_shared<T>(component)); *
* @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)...);
} }
template<typename T, typename ...Args> /**
std::shared_ptr<T> add_component(Args&&... args) { * @brief Patches the given component for an entity.
static_assert(std::is_base_of_v<Component, T>, "Component class must derive from simpleengine::Component"); *
* 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)...);
}
// Only allow one type of the same component /**
assert(!has_component<T>()); // TODO: Don't assert, give an error * @brief Removes the given components from an entity.
auto comp = std::make_shared<T>(std::forward<Args>(args)...); *
components.push_back(comp); * @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);
}
return comp; /**
* @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

@ -3,12 +3,14 @@
#include "texture.h" #include "texture.h"
#include <assimp/material.h> #include <assimp/material.h>
#include <unordered_map> #include <unordered_map>
#include <memory>
namespace simpleengine::gfx { namespace simpleengine::gfx {
class Material { class Material {
public: public:
std::unordered_map<aiTextureType, std::vector<Texture>> textures; std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>> textures;
float ambient_scalar; float ambient_scalar;
float diffuse_scalar; float diffuse_scalar;
@ -16,7 +18,7 @@ namespace simpleengine::gfx {
float shine; float shine;
float reflectivity; float reflectivity;
Material(std::unordered_map<aiTextureType, std::vector<Texture>> textures, float shine = 1.f, float reflectivity = 0.f, float specular_scalar = 0.f, float ambient_scalar = 0.f, float diffuse_scalar = 0.f) : Material(std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>> textures, float shine = 1.f, float reflectivity = 0.f, float specular_scalar = 0.f, float ambient_scalar = 0.f, float diffuse_scalar = 0.f) :
textures(textures), ambient_scalar(ambient_scalar), diffuse_scalar(diffuse_scalar), specular_scalar(specular_scalar), textures(textures), ambient_scalar(ambient_scalar), diffuse_scalar(diffuse_scalar), specular_scalar(specular_scalar),
shine(shine), reflectivity(reflectivity) { shine(shine), reflectivity(reflectivity) {

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

@ -10,6 +10,12 @@
//#include <assimp/mesh.h> //#include <assimp/mesh.h>
namespace simpleengine::gfx { namespace simpleengine::gfx {
enum ModelProcessingFlags : uint8_t {
MdlProcFlag_NONE = 0b00000000,
MdlProcFlag_FLIP_TEX_COORDS_VERTICALLY = 0b00000001,
MdlProcFlag_FLIP_TEX_COORDS_HORIZONTALLY = 0b00000010,
};
/** /**
* @brief A Model is a group of Meshes read from the 3D model file. * @brief A Model is a group of Meshes read from the 3D model file.
* *
@ -20,14 +26,25 @@ namespace simpleengine::gfx {
class Model : public simpleengine::Transformable { class Model : public simpleengine::Transformable {
protected: protected:
std::string model_directory; // May be needed std::string model_directory; // May be needed
int additional_assimp_flags;
int model_processing_flags;
public: public:
std::vector<gfx::Mesh> meshes; std::vector<gfx::Mesh> meshes;
Model(std::string file_path); Model(std::string file_path, int model_processing_flags = ModelProcessingFlags::MdlProcFlag_NONE, int assimp_flags = 0);
void load_model(std::string path); void load_model(std::string path);
void process_node(aiNode* node, const aiScene* scene); void process_node(std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>>& processed_textures, aiNode* node, const aiScene* scene);
gfx::Mesh process_mesh(aiMesh* mesh, const aiScene* scene); gfx::Mesh process_mesh(std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>>& processed_textures, aiMesh* mesh, const aiScene* scene);
std::vector<Texture> load_material_textures(aiMaterial* material, aiTextureType type);
std::unordered_map<aiTextureType, std::vector<Texture>> load_all_textures(aiMaterial* material);
std::vector<std::shared_ptr<Texture>> load_material_texture(std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>>& processed_textures, aiMaterial* material, aiTextureType type);
protected:
void post_process();
public:
void vertically_flip_tex_coords();
void horizontally_flip_tex_coords();
}; };
} }

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,5 +1,6 @@
#pragma once #pragma once
#include <cstdint>
#ifdef __linux__ #ifdef __linux__
#include <GL/glew.h> #include <GL/glew.h>
#include <GL/gl.h> #include <GL/gl.h>
@ -17,6 +18,13 @@
#include <iostream> #include <iostream>
namespace simpleengine::gfx { namespace simpleengine::gfx {
enum TextureFlags : uint8_t {
TexFlags_IMG_2D = 0b00000001,
TexFlags_FLIP_VERTICALLY = 0b00000010,
TexFlags_FLIP_HORIZONTALLY = 0b00000100,
TexFlags_MIPMAP = 0b00001000,
};
class Texture { class Texture {
private: private:
unsigned char* img_data; // TODO Free this if its not used anymore unsigned char* img_data; // TODO Free this if its not used anymore
@ -30,6 +38,7 @@ namespace simpleengine::gfx {
int width; int width;
int channels; int channels;
aiTextureType type; aiTextureType type;
std::string path;
/** /**
* @brief Construct a new Texture object from a path. * @brief Construct a new Texture object from a path.
@ -38,7 +47,8 @@ namespace simpleengine::gfx {
* @param img_2d Whether or not the texture is 2D. * @param img_2d Whether or not the texture is 2D.
* @param mipmap Whether or not to generate mipmaps for this texture. * @param mipmap Whether or not to generate mipmaps for this texture.
*/ */
Texture(const char* path, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, bool img_2d = true, bool mipmap = true); Texture(const char* path, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, int flags = TextureFlags::TexFlags_IMG_2D |
TextureFlags::TexFlags_MIPMAP);
/** /**
* @brief Construct a new Texture object from the loaded file buffer. * @brief Construct a new Texture object from the loaded file buffer.
@ -48,7 +58,8 @@ namespace simpleengine::gfx {
* @param img_2d Whether or not the texture is 2D. * @param img_2d Whether or not the texture is 2D.
* @param mipmap Whether or not to generate mipmaps for this texture. * @param mipmap Whether or not to generate mipmaps for this texture.
*/ */
Texture(const unsigned char *const buffer, int buffer_length, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, bool img_2d = true, bool mipmap = true); Texture(const unsigned char *const buffer, int buffer_length, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, int flags = TextureFlags::TexFlags_IMG_2D |
TextureFlags::TexFlags_MIPMAP);
/** /**
* @brief Construct a new Texture object from the loaded file buffer. * @brief Construct a new Texture object from the loaded file buffer.
@ -57,7 +68,8 @@ namespace simpleengine::gfx {
* @param img_2d Whether or not the texture is 2D. * @param img_2d Whether or not the texture is 2D.
* @param mipmap Whether or not to generate mipmaps for this texture. * @param mipmap Whether or not to generate mipmaps for this texture.
*/ */
Texture(std::vector<unsigned char> buffer, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, bool img_2d = true, bool mipmap = true); Texture(std::vector<unsigned char> buffer, aiTextureType type = aiTextureType::aiTextureType_DIFFUSE, int flags = TextureFlags::TexFlags_IMG_2D |
TextureFlags::TexFlags_MIPMAP);
static Texture white_texture(); static Texture white_texture();

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

@ -12,6 +12,5 @@ pkgs.mkShell {
glfw glfw
glm glm
assimp assimp
(callPackage ./soil2.nix { })
]; ];
} }

View File

@ -1,37 +0,0 @@
{ lib
, stdenv
, fetchFromGitHub
, cmake
, libGL
, libX11
}:
stdenv.mkDerivation rec {
version = "39028e64921c03cabbc53b937da4a85aba264e00";
pname = "soil2";
src = fetchFromGitHub {
owner = "SpartanJ";
repo = pname;
rev = version;
sha256 = "sha256-zQQ8lwOkMCxdlf6zfnIOYVUTGVqnJuHL/LL8fbzxwHY=";
};
nativeBuildInputs = [ cmake libGL libX11 ];
installPhase = ''
runHook preInstall
cmake --build . --target install --config Release
runHook postInstall
'';
meta = with lib; {
description = "SOIL2 is a tiny C library used primarily for uploading textures into OpenGL.";
homepage = "https://github.com/SpartanJ/SOIL2";
license = licenses.mit0;
platforms = platforms.unix;
maintainers = with maintainers; [ seanomik ];
};
}

View File

@ -63,19 +63,19 @@ namespace simpleengine {
} }
if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS) { if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS) {
rotation.z += camera_speed; rotation.z += camera_speed * .3;
} }
if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS) { if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS) {
rotation.z -= camera_speed; rotation.z -= camera_speed * .3;
} }
if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS) { if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS) {
rotation.y -= camera_speed; rotation.y -= camera_speed * .3;
} }
if (glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS) { if (glfwGetKey(window, GLFW_KEY_RIGHT) == GLFW_PRESS) {
rotation.y += camera_speed; rotation.y += camera_speed * .3;
} }
// Limit the pitch of the camera. // Limit the pitch of the camera.

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

@ -14,15 +14,26 @@
#include <vector> #include <vector>
namespace simpleengine::gfx { namespace simpleengine::gfx {
Model::Model(std::string file_path) { Model::Model(std::string file_path, int model_processing_flags, int assimp_flags): model_processing_flags(model_processing_flags),
additional_assimp_flags(assimp_flags) {
load_model(file_path); load_model(file_path);
} }
void Model::post_process() {
if (model_processing_flags & ModelProcessingFlags::MdlProcFlag_FLIP_TEX_COORDS_VERTICALLY) {
vertically_flip_tex_coords();
}
if (model_processing_flags & ModelProcessingFlags::MdlProcFlag_FLIP_TEX_COORDS_HORIZONTALLY) {
horizontally_flip_tex_coords();
}
}
void Model::load_model(std::string path) { void Model::load_model(std::string path) {
Assimp::Importer importer; Assimp::Importer importer;
// assimp post processing options: http://assimp.sourceforge.net/lib_html/postprocess_8h.html // assimp post processing options: http://assimp.sourceforge.net/lib_html/postprocess_8h.html
const aiScene *scene = importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs); const aiScene *scene = importer.ReadFile(path, additional_assimp_flags | aiProcess_Triangulate | aiProcess_FlipUVs);
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) { if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
std::cout << "ERROR::ASSIMP::" << importer.GetErrorString() << std::endl; std::cout << "ERROR::ASSIMP::" << importer.GetErrorString() << std::endl;
@ -31,26 +42,28 @@ namespace simpleengine::gfx {
model_directory = path.substr(0, path.find_last_of('/')); model_directory = path.substr(0, path.find_last_of('/'));
process_node(scene->mRootNode, scene); std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>> processed_textures;
process_node(processed_textures, scene->mRootNode, scene);
post_process();
} }
void Model::process_node(aiNode* node, const aiScene* scene) { void Model::process_node(std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>>& processed_textures, aiNode* node, const aiScene* scene) {
// 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(processed_textures, mesh, scene));
} }
// then do the same for each of its children // then do the same for each of its children
for (int i = 0; i < node->mNumChildren; i++) { for (int i = 0; i < node->mNumChildren; i++) {
process_node(node->mChildren[i], scene); process_node(processed_textures, node->mChildren[i], scene);
} }
} }
gfx::Mesh Model::process_mesh(aiMesh* mesh, const aiScene* scene) { gfx::Mesh Model::process_mesh(std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>>& processed_textures, aiMesh* mesh, const aiScene* scene) {
std::vector<LitVertex> vertices; std::vector<LitVertex> vertices;
std::vector<unsigned int> indices; std::vector<unsigned int> indices;
std::vector<Texture> textures;
for (unsigned int i = 0; i < mesh->mNumVertices; i++) { for (unsigned int i = 0; i < mesh->mNumVertices; i++) {
LitVertex vertex; LitVertex vertex;
@ -60,8 +73,10 @@ namespace simpleengine::gfx {
simpleengine::Vectorf position(mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z); simpleengine::Vectorf position(mesh->mVertices[i].x, mesh->mVertices[i].y, mesh->mVertices[i].z);
vertex.position = position; vertex.position = position;
if (mesh->HasNormals()) {
glm::vec3 normal(mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z); glm::vec3 normal(mesh->mNormals[i].x, mesh->mNormals[i].y, mesh->mNormals[i].z);
vertex.normal = normal; vertex.normal = normal;
}
if (mesh->mTextureCoords[0]) { if (mesh->mTextureCoords[0]) {
glm::vec2 tex_coord(mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y); glm::vec2 tex_coord(mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y);
@ -83,18 +98,18 @@ namespace simpleengine::gfx {
// Create a default material and white texture. // Create a default material and white texture.
auto white_texture = gfx::Texture::white_texture(); auto white_texture = gfx::Texture::white_texture();
std::unordered_map<aiTextureType, std::vector<Texture>> default_textures; std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>> default_textures;
default_textures.emplace(white_texture.type, std::vector<Texture>{ white_texture }); default_textures.emplace(white_texture.type, std::vector<std::shared_ptr<Texture>>{ std::make_shared<Texture>(white_texture) });
gfx::Material mat(default_textures, 1.f, 0.f, 0.f, 0.f, 0.f); gfx::Material mat(default_textures, 1.f, 0.f, 0.f, 0.f, 0.f);
if (mesh->mMaterialIndex >= 0) { if (mesh->mMaterialIndex >= 0) {
std::cout << "TODO: Process model materials!" << std::endl; std::cout << "TODO: Process model materials!" << std::endl;
std::unordered_map<aiTextureType, std::vector<Texture>> textures; std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>> textures;
aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex]; aiMaterial *material = scene->mMaterials[mesh->mMaterialIndex];
// Load Diffuse texture maps // Load Diffuse texture maps
std::vector<Texture> diffuse_maps = load_material_textures(material, aiTextureType_DIFFUSE); std::vector<std::shared_ptr<Texture>> diffuse_maps = load_material_texture(processed_textures, material, aiTextureType_DIFFUSE);
if (!diffuse_maps.empty()) textures.emplace(aiTextureType_DIFFUSE, diffuse_maps); if (!diffuse_maps.empty()) textures.emplace(aiTextureType_DIFFUSE, diffuse_maps);
// TODO Handle other types of texture maps // TODO Handle other types of texture maps
@ -102,27 +117,97 @@ namespace simpleengine::gfx {
if (!textures.empty()) { if (!textures.empty()) {
// TODO: Find a way to let the user set the scalars. // TODO: Find a way to let the user set the scalars.
mat = Material(textures, 1.f, 0.f, 0.f, 0.f, 0.f); mat = Material(textures, 1.f, 0.f, 0.f, 0.f, 0.f);
// Add `textures` into the `processed_textures` list.
for (const auto& pair : textures) {
for (const auto& texture : pair.second) {
bool contains = false;
auto found = processed_textures.find(pair.first);
if (found != processed_textures.end()) {
for (const auto& processed_text : found->second) {
if (processed_text->path == texture->path) {
contains = true;
break;
} }
} }
return Mesh(vertices, indices, mat); if (!contains) {
//found->second
found->second.emplace_back(texture);
}
} else {
processed_textures.emplace(pair.first, std::vector<std::shared_ptr<Texture>>{ pair.second });
}
}
}
}
} }
std::vector<Texture> Model::load_material_textures(aiMaterial* material, aiTextureType type) { Mesh se_mesh(vertices, indices, mat);
std::vector<Texture> textures;
if (!mesh->HasNormals()) {
se_mesh.calculate_normals();
}
return se_mesh;
}
std::unordered_map<aiTextureType, std::vector<Texture>> load_all_textures(aiMaterial* material) {
// Load Diffuse texture maps
return {};
}
std::vector<std::shared_ptr<Texture>> Model::load_material_texture(std::unordered_map<aiTextureType, std::vector<std::shared_ptr<Texture>>>& processed_textures, aiMaterial* material, aiTextureType type) {
std::vector<std::shared_ptr<Texture>> textures;
for (int i = 0; i < material->GetTextureCount(type); i++) { for (int i = 0; i < material->GetTextureCount(type); i++) {
aiString texture_path; aiString texture_path_ai;
material->GetTexture(type, i, &texture_path); material->GetTexture(type, i, &texture_path_ai);
std::string texture_path = texture_path_ai.C_Str();
// If the texture has been read, we should skip it.
bool skip = false;
for (const auto& pair : processed_textures) {
if (pair.first == type) {
for (const auto& texture : pair.second) {
if (texture->path == texture_path) {
textures.emplace_back(texture); // Push a copy of the texture for this Mesh.
skip = true;
break;
}
}
}
}
if (skip) continue;
std::stringstream ss; std::stringstream ss;
ss << model_directory << "/" << texture_path.C_Str(); ss << model_directory << "/" << texture_path;
std::string full_path = ss.str(); std::string full_path = ss.str();
Texture texture(full_path.c_str(), type); Texture texture(full_path.c_str(), type, /* TextureFlags::TexFlags_FLIP_VERTICALLY | */ TextureFlags::TexFlags_IMG_2D | TextureFlags::TexFlags_MIPMAP);
textures.emplace_back(texture); texture.path = texture_path;
textures.emplace_back(std::make_shared<Texture>(texture));
std::cout << "Texture full path: " << full_path << ", texture_path: " << texture_path << std::endl;
} }
return textures; return textures;
} }
void Model::vertically_flip_tex_coords() {
for (auto& mesh : meshes) {
for (auto& vertex : mesh.vertices) {
vertex.tex_coord.y *= -1;
}
}
}
void Model::horizontally_flip_tex_coords() {
for (auto& mesh : meshes) {
for (auto& vertex : mesh.vertices) {
vertex.tex_coord.x *= -1;
}
}
}
} // namespace simpleengine::gfx } // namespace simpleengine::gfx

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));
} }
return false; // 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;
}
} }
void Renderer::update(const float& delta_time) { void Renderer::update(const float& delta_time) {
@ -76,23 +81,22 @@ 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);
@ -107,82 +111,23 @@ namespace simpleengine::gfx {
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(); mesh.vao.bind();
if (model.indicies.empty()) { if (mesh.indicies.empty()) {
glDrawArrays(GL_TRIANGLES, 0, model.vertices.size()); glDrawArrays(GL_TRIANGLES, 0, mesh.vertices.size());
} else { } else {
glDrawElements(GL_TRIANGLES, model.indicies.size(), GL_UNSIGNED_INT, 0); 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);
}
} }

View File

@ -4,7 +4,11 @@
#include <stb_image.h> #include <stb_image.h>
namespace simpleengine::gfx { namespace simpleengine::gfx {
Texture::Texture(const char* path, aiTextureType type, bool img_2d, bool mipmap): type(type) { Texture::Texture(const char* path, aiTextureType type, int flags): type(type), path(path) {
bool img_2d = flags & TextureFlags::TexFlags_IMG_2D;
bool flip_vertically = flags & TextureFlags::TexFlags_FLIP_VERTICALLY;
bool mipmap = flags & TextureFlags::TexFlags_MIPMAP;
image_type_gl = img_2d ? GL_TEXTURE_2D : GL_TEXTURE_3D; image_type_gl = img_2d ? GL_TEXTURE_2D : GL_TEXTURE_3D;
glGenTextures(1, &texture_id); glGenTextures(1, &texture_id);
@ -17,6 +21,8 @@ namespace simpleengine::gfx {
glTexParameteri(image_type_gl, GL_TEXTURE_MIN_FILTER, linear_param); glTexParameteri(image_type_gl, GL_TEXTURE_MIN_FILTER, linear_param);
glTexParameteri(image_type_gl, GL_TEXTURE_MAG_FILTER, linear_param); glTexParameteri(image_type_gl, GL_TEXTURE_MAG_FILTER, linear_param);
stbi_set_flip_vertically_on_load(flip_vertically);
// Read 4 channels (RGBA) // Read 4 channels (RGBA)
img_data = stbi_load(path, &width, &height, &channels, 4); img_data = stbi_load(path, &width, &height, &channels, 4);
if(!img_data) { if(!img_data) {
@ -32,10 +38,16 @@ namespace simpleengine::gfx {
glGenerateMipmap(image_type_gl); glGenerateMipmap(image_type_gl);
} }
stbi_set_flip_vertically_on_load(false);
unbind(); unbind();
} }
Texture::Texture(const unsigned char *const buffer, int buffer_length, aiTextureType type, bool img_2d, bool mipmap): type(type) { Texture::Texture(const unsigned char *const buffer, int buffer_length, aiTextureType type, int flags): type(type) {
bool img_2d = flags & TextureFlags::TexFlags_IMG_2D;
bool flip_vertically = flags & TextureFlags::TexFlags_FLIP_VERTICALLY;
bool mipmap = flags & TextureFlags::TexFlags_MIPMAP;
image_type_gl = img_2d ? GL_TEXTURE_2D : GL_TEXTURE_3D; image_type_gl = img_2d ? GL_TEXTURE_2D : GL_TEXTURE_3D;
glGenTextures(1, &texture_id); glGenTextures(1, &texture_id);
@ -48,6 +60,8 @@ namespace simpleengine::gfx {
glTexParameteri(image_type_gl, GL_TEXTURE_MIN_FILTER, linear_param); glTexParameteri(image_type_gl, GL_TEXTURE_MIN_FILTER, linear_param);
glTexParameteri(image_type_gl, GL_TEXTURE_MAG_FILTER, linear_param); glTexParameteri(image_type_gl, GL_TEXTURE_MAG_FILTER, linear_param);
stbi_set_flip_vertically_on_load(flip_vertically);
// Read 4 channels (RGBA) // Read 4 channels (RGBA)
img_data = stbi_load_from_memory(buffer, buffer_length, &width, &height, &channels, 4); img_data = stbi_load_from_memory(buffer, buffer_length, &width, &height, &channels, 4);
if(!img_data) { if(!img_data) {
@ -63,11 +77,13 @@ namespace simpleengine::gfx {
glGenerateMipmap(image_type_gl); glGenerateMipmap(image_type_gl);
} }
stbi_set_flip_vertically_on_load(false);
unbind(); unbind();
} }
Texture::Texture(std::vector<unsigned char> buffer, aiTextureType type, bool img_2d, bool mipmap) : Texture::Texture(std::vector<unsigned char> buffer, aiTextureType type, int flags) :
Texture(buffer.data(), buffer.size(), type, img_2d, mipmap) { Texture(buffer.data(), buffer.size(), type, flags) {
} }

34
src/scene.cpp Normal file
View File

@ -0,0 +1,34 @@
#include "scene.h"
#include "ecs/component/mesh_component.h"
#include "ecs/component/model_component.h"
#include "ecs/component/transform_component.h"
#include "ecs/component/rotating_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));
});
registry.view<TransformComponent, RotatingComponent>().each([this, &delta_time](TransformComponent& transform, RotatingComponent& rotating) {
transform.rotate(rotating.rate * delta_time, rotating.rotation_axis);
});
}
}