From 9fbd7938d00c25508fd8051168cfea7248ebeb16 Mon Sep 17 00:00:00 2001 From: Austin Morlan Date: Sat, 3 Aug 2019 12:29:42 -0700 Subject: [PATCH] Add code --- CMakeLists.txt | 65 ++++++ Shaders/fragment.glsl | 29 +++ Shaders/vertex.glsl | 19 ++ Source/Components/Camera.hpp | 36 +++ Source/Components/Gravity.hpp | 9 + Source/Components/Player.hpp | 6 + Source/Components/Renderable.hpp | 8 + Source/Components/RigidBody.hpp | 10 + Source/Components/Thrust.hpp | 9 + Source/Components/Transform.hpp | 11 + Source/Core/ComponentArray.hpp | 73 ++++++ Source/Core/ComponentManager.hpp | 79 +++++++ Source/Core/Coordinator.hpp | 118 ++++++++++ Source/Core/EntityManager.hpp | 58 +++++ Source/Core/Event.hpp | 37 +++ Source/Core/EventManager.hpp | 40 ++++ Source/Core/System.hpp | 11 + Source/Core/SystemManager.hpp | 68 ++++++ Source/Core/Types.hpp | 62 +++++ Source/Graphics/GlLoader.cpp | 59 +++++ Source/Graphics/GlLoader.hpp | 30 +++ Source/Graphics/Shader.cpp | 84 +++++++ Source/Graphics/Shader.hpp | 33 +++ Source/Main.cpp | 165 ++++++++++++++ Source/Math/Mat44.hpp | 53 +++++ Source/Math/Vec2.hpp | 47 ++++ Source/Math/Vec3.hpp | 85 +++++++ Source/Math/Vec4.hpp | 59 +++++ Source/Systems/CameraControlSystem.cpp | 61 +++++ Source/Systems/CameraControlSystem.hpp | 20 ++ Source/Systems/PhysicsSystem.cpp | 31 +++ Source/Systems/PhysicsSystem.hpp | 12 + Source/Systems/PlayerControlSystem.cpp | 61 +++++ Source/Systems/PlayerControlSystem.hpp | 20 ++ Source/Systems/RenderSystem.cpp | 224 ++++++++++++++++++ Source/Systems/RenderSystem.hpp | 29 +++ Source/WindowManager.cpp | 301 +++++++++++++++++++++++++ Source/WindowManager.hpp | 28 +++ 38 files changed, 2150 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 Shaders/fragment.glsl create mode 100644 Shaders/vertex.glsl create mode 100644 Source/Components/Camera.hpp create mode 100644 Source/Components/Gravity.hpp create mode 100644 Source/Components/Player.hpp create mode 100644 Source/Components/Renderable.hpp create mode 100644 Source/Components/RigidBody.hpp create mode 100644 Source/Components/Thrust.hpp create mode 100644 Source/Components/Transform.hpp create mode 100644 Source/Core/ComponentArray.hpp create mode 100644 Source/Core/ComponentManager.hpp create mode 100644 Source/Core/Coordinator.hpp create mode 100644 Source/Core/EntityManager.hpp create mode 100644 Source/Core/Event.hpp create mode 100644 Source/Core/EventManager.hpp create mode 100644 Source/Core/System.hpp create mode 100644 Source/Core/SystemManager.hpp create mode 100644 Source/Core/Types.hpp create mode 100644 Source/Graphics/GlLoader.cpp create mode 100644 Source/Graphics/GlLoader.hpp create mode 100644 Source/Graphics/Shader.cpp create mode 100644 Source/Graphics/Shader.hpp create mode 100644 Source/Main.cpp create mode 100644 Source/Math/Mat44.hpp create mode 100644 Source/Math/Vec2.hpp create mode 100644 Source/Math/Vec3.hpp create mode 100644 Source/Math/Vec4.hpp create mode 100644 Source/Systems/CameraControlSystem.cpp create mode 100644 Source/Systems/CameraControlSystem.hpp create mode 100644 Source/Systems/PhysicsSystem.cpp create mode 100644 Source/Systems/PhysicsSystem.hpp create mode 100644 Source/Systems/PlayerControlSystem.cpp create mode 100644 Source/Systems/PlayerControlSystem.hpp create mode 100644 Source/Systems/RenderSystem.cpp create mode 100644 Source/Systems/RenderSystem.hpp create mode 100644 Source/WindowManager.cpp create mode 100644 Source/WindowManager.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..50cfd7d --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,65 @@ +cmake_minimum_required(VERSION 3.14) +project(nexus) + + +find_package(X11 REQUIRED) +find_package(OpenGL REQUIRED) + + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + + +add_executable(nexus) + +target_compile_options( + nexus + PRIVATE + -fno-exceptions + -Wall) + +target_sources( + nexus + PRIVATE + Source/Graphics/GlLoader.cpp + Source/Graphics/Shader.cpp + Source/Main.cpp + Source/Systems/CameraControlSystem.cpp + Source/Systems/PhysicsSystem.cpp + Source/Systems/PlayerControlSystem.cpp + Source/Systems/RenderSystem.cpp + Source/WindowManager.cpp) + +target_sources( + nexus + PRIVATE + Source/Components/Camera.hpp + Source/Components/Gravity.hpp + Source/Components/Player.hpp + Source/Components/Renderable.hpp + Source/Components/RigidBody.hpp + Source/Components/Thrust.hpp + Source/Components/Transform.hpp + Source/Core/ComponentArray.hpp + Source/Core/ComponentManager.hpp + Source/Core/Coordinator.hpp + Source/Core/EntityManager.hpp + Source/Core/EventManager.hpp + Source/Core/System.hpp + Source/Core/SystemManager.hpp + Source/Core/Types.hpp + Source/Math/Vec2.hpp + Source/Math/Vec3.hpp + Source/Math/Vec4.hpp) + +target_include_directories( + nexus + PRIVATE + Source) + +target_link_libraries( + nexus + PRIVATE + OpenGL::GLX + X11::X11) diff --git a/Shaders/fragment.glsl b/Shaders/fragment.glsl new file mode 100644 index 0000000..cf68f53 --- /dev/null +++ b/Shaders/fragment.glsl @@ -0,0 +1,29 @@ +#version 330 core + +out vec4 FragColor; + +in vec3 normal; +in vec3 fragPosWorld; + +uniform vec3 uColor; + +vec3 lightColor = vec3(1.0f, 1.0f, 1.0f); + +vec3 lightPos = vec3(-100.0f, 50.0f, 100.0f); + +void main() +{ + // Ambient + float ambientStrength = 0.5; + vec3 ambient = ambientStrength * lightColor; + + // Diffuse + vec3 norm = normalize(normal); + vec3 lightDir = normalize(lightPos - fragPosWorld); + float diff = max(dot(norm, lightDir), 0.0); + vec3 diffuse = diff * lightColor; + + + vec3 result = (ambient + diffuse) * uColor; + FragColor = vec4(result, 1.0); +} diff --git a/Shaders/vertex.glsl b/Shaders/vertex.glsl new file mode 100644 index 0000000..c863906 --- /dev/null +++ b/Shaders/vertex.glsl @@ -0,0 +1,19 @@ +#version 330 core + +layout (location = 0) in vec3 inPos; +layout (location = 1) in vec3 inNormal; + +uniform mat4 uModel; +uniform mat4 uView; +uniform mat4 uProjection; + +out vec3 normal; +out vec3 fragPosWorld; + +void main() +{ + fragPosWorld = vec3(uModel * vec4(inPos, 1.0)); + normal = vec3(uModel * vec4(inNormal, 0.0)); + + gl_Position = uProjection * uView * uModel * vec4(inPos, 1.0f); +} diff --git a/Source/Components/Camera.hpp b/Source/Components/Camera.hpp new file mode 100644 index 0000000..cc31a96 --- /dev/null +++ b/Source/Components/Camera.hpp @@ -0,0 +1,36 @@ +#pragma once + +#include "Math/Mat44.hpp" +#include + + + + +struct Camera +{ + Mat44 projectionTransform; + + + static Mat44 MakeProjectionTransform(float fov, float nearClip, float farClip, unsigned int viewWidth, unsigned int viewHeight) + { + float zClipBias0 = + (farClip + nearClip) + / (farClip - nearClip); + + float zClipBias1 = + (2.0f * farClip * nearClip) + / (farClip - nearClip); + + float zoomX = 1.0f / tanf((fov / 2.0f) * (M_PI / 180.0f)); + float zoomY = (zoomX * static_cast(viewWidth)) / static_cast(viewHeight); + + Mat44 transform; + transform.m[0][0] = zoomX; + transform.m[1][1] = zoomY; + transform.m[2][2] = -zClipBias0; + transform.m[3][2] = -1; + transform.m[2][3] = -zClipBias1; + + return transform; + } +}; diff --git a/Source/Components/Gravity.hpp b/Source/Components/Gravity.hpp new file mode 100644 index 0000000..ecf1895 --- /dev/null +++ b/Source/Components/Gravity.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include "Math/Vec3.hpp" + + +struct Gravity +{ + Vec3 force; +}; diff --git a/Source/Components/Player.hpp b/Source/Components/Player.hpp new file mode 100644 index 0000000..14ca615 --- /dev/null +++ b/Source/Components/Player.hpp @@ -0,0 +1,6 @@ +#pragma once + + +struct Player +{ +}; diff --git a/Source/Components/Renderable.hpp b/Source/Components/Renderable.hpp new file mode 100644 index 0000000..6dd0cc0 --- /dev/null +++ b/Source/Components/Renderable.hpp @@ -0,0 +1,8 @@ +#pragma once + +#include "Math/Vec3.hpp" + +struct Renderable +{ + Vec3 color; +}; diff --git a/Source/Components/RigidBody.hpp b/Source/Components/RigidBody.hpp new file mode 100644 index 0000000..a43a9f6 --- /dev/null +++ b/Source/Components/RigidBody.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include "Math/Vec3.hpp" + + +struct RigidBody +{ + Vec3 velocity; + Vec3 acceleration; +}; diff --git a/Source/Components/Thrust.hpp b/Source/Components/Thrust.hpp new file mode 100644 index 0000000..6e2e7b9 --- /dev/null +++ b/Source/Components/Thrust.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include "Math/Vec3.hpp" + + +struct Thrust +{ + Vec3 force; +}; diff --git a/Source/Components/Transform.hpp b/Source/Components/Transform.hpp new file mode 100644 index 0000000..9ea60f4 --- /dev/null +++ b/Source/Components/Transform.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include "Math/Vec3.hpp" + + +struct Transform +{ + Vec3 position; + Vec3 rotation; + Vec3 scale; +}; diff --git a/Source/Core/ComponentArray.hpp b/Source/Core/ComponentArray.hpp new file mode 100644 index 0000000..f4bd5b1 --- /dev/null +++ b/Source/Core/ComponentArray.hpp @@ -0,0 +1,73 @@ +#pragma once + +#include "Types.hpp" +#include +#include +#include + + +class IComponentArray +{ +public: + virtual ~IComponentArray() = default; + virtual void EntityDestroyed(Entity entity) = 0; +}; + + +template +class ComponentArray : public IComponentArray +{ +public: + void InsertData(Entity entity, T component) + { + assert(mEntityToIndexMap.find(entity) == mEntityToIndexMap.end() && "Component added to same entity more than once."); + + // Put new entry at end + size_t newIndex = mSize; + mEntityToIndexMap[entity] = newIndex; + mIndexToEntityMap[newIndex] = entity; + mComponentArray[newIndex] = component; + ++mSize; + } + + void RemoveData(Entity entity) + { + assert(mEntityToIndexMap.find(entity) != mEntityToIndexMap.end() && "Removing non-existent component."); + + // Copy element at end into deleted element's place to maintain density + size_t indexOfRemovedEntity = mEntityToIndexMap[entity]; + size_t indexOfLastElement = mSize - 1; + mComponentArray[indexOfRemovedEntity] = mComponentArray[indexOfLastElement]; + + // Update map to point to moved spot + Entity entityOfLastElement = mIndexToEntityMap[indexOfLastElement]; + mEntityToIndexMap[entityOfLastElement] = indexOfRemovedEntity; + mIndexToEntityMap[indexOfRemovedEntity] = entityOfLastElement; + + mEntityToIndexMap.erase(entity); + mIndexToEntityMap.erase(indexOfLastElement); + + --mSize; + } + + T& GetData(Entity entity) + { + assert(mEntityToIndexMap.find(entity) != mEntityToIndexMap.end() && "Retrieving non-existent component."); + + return mComponentArray[mEntityToIndexMap[entity]]; + } + + void EntityDestroyed(Entity entity) override + { + if (mEntityToIndexMap.find(entity) != mEntityToIndexMap.end()) + { + RemoveData(entity); + } + } + +private: + std::array mComponentArray{}; + std::unordered_map mEntityToIndexMap{}; + std::unordered_map mIndexToEntityMap{}; + size_t mSize{}; +}; diff --git a/Source/Core/ComponentManager.hpp b/Source/Core/ComponentManager.hpp new file mode 100644 index 0000000..c4e23e2 --- /dev/null +++ b/Source/Core/ComponentManager.hpp @@ -0,0 +1,79 @@ +#pragma once + +#include "ComponentArray.hpp" +#include "Types.hpp" +#include +#include +#include + + +class ComponentManager +{ +public: + template + void RegisterComponent() + { + const char* typeName = typeid(T).name(); + + assert(mComponentTypes.find(typeName) == mComponentTypes.end() && "Registering component type more than once."); + + mComponentTypes.insert({typeName, mNextComponentType}); + mComponentArrays.insert({typeName, std::make_shared>()}); + + ++mNextComponentType; + } + + template + ComponentType GetComponentType() + { + const char* typeName = typeid(T).name(); + + assert(mComponentTypes.find(typeName) != mComponentTypes.end() && "Component not registered before use."); + + return mComponentTypes[typeName]; + } + + template + void AddComponent(Entity entity, T component) + { + GetComponentArray()->InsertData(entity, component); + } + + template + void RemoveComponent(Entity entity) + { + GetComponentArray()->RemoveData(entity); + } + + template + T& GetComponent(Entity entity) + { + return GetComponentArray()->GetData(entity); + } + + void EntityDestroyed(Entity entity) + { + for (auto const& pair : mComponentArrays) + { + auto const& component = pair.second; + + component->EntityDestroyed(entity); + } + } + +private: + std::unordered_map mComponentTypes{}; + std::unordered_map> mComponentArrays{}; + ComponentType mNextComponentType{}; + + + template + std::shared_ptr> GetComponentArray() + { + const char* typeName = typeid(T).name(); + + assert(mComponentTypes.find(typeName) != mComponentTypes.end() && "Component not registered before use."); + + return std::static_pointer_cast>(mComponentArrays[typeName]); + } +}; diff --git a/Source/Core/Coordinator.hpp b/Source/Core/Coordinator.hpp new file mode 100644 index 0000000..9d78610 --- /dev/null +++ b/Source/Core/Coordinator.hpp @@ -0,0 +1,118 @@ +#pragma once + +#include "ComponentManager.hpp" +#include "EntityManager.hpp" +#include "EventManager.hpp" +#include "SystemManager.hpp" +#include "Types.hpp" +#include + + +class Coordinator +{ +public: + void Init() + { + mComponentManager = std::make_unique(); + mEntityManager = std::make_unique(); + mEventManager = std::make_unique(); + mSystemManager = std::make_unique(); + } + + + // Entity methods + Entity CreateEntity() + { + return mEntityManager->CreateEntity(); + } + + void DestroyEntity(Entity entity) + { + mEntityManager->DestroyEntity(entity); + + mComponentManager->EntityDestroyed(entity); + + mSystemManager->EntityDestroyed(entity); + } + + + // Component methods + template + void RegisterComponent() + { + mComponentManager->RegisterComponent(); + } + + template + void AddComponent(Entity entity, T component) + { + mComponentManager->AddComponent(entity, component); + + auto signature = mEntityManager->GetSignature(entity); + signature.set(mComponentManager->GetComponentType(), true); + mEntityManager->SetSignature(entity, signature); + + mSystemManager->EntitySignatureChanged(entity, signature); + } + + template + void RemoveComponent(Entity entity) + { + mComponentManager->RemoveComponent(entity); + + auto signature = mEntityManager->GetSignature(entity); + signature.set(mComponentManager->GetComponentType(), false); + mEntityManager->SetSignature(entity, signature); + + mSystemManager->EntitySignatureChanged(entity, signature); + } + + template + T& GetComponent(Entity entity) + { + return mComponentManager->GetComponent(entity); + } + + template + ComponentType GetComponentType() + { + return mComponentManager->GetComponentType(); + } + + + // System methods + template + std::shared_ptr RegisterSystem() + { + return mSystemManager->RegisterSystem(); + } + + template + void SetSystemSignature(Signature signature) + { + mSystemManager->SetSignature(signature); + } + + + // Event methods + void AddEventListener(EventId eventId, std::function const& listener) + { + mEventManager->AddListener(eventId, listener); + } + + void SendEvent(Event& event) + { + mEventManager->SendEvent(event); + } + + void SendEvent(EventId eventId) + { + mEventManager->SendEvent(eventId); + } + +private: + std::unique_ptr mComponentManager; + std::unique_ptr mEntityManager; + std::unique_ptr mEventManager; + std::unique_ptr mSystemManager; +}; diff --git a/Source/Core/EntityManager.hpp b/Source/Core/EntityManager.hpp new file mode 100644 index 0000000..82955ba --- /dev/null +++ b/Source/Core/EntityManager.hpp @@ -0,0 +1,58 @@ +#pragma once + +#include "Types.hpp" +#include +#include +#include + + +class EntityManager +{ +public: + EntityManager() + { + for (Entity entity = 0; entity < MAX_ENTITIES; ++entity) + { + mAvailableEntities.push(entity); + } + } + + Entity CreateEntity() + { + assert(mLivingEntityCount < MAX_ENTITIES && "Too many entities in existence."); + + Entity id = mAvailableEntities.front(); + mAvailableEntities.pop(); + ++mLivingEntityCount; + + return id; + } + + void DestroyEntity(Entity entity) + { + assert(entity < MAX_ENTITIES && "Entity out of range."); + + mSignatures[entity].reset(); + mAvailableEntities.push(entity); + --mLivingEntityCount; + } + + void SetSignature(Entity entity, Signature signature) + { + assert(entity < MAX_ENTITIES && "Entity out of range."); + + mSignatures[entity] = signature; + } + + Signature GetSignature(Entity entity) + { + assert(entity < MAX_ENTITIES && "Entity out of range."); + + return mSignatures[entity]; + } + +private: + std::queue mAvailableEntities{}; + std::array mSignatures{}; + uint32_t mLivingEntityCount{}; +}; diff --git a/Source/Core/Event.hpp b/Source/Core/Event.hpp new file mode 100644 index 0000000..0951c58 --- /dev/null +++ b/Source/Core/Event.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "Types.hpp" +#include +#include + + +class Event +{ +public: + Event() = delete; + + explicit Event(EventId type) + : mType(type) + {} + + template + void SetParam(EventId id, T value) + { + mData[id] = value; + } + + template + T GetParam(EventId id) + { + return std::any_cast(mData[id]); + } + + EventId GetType() const + { + return mType; + } + +private: + EventId mType{}; + std::unordered_map mData{}; +}; diff --git a/Source/Core/EventManager.hpp b/Source/Core/EventManager.hpp new file mode 100644 index 0000000..b8a9c32 --- /dev/null +++ b/Source/Core/EventManager.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include "Event.hpp" +#include "Types.hpp" +#include +#include +#include + + +class EventManager +{ +public: + void AddListener(EventId eventId, std::function const& listener) + { + listeners[eventId].push_back(listener); + } + + void SendEvent(Event& event) + { + uint32_t type = event.GetType(); + + for (auto const& listener : listeners[type]) + { + listener(event); + } + } + + void SendEvent(EventId eventId) + { + Event event(eventId); + + for (auto const& listener : listeners[eventId]) + { + listener(event); + } + } + +private: + std::unordered_map>> listeners; +}; diff --git a/Source/Core/System.hpp b/Source/Core/System.hpp new file mode 100644 index 0000000..b67d655 --- /dev/null +++ b/Source/Core/System.hpp @@ -0,0 +1,11 @@ +#pragma once + +#include "Types.hpp" +#include + + +class System +{ +public: + std::set mEntities; +}; diff --git a/Source/Core/SystemManager.hpp b/Source/Core/SystemManager.hpp new file mode 100644 index 0000000..ccaa2b3 --- /dev/null +++ b/Source/Core/SystemManager.hpp @@ -0,0 +1,68 @@ +#pragma once + +#include "System.hpp" +#include "Types.hpp" +#include +#include +#include + + +class SystemManager +{ +public: + template + std::shared_ptr RegisterSystem() + { + const char* typeName = typeid(T).name(); + + assert(mSystems.find(typeName) == mSystems.end() && "Registering system more than once."); + + auto system = std::make_shared(); + mSystems.insert({typeName, system}); + return system; + } + + template + void SetSignature(Signature signature) + { + const char* typeName = typeid(T).name(); + + assert(mSystems.find(typeName) != mSystems.end() && "System used before registered."); + + mSignatures.insert({typeName, signature}); + } + + void EntityDestroyed(Entity entity) + { + for (auto const& pair : mSystems) + { + auto const& system = pair.second; + + + system->mEntities.erase(entity); + } + } + + void EntitySignatureChanged(Entity entity, Signature entitySignature) + { + for (auto const& pair : mSystems) + { + auto const& type = pair.first; + auto const& system = pair.second; + auto const& systemSignature = mSignatures[type]; + + if ((entitySignature & systemSignature) == systemSignature) + { + system->mEntities.insert(entity); + } + else + { + system->mEntities.erase(entity); + } + } + } + +private: + std::unordered_map mSignatures{}; + std::unordered_map> mSystems{}; +}; diff --git a/Source/Core/Types.hpp b/Source/Core/Types.hpp new file mode 100644 index 0000000..dfa9bac --- /dev/null +++ b/Source/Core/Types.hpp @@ -0,0 +1,62 @@ +#pragma once + +#include +#include + + +// Source: https://gist.github.com/Lee-R/3839813 +constexpr std::uint32_t fnv1a_32(char const* s, std::size_t count) +{ + return ((count ? fnv1a_32(s, count - 1) : 2166136261u) ^ s[count]) * 16777619u; // NOLINT (hicpp-signed-bitwise) +} + +constexpr std::uint32_t operator "" _hash(char const* s, std::size_t count) +{ + return fnv1a_32(s, count); +} + + +// ECS +using Entity = std::uint32_t; +const Entity MAX_ENTITIES = 5000; +using ComponentType = std::uint8_t; +const ComponentType MAX_COMPONENTS = 32; +using Signature = std::bitset; + + +// Input +enum class InputButtons +{ + W, + A, + S, + D, + Q, + E +}; + + +// Events +using EventId = std::uint32_t; +using ParamId = std::uint32_t; + +#define METHOD_LISTENER(EventType, Listener) EventType, std::bind(&Listener, this, std::placeholders::_1) +#define FUNCTION_LISTENER(EventType, Listener) EventType, std::bind(&Listener, std::placeholders::_1) + +// TODO: Make these easier to define and use (macro?) +// TODO: Add some kind of enforcement/automation that a SetParam type and a GetParam type match + +namespace Events::Window { +const EventId QUIT = "Events::Window::QUIT"_hash; +const EventId RESIZED = "Events::Window::RESIZED"_hash; +const EventId INPUT = "Events::Window::INPUT"_hash; +} + +namespace Events::Window::Input { +const ParamId INPUT = "Events::Window::Input::INPUT"_hash; +} + +namespace Events::Window::Resized { +const ParamId WIDTH = "Events::Window::Resized::WIDTH"_hash; +const ParamId HEIGHT = "Events::Window::Resized::HEIGHT"_hash; +} diff --git a/Source/Graphics/GlLoader.cpp b/Source/Graphics/GlLoader.cpp new file mode 100644 index 0000000..2466a24 --- /dev/null +++ b/Source/Graphics/GlLoader.cpp @@ -0,0 +1,59 @@ +#include "GlLoader.hpp" + +#include + + +PFNGLCREATESHADERPROC glCreateShader = nullptr; +PFNGLSHADERSOURCEPROC glShaderSource = nullptr; +PFNGLCOMPILESHADERPROC glCompileShader = nullptr; +PFNGLGETSHADERIVPROC glGetShaderiv = nullptr; +PFNGLGETSHADERSOURCEPROC glGetShaderInfo = nullptr; +PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog = nullptr; +PFNGLCREATEPROGRAMPROC glCreateProgram = nullptr; +PFNGLATTACHSHADERPROC glAttachShader = nullptr; +PFNGLLINKPROGRAMPROC glLinkProgram = nullptr; +PFNGLGETPROGRAMIVPROC glGetProgramiv = nullptr; +PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog = nullptr; +PFNGLDELETESHADERPROC glDeleteShader = nullptr; +PFNGLGENBUFFERSPROC glGenBuffers = nullptr; +PFNGLBINDBUFFERPROC glBindBuffer = nullptr; +PFNGLBUFFERDATAPROC glBufferData = nullptr; +PFNGLGENVERTEXARRAYSPROC glGenVertexArrays = nullptr; +PFNGLBINDVERTEXARRAYPROC glBindVertexArray = nullptr; +PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer = nullptr; +PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray = nullptr; +PFNGLUSEPROGRAMPROC glUseProgram = nullptr; +PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation = nullptr; +PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv = nullptr; +PFNGLUNIFORM3FVPROC glUniform3fv = nullptr; + +#define LOAD_GL_FUNCTION(type, name) \ + (name) = (PFN##type##PROC) glXGetProcAddress((unsigned char *) #name); \ + assert(name && "GL function not found"); + +void LoadGlFunctions() +{ + LOAD_GL_FUNCTION(GLCREATESHADER, glCreateShader) + LOAD_GL_FUNCTION(GLSHADERSOURCE, glShaderSource) + LOAD_GL_FUNCTION(GLCOMPILESHADER, glCompileShader) + LOAD_GL_FUNCTION(GLGETSHADERIV, glGetShaderiv) + LOAD_GL_FUNCTION(GLGETSHADERSOURCE, glGetShaderInfo) + LOAD_GL_FUNCTION(GLGETSHADERINFOLOG, glGetShaderInfoLog) + LOAD_GL_FUNCTION(GLCREATEPROGRAM, glCreateProgram) + LOAD_GL_FUNCTION(GLATTACHSHADER, glAttachShader) + LOAD_GL_FUNCTION(GLLINKPROGRAM, glLinkProgram) + LOAD_GL_FUNCTION(GLGETPROGRAMIV, glGetProgramiv) + LOAD_GL_FUNCTION(GLGETPROGRAMINFOLOG, glGetProgramInfoLog) + LOAD_GL_FUNCTION(GLDELETESHADER, glDeleteShader) + LOAD_GL_FUNCTION(GLGENBUFFERS, glGenBuffers) + LOAD_GL_FUNCTION(GLBINDBUFFER, glBindBuffer) + LOAD_GL_FUNCTION(GLBUFFERDATA, glBufferData) + LOAD_GL_FUNCTION(GLGENVERTEXARRAYS, glGenVertexArrays) + LOAD_GL_FUNCTION(GLBINDVERTEXARRAY, glBindVertexArray) + LOAD_GL_FUNCTION(GLVERTEXATTRIBPOINTER, glVertexAttribPointer) + LOAD_GL_FUNCTION(GLENABLEVERTEXATTRIBARRAY, glEnableVertexAttribArray) + LOAD_GL_FUNCTION(GLUSEPROGRAM, glUseProgram) + LOAD_GL_FUNCTION(GLGETUNIFORMLOCATION, glGetUniformLocation) + LOAD_GL_FUNCTION(GLUNIFORMMATRIX4FV, glUniformMatrix4fv) + LOAD_GL_FUNCTION(GLUNIFORM3FV, glUniform3fv) +} diff --git a/Source/Graphics/GlLoader.hpp b/Source/Graphics/GlLoader.hpp new file mode 100644 index 0000000..6ba1e7a --- /dev/null +++ b/Source/Graphics/GlLoader.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include + + +extern PFNGLCREATESHADERPROC glCreateShader; +extern PFNGLSHADERSOURCEPROC glShaderSource; +extern PFNGLCOMPILESHADERPROC glCompileShader; +extern PFNGLGETSHADERIVPROC glGetShaderiv; +extern PFNGLGETSHADERSOURCEPROC glGetShaderInfo; +extern PFNGLGETSHADERINFOLOGPROC glGetShaderInfoLog; +extern PFNGLCREATEPROGRAMPROC glCreateProgram; +extern PFNGLATTACHSHADERPROC glAttachShader; +extern PFNGLLINKPROGRAMPROC glLinkProgram; +extern PFNGLGETPROGRAMIVPROC glGetProgramiv; +extern PFNGLGETPROGRAMINFOLOGPROC glGetProgramInfoLog; +extern PFNGLDELETESHADERPROC glDeleteShader; +extern PFNGLGENBUFFERSPROC glGenBuffers; +extern PFNGLBINDBUFFERPROC glBindBuffer; +extern PFNGLBUFFERDATAPROC glBufferData; +extern PFNGLGENVERTEXARRAYSPROC glGenVertexArrays; +extern PFNGLBINDVERTEXARRAYPROC glBindVertexArray; +extern PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer; +extern PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray; +extern PFNGLUSEPROGRAMPROC glUseProgram; +extern PFNGLGETUNIFORMLOCATIONPROC glGetUniformLocation; +extern PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv; +extern PFNGLUNIFORM3FVPROC glUniform3fv; + +void LoadGlFunctions(); diff --git a/Source/Graphics/Shader.cpp b/Source/Graphics/Shader.cpp new file mode 100644 index 0000000..d154ec5 --- /dev/null +++ b/Source/Graphics/Shader.cpp @@ -0,0 +1,84 @@ +#include "Shader.hpp" + +#include +#include +#include + + +Shader::Shader(std::string const& vertexPath, std::string const& fragmentPath) +{ + std::string fragmentFileContents; + std::string vertexFileContents; + + // Read in the vertex shader + std::ifstream vertex_file; + + vertex_file.open(vertexPath); + std::stringstream vertex_file_stream; + vertex_file_stream << vertex_file.rdbuf(); + vertex_file.close(); + vertexFileContents = vertex_file_stream.str(); + + // Read in the fragment shader + std::ifstream fragment_file; + + fragment_file.open(fragmentPath); + std::stringstream fragment_file_stream; + fragment_file_stream << fragment_file.rdbuf(); + fragment_file.close(); + fragmentFileContents = fragment_file_stream.str(); + + GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER); + + const GLchar* vertex_shader_source = vertexFileContents.c_str(); + glShaderSource(vertex_shader, 1, &vertex_shader_source, nullptr); + glCompileShader(vertex_shader); + + int success; + char infoLog[512]; + glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success); + + if (!success) + { + glGetShaderInfoLog(vertex_shader, 512, nullptr, infoLog); + std::cerr << "Error compiling vertex shader: " << infoLog << "\n"; + } + + + GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + + const GLchar* fragment_shader_source = fragmentFileContents.c_str(); + glShaderSource(fragment_shader, 1, &fragment_shader_source, nullptr); + glCompileShader(fragment_shader); + glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success); + + if (!success) + { + glGetShaderInfoLog(fragment_shader, 512, nullptr, infoLog); + std::cerr << "Error compiling fragment shader: " << infoLog << "\n"; + } + + + mId = glCreateProgram(); + + glAttachShader(mId, vertex_shader); + glAttachShader(mId, fragment_shader); + glLinkProgram(mId); + glGetProgramiv(mId, GL_LINK_STATUS, &success); + + if (!success) + { + glGetProgramInfoLog(mId, 512, nullptr, infoLog); + std::cerr << "Error linking shaders: " << infoLog << "\n"; + } + + glDeleteShader(vertex_shader); + glDeleteShader(fragment_shader); +} + +void Shader::Activate() +{ + glUseProgram(mId); +} + + diff --git a/Source/Graphics/Shader.hpp b/Source/Graphics/Shader.hpp new file mode 100644 index 0000000..ace4e60 --- /dev/null +++ b/Source/Graphics/Shader.hpp @@ -0,0 +1,33 @@ +#pragma once + +#include "GlLoader.hpp" +#include + + +class Mat44; +class Vec3; + + +class Shader +{ +public: + Shader(std::string const& vertexPath, std::string const& fragmentPath); + + void Activate(); + + template + void SetUniform(const std::string& name, const T& value) + { + if constexpr (std::is_same_v) + { + glUniformMatrix4fv(glGetUniformLocation(mId, name.c_str()), 1, GL_TRUE, (GLfloat*)value.m); + } + else if constexpr (std::is_same_v) + { + glUniform3fv(glGetUniformLocation(mId, name.c_str()), 1, (GLfloat*)&value); + } + } + +private: + GLuint mId; +}; diff --git a/Source/Main.cpp b/Source/Main.cpp new file mode 100644 index 0000000..625f1f3 --- /dev/null +++ b/Source/Main.cpp @@ -0,0 +1,165 @@ +#include "Components/Camera.hpp" +#include "Components/Gravity.hpp" +#include "Components/Player.hpp" +#include "Components/Renderable.hpp" +#include "Components/RigidBody.hpp" +#include "Components/Thrust.hpp" +#include "Components/Transform.hpp" +#include "Core/Coordinator.hpp" +#include "Systems/CameraControlSystem.hpp" +#include "Systems/PhysicsSystem.hpp" +#include "Systems/PlayerControlSystem.hpp" +#include "Systems/RenderSystem.hpp" +#include "WindowManager.hpp" +#include +#include + + +Coordinator gCoordinator; + +static bool quit = false; + + +void QuitHandler(Event& event) +{ + quit = true; +} + +int main() +{ + gCoordinator.Init(); + + + WindowManager windowManager; + windowManager.Init("Nexus", 1920, 1080, 0, 0); + + + LoadGlFunctions(); + + + gCoordinator.AddEventListener(FUNCTION_LISTENER(Events::Window::QUIT, QuitHandler)); + + + gCoordinator.RegisterComponent(); + gCoordinator.RegisterComponent(); + gCoordinator.RegisterComponent(); + gCoordinator.RegisterComponent(); + gCoordinator.RegisterComponent(); + gCoordinator.RegisterComponent(); + gCoordinator.RegisterComponent(); + + + auto physicsSystem = gCoordinator.RegisterSystem(); + { + Signature signature; + signature.set(gCoordinator.GetComponentType()); + signature.set(gCoordinator.GetComponentType()); + signature.set(gCoordinator.GetComponentType()); + gCoordinator.SetSystemSignature(signature); + } + + physicsSystem->Init(); + + + auto cameraControlSystem = gCoordinator.RegisterSystem(); + { + Signature signature; + signature.set(gCoordinator.GetComponentType()); + signature.set(gCoordinator.GetComponentType()); + gCoordinator.SetSystemSignature(signature); + } + + cameraControlSystem->Init(); + + + auto playerControlSystem = gCoordinator.RegisterSystem(); + { + Signature signature; + signature.set(gCoordinator.GetComponentType()); + signature.set(gCoordinator.GetComponentType()); + gCoordinator.SetSystemSignature(signature); + } + + playerControlSystem->Init(); + + + auto renderSystem = gCoordinator.RegisterSystem(); + { + Signature signature; + signature.set(gCoordinator.GetComponentType()); + signature.set(gCoordinator.GetComponentType()); + gCoordinator.SetSystemSignature(signature); + } + + renderSystem->Init(); + + std::vector entities(MAX_ENTITIES - 1); + + std::default_random_engine generator; + std::uniform_real_distribution randPosition(-100.0f, 100.0f); + std::uniform_real_distribution randRotation(0.0f, 3.0f); + std::uniform_real_distribution randScale(3.0f, 5.0f); + std::uniform_real_distribution randColor(0.0f, 1.0f); + std::uniform_real_distribution randGravity(-10.0f, -1.0f); + + float scale = randScale(generator); + + for (auto& entity : entities) + { + entity = gCoordinator.CreateEntity(); + gCoordinator.AddComponent(entity, Player{}); + + gCoordinator.AddComponent( + entity, + {Vec3(0.0f, randGravity(generator), 0.0f)}); + + gCoordinator.AddComponent( + entity, + RigidBody{ + .velocity = Vec3(0.0f, 0.0f, 0.0f), + .acceleration = Vec3(0.0f, 0.0f, 0.0f) + }); + + gCoordinator.AddComponent( + entity, + Transform{ + .position = Vec3(randPosition(generator), randPosition(generator), randPosition(generator)), + .rotation = Vec3(randRotation(generator), randRotation(generator), randRotation(generator)), + .scale = Vec3(scale, scale, scale) + }); + + gCoordinator.AddComponent( + entity, + Renderable{ + .color = Vec3(randColor(generator), randColor(generator), randColor(generator)) + }); + } + + float dt = 0.0f; + + while (!quit) + { + auto startTime = std::chrono::high_resolution_clock::now(); + + windowManager.ProcessEvents(); + + playerControlSystem->Update(dt); + + cameraControlSystem->Update(dt); + + physicsSystem->Update(dt); + + renderSystem->Update(dt); + + windowManager.Update(); + + auto stopTime = std::chrono::high_resolution_clock::now(); + + dt = std::chrono::duration(stopTime - startTime).count(); + } + + + windowManager.Shutdown(); + + return 0; +} diff --git a/Source/Math/Mat44.hpp b/Source/Math/Mat44.hpp new file mode 100644 index 0000000..4c288b3 --- /dev/null +++ b/Source/Math/Mat44.hpp @@ -0,0 +1,53 @@ +#pragma once + +#include "Vec4.hpp" + +class Mat44 +{ +public: + Mat44() + { + //@formatter:off + m[0][0] = 1.0f; m[0][1] = 0.0f; m[0][2] = 0.0f; m[0][3] = 0.0f; + m[1][0] = 0.0f; m[1][1] = 1.0f; m[1][2] = 0.0f; m[1][3] = 0.0f; + m[2][0] = 0.0f; m[2][1] = 0.0f; m[2][2] = 1.0f; m[2][3] = 0.0f; + m[3][0] = 0.0f; m[3][1] = 0.0f; m[3][2] = 0.0f; m[3][3] = 1.0f; + //@formatter:on + } + + + Mat44 operator*(Mat44 const& rhs) + { + Mat44 result; + + for (int row = 0; row < 4; ++row) + { + for (int col = 0; col < 4; ++col) + { + float sum = 0.0f; + + for (int i = 0; i < 4; ++i) + { + sum += m[row][i] * rhs.m[i][col]; + } + + result.m[row][col] = sum; + } + } + + return result; + } + + + Vec4 operator*(Vec4 const& rhs) + { + return Vec4( + (rhs.x * m[0][0]) + (rhs.y * m[0][1]) + (rhs.z * m[0][2]) + (rhs.w * m[0][3]), + (rhs.x * m[1][0]) + (rhs.y * m[1][1]) + (rhs.z * m[1][2]) + (rhs.w * m[1][3]), + (rhs.x * m[2][0]) + (rhs.y * m[2][1]) + (rhs.z * m[2][2]) + (rhs.w * m[2][3]), + (rhs.x * m[3][0]) + (rhs.y * m[3][1]) + (rhs.z * m[3][2]) + (rhs.w * m[3][3])); + } + + + float m[4][4]; +}; diff --git a/Source/Math/Vec2.hpp b/Source/Math/Vec2.hpp new file mode 100644 index 0000000..627bc55 --- /dev/null +++ b/Source/Math/Vec2.hpp @@ -0,0 +1,47 @@ +#pragma once + + +class Vec2 +{ +public: + Vec2() + : x(0.0f), y(0.0f) + {} + + Vec2(float x, float y) + : x(x), y(y) + {} + + Vec2 operator+(Vec2 const& v) + { + return Vec2( + x + v.x, + y + v.y); + } + + Vec2 operator+=(Vec2 const& v) + { + x += v.x; + y += v.y; + + return *this; + } + + Vec2 operator-(Vec2 const& v) + { + return Vec2( + x - v.x, + y - v.y); + } + + Vec2 operator-=(Vec2 const& v) + { + x -= v.x; + y -= v.y; + + return *this; + } + + + float x, y; +}; diff --git a/Source/Math/Vec3.hpp b/Source/Math/Vec3.hpp new file mode 100644 index 0000000..f5b58b1 --- /dev/null +++ b/Source/Math/Vec3.hpp @@ -0,0 +1,85 @@ +#pragma once + + +class Vec3 +{ +public: + Vec3() + : x(0.0f), y(0.0f), z(0.0f) + {} + + Vec3(float x, float y, float z) + : x(x), y(y), z(z) + {} + + Vec3 operator+(Vec3 const& rhs) const + { + return Vec3( + x + rhs.x, + y + rhs.y, + z + rhs.z); + } + + Vec3 operator+=(Vec3 const& rhs) + { + x += rhs.x; + y += rhs.y; + z += rhs.z; + + return *this; + } + + Vec3 operator-(Vec3 const& rhs) const + { + return Vec3( + x - rhs.x, + y - rhs.y, + z - rhs.z); + } + + Vec3 operator-=(Vec3 const& rhs) + { + x -= rhs.x; + y -= rhs.y; + z -= rhs.z; + + return *this; + } + + Vec3 operator*(Vec3 const& rhs) const + { + return Vec3( + x * rhs.x, + y * rhs.y, + z * rhs.z); + } + + Vec3 operator*=(Vec3 const& rhs) + { + x *= rhs.x; + y *= rhs.y; + z *= rhs.z; + + return *this; + } + + Vec3 operator*(float rhs) const + { + return Vec3( + x * rhs, + y * rhs, + z * rhs); + } + + Vec3 operator*=(float rhs) + { + x *= rhs; + y *= rhs; + z *= rhs; + + return *this; + } + + + float x, y, z; +}; diff --git a/Source/Math/Vec4.hpp b/Source/Math/Vec4.hpp new file mode 100644 index 0000000..d897ebe --- /dev/null +++ b/Source/Math/Vec4.hpp @@ -0,0 +1,59 @@ +#pragma once + + +class Vec4 +{ +public: + Vec4() + : x(0.0f), y(0.0f), z(0.0f), w(0.0f) + {} + + Vec4(float x, float y, float z) + : x(x), y(y), z(z), w(0.0f) + {} + + Vec4(float x, float y, float z, float w) + : x(x), y(y), z(z), w(w) + {} + + Vec4 operator+(Vec4 const& v) const + { + return Vec4( + x + v.x, + y + v.y, + z + v.z, + w + v.w); + } + + Vec4 operator+=(Vec4 const& v) + { + x += v.x; + y += v.y; + z += v.z; + w += v.w; + + return *this; + } + + Vec4 operator-(Vec4 const& v) const + { + return Vec4( + x - v.x, + y - v.y, + z - v.z, + w - v.w); + } + + Vec4 operator-=(Vec4 const& v) + { + x -= v.x; + y -= v.y; + z -= v.z; + w -= v.w; + + return *this; + } + + + float x, y, z, w; +}; diff --git a/Source/Systems/CameraControlSystem.cpp b/Source/Systems/CameraControlSystem.cpp new file mode 100644 index 0000000..e2fffbf --- /dev/null +++ b/Source/Systems/CameraControlSystem.cpp @@ -0,0 +1,61 @@ +#include "CameraControlSystem.hpp" + +#include "Components/Transform.hpp" +#include "Core/Coordinator.hpp" + + +extern Coordinator gCoordinator; + + +void CameraControlSystem::Init() +{ + gCoordinator.AddEventListener(METHOD_LISTENER(Events::Window::INPUT, CameraControlSystem::InputListener)); +} + +void CameraControlSystem::Update(float dt) +{ + for (auto& entity : mEntities) + { + auto& transform = gCoordinator.GetComponent(entity); + + float speed = 20.0f; + + + if (mButtons.test(static_cast(InputButtons::W))) + { + transform.position.z -= (dt * speed); + } + + else if (mButtons.test(static_cast(InputButtons::S))) + { + transform.position.z += (dt * speed); + } + + + if (mButtons.test(static_cast(InputButtons::Q))) + { + transform.position.y += (dt * speed); + } + + else if (mButtons.test(static_cast(InputButtons::E))) + { + transform.position.y -= (dt * speed); + } + + + if (mButtons.test(static_cast(InputButtons::A))) + { + transform.position.x -= (dt * speed); + } + + else if (mButtons.test(static_cast(InputButtons::D))) + { + transform.position.x += (dt * speed); + } + } +} + +void CameraControlSystem::InputListener(Event& event) +{ + mButtons = event.GetParam>(Events::Window::Input::INPUT); +} diff --git a/Source/Systems/CameraControlSystem.hpp b/Source/Systems/CameraControlSystem.hpp new file mode 100644 index 0000000..f5eb0f0 --- /dev/null +++ b/Source/Systems/CameraControlSystem.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "Core/System.hpp" + + +class Event; + + +class CameraControlSystem : public System +{ +public: + void Init(); + + void Update(float dt); + +private: + std::bitset<8> mButtons; + + void InputListener(Event& event); +}; diff --git a/Source/Systems/PhysicsSystem.cpp b/Source/Systems/PhysicsSystem.cpp new file mode 100644 index 0000000..c551896 --- /dev/null +++ b/Source/Systems/PhysicsSystem.cpp @@ -0,0 +1,31 @@ +#include "PhysicsSystem.hpp" + +#include "Components/Gravity.hpp" +#include "Components/RigidBody.hpp" +#include "Components/Thrust.hpp" +#include "Components/Transform.hpp" +#include "Core/Coordinator.hpp" + + +extern Coordinator gCoordinator; + + +void PhysicsSystem::Init() +{ +} + +void PhysicsSystem::Update(float dt) +{ + for (auto const& entity : mEntities) + { + auto& rigidBody = gCoordinator.GetComponent(entity); + auto& transform = gCoordinator.GetComponent(entity); + + // Forces + auto const& gravity = gCoordinator.GetComponent(entity); + + transform.position += rigidBody.velocity * dt; + + rigidBody.velocity += gravity.force * dt; + } +} diff --git a/Source/Systems/PhysicsSystem.hpp b/Source/Systems/PhysicsSystem.hpp new file mode 100644 index 0000000..dd2df40 --- /dev/null +++ b/Source/Systems/PhysicsSystem.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include "Core/System.hpp" + + +class PhysicsSystem : public System +{ +public: + void Init(); + + void Update(float dt); +}; diff --git a/Source/Systems/PlayerControlSystem.cpp b/Source/Systems/PlayerControlSystem.cpp new file mode 100644 index 0000000..69d649a --- /dev/null +++ b/Source/Systems/PlayerControlSystem.cpp @@ -0,0 +1,61 @@ +#include "PlayerControlSystem.hpp" + +#include "Components/Player.hpp" +#include "Components/Thrust.hpp" +#include "Components/Transform.hpp" +#include "Core/Coordinator.hpp" + + +extern Coordinator gCoordinator; + + +void PlayerControlSystem::Init() +{ + gCoordinator.AddEventListener(METHOD_LISTENER(Events::Window::INPUT, PlayerControlSystem::InputListener)); +} + +void PlayerControlSystem::Update(float dt) +{ + //for (auto& entity : mEntities) + //{ + // auto& transform = gCoordinator.Get(entity); + + + // if (mButtons.test(static_cast(InputButtons::W))) + // { + // transform.position.z += (dt * 10.0f); + // } + + // else if (mButtons.test(static_cast(InputButtons::S))) + // { + // transform.position.z -= (dt * 10.0f); + // } + + + // if (mButtons.test(static_cast(InputButtons::Q))) + // { + // transform.position.y += (dt * 10.0f); + // } + + // else if (mButtons.test(static_cast(InputButtons::E))) + // { + // transform.position.y -= (dt * 10.0f); + // } + + + // if (mButtons.test(static_cast(InputButtons::A))) + // { + // transform.position.x += (dt * 10.0f); + // } + + // else if (mButtons.test(static_cast(InputButtons::D))) + // { + // transform.position.x -= (dt * 10.0f); + // } + //} +} + +void PlayerControlSystem::InputListener(Event& event) +{ + mButtons = event.GetParam>(Events::Window::Input::INPUT); +} diff --git a/Source/Systems/PlayerControlSystem.hpp b/Source/Systems/PlayerControlSystem.hpp new file mode 100644 index 0000000..1ccd76e --- /dev/null +++ b/Source/Systems/PlayerControlSystem.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include "Core/System.hpp" + + +class Event; + + +class PlayerControlSystem : public System +{ +public: + void Init(); + + void Update(float dt); + +private: + std::bitset<8> mButtons; + + void InputListener(Event& event); +}; diff --git a/Source/Systems/RenderSystem.cpp b/Source/Systems/RenderSystem.cpp new file mode 100644 index 0000000..f49d2db --- /dev/null +++ b/Source/Systems/RenderSystem.cpp @@ -0,0 +1,224 @@ +#include "RenderSystem.hpp" + +#include "Components/Camera.hpp" +#include "Components/Renderable.hpp" +#include "Components/Transform.hpp" +#include "Core/Coordinator.hpp" +#include "Graphics/Shader.hpp" +#include + + +extern Coordinator gCoordinator; + + +void RenderSystem::Init() +{ + gCoordinator.AddEventListener(METHOD_LISTENER(Events::Window::RESIZED, RenderSystem::WindowSizeListener)); + + shader = std::make_unique("../Shaders/vertex.glsl", "../Shaders/fragment.glsl"); + + mCamera = gCoordinator.CreateEntity(); + + gCoordinator.AddComponent( + mCamera, + Transform{ + .position = Vec3(0.0f, 0.0f, 500.0f) + }); + + gCoordinator.AddComponent( + mCamera, + Camera{ + .projectionTransform = Camera::MakeProjectionTransform(45.0f, 0.1f, 1000.0f, 1920, 1080) + }); + + std::vector vertices = + { + Vec3(-0.5f, -0.5f, -0.5f), + Vec3(0.5f, -0.5f, -0.5f), + Vec3(0.5f, 0.5f, -0.5f), + Vec3(0.5f, 0.5f, -0.5f), + Vec3(-0.5f, 0.5f, -0.5f), + Vec3(-0.5f, -0.5f, -0.5f), + Vec3(-0.5f, -0.5f, 0.5), + Vec3(0.5f, -0.5f, 0.5), + Vec3(0.5f, 0.5f, 0.5), + Vec3(0.5f, 0.5f, 0.5), + Vec3(-0.5f, 0.5f, 0.5), + Vec3(-0.5f, -0.5f, 0.5), + Vec3(-0.5f, 0.5f, 0.5f), + Vec3(-0.5f, 0.5f, -0.5f), + Vec3(-0.5f, -0.5f, -0.5f), + Vec3(-0.5f, -0.5f, -0.5f), + Vec3(-0.5f, -0.5f, 0.5f), + Vec3(-0.5f, 0.5f, 0.5f), + Vec3(0.5f, 0.5f, 0.5), + Vec3(0.5f, 0.5f, -0.5), + Vec3(0.5f, -0.5f, -0.5), + Vec3(0.5f, -0.5f, -0.5), + Vec3(0.5f, -0.5f, 0.5), + Vec3(0.5f, 0.5f, 0.5), + Vec3(-0.5f, -0.5f, -0.5f), + Vec3(0.5f, -0.5f, -0.5f), + Vec3(0.5f, -0.5f, 0.5f), + Vec3(0.5f, -0.5f, 0.5f), + Vec3(-0.5f, -0.5f, 0.5f), + Vec3(-0.5f, -0.5f, -0.5f), + Vec3(-0.5f, 0.5f, -0.5), + Vec3(0.5f, 0.5f, -0.5), + Vec3(0.5f, 0.5f, 0.5), + Vec3(0.5f, 0.5f, 0.5), + Vec3(-0.5f, 0.5f, 0.5), + Vec3(-0.5f, 0.5f, -0.5) + }; + + std::vector normals = + { + Vec3(0.0f, 0.0f, -1.0f), + Vec3(0.0f, 0.0f, -1.0f), + Vec3(0.0f, 0.0f, -1.0f), + Vec3(0.0f, 0.0f, -1.0f), + Vec3(0.0f, 0.0f, -1.0f), + Vec3(0.0f, 0.0f, -1.0f), + Vec3(0.0f, 0.0f, 1.0f), + Vec3(0.0f, 0.0f, 1.0f), + Vec3(0.0f, 0.0f, 1.0f), + Vec3(0.0f, 0.0f, 1.0f), + Vec3(0.0f, 0.0f, 1.0f), + Vec3(0.0f, 0.0f, 1.0f), + Vec3(-1.0f, 0.0f, 0.0f), + Vec3(-1.0f, 0.0f, 0.0f), + Vec3(-1.0f, 0.0f, 0.0f), + Vec3(-1.0f, 0.0f, 0.0f), + Vec3(-1.0f, 0.0f, 0.0f), + Vec3(-1.0f, 0.0f, 0.0f), + Vec3(1.0f, 0.0f, 0.0f), + Vec3(1.0f, 0.0f, 0.0f), + Vec3(1.0f, 0.0f, 0.0f), + Vec3(1.0f, 0.0f, 0.0f), + Vec3(1.0f, 0.0f, 0.0f), + Vec3(1.0f, 0.0f, 0.0f), + Vec3(0.0f, -1.0f, 0.0f), + Vec3(0.0f, -1.0f, 0.0f), + Vec3(0.0f, -1.0f, 0.0f), + Vec3(0.0f, -1.0f, 0.0f), + Vec3(0.0f, -1.0f, 0.0f), + Vec3(0.0f, -1.0f, 0.0f), + Vec3(0.0f, 1.0f, 0.0f), + Vec3(0.0f, 1.0f, 0.0f), + Vec3(0.0f, 1.0f, 0.0f), + Vec3(0.0f, 1.0f, 0.0f), + Vec3(0.0f, 1.0f, 0.0f), + Vec3(0.0f, 1.0f, 0.0) + }; + + + glGenVertexArrays(1, &mVao); + glBindVertexArray(mVao); + + // Vertices + glGenBuffers(1, &mVboVertices); + glBindBuffer(GL_ARRAY_BUFFER, mVboVertices); + glBufferData(GL_ARRAY_BUFFER, sizeof(Vec3) * vertices.size(), vertices.data(), GL_STATIC_DRAW); + glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vec3), (void*)nullptr); + glEnableVertexAttribArray(0); + + // Surface normal + glGenBuffers(1, &mVboNormals); + glBindBuffer(GL_ARRAY_BUFFER, mVboNormals); + glBufferData(GL_ARRAY_BUFFER, sizeof(Vec3) * normals.size(), normals.data(), GL_STATIC_DRAW); + glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vec3), (void*)nullptr); + glEnableVertexAttribArray(1); + + glBindVertexArray(0); +} + + +void RenderSystem::Update(float dt) +{ + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // NOLINT (hicpp-signed-bitwise) + + shader->Activate(); + glBindVertexArray(mVao); + + + auto& cameraTransform = gCoordinator.GetComponent(mCamera); + auto& camera = gCoordinator.GetComponent(mCamera); + + for (auto const& entity : mEntities) + { + auto const& transform = gCoordinator.GetComponent(entity); + auto const& renderable = gCoordinator.GetComponent(entity); + + Mat44 view; + view.m[0][3] = -cameraTransform.position.x; + view.m[1][3] = -cameraTransform.position.y; + view.m[2][3] = -cameraTransform.position.z; + + Mat44 rotY; + + float cos_theta_y = cosf(transform.rotation.y); + float sin_theta_y = sinf(transform.rotation.y); + + rotY.m[0][0] = cos_theta_y; + rotY.m[2][0] = -sin_theta_y; + rotY.m[0][2] = sin_theta_y; + rotY.m[2][2] = cos_theta_y; + + + Mat44 rotX; + + float cosThetaX = cosf(transform.rotation.x); + float sinThetaX = sinf(transform.rotation.x); + + rotX.m[1][1] = cosThetaX; + rotX.m[2][1] = sinThetaX; + rotX.m[1][2] = -sinThetaX; + rotX.m[2][2] = cosThetaX; + + + Mat44 rotZ; + + float cosThetaZ = cosf(transform.rotation.z); + float sinThetaZ = sinf(transform.rotation.z); + + rotZ.m[0][0] = cosThetaZ; + rotZ.m[1][0] = sinThetaZ; + rotZ.m[0][1] = -sinThetaZ; + rotZ.m[1][1] = cosThetaZ; + + + Mat44 translate; + translate.m[0][3] = transform.position.x; + translate.m[1][3] = transform.position.y; + translate.m[2][3] = transform.position.z; + + Mat44 scaleMat; + scaleMat.m[0][0] = transform.scale.x; + scaleMat.m[1][1] = transform.scale.y; + scaleMat.m[2][2] = transform.scale.z; + + Mat44 model = translate * scaleMat * rotY; + + Mat44 projection = camera.projectionTransform; + + shader->SetUniform("uModel", model); + shader->SetUniform("uView", view); + shader->SetUniform("uProjection", projection); + shader->SetUniform("uColor", renderable.color); + + glDrawArrays(GL_TRIANGLES, 0, 36); + } + + glBindVertexArray(0); +} + +void RenderSystem::WindowSizeListener(Event& event) +{ + auto windowWidth = event.GetParam(Events::Window::Resized::WIDTH); + auto windowHeight = event.GetParam(Events::Window::Resized::HEIGHT); + + auto& camera = gCoordinator.GetComponent(mCamera); + camera.projectionTransform = Camera::MakeProjectionTransform(45.0f, 0.1f, 1000.0f, windowWidth, windowHeight); +} + diff --git a/Source/Systems/RenderSystem.hpp b/Source/Systems/RenderSystem.hpp new file mode 100644 index 0000000..9d8e2b1 --- /dev/null +++ b/Source/Systems/RenderSystem.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include "Core/System.hpp" +#include "Graphics/GlLoader.hpp" +#include "Graphics/Shader.hpp" +#include + + +class Event; + + +class RenderSystem : public System +{ +public: + void Init(); + + void Update(float dt); + +private: + void WindowSizeListener(Event& event); + + std::unique_ptr shader; + + Entity mCamera; + + GLuint mVao{}; + GLuint mVboVertices{}; + GLuint mVboNormals{}; +}; diff --git a/Source/WindowManager.cpp b/Source/WindowManager.cpp new file mode 100644 index 0000000..faf3d1e --- /dev/null +++ b/Source/WindowManager.cpp @@ -0,0 +1,301 @@ +#include "WindowManager.hpp" + +#include "Core/Coordinator.hpp" +#include +#include +#include + + +extern Coordinator gCoordinator; + + +// TODO: Return error to caller +void WindowManager::Init( + std::string const& windowTitle, unsigned int windowWidth, unsigned int windowHeight, unsigned int windowPositionX, + unsigned int windowPositionY) +{ + // Open the mDisplay + { + mDisplay = XOpenDisplay(nullptr); + + if (!mDisplay) + { + std::cerr << "Error: XOpenDisplay()\n"; + return; + } + } + + // Get the proper frame buffer config + GLXFBConfig frameBufferConfig{}; + { + // Frame buffer configs were added in GLX version 1.3. + if (int glxMajor, glxMinor; + !glXQueryVersion(mDisplay, &glxMajor, &glxMinor) + || ((glxMajor == 1) && (glxMinor < 3)) || (glxMajor < 1)) + { + std::cerr << "Invalid GLX version.\n"; + return; + } + + static int visualAttributes[] = + { + GLX_X_RENDERABLE, True, + GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, + GLX_RENDER_TYPE, GLX_RGBA_BIT, + GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR, + GLX_RED_SIZE, 8, + GLX_GREEN_SIZE, 8, + GLX_BLUE_SIZE, 8, + GLX_ALPHA_SIZE, 8, + GLX_DEPTH_SIZE, 24, + GLX_STENCIL_SIZE, 8, + GLX_DOUBLEBUFFER, True, + None + }; + + + int frameBufferCount; + GLXFBConfig* frameBufferConfigList = glXChooseFBConfig( + mDisplay, DefaultScreen(mDisplay), visualAttributes, &frameBufferCount); + + if (!frameBufferConfigList) + { + std::cerr << "Error: glXChooseFBConfig()\n"; + return; + } + + frameBufferConfig = frameBufferConfigList[0]; + + XFree(frameBufferConfigList); + } + + + // Create the window + { + XVisualInfo* visualInfo = glXGetVisualFromFBConfig(mDisplay, frameBufferConfig); + + XSetWindowAttributes swa; + swa.colormap = XCreateColormap( + mDisplay, + RootWindow(mDisplay, visualInfo->screen), + visualInfo->visual, AllocNone); + + swa.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask; // NOLINT(hicpp-signed-bitwise) + + mWindow = XCreateWindow( + mDisplay, RootWindow(mDisplay, visualInfo->screen), + windowPositionX, windowPositionY, + windowWidth, windowHeight, + 0, visualInfo->depth, InputOutput, + visualInfo->visual, + CWColormap | CWEventMask, &swa); // NOLINT (hicpp-signed-bitwise) + + if (!mWindow) + { + std::cerr << "Error: XCreateWindow()\n"; + return; + } + + XFree(visualInfo); + XStoreName(mDisplay, mWindow, windowTitle.c_str()); + XMapWindow(mDisplay, mWindow); + } + + + // Create the context + { + typedef GLXContext (* glXCreateContextAttribsARBProc)(Display*, GLXFBConfig, GLXContext, Bool, const int*); + + auto glXCreateContextAttribsARB = (glXCreateContextAttribsARBProc)glXGetProcAddress( + (const GLubyte*)"glXCreateContextAttribsARB"); + + if (!glXCreateContextAttribsARB) + { + std::cerr << "glXCreateContextAttribsARB() not found\n"; + return; + } + + int contextAttributes[] = + { + GLX_CONTEXT_MAJOR_VERSION_ARB, 3, + GLX_CONTEXT_MINOR_VERSION_ARB, 0, + None + }; + + mGlxContext = glXCreateContextAttribsARB( + mDisplay, frameBufferConfig, nullptr, + True, contextAttributes); + + if (!mGlxContext) + { + std::cerr << "Failed to create an OpenGL context\n"; + return; + } + } + + // Generate KeyRelease event only when physical key is actually released + { + bool detectableSet = XkbSetDetectableAutoRepeat(mDisplay, true, nullptr); + + if (!detectableSet) + { + std::cerr << "Detectable auto repeat not set - holding a key down will cause event spamming and delays.\n"; + } + } + + glXMakeCurrent(mDisplay, mWindow, mGlxContext); + glClearColor(0.0f, 0.0f, 0.0f, 1.0f); + glXSwapBuffers(mDisplay, mWindow); + glEnable(GL_DEPTH_TEST); +} + + +void WindowManager::Update() +{ + glXSwapBuffers(mDisplay, mWindow); +} + + +void WindowManager::Shutdown() +{ + glXMakeCurrent(mDisplay, 0, nullptr); + glXDestroyContext(mDisplay, mGlxContext); + XDestroyWindow(mDisplay, mWindow); + XCloseDisplay(mDisplay); +} + +void WindowManager::ProcessEvents() +{ + XEvent xEvent; + + if (XCheckWindowEvent(mDisplay, mWindow, ExposureMask | KeyPressMask | KeyReleaseMask, &xEvent)) // NOLINT (hicpp-signed-bitwise) + { + if (xEvent.type == Expose) + { + XWindowAttributes gwa; + XGetWindowAttributes(mDisplay, mWindow, &gwa); + glViewport(0, 0, gwa.width, gwa.height); + + Event event(Events::Window::RESIZED); + event.SetParam(Events::Window::Resized::WIDTH, gwa.width); + event.SetParam(Events::Window::Resized::HEIGHT, gwa.height); + + gCoordinator.SendEvent(event); + } + else if (xEvent.type == KeyPress) + { + KeySym key = XkbKeycodeToKeysym( + mDisplay, + static_cast(xEvent.xkey.keycode), + 0, 0); + + bool buttonStateChanged = true; + + if (key == XK_Escape) + { + gCoordinator.SendEvent(Events::Window::QUIT); + } + + else if (key == XK_w) + { + mButtons.set(static_cast(InputButtons::W)); + } + + else if (key == XK_a) + { + mButtons.set(static_cast(InputButtons::A)); + } + + else if (key == XK_s) + { + mButtons.set(static_cast(InputButtons::S)); + } + + else if (key == XK_d) + { + mButtons.set(static_cast(InputButtons::D)); + } + + else if (key == XK_q) + { + mButtons.set(static_cast(InputButtons::Q)); + } + + else if (key == XK_e) + { + mButtons.set(static_cast(InputButtons::E)); + } + else + { + buttonStateChanged = false; + XFlush(mDisplay); + } + + if (buttonStateChanged) + { + Event event(Events::Window::INPUT); + event.SetParam(Events::Window::Input::INPUT, mButtons); + gCoordinator.SendEvent(event); + } + } + else if (xEvent.type == KeyRelease) + { + KeySym key = XkbKeycodeToKeysym( + mDisplay, + static_cast(xEvent.xkey.keycode), + 0, 0); + + bool buttonStateChanged = true; + + if (key == XK_Escape) + { + gCoordinator.SendEvent(Events::Window::QUIT); + } + + else if (key == XK_w) + { + mButtons.reset(static_cast(InputButtons::W)); + } + + else if (key == XK_a) + { + mButtons.reset(static_cast(InputButtons::A)); + } + + else if (key == XK_s) + { + mButtons.reset(static_cast(InputButtons::S)); + } + + else if (key == XK_d) + { + mButtons.reset(static_cast(InputButtons::D)); + } + else if (key == XK_q) + { + mButtons.reset(static_cast(InputButtons::Q)); + } + + else if (key == XK_e) + { + mButtons.reset(static_cast(InputButtons::E)); + } + else + { + buttonStateChanged = false; + XFlush(mDisplay); + } + + if (buttonStateChanged) + { + Event event(Events::Window::INPUT); + event.SetParam(Events::Window::Input::INPUT, mButtons); + gCoordinator.SendEvent(event); + } + } + else + { + XFlush(mDisplay); + } + } +} diff --git a/Source/WindowManager.hpp b/Source/WindowManager.hpp new file mode 100644 index 0000000..dcf1e7c --- /dev/null +++ b/Source/WindowManager.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include +#include + + +class WindowManager +{ +public: + void Init( + std::string const& windowTitle, unsigned int windowWidth, unsigned int windowHeight, + unsigned int windowPositionX, unsigned int windowPositionY); + + void Update(); + + void ProcessEvents(); + + void Shutdown(); + +private: + Display* mDisplay; + Window mWindow; + GLXContext mGlxContext; + + std::bitset<8> mButtons; +};