Core
The heart of the engine, handling entity functionality, events and whatnot
Entity component system
At the center of this entity component system implementation sits EnTT library, everything I wrote is a wrapper for that library like entt::dispatcher for events, entt::registry for registry related tasks like entity creation and a means to hold pointers to task manager, imgui manager and so on
Components
We have a lot of components and since the engine is ever expanding, we will have a lot more
Directional light
This component adds some key details to the directional light entity
struct DirectionalLightComponent
{
// index in the directional light array of the scene ssbo buffer
int directionalLightIndex{ 0 };
// near and far plane, outside of those we don't cast shadows
float nearPlane{ 0.1f };
float farPlane{ 300.0f };
// how much in front of it does it see
float orthoSize{ 70.0f };
// just a positional modifier
float positionFactor{ 20.0f };
};
Direction, diffuse color, specular color, color, those should be handled by the next struct
struct DirectionalLight
{
glm::vec4 direction{ -0.3f, -1.0f, -0.5f, 0.0f };
glm::vec4 diffuse{ 1.0f, 1.0f, 1.0f, 1.0f };
glm::vec4 specular{ 1.0f, 1.0f, 1.0f, 1.0f };
};
Point light
The point light component holds just the index in the pointlight array of the scene ssbo buffer
struct PointLightComponent
{
uint32_t pointLightIndex;
};
On the other hand, the PointLight holds much more information
struct PointLight
{
glm::vec4 translation{ 0.0f, 10.0f, 0.0f, 1.0f };
glm::vec4 ambient{ 0.1f, 0.1f, 0.1f, 1.0f };
glm::vec4 diffuse{ 1.0f, 1.0f, 1.0f, 1.0f };
glm::vec4 specular{ 1.0f, 1.0f, 1.0f, 1.0f };
glm::vec4 color{ 1.0f, 1.0f, 1.0f, 1.0f };
// x = constant, y = linear, z = quadratic, w = enabled
glm::vec4 params{ 1.0f, 0.022f, 0.0019f, 1.0f };
};
Since the shader buffers are constrained by some size rules I packed the point light calculation variables in the params vector so everything is aligned
Identifier
This is just an id like component where the name, type and group of the entity are stored, it is very easy to understand its use
We add an IdentifierComponent to each Entity created/ copied/ moved
struct IdentifierComponent
{
std::string name{};
EntityType type{};
std::string group{};
};
Index
IndexComponent is something I use to know where the instancing data of a particular Mesh is stored
It looks like this
struct IndexComponent
{
uint32_t index;
};
In the scene we have an InstancedData structure and that is what this is used for, see add scene chapter chapter for more info on this topic
Mesh
The mesh component holds just a pointer to the Mesh object, the object itself lives in the asset manager
That's why I said the manager is tied deeply with assets lifetime too
Static mesh flag tells us if we can move a certain submesh relative to the parent (the Mesh object) or just treat it as a "single" mesh but this is not entirely implemented yet
struct MeshComponent
{
kogayonon_resources::Mesh* pMesh;
bool staticMesh{ false };
bool loaded{ false };
};
Outline
Outline is used to filter normal entities from the selected ones, currently the engine supports the selection of a single entity and it is as designed
This is not provided to the fragment shader yet
struct OutlineComponent
{
glm::vec4 color{ 1.0f, 1.0f, 1.0f, 1.0f };
};
Rigidbody
We also have physics with Nvidia PhysX library, it is although early stages so I did not have that much playtime with those things
struct DynamicRigidbodyComponent
{
physx::PxRigidDynamic* pBody{ nullptr };
};
struct StaticRigidbodyComponent
{
physx::PxRigidStatic* pBody{ nullptr };
};
Texture
Having so much work with geometry I neglected the textures part of the engine, this has much more improvement coming its way
struct TextureComponent
{
std::weak_ptr<kogayonon_resources::Texture> pTexture;
};
Transform
A very important component for entities that also have MeshComponent
struct TransformComponent
{
glm::vec3 translation{ 0.0f };
glm::vec3 rotation{ 0.0f };
glm::vec3 scale{ 1.0f };
};
Currently I go back and forth with euler angles and quaternions, will make it so the transform will use and hold a quaternion for rotations for avoiding gimbal lock
Entity
This is a wrapper around some entt::registry functionality, it also has just two member variables
Those are:
- pointer to the registry entity belongs to
- entityId
private:
Registry* m_regsitry;
entt::entity m_entity;
Other than that the Entity has many templated functions centered around components
Each function is self explanatory
template <typename TComponent>
inline bool hasComponent();
template <typename TComponent>
inline auto tryGetComponent() -> TComponent*;
template <typename TComponent>
inline auto getComponent() -> TComponent&;
template <typename TComponent, typename... Args>
inline auto addComponent( Args&&... args ) -> TComponent&;
template <typename TComponent>
inline void removeComponent();
template <typename TComponent, typename... Args>
inline void replaceComponent( Args&&... args );
Registry
This is the wrapper around entt::registry and currently, for convenience reasons, holds the implementations of the functions that entity has as well
We can choose to construct an Entity from the scene registry and the id, since it already exists the registry will not make a new one, and then access the funcs
Or we can just call registry.getComponent<TComponent>(entityId)
Events
Every Event inherits from a base IEvent class
class IEvent
{
public:
IEvent() = default;
virtual ~IEvent() = default;
inline virtual EventType getEventType()
{
return m_type;
}
private:
EventType m_type{ EventType::None };
};
At the moment of writing the documentation I don't seem to have a usecase for the EventType, we'll see how things develop
App events
Those are simple and easy enough to understand just from the code provided
Window close
class WindowCloseEvent : public IEvent
{
public:
WindowCloseEvent() = default;
private:
};
Window resize
class WindowResizeEvent : public IEvent
{
public:
WindowResizeEvent( int width, int height )
: m_Width( width )
, m_Height( height )
{
}
int getWidth() const
{
return m_Width;
}
int getHeight() const
{
return m_Height;
}
private:
int m_Width, m_Height;
};
File events
When I wrote this documentation I realised that the implementation is redundant, there is a class for each file event type, delete, create, modify, rename, and all of them just store the path of the file and the filename
So by adding just another variable we also store the type
// this is the current implementation for created event and we have 3 more of those classes
// in the hpp file
class FileCreatedEvent : public IEvent
{
public:
explicit FileCreatedEvent( const std::string& path, const std::string& name );
~FileCreatedEvent() = default;
inline std::string getName() const;
inline std::string getPath() const;
protected:
std::string m_name;
std::string m_path;
};
This is all the file_events.hpp file should contain
enum class FileEventType
{
Created,
Modified,
Deleted,
Renamed
};
class FileEvent : public IEvent
{
public:
explicit FileEvent( const std::string& path,
const std::string& name,
const FileEventType& type );
~FileEvent() = default;
inline std::string getName() const;
inline std::string getPath() const;
inline FileEventType getType() const;
protected:
std::string m_name;
std::string m_path;
FileEventType m_type;
};