Merge pull request #2 from bailwillharr/vulkan

Merge vulkan into master
This commit is contained in:
Bailey Harrison 2022-11-01 16:31:30 +00:00 committed by GitHub
commit 98e49448ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 3433 additions and 1681 deletions

6
.gitmodules vendored
View File

@ -16,3 +16,9 @@
[submodule "dependencies/freetype"] [submodule "dependencies/freetype"]
path = dependencies/freetype path = dependencies/freetype
url = https://gitlab.freedesktop.org/freetype/freetype.git url = https://gitlab.freedesktop.org/freetype/freetype.git
[submodule "dependencies/volk"]
path = dependencies/volk
url = https://github.com/zeux/volk
[submodule "dependencies/VulkanMemoryAllocator"]
path = dependencies/VulkanMemoryAllocator
url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.4) cmake_minimum_required(VERSION 3.8)
# options # options
@ -6,8 +6,7 @@ project(engine LANGUAGES CXX
VERSION "0.1.0" VERSION "0.1.0"
) )
add_library(${PROJECT_NAME} SHARED set(SRC_FILES
"src/engine.cpp" "src/engine.cpp"
"src/window.cpp" "src/window.cpp"
"src/input.cpp" #TODO make input_manager "src/input.cpp" #TODO make input_manager
@ -31,13 +30,18 @@ add_library(${PROJECT_NAME} SHARED
"src/resource_manager.cpp" "src/resource_manager.cpp"
"src/gfx_device_vulkan.cpp" "src/gfx_device_vulkan.cpp"
"src/gfx_device_null.cpp"
"src/gfx_device_opengl45.cpp"
)
# PUBLIC API set(INCLUDE_FILES
"include/engine_api.h" "include/engine_api.h"
"include/engine.hpp" "include/engine.hpp"
"include/util.hpp"
"include/log.hpp" "include/log.hpp"
"include/window.hpp" "include/window.hpp"
@ -67,19 +71,32 @@ add_library(${PROJECT_NAME} SHARED
"include/gfx.hpp" "include/gfx.hpp"
"include/gfx_device.hpp" "include/gfx_device.hpp"
)
add_library(${PROJECT_NAME} STATIC
${SRC_FILES}
${INCLUDE_FILES}
) )
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/src" PREFIX "Source" FILES ${SRC_FILES})
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}/include" PREFIX "Include" FILES ${INCLUDE_FILES})
# compiling options: # compiling options:
target_compile_definitions(${PROJECT_NAME} PRIVATE DEFINITIONS "ENGINE_EXPORTS") target_compile_definitions(${PROJECT_NAME} PRIVATE DEFINITIONS "ENGINE_EXPORTS")
set_property(TARGET ${PROJECT_NAME} PROPERTY WINDOWS_EXPORT_ALL_SYMBOLS ON)
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20) set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20)
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD_REQUIRED ON) set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
if (MSVC) if (MSVC)
target_compile_options(${PROJECT_NAME} PRIVATE /W3) target_compile_options(${PROJECT_NAME} PRIVATE /W3)
target_compile_options(${PROJECT_NAME} PRIVATE /MP) target_compile_options(${PROJECT_NAME} PRIVATE /MP)
target_compile_definitions(${PROJECT_NAME} PRIVATE _CRT_SECURE_NO_WARNINGS)
endif() endif()
target_include_directories(${PROJECT_NAME} PUBLIC include) target_include_directories(${PROJECT_NAME} PUBLIC include)
@ -98,10 +115,31 @@ if (MINGW)
target_link_libraries(${PROJECT_NAME} PUBLIC mingw32) target_link_libraries(${PROJECT_NAME} PUBLIC mingw32)
endif() endif()
# Vulkan # GLAD:
find_package(Vulkan REQUIRED) set(GLAD_PROFILE "core" CACHE INTERNAL "" FORCE)
target_include_directories(${PROJECT_NAME} PRIVATE ${Vulkan_INCLUDE_DIRS}) set(GLAD_API "gl=3.3" CACHE INTERNAL "" FORCE)
target_link_libraries(${PROJECT_NAME} PRIVATE Vulkan::Vulkan) if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(GLAD_GENERATOR "c-debug" CACHE INTERNAL "" FORCE)
else()
set(GLAD_GENERATOR "c" CACHE INTERNAL "" FORCE)
endif()
set(GLAD_SPEC "gl" CACHE INTERNAL "" FORCE)
set(BUILD_SHARED_LIBS OFF)
add_subdirectory(dependencies/glad)
set_property(TARGET glad PROPERTY POSITION_INDEPENDENT_CODE ON)
target_link_libraries(${PROJECT_NAME} PUBLIC glad)
target_include_directories(${PROJECT_NAME} PUBLIC dependencies/glad/include)
# Volk
set(VOLK_STATIC_DEFINES "")
set(VOLK_PULL_IN_VULKAN ON)
set(VOLK_INSTALL OFF)
set(VOLK_HEADERS_ONLY ON)
add_subdirectory(dependencies/volk)
target_link_libraries(${PROJECT_NAME} PRIVATE volk_headers)
# Vulkan Memory Allocator
target_include_directories(${PROJECT_NAME} PRIVATE dependencies/VulkanMemoryAllocator/include)
# SDL2: # SDL2:
find_package(SDL2) find_package(SDL2)
@ -124,24 +162,9 @@ set(BUILD_SHARED_LIBS OFF)
add_subdirectory(dependencies/glm) add_subdirectory(dependencies/glm)
target_include_directories(${PROJECT_NAME} PUBLIC dependencies/glm) target_include_directories(${PROJECT_NAME} PUBLIC dependencies/glm)
# GLAD:
set(GLAD_PROFILE "core" CACHE INTERNAL "" FORCE)
set(GLAD_API "gl=3.3" CACHE INTERNAL "" FORCE)
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(GLAD_GENERATOR "c-debug" CACHE INTERNAL "" FORCE)
else()
set(GLAD_GENERATOR "c" CACHE INTERNAL "" FORCE)
endif()
set(GLAD_SPEC "gl" CACHE INTERNAL "" FORCE)
set(BUILD_SHARED_LIBS OFF)
add_subdirectory(dependencies/glad)
set_property(TARGET glad PROPERTY POSITION_INDEPENDENT_CODE ON)
target_link_libraries(${PROJECT_NAME} PUBLIC glad)
target_include_directories(${PROJECT_NAME} PUBLIC dependencies/glad/include)
# spdlog # spdlog
set(SPDLOG_BUILD_SHARED ON CACHE INTERNAL "" FORCE) set(SPDLOG_BUILD_SHARED OFF CACHE INTERNAL "" FORCE)
set(BUILD_SHARED_LIBS ON) set(BUILD_SHARED_LIBS OFF)
add_subdirectory(dependencies/spdlog) add_subdirectory(dependencies/spdlog)
target_link_libraries(${PROJECT_NAME} PUBLIC spdlog) target_link_libraries(${PROJECT_NAME} PUBLIC spdlog)
target_include_directories(${PROJECT_NAME} PUBLIC dependencies/spdlog/include) target_include_directories(${PROJECT_NAME} PUBLIC dependencies/spdlog/include)

3
TODO
View File

@ -15,3 +15,6 @@ Add support for shadows and other complex lighting. Also add post-processing.
For font rendering, put all ASCII characters in one large texture and use For font rendering, put all ASCII characters in one large texture and use
'instancing' (and uniform buffer objects?) to reduce draw calls. 'instancing' (and uniform buffer objects?) to reduce draw calls.
# VULKAN #
The entire vulkan backend needs redesigning without so many classes

1
dependencies/VulkanMemoryAllocator vendored Submodule

@ -0,0 +1 @@
Subproject commit c351692490513cdb0e5a2c925aaf7ea4a9b672f4

1
dependencies/volk vendored Submodule

@ -0,0 +1 @@
Subproject commit 121a4584f69056d2c6db2eb4104650ce749d4c72

View File

@ -8,10 +8,11 @@
#include <vector> #include <vector>
#include <glm/glm.hpp> #include <glm/vec3.hpp>
#include <glm/ext.hpp> #include <glm/vec4.hpp>
#include <glm/mat4x4.hpp>
namespace components { namespace engine::components {
class ENGINE_API Camera : public Component { class ENGINE_API Camera : public Component {

View File

@ -2,42 +2,46 @@
#include "engine_api.h" #include "engine_api.h"
class Object; namespace engine {
class Window; class Window;
class Input; class Input;
class ResourceManager;
class ENGINE_API Component { class Object;
class ResourceManager;
public: class ENGINE_API Component {
public:
enum class TypeEnum {
TRANSFORM,
CAMERA,
RENDERER,
UI,
CUSTOM,
};
Component(Object* parent, TypeEnum type);
Component(const Component&) = delete;
Component& operator=(const Component&) = delete;
virtual ~Component() = 0;
int getID();
TypeEnum getType();
Object& parent;
protected:
engine::Window& win;
engine::Input& inp;
ResourceManager& res;
private:
static int s_next_component_id;
int m_id = s_next_component_id;
TypeEnum m_type;
enum class TypeEnum {
TRANSFORM,
CAMERA,
RENDERER,
UI,
CUSTOM,
}; };
Component(Object* parent, TypeEnum type); }
Component(const Component&) = delete;
Component& operator=(const Component&) = delete;
virtual ~Component() = 0;
int getID();
TypeEnum getType();
Object& parent;
protected:
Window& win;
Input& inp;
ResourceManager& res;
private:
static int s_next_component_id;
int m_id = s_next_component_id;
TypeEnum m_type;
};

View File

@ -6,7 +6,7 @@
#include <glm/mat4x4.hpp> #include <glm/mat4x4.hpp>
namespace components { namespace engine::components {
class ENGINE_API CustomComponent : public Component { class ENGINE_API CustomComponent : public Component {

View File

@ -12,7 +12,7 @@
#include <string> #include <string>
#include <memory> #include <memory>
namespace components { namespace engine::components {
class ENGINE_API Renderer : public Component { class ENGINE_API Renderer : public Component {
@ -21,7 +21,7 @@ public:
~Renderer() override; ~Renderer() override;
// called every frame, do not call manually // called every frame, do not call manually
void render(glm::mat4 transform); void render(glm::mat4 model);
void setMesh(const std::string& name); void setMesh(const std::string& name);
void setTexture(const std::string& name); void setTexture(const std::string& name);

View File

@ -6,10 +6,11 @@
#include "resources/font.hpp" #include "resources/font.hpp"
#include "resources/mesh.hpp" #include "resources/mesh.hpp"
#include "resources/shader.hpp"
#include <glm/mat4x4.hpp> #include <glm/mat4x4.hpp>
namespace components { namespace engine::components {
class ENGINE_API UI : public Component { class ENGINE_API UI : public Component {

View File

@ -1,19 +1,53 @@
#pragma once #pragma once
#include <filesystem> #include <memory>
#include "log.hpp"
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/sinks/basic_file_sink.h>
namespace engine { namespace engine {
struct AppInfo { class Window;
const char* name; class Input;
const char* version; class ResourceManager;
class SceneRoot;
class Application {
public:
Application(const char* appName, const char* appVersion);
Application(const Application&) = delete;
Application& operator=(const Application&) = delete;
~Application();
void gameLoop();
Window* window()
{
return m_win;
}
Input* input()
{
return m_input;
}
ResourceManager* resources()
{
return m_res;
}
SceneRoot* scene()
{
return m_scene;
}
private:
Window* m_win;
Input* m_input;
ResourceManager* m_res;
SceneRoot* m_scene;
}; };
bool versionFromCharArray(const char* version, int* major, int* minor, int* patch);
} }

View File

@ -1,5 +1,6 @@
#pragma once #pragma once
/*
#ifndef ENGINE_API #ifndef ENGINE_API
# ifdef _MSC_VER # ifdef _MSC_VER
# ifdef ENGINE_EXPORTS # ifdef ENGINE_EXPORTS
@ -11,3 +12,6 @@
# define ENGINE_API # define ENGINE_API
# endif # endif
#endif #endif
*/
#define ENGINE_API

View File

@ -2,29 +2,19 @@
#include <cstdint> #include <cstdint>
#include <cstddef> #include <cstddef>
#include <vector>
namespace gfx { namespace engine::gfx {
enum class ShaderType { enum class ShaderType {
VERTEX, VERTEX,
FRAGMENT, FRAGMENT,
}; };
struct Shader {
ShaderType type;
uint32_t handle;
};
typedef uint32_t Program;
enum class BufferType { enum class BufferType {
VERTEX, VERTEX,
INDEX, INDEX,
}; UNIFORM,
struct Buffer {
BufferType type;
uint32_t handle;
}; };
enum class Primitive { enum class Primitive {
@ -35,10 +25,28 @@ namespace gfx {
TRIANGLE_STRIP, TRIANGLE_STRIP,
}; };
enum class IndexBufferFormat { enum class VertexAttribFormat {
UNSIGNED_8_BITS, VEC2,
UNSIGNED_16_BITS, VEC3,
UNSIGNED_32_BITS,
}; };
struct VertexBufferDesc {
uint64_t size;
};
struct VertexAttribDescription {
uint32_t location;
VertexAttribFormat format;
uint32_t offset;
};
struct VertexFormat {
uint32_t stride;
std::vector<VertexAttribDescription> attributeDescriptions;
};
// handles (incomplete types)
struct Pipeline;
struct Buffer;
} }

View File

@ -2,21 +2,50 @@
#include "engine_api.h" #include "engine_api.h"
#include "engine.hpp" #include "gfx.hpp"
class Window; #include <memory>
namespace engine::gfx { struct SDL_Window;
namespace engine {
struct ENGINE_API Device { class ENGINE_API GFXDevice {
Device(AppInfo appInfo, const Window& window); public:
~Device(); GFXDevice(const char* appName, const char* appVersion, SDL_Window* window);
GFXDevice(const GFXDevice&) = delete;
GFXDevice& operator=(const GFXDevice&) = delete;
~GFXDevice();
void getViewportSize(uint32_t *w, uint32_t *h);
// adds a draw call to the queue
// vertexBuffer is required, indexBuffer can be NULL, uniformData is required
void draw(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t count, const void* pushConstantData);
// Call once per frame. Executes all queued draw calls and renders to the screen.
void renderFrame();
// creates the equivalent of an OpenGL shader program & vertex attrib configuration
gfx::Pipeline* createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat, uint64_t uniformBufferSize);
void destroyPipeline(const gfx::Pipeline* pipeline);
void updateUniformBuffer(const gfx::Pipeline* pipeline, void* data);
gfx::Buffer* createBuffer(gfx::BufferType type, uint64_t size, const void* data);
void destroyBuffer(const gfx::Buffer* buffer);
// wait until all the active GPU queues have finished working
void waitIdle();
private: private:
class Impl; struct Impl;
std::unique_ptr<Impl> pimpl{}; std::unique_ptr<Impl> pimpl;
}; };
extern GFXDevice* gfxdev;
} }

View File

@ -9,81 +9,85 @@
#include <array> #include <array>
#include <string> #include <string>
class Window; namespace engine {
enum class InputDevice : int { class Window;
MOUSE,
KEYBOARD,
CONTROLLER,
SIZE
};
// This class should be used to get platform/input-device independent input enum class InputDevice : int {
class ENGINE_API Input { MOUSE,
KEYBOARD,
public: CONTROLLER,
SIZE
// requires a window reference to get input from
Input(const Window& win);
Input(const Input&) = delete;
Input& operator=(const Input&) = delete;
~Input();
// Add a mouse input
void addInputButton(const std::string& name, inputs::MouseButton button);
void addInputAxis(const std::string& name, inputs::MouseAxis axis);
void addInputButtonAsAxis(const std::string& name, inputs::MouseButton high, inputs::MouseButton low);
// Add a keyboard input
void addInputButton(const std::string& name, inputs::Key button);
void addInputButtonAsAxis(const std::string& name, inputs::Key high, inputs::Key low);
void delInputButton(int index);
void delInputAxis(int index);
void setDeviceActive(enum InputDevice device, bool active);
bool getDeviceActive(enum InputDevice device) const;
float getAxis(const std::string& axisName) const;
bool getButton(const std::string& buttonName) const;
bool getButtonPress(const std::string& buttonName) const;
bool getButtonRelease(const std::string& buttonName) const;
private:
struct ButtonEntry {
std::string name;
enum InputDevice device;
int button; // enumeration of device buttons or axes
}; };
struct AxisEntry { // This class should be used to get platform/input-device independent input
std::string name; class ENGINE_API Input {
enum InputDevice device;
int axis; public:
bool isButtonAxis;
int high; // requires a window reference to get input from
int low; Input(const Window& win);
Input(const Input&) = delete;
Input& operator=(const Input&) = delete;
~Input();
// Add a mouse input
void addInputButton(const std::string& name, inputs::MouseButton button);
void addInputAxis(const std::string& name, inputs::MouseAxis axis);
void addInputButtonAsAxis(const std::string& name, inputs::MouseButton high, inputs::MouseButton low);
// Add a keyboard input
void addInputButton(const std::string& name, inputs::Key button);
void addInputButtonAsAxis(const std::string& name, inputs::Key high, inputs::Key low);
void delInputButton(int index);
void delInputAxis(int index);
void setDeviceActive(enum InputDevice device, bool active);
bool getDeviceActive(enum InputDevice device) const;
float getAxis(const std::string& axisName) const;
bool getButton(const std::string& buttonName) const;
bool getButtonPress(const std::string& buttonName) const;
bool getButtonRelease(const std::string& buttonName) const;
private:
struct ButtonEntry {
std::string name;
enum InputDevice device;
int button; // enumeration of device buttons or axes
};
struct AxisEntry {
std::string name;
enum InputDevice device;
int axis;
bool isButtonAxis;
int high;
int low;
};
const Window& m_win;
std::vector<struct ButtonEntry> m_buttonEntries;
std::vector<struct AxisEntry> m_axisEntries;
std::array<bool, static_cast<int>(InputDevice::SIZE)> m_enabledDevices;
// private methods
float getDeviceAxis(enum InputDevice device, int axis) const;
bool getDeviceButton(enum InputDevice device, int button) const;
bool getDeviceButtonDown(enum InputDevice device, int button) const;
bool getDeviceButtonUp(enum InputDevice device, int button) const;
float getButtonAxis(enum InputDevice device, int high, int low) const;
void addInputButton(const std::string& name, InputDevice device, int button);
void addInputAxis(const std::string& name, InputDevice device, int axis);
void addInputButtonAsAxis(const std::string& name, InputDevice device, int high, int low);
}; };
const Window& m_win; }
std::vector<struct ButtonEntry> m_buttonEntries;
std::vector<struct AxisEntry> m_axisEntries;
std::array<bool, static_cast<int>(InputDevice::SIZE)> m_enabledDevices;
// private methods
float getDeviceAxis(enum InputDevice device, int axis) const;
bool getDeviceButton(enum InputDevice device, int button) const;
bool getDeviceButtonDown(enum InputDevice device, int button) const;
bool getDeviceButtonUp(enum InputDevice device, int button) const;
float getButtonAxis(enum InputDevice device, int high, int low) const;
void addInputButton(const std::string& name, InputDevice device, int button);
void addInputAxis(const std::string& name, InputDevice device, int axis);
void addInputButtonAsAxis(const std::string& name, InputDevice device, int high, int low);
};

View File

@ -2,7 +2,7 @@
// Keyboard scancodes, taken from SDL_scancode.h // Keyboard scancodes, taken from SDL_scancode.h
namespace inputs { namespace engine::inputs {
enum class Key : int { enum class Key : int {
UNKNOWN = 0, UNKNOWN = 0,

View File

@ -1,6 +1,6 @@
#pragma once #pragma once
namespace inputs { namespace engine::inputs {
enum class MouseButton : int { enum class MouseButton : int {
M_LEFT, M_LEFT,

View File

@ -6,135 +6,140 @@
#include "transform.hpp" #include "transform.hpp"
#include "components/component.hpp"
#include <list> #include <list>
#include <vector> #include <vector>
#include <string> #include <string>
#include <memory> #include <memory>
#include <stdexcept> #include <stdexcept>
class Window; namespace engine {
class Input; class Window;
class ResourceManager; class Input;
class ResourceManager;
class SceneRoot; class SceneRoot;
class Component; class Component;
namespace components { namespace components {
class Transform; class Camera;
class Camera; class Renderer;
class Renderer; class UI;
class UI; class CustomComponent;
class CustomComponent; }
}
struct GameIO { struct GameIO {
Window * const win; Window* win;
Input * const input; Input* input;
ResourceManager * const resMan; ResourceManager* resMan;
};
// This object lives until it is deleted by its parent(s) or finally when the "Scene" is destroyed.
// Therefore it is safe to return raw pointers
class ENGINE_API Object {
public:
Object(std::string name, Object* parent, SceneRoot& root, struct GameIO things);
Object(const Object&) = delete;
Object& operator=(const Object&) = delete;
~Object();
Window& win;
Input& inp;
ResourceManager& res;
SceneRoot& root;
std::string getName();
Object* getParent();
Object* getChild(std::string name);
std::vector<Object*> getChildren();
Object* createChild(std::string name);
void deleteChild(std::string name);
void printTree(int level = 0);
// Returns the component of type T
// Returns nullptr if the component is not found.
template<class T> T* getComponent();
// returns the component added
template<class T> T* createComponent();
template<class T> void deleteComponent();
struct CompList {
std::vector<std::pair<components::Camera*, glm::mat4>> cameras;
std::vector<std::pair<components::Renderer*, glm::mat4>> renderers;
std::vector<std::pair<components::UI*, glm::mat4>> uis;
std::vector<std::pair<components::CustomComponent*, glm::mat4>> customs;
}; };
// Adds to the provided vector all components of this object and of its children recursively.
// Ignores 'Transform'
void getAllSubComponents(struct CompList& compList, glm::mat4 t);
Transform transform; // This object lives until it is deleted by its parent(s) or finally when the "Scene" is destroyed.
// Therefore it is safe to return raw pointers
class ENGINE_API Object {
private: public:
static int s_object_count; Object(std::string name, Object* parent, SceneRoot& root, struct GameIO things);
int m_id = s_object_count; Object(const Object&) = delete;
std::string m_name; Object& operator=(const Object&) = delete;
~Object();
std::list<std::unique_ptr<Object>> m_children{}; Window& win;
std::list<std::unique_ptr<Component>> m_components{}; Input& inp;
ResourceManager& res;
// If nullptr, this is the root object
Object* const m_parent;
struct GameIO m_gameIO;
}; SceneRoot& root;
// implementation of template functions std::string getName();
template<class T> T* Object::getComponent() Object* getParent();
{
if (std::is_base_of<Component, T>::value == false) { Object* getChild(std::string name);
throw std::runtime_error("getComponent() error: specified type is not a subclass of 'Component'"); std::vector<Object*> getChildren();
}
for (const auto& component : m_components) { Object* createChild(std::string name);
T* derived = dynamic_cast<T*>(component.get()); void deleteChild(std::string name);
if (derived != nullptr) {
return derived; void printTree(int level = 0);
// Returns the component of type T
// Returns nullptr if the component is not found.
template<class T> T* getComponent();
// returns the component added
template<class T> T* createComponent();
template<class T> void deleteComponent();
struct CompList {
std::vector<std::pair<components::Camera*, glm::mat4>> cameras;
std::vector<std::pair<components::Renderer*, glm::mat4>> renderers;
std::vector<std::pair<components::UI*, glm::mat4>> uis;
std::vector<std::pair<components::CustomComponent*, glm::mat4>> customs;
};
// Adds to the provided vector all components of this object and of its children recursively.
// Ignores 'Transform'
void getAllSubComponents(struct CompList& compList, glm::mat4 t);
Transform transform;
private:
static int s_object_count;
int m_id = s_object_count;
std::string m_name;
std::list<std::unique_ptr<Object>> m_children{};
std::list<std::unique_ptr<Component>> m_components{};
// If nullptr, this is the root object
Object* const m_parent;
struct GameIO m_gameIO;
};
// implementation of template functions
template<class T> T* Object::getComponent()
{
if (std::is_base_of<Component, T>::value == false) {
throw std::runtime_error("getComponent() error: specified type is not a subclass of 'Component'");
} }
} for (const auto& component : m_components) {
return nullptr; T* derived = dynamic_cast<T*>(component.get());
} if (derived != nullptr) {
return derived;
template <class T> T* Object::createComponent() }
{
if (std::is_base_of<Component, T>::value == false) {
throw std::runtime_error("addComponent() error: specified type is not a subclass of 'Component'");
}
if (getComponent<T>() != nullptr) {
throw std::runtime_error("addComponent() error: attempt to add component of a type already present");
}
m_components.emplace_back(std::make_unique<T>(this));
return dynamic_cast<T*>(m_components.back().get());
}
template<class T> void Object::deleteComponent()
{
if (std::is_base_of<Component, T>::value == false) {
throw std::runtime_error("deleteComponent() error: specified type is not a subclass of 'Component'");
}
for (auto itr = m_components.begin(); itr != m_components.end(); ++itr) {
if (dynamic_cast<T*>((*itr).get()) != nullptr) {
m_components.erase(itr);
return;
} }
return nullptr;
} }
throw std::runtime_error("deleteComponent() error: attempt to delete component that is not present.");
template <class T> T* Object::createComponent()
{
if (std::is_base_of<Component, T>::value == false) {
throw std::runtime_error("addComponent() error: specified type is not a subclass of 'Component'");
}
if (getComponent<T>() != nullptr) {
throw std::runtime_error("addComponent() error: attempt to add component of a type already present");
}
m_components.emplace_back(std::make_unique<T>(this));
return dynamic_cast<T*>(m_components.back().get());
}
template<class T> void Object::deleteComponent()
{
if (std::is_base_of<Component, T>::value == false) {
throw std::runtime_error("deleteComponent() error: specified type is not a subclass of 'Component'");
}
for (auto itr = m_components.begin(); itr != m_components.end(); ++itr) {
if (dynamic_cast<T*>((*itr).get()) != nullptr) {
m_components.erase(itr);
return;
}
}
throw std::runtime_error("deleteComponent() error: attempt to delete component that is not present.");
}
} }

View File

@ -9,73 +9,77 @@
// Doesn't own resources, only holds weak_ptrs // Doesn't own resources, only holds weak_ptrs
class ENGINE_API ResourceManager { namespace engine {
public: class ENGINE_API ResourceManager {
ResourceManager();
ResourceManager(const ResourceManager&) = delete; public:
ResourceManager& operator=(const ResourceManager&) = delete; ResourceManager();
~ResourceManager() = default; ResourceManager(const ResourceManager&) = delete;
ResourceManager& operator=(const ResourceManager&) = delete;
~ResourceManager() = default;
template <class T>
std::shared_ptr<T> create(const std::string& name);
// creates the resource if it is not in the map or the weak_ptr has expired
template <class T>
std::shared_ptr<T> get(const std::string& name);
std::unique_ptr<std::string> getResourcesListString();
std::vector<std::weak_ptr<Resource>> getAllResourcesOfType(const std::string& type);
std::filesystem::path getFilePath(const std::string& name);
private:
std::filesystem::path m_resourcesPath;
std::unordered_map<std::string, std::weak_ptr<Resource>> m_resources;
};
template <class T> template <class T>
std::shared_ptr<T> create(const std::string& name); std::shared_ptr<T> ResourceManager::create(const std::string& name)
{
if (std::is_base_of<Resource, T>::value == false) {
throw std::runtime_error("ResourceManager::create() error: specified type is not a subclass of 'Resource'");
}
auto resource = std::make_shared<T>(getFilePath(name));
m_resources.emplace(name, std::dynamic_pointer_cast<Resource>(resource));
return resource;
}
// creates the resource if it is not in the map or the weak_ptr has expired
template <class T> template <class T>
std::shared_ptr<T> get(const std::string& name); std::shared_ptr<T> ResourceManager::get(const std::string& name)
{
std::unique_ptr<std::string> getResourcesListString(); if (std::is_base_of<Resource, T>::value == false) {
throw std::runtime_error("ResourceManager::get() error: specified type is not a subclass of 'Resource'");
}
std::vector<std::weak_ptr<Resource>> getAllResourcesOfType(const std::string& type); if (m_resources.contains(name)) {
std::filesystem::path getFilePath(const std::string& name); std::weak_ptr<Resource> res = m_resources.at(name);
private: if (res.expired() == false) {
std::filesystem::path m_resourcesPath; // resource definitely exists
std::unordered_map<std::string, std::weak_ptr<Resource>> m_resources; auto castedSharedPtr = std::dynamic_pointer_cast<T>(res.lock());
if (castedSharedPtr == nullptr) {
}; throw std::runtime_error("error: attempt to get Resource which already exists as another type");
}
template <class T> else {
std::shared_ptr<T> ResourceManager::create(const std::string& name) return castedSharedPtr;
{ }
if (std::is_base_of<Resource, T>::value == false) {
throw std::runtime_error("ResourceManager::create() error: specified type is not a subclass of 'Resource'");
}
auto resource = std::make_shared<T>(getFilePath(name));
m_resources.emplace(name, std::dynamic_pointer_cast<Resource>(resource));
return resource;
}
template <class T>
std::shared_ptr<T> ResourceManager::get(const std::string& name)
{
if (std::is_base_of<Resource, T>::value == false) {
throw std::runtime_error("ResourceManager::get() error: specified type is not a subclass of 'Resource'");
}
if (m_resources.contains(name)) {
std::weak_ptr<Resource> res = m_resources.at(name);
if (res.expired() == false) {
// resource definitely exists
auto castedSharedPtr = std::dynamic_pointer_cast<T>(res.lock());
if (castedSharedPtr == nullptr) {
throw std::runtime_error("error: attempt to get Resource which already exists as another type");
} }
else { else {
return castedSharedPtr; // Resource in map but no longer exists. Delete it.
m_resources.erase(name);
} }
} }
else {
// Resource in map but no longer exists. Delete it. return create<T>(name);
m_resources.erase(name);
}
} }
return create<T>(name);
} }

View File

@ -10,7 +10,7 @@
#include <map> #include <map>
namespace resources { namespace engine::resources {
class ENGINE_API Font : public Resource { class ENGINE_API Font : public Resource {

View File

@ -4,9 +4,7 @@
#include "resource.hpp" #include "resource.hpp"
#include "resources/shader.hpp" #include "gfx.hpp"
#include <glad/glad.h>
#include <glm/vec3.hpp> #include <glm/vec3.hpp>
#include <glm/vec2.hpp> #include <glm/vec2.hpp>
@ -20,34 +18,23 @@ struct Vertex {
glm::vec2 uv; glm::vec2 uv;
}; };
namespace resources { namespace engine::resources {
class ENGINE_API Mesh : public Resource { class ENGINE_API Mesh : public Resource {
public: public:
Mesh(const std::vector<Vertex>& vertices); Mesh(const std::vector<Vertex>& vertices);
Mesh(const std::vector<Vertex>& vertices, const std::vector<unsigned int>& indices); Mesh(const std::vector<Vertex>& vertices, const std::vector<uint32_t>& indices);
Mesh(const std::filesystem::path& resPath); Mesh(const std::filesystem::path& resPath);
~Mesh() override; ~Mesh() override;
void drawMesh(const Shader& shader);
static void invalidate()
{
s_active_vao = -1;
}
std::vector<Vertex> m_vertices; std::vector<Vertex> m_vertices;
std::vector<unsigned int> m_indices; std::vector<uint32_t> m_indices;
const gfx::Buffer* vb;
const gfx::Buffer* ib;
private: private:
static int s_active_vao;
GLuint m_vao;
GLuint m_vbo;
GLuint m_ebo;
void bindVAO() const;
void initMesh(); void initMesh();

View File

@ -5,20 +5,24 @@
#include <string> #include <string>
#include <filesystem> #include <filesystem>
class ENGINE_API Resource { namespace engine {
public: class ENGINE_API Resource {
Resource(const std::filesystem::path& resPath, const std::string& type);
Resource(const Resource&) = delete;
Resource& operator=(const Resource&) = delete;
virtual ~Resource() = 0;
std::string getType(); public:
Resource(const std::filesystem::path& resPath, const std::string& type);
Resource(const Resource&) = delete;
Resource& operator=(const Resource&) = delete;
virtual ~Resource() = 0;
protected: std::string getType();
std::filesystem::path m_resourcePath;
private: protected:
const std::string m_type; std::filesystem::path m_resourcePath;
}; private:
const std::string m_type;
};
}

View File

@ -4,14 +4,16 @@
#include "resource.hpp" #include "resource.hpp"
#include <glad/glad.h>
#include <glm/mat4x4.hpp> #include <glm/mat4x4.hpp>
#include <string> #include <string>
#include <map> #include <map>
namespace resources { namespace engine::gfx {
struct Pipeline;
}
namespace engine::resources {
class ENGINE_API Shader : public Resource { class ENGINE_API Shader : public Resource {
@ -19,58 +21,18 @@ public:
Shader(const std::filesystem::path& resPath); Shader(const std::filesystem::path& resPath);
~Shader() override; ~Shader() override;
enum class UniformType { struct UniformBuffer {
FLOAT_MAT4 = GL_FLOAT_MAT4, glm::mat4 v;
FLOAT_VEC2 = GL_FLOAT_VEC2, glm::mat4 p;
FLOAT_VEC3 = GL_FLOAT_VEC3,
SAMPLER_2D = GL_SAMPLER_2D,
NOTFOUND
}; };
void makeActive() const;
bool setUniform_m4(const std::string& name, const glm::mat4&) const; gfx::Pipeline* getPipeline()
bool setUniform_v2(const std::string& name, const glm::vec2&) const;
bool setUniform_v3(const std::string& name, const glm::vec3&) const;
bool setUniform_i(const std::string& name, int) const;
bool setUniform_f(const std::string& name, float) const;
UniformType getUniformType(const std::string& name) const;
int getAttribLocation(const std::string& name) const;
static void invalidate()
{ {
s_activeProgram = -1; return m_pipeline;
} }
private: private:
gfx::Pipeline* m_pipeline = nullptr;
struct Uniform {
GLint size;
UniformType type;
GLuint location;
};
struct Attribute {
GLint size;
UniformType type;
GLuint location;
};
// fields
GLuint m_program;
std::map<std::string, Uniform> m_uniforms{};
std::map<std::string, Attribute> m_attributes{};
// static members
// Only valid if glUseProgram is never called outside this class's method
static GLuint s_activeProgram;
// -1 if not found
int getUniformLocation(const std::string& name) const;
}; };

View File

@ -6,7 +6,7 @@
#include <glad/glad.h> #include <glad/glad.h>
namespace resources { namespace engine::resources {
class ENGINE_API Texture : public Resource { class ENGINE_API Texture : public Resource {

View File

@ -6,24 +6,27 @@
#include <filesystem> #include <filesystem>
// Holds everything you would expect to find in a game scene namespace engine {
class ENGINE_API SceneRoot : public Object {
public: // Holds everything you would expect to find in a game scene
// create a new empty scene class ENGINE_API SceneRoot : public Object {
SceneRoot(struct GameIO things);
SceneRoot(const std::filesystem::path& file, struct GameIO things);
SceneRoot(const SceneRoot&) = delete; public:
SceneRoot& operator=(const SceneRoot&) = delete; // create a new empty scene
~SceneRoot(); SceneRoot(struct GameIO things);
void updateStuff(); SceneRoot(const SceneRoot&) = delete;
SceneRoot& operator=(const SceneRoot&) = delete;
~SceneRoot();
void activateCam(int id); void updateStuff();
void deactivateCam(int id);
private: void activateCam(int id);
std::vector<int> m_activeCameras{}; void deactivateCam(int id);
}; private:
std::vector<int> m_activeCameras{};
};
}

View File

@ -6,12 +6,16 @@
#include <glm/vec3.hpp> #include <glm/vec3.hpp>
#include <glm/ext/quaternion_float.hpp> #include <glm/ext/quaternion_float.hpp>
struct ENGINE_API Transform { namespace engine {
// Scale, rotate (XYZ), translate struct ENGINE_API Transform {
glm::vec3 position{0.0f}; // Scale, rotate (XYZ), translate
glm::quat rotation{};
glm::vec3 scale{1.0f};
}; glm::vec3 position{ 0.0f };
glm::quat rotation{};
glm::vec3 scale{ 1.0f };
};
}

18
include/util.hpp Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <cstdio>
namespace engine {
inline bool versionFromCharArray(const char* version, int* major, int* minor, int* patch)
{
if (sscanf(version, "%d.%d.%d", major, minor, patch) != 3) {
*major = 0;
*minor = 0;
*patch = 0;
return false;
}
return true;
}
}

View File

@ -17,149 +17,136 @@
ENGINE_API extern const uint64_t BILLION; ENGINE_API extern const uint64_t BILLION;
class ENGINE_API Window { namespace engine {
public: class ENGINE_API Window {
Window(const std::string& title);
Window(const Window&) = delete;
Window& operator=(const Window&) = delete;
~Window();
// Return the title name public:
std::string getTitle() const; Window(const std::string& title, bool resizable = true);
Window(const Window&) = delete;
Window& operator=(const Window&) = delete;
~Window();
// Make this window the current OpenGL context. SDL_Window* getHandle() const;
// This is already done in window initialisation.
void makeContextCurrent();
// Tell the GPU to render the back buffer to the screen. // Return the title name
// Run this on every frame. std::string getTitle() const;
void swapBuffers();
// Update the window state to capture any events that have occurred.
// Run this on every frame.
void getInputAndEvents();
// if 'true', swapBuffers() will wait in order to synchronise with the // Update the window state to capture any events that have occurred.
// monitor's refresh rate. // Run this on every frame.
void setVSync(bool enable); void getInputAndEvents();
// Returns true if VSync is enabled.
bool getVSync() const;
glm::ivec2 getViewportSize(); void setTitle(std::string title);
void setTitle(std::string title); // Hides the window (it will appear closed to the user).
void hide();
// Shows the window again.
void show();
// Raises the window above other windows and sets the input focus
void focus();
// Returns true if the window has focus
bool hasFocus() const;
// Hides the window (it will appear closed to the user). // Sets the close flag, check this with shouldClose()
void hide(); void setCloseFlag();
// Shows the window again. // Returns true if the window should remain open
void show(); bool isRunning() const;
// Raises the window above other windows and sets the input focus
void focus();
// Returns true if the window has focus
bool hasFocus() const;
// Sets the close flag, check this with shouldClose() void setFullscreen(bool fullscreen, bool exclusive = false);
void setCloseFlag(); void toggleFullscreen();
// Returns true if the window should remain open
bool isRunning() const;
void setFullscreen(bool fullscreen, bool exclusive=true); bool isFullscreen() const;
void toggleFullscreen();
bool isFullscreen() const; // Relative mouse mode captures the cursor for FPS style use. Returns false if unsupported.
bool setRelativeMouseMode(bool enabled);
// Relative mouse mode captures the cursor for FPS style use. Returns false if unsupported. // returns true if relative mouse mode is enabled
bool setRelativeMouseMode(bool enabled); bool mouseCaptured();
// returns true if relative mouse mode is enabled // window events
bool mouseCaptured();
// window events // Returns true if the window was just resized during the previous frame
bool getWindowResized() const;
// Set the window resized flag (to recalculate aspect ratios and such)
inline void setResizedFlag()
{
m_justResized = true;
}
// Returns true if the window was just resized during the previous frame // keyboard events
bool getWindowResized() const;
// Set the window resized flag (to recalculate aspect ratios and such)
inline void setResizedFlag()
{
m_justResized = true;
}
// keyboard events // returns true if key is down
bool getKey(inputs::Key key) const;
// returns true if key was just pressed
bool getKeyPress(inputs::Key key) const;
// returns true if key was just released
bool getKeyRelease(inputs::Key key) const;
// returns true if key is down // mouse events
bool getKey(inputs::Key key) const;
// returns true if key was just pressed
bool getKeyPress(inputs::Key key) const;
// returns true if key was just released
bool getKeyRelease(inputs::Key key) const;
// mouse events // returns true if button is down
bool getButton(inputs::MouseButton button) const;
// returns true if button was just pressed
bool getButtonPress(inputs::MouseButton button) const;
// returns true if button was just released
bool getButtonRelease(inputs::MouseButton button) const;
// returns true if button is down // retrieves x coordinate of the mouse
bool getButton(inputs::MouseButton button) const; int getMouseX() const;
// returns true if button was just pressed // retrieves y coordinate of the mouse
bool getButtonPress(inputs::MouseButton button) const; int getMouseY() const;
// returns true if button was just released // retrieves mouse x coordinate normalised for OpenGL
bool getButtonRelease(inputs::MouseButton button) const; float getMouseNormX() const;
// retrieves mouse y coordinate normalised for OpenGL
float getMouseNormY() const;
// retrieves dx of the mouse since the last frame
int getMouseDX() const;
// retrieves dy of the mouse since the last frame
int getMouseDY() const;
// retrieves amount scrolled vertically
float getMouseScrollX() const;
// retrieves amount scrolled horizontally
float getMouseScrollY() const;
// retrieves x coordinate of the mouse // joystick/gamepad events (maybe), other misc events
int getMouseX() const;
// retrieves y coordinate of the mouse
int getMouseY() const;
// retrieves mouse x coordinate normalised for OpenGL
float getMouseNormX() const;
// retrieves mouse y coordinate normalised for OpenGL
float getMouseNormY() const;
// retrieves dx of the mouse since the last frame
int getMouseDX() const;
// retrieves dy of the mouse since the last frame
int getMouseDY() const;
// retrieves amount scrolled vertically
float getMouseScrollX() const;
// retrieves amount scrolled horizontally
float getMouseScrollY() const;
// joystick/gamepad events (maybe), other misc events
// returns the performance counter value in nanoseconds; // returns the performance counter value in nanoseconds;
uint64_t getNanos() const; uint64_t getNanos() const;
// get the time recorded at the end of the last frame // get the time recorded at the end of the last frame
uint64_t getLastFrameStamp() const; uint64_t getLastFrameStamp() const;
// returns the number of frames elapsed since window creation // returns the number of frames elapsed since window creation
uint64_t getFrameCount() const; uint64_t getFrameCount() const;
uint64_t getStartTime() const;; uint64_t getStartTime() const;;
float dt() const; // returns delta time in seconds float dt() const; // returns delta time in seconds
uint64_t getFPS() const; uint64_t getFPS() const;
uint64_t getAvgFPS() const; uint64_t getAvgFPS() const;
void resetAvgFPS(); void resetAvgFPS();
bool infoBox(const std::string& title, const std::string& msg); bool infoBox(const std::string& title, const std::string& msg);
std::vector<const char*> getRequiredVulkanExtensions() const; /* STATIC METHODS */
static void errorBox(const std::string& message);
/* STATIC METHODS */
static void errorBox(const std::string& message);
private: private:
SDL_Window* m_handle; SDL_Window* m_handle;
SDL_GLContext m_glContext;
bool m_shouldClose = false; bool m_shouldClose = false;
std::string m_title; std::string m_title;
bool m_resizable;
bool m_fullscreen = false; bool m_fullscreen = false;
bool m_justResized = false; bool m_justResized = false;
bool m_keyboardFocus = true; bool m_keyboardFocus = true;
// size in screen coordinates // size in screen coordinates
glm::ivec2 m_winSize = glm::vec2(640, 480); glm::ivec2 m_winSize = glm::vec2(640, 480);
// actual framebuffer size
glm::ivec2 m_fbSize;
// performance counter frequency // performance counter frequency
uint64_t m_counterFreq; uint64_t m_counterFreq;
@ -214,5 +201,6 @@ public:
void onMouseButtonEvent(SDL_MouseButtonEvent& e); void onMouseButtonEvent(SDL_MouseButtonEvent& e);
void onMouseMotionEvent(SDL_MouseMotionEvent& e); void onMouseMotionEvent(SDL_MouseMotionEvent& e);
void onMouseWheelEvent(SDL_MouseWheelEvent& e); void onMouseWheelEvent(SDL_MouseWheelEvent& e);
};
}; }

View File

@ -7,24 +7,17 @@
#include "window.hpp" #include "window.hpp"
#include "gfx_device.hpp"
#include "log.hpp" #include "log.hpp"
static const std::string VIEW_MAT_UNIFORM = "viewMat"; namespace engine::components {
static const std::string PROJ_MAT_UNIFORM = "projMat";
static const std::string WINDOW_SIZE_UNIFORM = "windowSize";
namespace components {
glm::vec4 Camera::s_clearColor{-999.0f, -999.0f, -999.0f, -999.0f}; glm::vec4 Camera::s_clearColor{-999.0f, -999.0f, -999.0f, -999.0f};
Camera::Camera(Object* parent) : Component(parent, TypeEnum::CAMERA) Camera::Camera(Object* parent) : Component(parent, TypeEnum::CAMERA)
{ {
parent->root.activateCam(getID()); parent->root.activateCam(getID());
glEnable(GL_DEPTH_TEST);
glDisable(GL_STENCIL_TEST);
glEnable(GL_CULL_FACE);
} }
Camera::~Camera() Camera::~Camera()
@ -45,6 +38,14 @@ void Camera::updateCam(glm::mat4 transform)
glm::mat4 viewMatrix = glm::inverse(transform); glm::mat4 viewMatrix = glm::inverse(transform);
struct {
glm::mat4 view;
glm::mat4 proj;
} uniformData{};
uniformData.view = viewMatrix;
uniformData.proj = m_projMatrix;
using namespace resources; using namespace resources;
auto resPtrs = parent.res.getAllResourcesOfType("shader"); auto resPtrs = parent.res.getAllResourcesOfType("shader");
@ -52,26 +53,18 @@ void Camera::updateCam(glm::mat4 transform)
// shader ref count increased by 1, but only in this scope // shader ref count increased by 1, but only in this scope
auto lockedPtr = resPtr.lock(); auto lockedPtr = resPtr.lock();
auto shader = dynamic_cast<Shader*>(lockedPtr.get()); auto shader = dynamic_cast<Shader*>(lockedPtr.get());
shader->setUniform_m4(VIEW_MAT_UNIFORM, viewMatrix); // SET VIEW TRANSFORM HERE
shader->setUniform_m4(PROJ_MAT_UNIFORM, m_projMatrix); gfxdev->updateUniformBuffer(shader->getPipeline(), &uniformData);
shader->setUniform_v2(WINDOW_SIZE_UNIFORM, win.getViewportSize());
shader->setUniform_v3("lightPos", m_lightPos);
} }
if (s_clearColor != clearColor) {
s_clearColor = clearColor;
glClearColor(clearColor.r, clearColor.g, clearColor.b, clearColor.a);
}
glClear((m_noClear ? 0 : GL_COLOR_BUFFER_BIT) | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
} }
static glm::vec2 getViewportSize() static glm::vec2 getViewportSize()
{ {
GLint64 viewportParams[4]; uint32_t width;
glGetInteger64v(GL_VIEWPORT, viewportParams); uint32_t height;
return { viewportParams[2], viewportParams[3] }; gfxdev->getViewportSize(&width, &height);
return {width, height};
} }
void Camera::usePerspective(float fovDeg) void Camera::usePerspective(float fovDeg)
@ -85,7 +78,8 @@ void Camera::usePerspective(float fovDeg)
glm::vec2 viewportDim = getViewportSize(); glm::vec2 viewportDim = getViewportSize();
float fovRad = glm::radians(fovDeg); float fovRad = glm::radians(fovDeg);
m_projMatrix = glm::perspectiveFov(fovRad, viewportDim.x, viewportDim.y, NEAR, FAR); m_projMatrix = glm::perspectiveFovRH_ZO(fovRad, viewportDim.x, viewportDim.y, NEAR, FAR);
m_projMatrix[1][1] *= -1;
} }
void Camera::useOrtho() void Camera::useOrtho()
@ -95,7 +89,8 @@ void Camera::useOrtho()
glm::vec2 viewportDim = getViewportSize(); glm::vec2 viewportDim = getViewportSize();
float aspect = viewportDim.x / viewportDim.y; float aspect = viewportDim.x / viewportDim.y;
m_projMatrix = glm::ortho(-10.0f * aspect, 10.0f * aspect, -10.0f, 10.0f, -100.0f, 100.0f); m_projMatrix = glm::orthoRH_ZO(-10.0f * aspect, 10.0f * aspect, -10.0f, 10.0f, -100.0f, 100.0f);
m_projMatrix[1][1] *= -1;
} }
} }

View File

@ -4,27 +4,31 @@
#include <iostream> #include <iostream>
int Component::s_next_component_id = 0; namespace engine {
Component::Component(Object* parent, TypeEnum type) : int Component::s_next_component_id = 0;
m_type(type), parent(*parent),
win(parent->win),
inp(parent->inp),
res(parent->res)
{
s_next_component_id++;
}
Component::~Component() Component::Component(Object* parent, TypeEnum type) :
{ m_type(type), parent(*parent),
} win(parent->win),
inp(parent->inp),
res(parent->res)
{
s_next_component_id++;
}
int Component::getID() Component::~Component()
{ {
return m_id; }
}
Component::TypeEnum Component::getType() int Component::getID()
{ {
return m_type; return m_id;
} }
Component::TypeEnum Component::getType()
{
return m_type;
}
}

View File

@ -1,6 +1,6 @@
#include "components/custom.hpp" #include "components/custom.hpp"
namespace components { namespace engine::components {
CustomComponent::CustomComponent(Object* parent) : Component(parent, TypeEnum::CUSTOM) CustomComponent::CustomComponent(Object* parent) : Component(parent, TypeEnum::CUSTOM)
{ {

View File

@ -4,14 +4,18 @@
#include "resource_manager.hpp" #include "resource_manager.hpp"
#include "gfx_device.hpp"
#include "log.hpp"
#include <iostream> #include <iostream>
namespace components { namespace engine::components {
Renderer::Renderer(Object* parent) : Component(parent, TypeEnum::RENDERER) Renderer::Renderer(Object* parent) : Component(parent, TypeEnum::RENDERER)
{ {
m_shader = this->parent.res.get<resources::Shader>("shaders/basic.glsl"); m_shader = this->parent.res.get<resources::Shader>("shader.glsl");
m_texture = this->parent.res.get<resources::Texture>("textures/missing.png"); // m_texture = this->parent.res.get<resources::Texture>("textures/missing.png");
} }
Renderer::~Renderer() Renderer::~Renderer()
@ -21,20 +25,7 @@ Renderer::~Renderer()
void Renderer::render(glm::mat4 transform) void Renderer::render(glm::mat4 transform)
{ {
gfxdev->draw(m_shader->getPipeline(), m_mesh->vb, m_mesh->ib, m_mesh->m_vertices.size(), &transform);
m_shader->setUniform_f("ambientStrength", 0.4f);
m_shader->setUniform_v3("ambientColor", { 1.0f, 1.0f, 1.0f });
m_shader->setUniform_v3("lightColor", { 1.0f, 1.0f, 1.0f });
m_shader->setUniform_m4("modelMat", transform );
m_texture->bindTexture();
m_shader->setUniform_v3("baseColor", m_color );
m_shader->setUniform_v3("emission", m_emission );
if (m_mesh)
m_mesh->drawMesh(*m_shader);
} }
void Renderer::setMesh(const std::string& name) void Renderer::setMesh(const std::string& name)
@ -44,7 +35,7 @@ void Renderer::setMesh(const std::string& name)
void Renderer::setTexture(const std::string& name) void Renderer::setTexture(const std::string& name)
{ {
m_texture = parent.res.get<resources::Texture>(name); // m_texture = parent.res.get<resources::Texture>(name);
} }
} }

View File

@ -4,7 +4,7 @@
#include "resource_manager.hpp" #include "resource_manager.hpp"
#include "resources/texture.hpp" #include "resources/texture.hpp"
namespace components { namespace engine::components {
UI::UI(Object* parent) : Component(parent, TypeEnum::UI) UI::UI(Object* parent) : Component(parent, TypeEnum::UI)
{ {
@ -23,12 +23,15 @@ UI::~UI()
void UI::render(glm::mat4 transform) void UI::render(glm::mat4 transform)
{ {
/*
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
m_shader->setUniform_m4("modelMat", transform); m_shader->setUniform_m4("modelMat", transform);
m_shader->setUniform_v3("textColor", m_color); m_shader->setUniform_v3("textColor", m_color);
m_shader->setUniform_i("textScaling", (int)m_scaled); m_shader->setUniform_i("textScaling", (int)m_scaled);
*/
std::vector<resources::Font::Character> glyphs; std::vector<resources::Font::Character> glyphs;
for (char c : m_text) { for (char c : m_text) {
glyphs.push_back(m_font->getChar(c)); glyphs.push_back(m_font->getChar(c));
@ -57,6 +60,7 @@ void UI::render(glm::mat4 transform)
float w = glyph.size.x * scale; float w = glyph.size.x * scale;
float h = glyph.size.y * scale; float h = glyph.size.y * scale;
/*
resources::Mesh mesh({ resources::Mesh mesh({
{{xpos, ypos + h, 0.0f}, {}, {0.0f, 0.0f}}, {{xpos, ypos + h, 0.0f}, {}, {0.0f, 0.0f}},
{{xpos, ypos , 0.0f}, {}, {0.0f, 1.0f}}, {{xpos, ypos , 0.0f}, {}, {0.0f, 1.0f}},
@ -64,11 +68,11 @@ void UI::render(glm::mat4 transform)
{{xpos, ypos + h, 0.0f}, {}, {0.0f, 0.0f}}, {{xpos, ypos + h, 0.0f}, {}, {0.0f, 0.0f}},
{{xpos + w, ypos, 0.0f}, {}, {1.0f, 1.0f}}, {{xpos + w, ypos, 0.0f}, {}, {1.0f, 1.0f}},
{{xpos + w, ypos + h, 0.0f}, {}, {1.0f, 0.0f}}, {{xpos + w, ypos + h, 0.0f}, {}, {1.0f, 0.0f}},
}); });*/
glBindTexture(GL_TEXTURE_2D, glyph.textureID); glBindTexture(GL_TEXTURE_2D, glyph.textureID);
mesh.drawMesh(*m_shader); // mesh.drawMesh(*m_shader);
x += (glyph.advance >> 6) * scale; x += (glyph.advance >> 6) * scale;

View File

@ -1,16 +1,87 @@
#include "engine.hpp" #include "engine.hpp"
#include "log.hpp"
#include "window.hpp"
#include "input.hpp"
#include "resource_manager.hpp"
#include "sceneroot.hpp"
#include "gfx_device.hpp"
#include "resources/mesh.hpp"
#include "components/mesh_renderer.hpp"
#include "components/camera.hpp"
namespace engine { namespace engine {
bool versionFromCharArray(const char* version, int* major, int* minor, int* patch) Application::Application(const char* appName, const char* appVersion)
{ {
if (sscanf(version, "%d.%d.%d", major, minor, patch) != 3) { m_win = new Window(appName, true);
*major = 0;
*minor = 0; gfxdev = new GFXDevice(appName, appVersion, m_win->getHandle());
*patch = 0;
return false; m_input = new Input(*m_win);
m_res = new ResourceManager();
GameIO things{
m_win,
m_input,
m_res
};
m_scene = new SceneRoot(things);
}
Application::~Application()
{
delete m_scene;
delete m_res;
delete m_input;
delete gfxdev;
delete m_win;
}
void Application::gameLoop()
{
TRACE("Begin game loop...");
uint64_t lastTick = m_win->getNanos();
constexpr int TICKFREQ = 1; // in hz
// single-threaded game loop
while (m_win->isRunning()) {
/* logic */
if (m_win->getLastFrameStamp() >= lastTick + (BILLION / TICKFREQ)) {
lastTick = m_win->getLastFrameStamp();
// do tick stuff here
m_win->setTitle("frame time: " + std::to_string(m_win->dt() * 1000.0f) + " ms, " + std::to_string(m_win->getAvgFPS()) + " fps");
m_win->resetAvgFPS();
}
if (m_win->getKeyPress(inputs::Key::F11)) {
m_win->toggleFullscreen();
}
if (m_win->getKeyPress(inputs::Key::ESCAPE)) {
m_win->setCloseFlag();
}
m_scene->updateStuff();
/* draw */
gfxdev->renderFrame();
/* poll events */
m_win->getInputAndEvents();
}
gfxdev->waitIdle();
} }
return true;
}
} }

79
src/gfx_device_null.cpp Normal file
View File

@ -0,0 +1,79 @@
// This implementation of the graphics layer does nothing
//#define ENGINE_BUILD_NULLGFX
#ifdef ENGINE_BUILD_NULLGFX
#include "gfx_device.hpp"
#include "util.hpp"
#include "config.h"
#include "log.hpp"
#include <SDL2/SDL.h>
#include <assert.h>
#include <unordered_set>
#include <array>
#include <fstream>
#include <filesystem>
#include <optional>
namespace engine {
// structures and enums
// class definitions
struct GFXDevice::Impl {
};
GFXDevice::GFXDevice(const char* appName, const char* appVersion, SDL_Window* window)
{
pimpl = std::make_unique<Impl>();
}
GFXDevice::~GFXDevice()
{
TRACE("Destroying GFXDevice...");
}
void GFXDevice::drawBuffer(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, uint32_t count)
{
}
void GFXDevice::drawIndexed(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t indexCount)
{
}
void GFXDevice::renderFrame()
{
}
gfx::Pipeline* GFXDevice::createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat)
{
return nullptr;
}
void GFXDevice::destroyPipeline(const gfx::Pipeline* pipeline)
{
}
gfx::Buffer* GFXDevice::createBuffer(gfx::BufferType type, uint64_t size, const void* data)
{
return nullptr;
}
void GFXDevice::destroyBuffer(const gfx::Buffer* buffer)
{
}
void GFXDevice::waitIdle()
{
}
}
#endif

105
src/gfx_device_opengl45.cpp Normal file
View File

@ -0,0 +1,105 @@
// The implementation of the graphics layer using OpenGL 4.5
// This uses SDL specific code
//#undef ENGINE_BUILD_OPENGL
#ifdef ENGINE_BUILD_OPENGL
#include "gfx_device.hpp"
#include "util.hpp"
#include "config.h"
#include "log.hpp"
#include <glad/glad.h>
#include <SDL2/SDL.h>
#include <assert.h>
#include <unordered_set>
#include <array>
#include <fstream>
#include <filesystem>
#include <optional>
namespace engine {
// structures and enums
static std::vector<char> readFile(const std::string& filename)
{
std::ifstream file(filename, std::ios::ate | std::ios::binary);
if (file.is_open() == false) {
throw std::runtime_error("Unable to open file " + filename);
}
std::vector<char> buffer(file.tellg());
file.seekg(0);
file.read(buffer.data(), buffer.size());
file.close();
return buffer;
}
// class definitions
struct GFXDevice::Impl {
SDL_GLContext context = nullptr;
};
GFXDevice::GFXDevice(const char* appName, const char* appVersion, SDL_Window* window)
{
pimpl = std::make_unique<Impl>();
pimpl->context = SDL_GL_CreateContext(window);
}
GFXDevice::~GFXDevice()
{
TRACE("Destroying GFXDevice...");
SDL_GL_DeleteContext(pimpl->context);
}
void GFXDevice::drawBuffer(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, uint32_t count)
{
}
void GFXDevice::drawIndexed(const gfx::Pipeline* pipeline, const gfx::Buffer* vertexBuffer, const gfx::Buffer* indexBuffer, uint32_t indexCount)
{
}
void GFXDevice::renderFrame()
{
}
gfx::Pipeline* GFXDevice::createPipeline(const char* vertShaderPath, const char* fragShaderPath, const gfx::VertexFormat& vertexFormat)
{
return nullptr;
}
void GFXDevice::destroyPipeline(const gfx::Pipeline* pipeline)
{
}
gfx::Buffer* GFXDevice::createBuffer(gfx::BufferType type, uint64_t size, const void* data)
{
return nullptr;
}
void GFXDevice::destroyBuffer(const gfx::Buffer* buffer)
{
}
void GFXDevice::waitIdle()
{
glFinish();
}
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -5,31 +5,33 @@
#include <string> #include <string>
#include <stdexcept> #include <stdexcept>
Input::Input(const Window &win) : m_win(win) namespace engine {
{
m_enabledDevices.fill(true);
}
Input::~Input() Input::Input(const Window& win) : m_win(win)
{ {
} m_enabledDevices.fill(true);
}
// private methods Input::~Input()
{
}
float Input::getDeviceAxis(enum InputDevice device, int axis) const // private methods
{
switch (device) { float Input::getDeviceAxis(enum InputDevice device, int axis) const
{
switch (device) {
case InputDevice::MOUSE: case InputDevice::MOUSE:
switch (static_cast<inputs::MouseAxis>(axis)) { switch (static_cast<inputs::MouseAxis>(axis)) {
case inputs::MouseAxis::X: case inputs::MouseAxis::X:
return static_cast<float>(m_win.getMouseDX()); return static_cast<float>(m_win.getMouseDX());
case inputs::MouseAxis::Y: case inputs::MouseAxis::Y:
return static_cast<float>(m_win.getMouseDY()); return static_cast<float>(m_win.getMouseDY());
case inputs::MouseAxis::X_SCR: case inputs::MouseAxis::X_SCR:
return m_win.getMouseScrollX(); return m_win.getMouseScrollX();
case inputs::MouseAxis::Y_SCR: case inputs::MouseAxis::Y_SCR:
return m_win.getMouseScrollY(); return m_win.getMouseScrollY();
default: break; default: break;
} }
break; break;
case InputDevice::KEYBOARD: case InputDevice::KEYBOARD:
@ -37,13 +39,13 @@ float Input::getDeviceAxis(enum InputDevice device, int axis) const
case InputDevice::CONTROLLER: case InputDevice::CONTROLLER:
break; break;
default: break; default: break;
}
throw std::runtime_error("Error getting device axis");
} }
throw std::runtime_error("Error getting device axis");
}
bool Input::getDeviceButton(enum InputDevice device, int button) const bool Input::getDeviceButton(enum InputDevice device, int button) const
{ {
switch (device) { switch (device) {
case InputDevice::MOUSE: case InputDevice::MOUSE:
return m_win.getButton(static_cast<inputs::MouseButton>(button)); return m_win.getButton(static_cast<inputs::MouseButton>(button));
case InputDevice::KEYBOARD: case InputDevice::KEYBOARD:
@ -51,13 +53,13 @@ bool Input::getDeviceButton(enum InputDevice device, int button) const
case InputDevice::CONTROLLER: case InputDevice::CONTROLLER:
break; break;
default: break; default: break;
}
throw std::runtime_error("Error getting device button");
} }
throw std::runtime_error("Error getting device button");
}
bool Input::getDeviceButtonDown(enum InputDevice device, int button) const bool Input::getDeviceButtonDown(enum InputDevice device, int button) const
{ {
switch (device) { switch (device) {
case InputDevice::MOUSE: case InputDevice::MOUSE:
return m_win.getButtonPress(static_cast<enum inputs::MouseButton>(button)); return m_win.getButtonPress(static_cast<enum inputs::MouseButton>(button));
case InputDevice::KEYBOARD: case InputDevice::KEYBOARD:
@ -65,13 +67,13 @@ bool Input::getDeviceButtonDown(enum InputDevice device, int button) const
case InputDevice::CONTROLLER: case InputDevice::CONTROLLER:
break; break;
default: break; default: break;
}
throw std::runtime_error("Error getting device button");
} }
throw std::runtime_error("Error getting device button");
}
bool Input::getDeviceButtonUp(enum InputDevice device, int button) const bool Input::getDeviceButtonUp(enum InputDevice device, int button) const
{ {
switch (device) { switch (device) {
case InputDevice::MOUSE: case InputDevice::MOUSE:
return m_win.getButtonRelease(static_cast<enum inputs::MouseButton>(button)); return m_win.getButtonRelease(static_cast<enum inputs::MouseButton>(button));
case InputDevice::KEYBOARD: case InputDevice::KEYBOARD:
@ -79,154 +81,157 @@ bool Input::getDeviceButtonUp(enum InputDevice device, int button) const
case InputDevice::CONTROLLER: case InputDevice::CONTROLLER:
break; break;
default: break; default: break;
}
throw std::runtime_error("Error getting device button");
} }
throw std::runtime_error("Error getting device button");
}
float Input::getButtonAxis(enum InputDevice device, int high, int low) const float Input::getButtonAxis(enum InputDevice device, int high, int low) const
{ {
float value = 0.0f; float value = 0.0f;
if (getDeviceButton(device, high)) value += 1.0f; if (getDeviceButton(device, high)) value += 1.0f;
if (low != 0) { if (low != 0) {
if (getDeviceButton(device, low)) value += -1.0f; if (getDeviceButton(device, low)) value += -1.0f;
}
return value;
} }
return value;
}
// public methods // public methods
void Input::addInputButton(const std::string& name, InputDevice device, int button) void Input::addInputButton(const std::string& name, InputDevice device, int button)
{ {
m_buttonEntries.push_back( { name, device, button } ); m_buttonEntries.push_back({ name, device, button });
} }
void Input::addInputAxis(const std::string& name, InputDevice device, int axis) void Input::addInputAxis(const std::string& name, InputDevice device, int axis)
{ {
m_axisEntries.push_back( { name, device, axis, false, 0, 0 } ); m_axisEntries.push_back({ name, device, axis, false, 0, 0 });
} }
void Input::addInputButtonAsAxis(const std::string& name, InputDevice device, int high, int low) void Input::addInputButtonAsAxis(const std::string& name, InputDevice device, int high, int low)
{ {
m_axisEntries.push_back( { name, device, 0, true, high, low } ); m_axisEntries.push_back({ name, device, 0, true, high, low });
} }
// OVERLOADS: // OVERLOADS:
// Add a mouse input // Add a mouse input
void Input::addInputButton(const std::string& name, inputs::MouseButton button) void Input::addInputButton(const std::string& name, inputs::MouseButton button)
{ {
addInputButton(name, InputDevice::MOUSE, static_cast<int>(button)); addInputButton(name, InputDevice::MOUSE, static_cast<int>(button));
} }
void Input::addInputAxis(const std::string& name, inputs::MouseAxis axis) void Input::addInputAxis(const std::string& name, inputs::MouseAxis axis)
{ {
addInputAxis(name, InputDevice::MOUSE, static_cast<int>(axis)); addInputAxis(name, InputDevice::MOUSE, static_cast<int>(axis));
} }
void Input::addInputButtonAsAxis(const std::string& name, inputs::MouseButton high, inputs::MouseButton low) void Input::addInputButtonAsAxis(const std::string& name, inputs::MouseButton high, inputs::MouseButton low)
{ {
addInputButtonAsAxis(name, InputDevice::MOUSE, static_cast<int>(high), static_cast<int>(low)); addInputButtonAsAxis(name, InputDevice::MOUSE, static_cast<int>(high), static_cast<int>(low));
} }
// Add a keyboard input (TODO: add KeyboardButton enum class) // Add a keyboard input (TODO: add KeyboardButton enum class)
void Input::addInputButton(const std::string& name, inputs::Key button) void Input::addInputButton(const std::string& name, inputs::Key button)
{ {
addInputButton(name, InputDevice::KEYBOARD, static_cast<int>(button)); addInputButton(name, InputDevice::KEYBOARD, static_cast<int>(button));
} }
void Input::addInputButtonAsAxis(const std::string& name, inputs::Key high, inputs::Key low) void Input::addInputButtonAsAxis(const std::string& name, inputs::Key high, inputs::Key low)
{ {
addInputButtonAsAxis(name, InputDevice::KEYBOARD, static_cast<int>(high), static_cast<int>(low)); addInputButtonAsAxis(name, InputDevice::KEYBOARD, static_cast<int>(high), static_cast<int>(low));
} }
void Input::delInputButton(int index) void Input::delInputButton(int index)
{ {
std::vector<struct ButtonEntry>::iterator it = m_buttonEntries.begin(); std::vector<struct ButtonEntry>::iterator it = m_buttonEntries.begin();
std::advance(it, index); std::advance(it, index);
m_buttonEntries.erase(it); m_buttonEntries.erase(it);
} }
void Input::delInputAxis(int index) void Input::delInputAxis(int index)
{ {
std::vector<struct AxisEntry>::iterator it = m_axisEntries.begin(); std::vector<struct AxisEntry>::iterator it = m_axisEntries.begin();
std::advance(it, index); std::advance(it, index);
m_axisEntries.erase(it); m_axisEntries.erase(it);
} }
void Input::setDeviceActive(enum InputDevice device, bool active) void Input::setDeviceActive(enum InputDevice device, bool active)
{ {
m_enabledDevices[static_cast<int>(device)] = active; m_enabledDevices[static_cast<int>(device)] = active;
} }
bool Input::getDeviceActive(enum InputDevice device) const bool Input::getDeviceActive(enum InputDevice device) const
{ {
return m_enabledDevices[static_cast<int>(device)]; return m_enabledDevices[static_cast<int>(device)];
} }
float Input::getAxis(const std::string& axisName) const float Input::getAxis(const std::string& axisName) const
{ {
for (const AxisEntry& e : m_axisEntries) { for (const AxisEntry& e : m_axisEntries) {
if (e.name == axisName) { if (e.name == axisName) {
if (m_enabledDevices[static_cast<int>(e.device)]) { if (m_enabledDevices[static_cast<int>(e.device)]) {
if (e.isButtonAxis) { if (e.isButtonAxis) {
return getButtonAxis(e.device, e.high, e.low); return getButtonAxis(e.device, e.high, e.low);
} else { }
return getDeviceAxis(e.device, e.axis); else {
return getDeviceAxis(e.device, e.axis);
}
} }
} }
} }
return 0.0f; // instead of throwing an exception, just return nothing
// throw std::runtime_error("Unable to find mapping in input table");
} }
return 0.0f; // instead of throwing an exception, just return nothing
// throw std::runtime_error("Unable to find mapping in input table");
}
bool Input::getButton(const std::string& buttonName) const bool Input::getButton(const std::string& buttonName) const
{ {
bool isDown = false; bool isDown = false;
for (const ButtonEntry& e : m_buttonEntries) { for (const ButtonEntry& e : m_buttonEntries) {
if (e.name == buttonName) { if (e.name == buttonName) {
if (m_enabledDevices[static_cast<int>(e.device)]) { if (m_enabledDevices[static_cast<int>(e.device)]) {
if (getDeviceButton(e.device, e.button) == true) { if (getDeviceButton(e.device, e.button) == true) {
isDown = true; isDown = true;
break; break;
}
} }
} }
} }
return isDown;
} }
return isDown;
}
bool Input::getButtonPress(const std::string& buttonName) const bool Input::getButtonPress(const std::string& buttonName) const
{ {
bool isPressed = false; bool isPressed = false;
for (const ButtonEntry& e : m_buttonEntries) { for (const ButtonEntry& e : m_buttonEntries) {
if (e.name == buttonName) { if (e.name == buttonName) {
if (m_enabledDevices[static_cast<int>(e.device)]) { if (m_enabledDevices[static_cast<int>(e.device)]) {
if (getDeviceButtonDown(e.device, e.button) == true) { if (getDeviceButtonDown(e.device, e.button) == true) {
isPressed = true; isPressed = true;
break; break;
}
} }
} }
} }
return isPressed;
} }
return isPressed;
}
bool Input::getButtonRelease(const std::string& buttonName) const bool Input::getButtonRelease(const std::string& buttonName) const
{ {
bool isReleased = false; bool isReleased = false;
for (const ButtonEntry& e : m_buttonEntries) { for (const ButtonEntry& e : m_buttonEntries) {
if (e.name == buttonName) { if (e.name == buttonName) {
if (m_enabledDevices[static_cast<int>(e.device)]) { if (m_enabledDevices[static_cast<int>(e.device)]) {
if (getDeviceButtonUp(e.device, e.button) == true) { if (getDeviceButtonUp(e.device, e.button) == true) {
isReleased = true; isReleased = true;
break; break;
}
} }
} }
} }
return isReleased;
} }
return isReleased;
} }

View File

@ -7,127 +7,134 @@
#include <log.hpp> #include <log.hpp>
int Object::s_object_count = 0; #include <glm/gtc/quaternion.hpp>
Object::Object(std::string name, Object* parent, SceneRoot& root, struct GameIO things) namespace engine {
: m_name(name), m_parent(parent), root(root),
m_gameIO(things),
win(*things.win),
inp(*things.input),
res(*things.resMan)
{
s_object_count++;
}
Object::~Object() int Object::s_object_count = 0;
{
}
std::string Object::getName() Object::Object(std::string name, Object* parent, SceneRoot& root, struct GameIO things)
{ : m_name(name), m_parent(parent), root(root),
return m_name; m_gameIO(things),
} win(*things.win),
inp(*things.input),
res(*things.resMan)
{
s_object_count++;
}
Object* Object::getParent() Object::~Object()
{ {
return m_parent; }
}
Object* Object::getChild(std::string name) std::string Object::getName()
{ {
for (const auto& child : m_children) { return m_name;
if (name == child->getName()) { }
return child.get();
Object* Object::getParent()
{
return m_parent;
}
Object* Object::getChild(std::string name)
{
for (const auto& child : m_children) {
if (name == child->getName()) {
return child.get();
}
}
return nullptr;
}
std::vector<Object*> Object::getChildren()
{
std::vector<Object*> newVector{};
for (const auto& child : m_children) {
newVector.push_back(child.get());
}
return newVector;
}
Object* Object::createChild(std::string name)
{
if (getChild(name) != nullptr) {
throw std::runtime_error("Attempt to create child object with existing name");
}
m_children.emplace_back(std::make_unique<Object>(name, this, root, m_gameIO));
return m_children.back().get();
}
void Object::deleteChild(std::string name)
{
for (auto itr = m_children.begin(); itr != m_children.end(); ++itr) {
if ((*itr)->getName() == name) {
m_children.erase(itr);
return;
}
}
throw std::runtime_error("Unable to delete child '" + name + "' as it does not exist");
}
void Object::printTree(int level)
{
std::string buf;
for (int i = 0; i < level; i++) {
if (i + 1 == level) {
buf += "\\_______";
}
else {
buf += " ";
}
}
buf += m_name;
INFO(buf);
for (const auto& child : this->getChildren()) {
child->printTree(level + 1);
} }
} }
return nullptr;
}
std::vector<Object*> Object::getChildren() void Object::getAllSubComponents(struct CompList& compList, glm::mat4 parentTransform)
{ {
std::vector<Object*> newVector{}; using namespace components;
for (const auto& child : m_children) {
newVector.push_back(child.get());
}
return newVector;
}
Object* Object::createChild(std::string name) glm::mat4 objTransform{ 1.0f };
{
if (getChild(name) != nullptr) {
throw std::runtime_error("Attempt to create child object with existing name");
}
m_children.emplace_back(std::make_unique<Object>(name, this, root, m_gameIO));
return m_children.back().get();
}
void Object::deleteChild(std::string name) auto t = transform;
{
for (auto itr = m_children.begin(); itr != m_children.end(); ++itr) { // rotation
if ((*itr)->getName() == name) { objTransform = glm::mat4_cast(t.rotation);
m_children.erase(itr);
return; // position
reinterpret_cast<glm::vec3&>(objTransform[3]) = t.position;
// scale (effectively applied first
objTransform = glm::scale(objTransform, t.scale);
glm::mat4 newTransform = parentTransform * objTransform;
for (const auto& compUnq : m_components) {
const auto comp = compUnq.get();
switch (comp->getType()) {
case Component::TypeEnum::CAMERA:
compList.cameras.emplace_back(dynamic_cast<Camera*>(comp), newTransform);
break;
case Component::TypeEnum::RENDERER:
compList.renderers.emplace_back(dynamic_cast<Renderer*>(comp), newTransform);
break;
case Component::TypeEnum::UI:
compList.uis.emplace_back(dynamic_cast<UI*>(comp), newTransform);
break;
case Component::TypeEnum::CUSTOM:
compList.customs.emplace_back(dynamic_cast<CustomComponent*>(comp), newTransform);
break;
default:
break;
}
}
for (const auto& child : m_children) {
child->getAllSubComponents(compList, newTransform);
} }
} }
throw std::runtime_error("Unable to delete child '" + name + "' as it does not exist");
}
void Object::printTree(int level) }
{
std::string buf;
for (int i = 0; i < level; i++) {
if (i+1 == level) {
buf += "\\_______";
} else {
buf += " ";
}
}
buf += m_name;
INFO(buf);
for (const auto& child : this->getChildren()) {
child->printTree(level+1);
}
}
void Object::getAllSubComponents(struct CompList& compList, glm::mat4 parentTransform)
{
using namespace components;
glm::mat4 objTransform{1.0f};
auto t = transform;
// rotation
objTransform = glm::mat4_cast(t.rotation);
// position
reinterpret_cast<glm::vec3&>(objTransform[3]) = t.position;
// scale (effectively applied first
objTransform = glm::scale(objTransform, t.scale);
const glm::mat4 newTransform = parentTransform * objTransform;
for (const auto& compUnq : m_components) {
const auto comp = compUnq.get();
switch (comp->getType()) {
case Component::TypeEnum::CAMERA:
compList.cameras.emplace_back(dynamic_cast<Camera*>(comp), newTransform);
break;
case Component::TypeEnum::RENDERER:
compList.renderers.emplace_back(dynamic_cast<Renderer*>(comp), newTransform);
break;
case Component::TypeEnum::UI:
compList.uis.emplace_back(dynamic_cast<UI*>(comp), newTransform);
break;
case Component::TypeEnum::CUSTOM:
compList.customs.emplace_back(dynamic_cast<CustomComponent*>(comp), newTransform);
break;
default:
break;
}
}
for (const auto& child : m_children) {
child->getAllSubComponents(compList, newTransform);
}
}

View File

@ -8,68 +8,75 @@
#include "log.hpp" #include "log.hpp"
ResourceManager::ResourceManager() namespace engine {
{
ResourceManager::ResourceManager()
{
#ifdef _MSC_VER #ifdef _MSC_VER
CHAR exeDirBuf[MAX_PATH + 1]; CHAR exeDirBuf[MAX_PATH + 1];
GetModuleFileNameA(NULL, exeDirBuf, MAX_PATH + 1); GetModuleFileNameA(NULL, exeDirBuf, MAX_PATH + 1);
std::filesystem::path cwd = std::filesystem::path(exeDirBuf).parent_path(); std::filesystem::path cwd = std::filesystem::path(exeDirBuf).parent_path();
(void)_chdir((const char*)std::filesystem::absolute(cwd).c_str()); (void)_chdir((const char*)std::filesystem::absolute(cwd).c_str());
#else #else
std::filesystem::path cwd = std::filesystem::current_path(); std::filesystem::path cwd = std::filesystem::current_path();
#endif #endif
if (std::filesystem::is_directory(cwd / "res")) { if (std::filesystem::is_directory(cwd / "res")) {
m_resourcesPath = cwd / "res"; m_resourcesPath = cwd / "res";
} else {
m_resourcesPath = cwd.parent_path() / "share" / "sdltest";
}
if (std::filesystem::is_directory(m_resourcesPath) == false) {
m_resourcesPath = cwd.root_path() / "usr" / "local" / "share" / "sdltest";
}
if (std::filesystem::is_directory(m_resourcesPath) == false) {
throw std::runtime_error("Unable to determine resources location. CWD: " + cwd.string());
}
}
std::unique_ptr<std::string> ResourceManager::getResourcesListString()
{
auto bufPtr = std::make_unique<std::string>();
std::string& buf = *bufPtr;
int maxLength = 0;
for (const auto& [name, ptr] : m_resources) {
if (name.length() > maxLength)
maxLength = name.length();
}
for (const auto& [name, ptr] : m_resources) {
buf += name;
for (int i = 0; i < (maxLength - name.length() + 4); i++) {
buf += " ";
} }
buf += std::to_string(ptr.use_count()) + "\n"; else {
m_resourcesPath = cwd.parent_path() / "share" / "sdltest";
}
if (std::filesystem::is_directory(m_resourcesPath) == false) {
m_resourcesPath = cwd.root_path() / "usr" / "local" / "share" / "sdltest";
}
if (std::filesystem::is_directory(m_resourcesPath) == false) {
throw std::runtime_error("Unable to determine resources location. CWD: " + cwd.string());
}
m_resourcesPath = "C:/Users/Bailey/source/repos/game/res";
}
std::unique_ptr<std::string> ResourceManager::getResourcesListString()
{
auto bufPtr = std::make_unique<std::string>();
std::string& buf = *bufPtr;
int maxLength = 0;
for (const auto& [name, ptr] : m_resources) {
if (name.length() > maxLength)
maxLength = name.length();
}
for (const auto& [name, ptr] : m_resources) {
buf += name;
for (int i = 0; i < (maxLength - name.length() + 4); i++) {
buf += " ";
}
buf += std::to_string(ptr.use_count()) + "\n";
}
return bufPtr;
} }
return bufPtr;
}
std::vector<std::weak_ptr<Resource>> ResourceManager::getAllResourcesOfType(const std::string& type) std::vector<std::weak_ptr<Resource>> ResourceManager::getAllResourcesOfType(const std::string& type)
{ {
std::vector<std::weak_ptr<Resource>> resources; std::vector<std::weak_ptr<Resource>> resources;
for (const auto& [name, ptr] : m_resources) { for (const auto& [name, ptr] : m_resources) {
if (ptr.expired() == false) { if (ptr.expired() == false) {
if (ptr.lock()->getType() == type) { if (ptr.lock()->getType() == type) {
resources.push_back(ptr); resources.push_back(ptr);
}
} }
} }
return resources;
} }
return resources;
}
// private // private
std::filesystem::path ResourceManager::getFilePath(const std::string& name) std::filesystem::path ResourceManager::getFilePath(const std::string& name)
{ {
return m_resourcesPath / name; return m_resourcesPath / name;
} }
}

View File

@ -3,7 +3,7 @@
#include <ft2build.h> #include <ft2build.h>
#include FT_FREETYPE_H #include FT_FREETYPE_H
namespace resources { namespace engine::resources {
Font::Font(const std::filesystem::path& resPath) : Resource(resPath, "font") Font::Font(const std::filesystem::path& resPath) : Resource(resPath, "font")
{ {

View File

@ -1,14 +1,18 @@
#include "resources/mesh.hpp" #include "resources/mesh.hpp"
namespace resources { #include "gfx_device.hpp"
#include "log.hpp"
namespace engine::resources {
struct MeshFileHeader { struct MeshFileHeader {
unsigned int vertex_count; uint32_t vertex_count;
unsigned int index_count; uint32_t index_count;
int material; int32_t material;
}; };
static void loadMeshFromFile(const std::filesystem::path& path, std::vector<Vertex>* vertices, std::vector<unsigned int>* indices) static void loadMeshFromFile(const std::filesystem::path& path, std::vector<Vertex>* vertices, std::vector<uint32_t>* indices)
{ {
// TODO // TODO
@ -26,73 +30,43 @@ static void loadMeshFromFile(const std::filesystem::path& path, std::vector<Vert
indices->resize(header.index_count); indices->resize(header.index_count);
vertices->resize(header.vertex_count); vertices->resize(header.vertex_count);
fread(&(*indices)[0], sizeof(unsigned int) * header.index_count, 1, fp); fread(indices->data(), sizeof(uint32_t) * header.index_count, 1, fp);
fread(&((*vertices)[0].pos[0]), sizeof(float) * 8 * header.vertex_count, 1, fp); fread(vertices->data(), sizeof(float) * 8 * header.vertex_count, 1, fp);
fclose(fp); fclose(fp);
} }
static void loadObjFromFile(const std::filesystem::path& path, std::vector<Vertex>* vertices, std::vector<unsigned int>* indices)
{
}
// -1 means invalidated
int Mesh::s_active_vao = -1;
void Mesh::bindVAO() const
{
if (s_active_vao != m_vao) {
glBindVertexArray(m_vao);
s_active_vao = m_vao;
}
}
void Mesh::initMesh() void Mesh::initMesh()
{ {
glGenVertexArrays(1, &m_vao); vb = gfxdev->createBuffer(gfx::BufferType::VERTEX, m_vertices.size() * sizeof(Vertex), m_vertices.data());
bindVAO(); ib = gfxdev->createBuffer(gfx::BufferType::INDEX, m_indices.size() * sizeof(uint32_t), m_indices.data());
glGenBuffers(1, &m_vbo);
glGenBuffers(1, &m_ebo);
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glBufferData(GL_ARRAY_BUFFER, m_vertices.size()*sizeof(Vertex), &m_vertices[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, m_indices.size() * sizeof(unsigned int), &(m_indices[0]), GL_STATIC_DRAW);
// position TRACE("VB PTR in mesh: {}", (void*)vb);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)offsetof(Vertex, pos));
// normal
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)offsetof(Vertex, norm));
// uv
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)offsetof(Vertex, uv));
} TRACE("Vertices:");
void Mesh::drawMesh(const Shader& shader) for (const auto& v : m_vertices) {
{ TRACE("pos: {}, {}, {}", v.pos.x, v.pos.y, v.pos.z);
bindVAO(); }
shader.makeActive();
#ifndef SDLTEST_NOGFX TRACE("Indices:");
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(m_indices.size()), GL_UNSIGNED_INT, 0);
#endif for (const uint32_t i : m_indices) {
TRACE("\t{}", i);
}
} }
Mesh::Mesh(const std::vector<Vertex>& vertices) : Resource("", "mesh") Mesh::Mesh(const std::vector<Vertex>& vertices) : Resource("", "mesh")
{ {
// constructor for custom meshes without an index array // constructor for custom meshes without an index array
m_vertices = vertices; // COPY over vertices m_vertices = vertices; // COPY over vertices
for (int i = 0; i < m_vertices.size(); i++) { for (uint32_t i = 0; i < m_vertices.size(); i++) {
m_indices.push_back(i); m_indices.push_back(i);
} }
initMesh(); initMesh();
} }
Mesh::Mesh(const std::vector<Vertex>& vertices, const std::vector<unsigned int>& indices) : Resource("", "mesh") Mesh::Mesh(const std::vector<Vertex>& vertices, const std::vector<uint32_t>& indices) : Resource("", "mesh")
{ {
m_vertices = vertices; // COPY over vertices m_vertices = vertices; // COPY over vertices
m_indices = indices; // COPY over indices; m_indices = indices; // COPY over indices;
@ -108,12 +82,8 @@ Mesh::Mesh(const std::filesystem::path& resPath) : Resource(resPath, "mesh")
Mesh::~Mesh() Mesh::~Mesh()
{ {
glDeleteVertexArrays(1, &m_vao); gfxdev->destroyBuffer(ib);
glDeleteBuffers(1, &m_vbo); gfxdev->destroyBuffer(vb);
glDeleteBuffers(1, &m_ebo);
if (s_active_vao == m_vao) {
s_active_vao = -1;
}
} }
} }

View File

@ -2,19 +2,23 @@
#include <log.hpp> #include <log.hpp>
Resource::Resource(const std::filesystem::path& resPath, const std::string& type) : m_resourcePath(resPath), m_type(type) namespace engine {
{
if (m_type != "mesh")
TRACE("Creating {} resource: {}", type, resPath.filename().string());
}
Resource::~Resource() Resource::Resource(const std::filesystem::path& resPath, const std::string& type) : m_resourcePath(resPath), m_type(type)
{ {
if (m_type != "mesh") if (m_type != "mesh")
TRACE("Destroyed {} resource: {}", m_type, m_resourcePath.filename().string()); TRACE("Creating {} resource: {}", type, resPath.filename().string());
} }
std::string Resource::getType() Resource::~Resource()
{ {
return m_type; if (m_type != "mesh")
} TRACE("Destroyed {} resource: {}", m_type, m_resourcePath.filename().string());
}
std::string Resource::getType()
{
return m_type;
}
}

View File

@ -1,8 +1,8 @@
#include "resources/shader.hpp" #include "resources/shader.hpp"
#include <glad/glad.h> #include "log.hpp"
#include <log.hpp> #include "gfx_device.hpp"
#include <string> #include <string>
#include <fstream> #include <fstream>
@ -24,200 +24,27 @@ static std::unique_ptr<std::vector<char>> readFile(const char * path)
return buf; return buf;
} }
static GLuint compile(const char *path, GLenum type) namespace engine::resources {
{
auto src = readFile(path);
// compile shader
GLuint handle = glCreateShader(type);
GLint size = src->size();
GLchar *data = src->data();
glShaderSource(handle, 1, &data, &size);
glCompileShader(handle);
// check for compilation error
GLint compiled;
glGetShaderiv(handle, GL_COMPILE_STATUS, &compiled);
if (compiled == 0) {
GLint log_len;
glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &log_len);
GLchar *log_msg = (GLchar *)calloc(1, log_len);
if (log_msg == NULL) {
throw std::runtime_error("Error allocating memory for shader compilation error log");
}
glGetShaderInfoLog(handle, log_len, NULL, log_msg);
throw std::runtime_error("Shader compilation error in " + std::string(path) + " log:\n" + std::string(log_msg));
} return handle;
}
namespace resources {
// I've got to do this because of GL's stupid state machine
GLuint Shader::s_activeProgram = 0;
Shader::Shader(const std::filesystem::path& resPath) : Resource(resPath, "shader") Shader::Shader(const std::filesystem::path& resPath) : Resource(resPath, "shader")
{ {
const std::string vertexShaderPath = (resPath.parent_path()/std::filesystem::path(resPath.stem().string() + ".vert")).string(); gfx::VertexFormat vertexFormat {};
const std::string fragmentShaderPath = (resPath.parent_path()/std::filesystem::path(resPath.stem().string() + ".frag")).string(); vertexFormat.stride = 8 * sizeof(float);
GLuint vs = compile(vertexShaderPath.c_str(), GL_VERTEX_SHADER); vertexFormat.attributeDescriptions.emplace_back(0, gfx::VertexAttribFormat::VEC3, 0); // pos
GLuint fs = compile(fragmentShaderPath.c_str(), GL_FRAGMENT_SHADER); vertexFormat.attributeDescriptions.emplace_back(1, gfx::VertexAttribFormat::VEC3, sizeof(glm::vec3)); // norm
m_program = glCreateProgram(); vertexFormat.attributeDescriptions.emplace_back(2, gfx::VertexAttribFormat::VEC2, sizeof(glm::vec3) + sizeof(glm::vec3)); // uv
glAttachShader(m_program, vs);
glAttachShader(m_program, fs);
glLinkProgram(m_program);
glValidateProgram(m_program);
// flag shader objects for deletion, this does not take effect until the program is deleted const std::string vertexShaderPath = (resPath.parent_path()/std::filesystem::path(resPath.stem().string() + ".vert.spv")).string();
glDeleteShader(vs); const std::string fragmentShaderPath = (resPath.parent_path()/std::filesystem::path(resPath.stem().string() + ".frag.spv")).string();
glDeleteShader(fs);
GLint linked, validated; m_pipeline = gfxdev->createPipeline(vertexShaderPath.c_str(), fragmentShaderPath.c_str(), vertexFormat, sizeof(UniformBuffer));
glGetProgramiv(m_program, GL_LINK_STATUS, &linked);
glGetProgramiv(m_program, GL_VALIDATE_STATUS, &validated);
if (linked == 0 || validated == 0) {
GLint log_len;
glGetProgramiv(m_program, GL_INFO_LOG_LENGTH, &log_len);
GLchar *log_msg = (GLchar *)calloc(1, log_len);
if (log_msg == NULL) {
throw std::runtime_error("Error allocating memory for shader linking error log");
}
glGetProgramInfoLog(m_program, log_len, NULL, log_msg);
throw std::runtime_error("Program linking error with " + vertexShaderPath + " and " + fragmentShaderPath + " log:\n" + log_msg);
}
DEBUG("For shader {}:", resPath.filename().string());
// now get uniforms
GLint count;
glGetProgramiv(m_program, GL_ACTIVE_UNIFORMS, &count);
for (int i = 0; i < count; i++) {
char nameBuf[64] = {};
GLint size;
GLenum type;
glGetActiveUniform(m_program, i, 63, NULL, &size, &type, nameBuf);
m_uniforms[nameBuf] = Uniform{size, static_cast<UniformType>(type), (GLuint)i};
DEBUG("\tuniform {}", nameBuf);
}
// now get all attributes
glGetProgramiv(m_program, GL_ACTIVE_ATTRIBUTES, &count);
for (int i = 0; i < count; i++) {
char nameBuf[64] = {};
GLint size;
GLenum type;
glGetActiveAttrib(m_program, i, 63, NULL, &size, &type, nameBuf);
m_attributes[nameBuf] = Attribute{size, static_cast<UniformType>(type), (GLuint)i};
DEBUG("\tattrib {}", nameBuf);
}
} }
Shader::~Shader() Shader::~Shader()
{ {
glDeleteProgram(m_program); gfxdev->destroyPipeline(m_pipeline);
}
void Shader::makeActive() const
{
if (s_activeProgram != m_program) {
glUseProgram(m_program);
s_activeProgram = m_program;
}
}
int Shader::getUniformLocation(const std::string& name) const
{
auto it = m_uniforms.find(name);
if (it != m_uniforms.end()) {
Uniform u = it->second;
return u.location;
}
else {
return -1;
}
}
bool Shader::setUniform_m4(const std::string& name, const glm::mat4& m) const
{
makeActive();
int loc = getUniformLocation(name);
if (loc != -1) {
glUniformMatrix4fv(loc, 1, GL_FALSE, &m[0][0]);
return true;
}
else {
return false;
}
}
bool Shader::setUniform_v2(const std::string& name, const glm::vec2& v) const
{
makeActive();
int loc = getUniformLocation(name);
if (loc != -1) {
glUniform2f(loc, v.x, v.y);
return true;
}
else {
return false;
}
}
bool Shader::setUniform_v3(const std::string& name, const glm::vec3& v) const
{
makeActive();
int loc = getUniformLocation(name);
if (loc != -1) {
glUniform3f(loc, v.x, v.y, v.z);
return true;
}
else {
return false;
}
}
bool Shader::setUniform_i(const std::string& name, int n) const
{
makeActive();
int loc = getUniformLocation(name);
if (loc != -1) {
glUniform1i(loc, n);
return true;
}
else {
return false;
}
}
bool Shader::setUniform_f(const std::string& name, float n) const
{
makeActive();
int loc = getUniformLocation(name);
if (loc != -1) {
glUniform1f(loc, n);
return true;
}
else {
return false;
}
}
Shader::UniformType Shader::getUniformType(const std::string& name) const
{
auto it = m_uniforms.find(name);
if (it != m_uniforms.end()) {
return it->second.type;
}
else {
return UniformType::NOTFOUND;
}
}
int Shader::getAttribLocation(const std::string& name) const
{
return m_attributes.at(name).location;
} }
} }

View File

@ -7,7 +7,7 @@
#include <vector> #include <vector>
namespace resources { namespace engine::resources {
// -1 means invalid / no bind // -1 means invalid / no bind
GLuint Texture::s_binded_texture = -1; GLuint Texture::s_binded_texture = -1;
@ -28,7 +28,7 @@ static bool readPNG(const std::string& path, std::vector<uint8_t>& texbuf, int *
return false; return false;
} }
const size_t size = x * y * n; const size_t size = (size_t)x * (size_t)y * (size_t)n;
texbuf.resize(size); texbuf.resize(size);
memcpy(texbuf.data(), data, size); memcpy(texbuf.data(), data, size);
@ -62,7 +62,7 @@ static bool readGLRaw(const std::string& path, std::vector<uint8_t>& texbuf, int
uint64_t end = ftell(fp); uint64_t end = ftell(fp);
texbuf.resize(end); texbuf.resize(end);
fseek(fp, tex_data_offset, SEEK_SET); fseek(fp, (long)tex_data_offset, SEEK_SET);
fread(texbuf.data(), 1, end, fp); fread(texbuf.data(), 1, end, fp);
fclose(fp); fclose(fp);

View File

@ -7,6 +7,8 @@
#include "components/mesh_renderer.hpp" #include "components/mesh_renderer.hpp"
#include "components/text_ui_renderer.hpp" #include "components/text_ui_renderer.hpp"
#include "gfx_device.hpp"
#include <glm/mat4x4.hpp> #include <glm/mat4x4.hpp>
#include <iostream> #include <iostream>
@ -14,77 +16,75 @@
#include "log.hpp" #include "log.hpp"
SceneRoot::SceneRoot(struct GameIO things) : Object("root", nullptr, *this, things) namespace engine {
{
}
SceneRoot::SceneRoot(const std::filesystem::path& file, struct GameIO things) : SceneRoot(things) SceneRoot::SceneRoot(struct GameIO things) : Object("root", nullptr, *this, things)
{ {
// TODO: make this a resource
//loadFromSceneFile(file);
}
SceneRoot::~SceneRoot()
{
}
// private methods
// public methods
void SceneRoot::updateStuff()
{
using namespace components;
using namespace glm;
struct CompList compList{};
getAllSubComponents(compList, glm::mat4{1.0f});
// update
for (const auto& [c, t] : compList.customs) {
c->onUpdate(t);
} }
// render SceneRoot::~SceneRoot()
{
}
for (const auto& [c, t] : compList.cameras) { // private methods
for (int id : m_activeCameras) {
if (c->getID() == id) { // public methods
c->updateCam(t);
for (const auto& [c, t] : compList.renderers) { void SceneRoot::updateStuff()
c->render(t); {
using namespace components;
using namespace glm;
struct CompList compList {};
getAllSubComponents(compList, glm::mat4{ 1.0f });
// update
for (const auto& [c, t] : compList.customs) {
c->onUpdate(t);
}
// render
for (const auto& [c, camt] : compList.cameras) {
for (int id : m_activeCameras) {
if (c->getID() == id) {
c->updateCam(camt);
for (const auto& [ren, ren_t] : compList.renderers) {
ren->render(ren_t);
}
break;
} }
}
}
break; for (const auto& [c, t] : compList.uis) {
c->render(t);
}
}
void SceneRoot::activateCam(int id)
{
auto& v = m_activeCameras;
if (std::find(v.begin(), v.end(), id) == v.end()) {
v.push_back(id);
}
}
void SceneRoot::deactivateCam(int id)
{
auto& v = m_activeCameras;
for (auto it = v.begin(); it != v.end(); it++) {
if (*it == id) {
v.erase(it);
} }
} }
} }
for (const auto& [c, t] : compList.uis) { }
c->render(t);
}
}
void SceneRoot::activateCam(int id)
{
auto& v = m_activeCameras;
if (std::find(v.begin(), v.end(), id) == v.end()) {
v.push_back(id);
}
}
void SceneRoot::deactivateCam(int id)
{
auto& v = m_activeCameras;
for (auto it = v.begin(); it != v.end(); it++) {
if (*it == id) {
v.erase(it);
}
}
}

View File

@ -1,120 +1,118 @@
#include "window.hpp" #include "window.hpp"
#include <glad/glad.h> #include "log.hpp"
#include <iostream> #include <iostream>
#include <stdexcept> #include <stdexcept>
#ifdef ENGINE_BUILD_VULKAN
#include <SDL2/SDL_vulkan.h>
#endif
const uint64_t BILLION = 1000000000; const uint64_t BILLION = 1000000000;
Window::Window(const std::string& title) : m_title(title) namespace engine {
{
// init SDL Window::Window(const std::string& title, bool resizable) : m_title(title), m_resizable(resizable)
if (SDL_Init(SDL_INIT_VIDEO) != 0) { {
const std::string errMsg("Unable to initialise SDL: " + std::string(SDL_GetError()));
if (SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "SDL error", errMsg.c_str(), NULL) != 0) { // init SDL
std::cerr << errMsg << "\nAre you in a graphical environment?\n"; if (SDL_Init(SDL_INIT_VIDEO) != 0) {
const std::string errMsg("Unable to initialise SDL: " + std::string(SDL_GetError()));
if (SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "SDL error", errMsg.c_str(), NULL) != 0) {
std::cerr << errMsg << "\nAre you in a graphical environment?\n";
}
throw std::runtime_error(errMsg);
} }
throw std::runtime_error(errMsg);
}
m_counterFreq = SDL_GetPerformanceFrequency(); m_counterFreq = SDL_GetPerformanceFrequency();
m_startTime = getNanos(); m_startTime = getNanos();
m_lastFrameStamp = m_startTime - 1; m_lastFrameStamp = m_startTime - 1;
m_avgFpsStart = m_startTime; m_avgFpsStart = m_startTime;
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); Uint32 windowFlags = SDL_WINDOW_SHOWN;
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
// create the window #ifdef ENGINE_BUILD_VULKAN
m_handle = SDL_CreateWindow( windowFlags |= SDL_WINDOW_VULKAN;
#endif
if (m_resizable) {
windowFlags |= SDL_WINDOW_RESIZABLE;
}
// create the window
m_handle = SDL_CreateWindow(
m_title.c_str(), m_title.c_str(),
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
static_cast<int>(m_winSize.x), static_cast<int>(m_winSize.x),
static_cast<int>(m_winSize.y), static_cast<int>(m_winSize.y),
SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI); windowFlags);
if (m_handle == NULL) { if (m_handle == NULL) {
SDL_Quit(); SDL_Quit();
throw std::runtime_error("Unable to create window: " + std::string(SDL_GetError())); throw std::runtime_error("Unable to create window: " + std::string(SDL_GetError()));
}
// get window size
int winWidth, winHeight;
SDL_GetWindowSize(m_handle, &winWidth, &winHeight);
m_winSize.x = winWidth;
m_winSize.y = winHeight;
const int WINDOWED_MIN_WIDTH = 640;
const int WINDOWED_MIN_HEIGHT = 480;
SDL_SetWindowMinimumSize(m_handle, WINDOWED_MIN_WIDTH, WINDOWED_MIN_HEIGHT);
/*
m_glContext = SDL_GL_CreateContext(m_handle);
if (m_glContext == NULL) {
SDL_DestroyWindow(m_handle);
SDL_Quit();
throw std::runtime_error("Unable to create OpenGL context: " + std::string(SDL_GetError()));
}
if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) {
SDL_DestroyWindow(m_handle);
SDL_Quit();
throw std::runtime_error("Unable to initialise GLAD");
}
*/
// onResize(m_winSize.x, m_winSize.y);
} }
// get window size Window::~Window()
int winWidth, winHeight; {
SDL_GetWindowSize(m_handle, &winWidth, &winHeight);
m_winSize.x = winWidth;
m_winSize.y = winHeight;
const int WINDOWED_MIN_WIDTH = 640;
const int WINDOWED_MIN_HEIGHT = 480;
SDL_SetWindowMinimumSize(m_handle, WINDOWED_MIN_WIDTH, WINDOWED_MIN_HEIGHT);
m_glContext = SDL_GL_CreateContext(m_handle);
if (m_glContext == NULL) {
SDL_DestroyWindow(m_handle); SDL_DestroyWindow(m_handle);
SDL_Quit(); SDL_Quit();
throw std::runtime_error("Unable to create OpenGL context: " + std::string(SDL_GetError()));
} }
if (!gladLoadGLLoader((GLADloadproc)SDL_GL_GetProcAddress)) { // private methods
SDL_DestroyWindow(m_handle);
SDL_Quit(); void Window::onResize(Sint32 width, Sint32 height)
throw std::runtime_error("Unable to initialise GLAD"); {
// get window size
m_winSize.x = static_cast<int>(width);
m_winSize.y = static_cast<int>(height);
m_justResized = true;
} }
onResize(m_winSize.x, m_winSize.y); void Window::resetInputDeltas()
{
m_justResized = false;
} m_keyboard.deltas.fill(ButtonDelta::SAME);
Window::~Window() m_mouse.deltas.fill(ButtonDelta::SAME);
{ m_mouse.dx = 0;
SDL_GL_DeleteContext(m_glContext); m_mouse.dy = 0;
SDL_DestroyWindow(m_handle); m_mouse.xscroll = 0.0f;
SDL_Quit(); m_mouse.yscroll = 0.0f;
} }
// private methods // TODO event methods (like callbacks)
void Window::onResize(Sint32 width, Sint32 height) void Window::onWindowEvent(SDL_WindowEvent& e)
{ {
// get window size
m_winSize.x = static_cast<int>(width);
m_winSize.y = static_cast<int>(height);
// get framebuffer size switch (e.event) {
int fbWidth, fbHeight;
SDL_GL_GetDrawableSize(m_handle, &fbWidth, &fbHeight);
m_fbSize.x = static_cast<int>(fbWidth);
m_fbSize.y = static_cast<int>(fbHeight);
glViewport(0, 0, fbWidth, fbHeight);
m_justResized = true;
}
void Window::resetInputDeltas()
{
m_justResized = false;
m_keyboard.deltas.fill(ButtonDelta::SAME);
m_mouse.deltas.fill(ButtonDelta::SAME);
m_mouse.dx = 0.0f;
m_mouse.dy = 0.0f;
m_mouse.xscroll = 0.0f;
m_mouse.yscroll = 0.0f;
}
// TODO event methods (like callbacks)
void Window::onWindowEvent(SDL_WindowEvent &e)
{
switch (e.event) {
case SDL_WINDOWEVENT_SIZE_CHANGED: case SDL_WINDOWEVENT_SIZE_CHANGED:
onResize(e.data1, e.data2); onResize(e.data1, e.data2);
break; break;
@ -124,23 +122,23 @@ void Window::onWindowEvent(SDL_WindowEvent &e)
case SDL_WINDOWEVENT_FOCUS_LOST: case SDL_WINDOWEVENT_FOCUS_LOST:
m_keyboardFocus = false; m_keyboardFocus = false;
break; break;
}
} }
}
void Window::onKeyEvent(SDL_KeyboardEvent &e) void Window::onKeyEvent(SDL_KeyboardEvent& e)
{ {
bool keyWasDown = m_keyboard.keys[e.keysym.scancode]; bool keyWasDown = m_keyboard.keys[e.keysym.scancode];
bool keyIsDown = (e.state == SDL_PRESSED); bool keyIsDown = (e.state == SDL_PRESSED);
m_keyboard.keys[e.keysym.scancode] = keyIsDown; m_keyboard.keys[e.keysym.scancode] = keyIsDown;
if (keyIsDown != keyWasDown) { // (if key was pressed or released) if (keyIsDown != keyWasDown) { // (if key was pressed or released)
m_keyboard.deltas[e.keysym.scancode] = keyIsDown ? ButtonDelta::PRESSED : ButtonDelta::RELEASED; m_keyboard.deltas[e.keysym.scancode] = keyIsDown ? ButtonDelta::PRESSED : ButtonDelta::RELEASED;
}
} }
}
void Window::onMouseButtonEvent(SDL_MouseButtonEvent &e) void Window::onMouseButtonEvent(SDL_MouseButtonEvent& e)
{ {
enum inputs::MouseButton button = inputs::MouseButton::M_INVALID; enum inputs::MouseButton button = inputs::MouseButton::M_INVALID;
switch (e.button) { switch (e.button) {
case SDL_BUTTON_LEFT: case SDL_BUTTON_LEFT:
button = inputs::MouseButton::M_LEFT; button = inputs::MouseButton::M_LEFT;
break; break;
@ -156,72 +154,65 @@ void Window::onMouseButtonEvent(SDL_MouseButtonEvent &e)
case SDL_BUTTON_X2: case SDL_BUTTON_X2:
button = inputs::MouseButton::M_X2; button = inputs::MouseButton::M_X2;
break; break;
} }
int buttonIndex = static_cast<int>(button);
bool buttonWasDown = m_mouse.buttons[static_cast<int>(button)]; bool buttonWasDown = m_mouse.buttons.at(buttonIndex);
bool buttonIsDown = (e.state == SDL_PRESSED); bool buttonIsDown = (e.state == SDL_PRESSED);
m_mouse.buttons[static_cast<int>(button)] = buttonIsDown; m_mouse.buttons.at(buttonIndex) = buttonIsDown;
if (buttonIsDown != buttonWasDown) { // (if button was pressed or released) if (buttonIsDown != buttonWasDown) { // (if button was pressed or released)
// only sets delta if it hasn't already been set this frame (to detect very fast presses) // only sets delta if it hasn't already been set this frame (to detect very fast presses)
if (m_mouse.deltas[static_cast<int>(button)] == ButtonDelta::SAME) { if (m_mouse.deltas[buttonIndex] == ButtonDelta::SAME) {
m_mouse.deltas[static_cast<int>(button)] = buttonIsDown ? ButtonDelta::PRESSED : ButtonDelta::RELEASED; m_mouse.deltas[buttonIndex] = buttonIsDown ? ButtonDelta::PRESSED : ButtonDelta::RELEASED;
}
} }
} }
}
void Window::onMouseMotionEvent(SDL_MouseMotionEvent &e) void Window::onMouseMotionEvent(SDL_MouseMotionEvent& e)
{ {
m_mouse.x = e.x; m_mouse.x = e.x;
m_mouse.y = e.y; m_mouse.y = e.y;
m_mouse.dx = e.xrel; m_mouse.dx = e.xrel;
m_mouse.dy = e.yrel; m_mouse.dy = e.yrel;
}
void Window::onMouseWheelEvent(SDL_MouseWheelEvent &e)
{
if (e.direction == SDL_MOUSEWHEEL_NORMAL) {
m_mouse.xscroll = e.preciseX;
m_mouse.yscroll = e.preciseY;
} else { // flipped
m_mouse.xscroll = -e.preciseX;
m_mouse.yscroll = -e.preciseY;
} }
}
// public methods void Window::onMouseWheelEvent(SDL_MouseWheelEvent& e)
{
std::string Window::getTitle() const if (e.direction == SDL_MOUSEWHEEL_NORMAL) {
{ m_mouse.xscroll = e.preciseX;
return m_title; m_mouse.yscroll = e.preciseY;
} }
else { // flipped
void Window::makeContextCurrent() m_mouse.xscroll = -e.preciseX;
{ m_mouse.yscroll = -e.preciseY;
if (SDL_GL_MakeCurrent(m_handle, m_glContext) != 0) { }
throw std::runtime_error("Failed to make GL context current");
} }
}
void Window::swapBuffers() // public methods
{
#ifndef SDLTEST_NOGFX
SDL_GL_SwapWindow(m_handle);
#endif
m_frames++;
uint64_t currentFrameStamp = getNanos();
m_lastFrameTime = currentFrameStamp - m_lastFrameStamp;
m_lastFrameStamp = currentFrameStamp;
}
void Window::getInputAndEvents() SDL_Window* Window::getHandle() const
{ {
return m_handle;
}
resetInputDeltas(); std::string Window::getTitle() const
{
return m_title;
}
// loop through all available events void Window::getInputAndEvents()
SDL_Event e; {
while (SDL_PollEvent(&e)) {
switch (e.type) { m_frames++;
uint64_t currentFrameStamp = getNanos();
m_lastFrameTime = currentFrameStamp - m_lastFrameStamp;
m_lastFrameStamp = currentFrameStamp;
resetInputDeltas();
// loop through all available events
SDL_Event e;
while (SDL_PollEvent(&e)) {
switch (e.type) {
case SDL_QUIT: case SDL_QUIT:
setCloseFlag(); setCloseFlag();
@ -249,264 +240,240 @@ void Window::getInputAndEvents()
onMouseWheelEvent(e.wheel); onMouseWheelEvent(e.wheel);
break; break;
}
}
}
void Window::setTitle(std::string title)
{
SDL_SetWindowTitle(m_handle, title.c_str());
}
bool Window::getWindowResized() const
{
return m_justResized;
}
void Window::show()
{
SDL_ShowWindow(m_handle);
}
void Window::hide()
{
SDL_HideWindow(m_handle);
}
void Window::focus()
{
SDL_RaiseWindow(m_handle);
m_keyboardFocus = true;
}
bool Window::hasFocus() const
{
return m_keyboardFocus;
}
void Window::setCloseFlag()
{
m_shouldClose = true;
}
bool Window::isRunning() const
{
return !m_shouldClose;
}
void Window::setFullscreen(bool fullscreen, bool exclusive)
{
if (m_resizable) {
if (SDL_SetWindowFullscreen(m_handle, fullscreen ? (exclusive ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP) : 0) != 0) {
throw std::runtime_error("Unable to set window to fullscreen/windowed");
}
m_fullscreen = fullscreen;
if (fullscreen) {
int width, height;
SDL_GetWindowSize(m_handle, &width, &height);
onResize(width, height);
}
} }
} }
} void Window::toggleFullscreen()
{
void Window::setVSync(bool enable) setFullscreen(!m_fullscreen);
{
if (SDL_GL_SetSwapInterval(enable ? 1 : 0) != 0) {
throw std::runtime_error("Failed to set swap interval");
} }
}
bool Window::getVSync() const bool Window::isFullscreen() const
{ {
return SDL_GL_GetSwapInterval() == 0 ? false : true; return m_fullscreen;
}
glm::ivec2 Window::getViewportSize()
{
return m_fbSize;
}
void Window::setTitle(std::string title)
{
SDL_SetWindowTitle(m_handle, title.c_str());
}
bool Window::getWindowResized() const
{
return m_justResized;
}
void Window::show()
{
SDL_ShowWindow(m_handle);
}
void Window::hide()
{
SDL_HideWindow(m_handle);
}
void Window::focus()
{
SDL_RaiseWindow(m_handle);
m_keyboardFocus = true;
}
bool Window::hasFocus() const
{
return m_keyboardFocus;
}
void Window::setCloseFlag()
{
m_shouldClose = true;
}
bool Window::isRunning() const
{
return !m_shouldClose;
}
void Window::setFullscreen(bool fullscreen, bool exclusive)
{
if (SDL_SetWindowFullscreen(m_handle, fullscreen ? (exclusive ? SDL_WINDOW_FULLSCREEN : SDL_WINDOW_FULLSCREEN_DESKTOP) : 0) != 0) {
throw std::runtime_error("Unable to set window to fullscreen/windowed");
} }
m_fullscreen = fullscreen;
if (fullscreen) { bool Window::setRelativeMouseMode(bool enabled)
int width, height; {
SDL_GetWindowSize(m_handle, &width, &height); m_mouse.captured = enabled;
onResize(width, height); int code = SDL_SetRelativeMouseMode(static_cast<SDL_bool>(enabled));
if (code != 0) {
throw std::runtime_error("Unable to set relative mouse mode");
}
else {
return true;
}
} }
}
void Window::toggleFullscreen() bool Window::mouseCaptured()
{ {
setFullscreen(!m_fullscreen); return m_mouse.captured;
}
bool Window::isFullscreen() const
{
return m_fullscreen;
}
bool Window::setRelativeMouseMode(bool enabled)
{
m_mouse.captured = enabled;
int code = SDL_SetRelativeMouseMode(static_cast<SDL_bool>(enabled));
if (code != 0) {
throw std::runtime_error("Unable to set relative mouse mode");
} else {
return true;
} }
}
bool Window::mouseCaptured() // getting input
{
return m_mouse.captured;
}
// getting input bool Window::getKey(inputs::Key key) const
{
bool Window::getKey(inputs::Key key) const return m_keyboard.keys[static_cast<int>(key)];
{
return m_keyboard.keys[static_cast<int>(key)];
}
bool Window::getKeyPress(inputs::Key key) const
{
return m_keyboard.deltas[static_cast<int>(key)] == ButtonDelta::PRESSED;
}
bool Window::getKeyRelease(inputs::Key key) const
{
return m_keyboard.deltas[static_cast<int>(key)] == ButtonDelta::RELEASED;
}
// TODO mouse input
bool Window::getButton(inputs::MouseButton button) const
{
return m_mouse.buttons[static_cast<int>(button)];
}
bool Window::getButtonPress(inputs::MouseButton button) const
{
return m_mouse.deltas[static_cast<int>(button)] == ButtonDelta::PRESSED;
}
bool Window::getButtonRelease(inputs::MouseButton button) const
{
return m_mouse.deltas[static_cast<int>(button)] == ButtonDelta::RELEASED;
}
int Window::getMouseX() const
{
return static_cast<int>(m_mouse.x);
}
int Window::getMouseY() const
{
return static_cast<int>(m_mouse.y);
}
float Window::getMouseNormX() const
{
return ((float)m_mouse.x * 2.0f / (float)m_winSize.x) - 1.0f;
}
float Window::getMouseNormY() const
{
return ((float)m_mouse.y * -2.0f / (float)m_winSize.y) + 1.0f;
}
int Window::getMouseDX() const
{
return static_cast<int>(m_mouse.dx);
}
int Window::getMouseDY() const
{
return static_cast<int>(m_mouse.dy);
}
float Window::getMouseScrollX() const
{
return m_mouse.xscroll;
}
float Window::getMouseScrollY() const
{
return m_mouse.yscroll;
}
// TODO game pad
// get timer value
uint64_t Window::getNanos() const
{
uint64_t count;
count = SDL_GetPerformanceCounter();
if (m_counterFreq == BILLION) {
return count;
} else {
return count * (BILLION / m_counterFreq);
} }
}
uint64_t Window::getLastFrameStamp() const bool Window::getKeyPress(inputs::Key key) const
{ {
return m_lastFrameStamp; return m_keyboard.deltas[static_cast<int>(key)] == ButtonDelta::PRESSED;
}
uint64_t Window::getFrameCount() const
{
return m_frames;
}
uint64_t Window::getStartTime() const
{
return m_startTime;
}
float Window::dt() const
{
return (float)m_lastFrameTime / (float)BILLION;
}
uint64_t Window::getFPS() const
{
if (m_lastFrameTime == 0) return 0;
return BILLION / m_lastFrameTime;
}
uint64_t Window::getAvgFPS() const
{
uint64_t delta_t = getNanos() - m_avgFpsStart;
if (delta_t == 0) return 0;
return BILLION * (m_frames - m_avgFpsStartCount) / delta_t;
}
void Window::resetAvgFPS()
{
m_avgFpsStart = getNanos();
m_avgFpsStartCount = getFrameCount();
}
bool Window::infoBox(const std::string& title, const std::string& msg)
{
if (isFullscreen() == false) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, title.c_str(), msg.c_str(), m_handle);
return true;
} else {
return false;
} }
}
std::vector<const char*> Window::getRequiredVulkanExtensions() const bool Window::getKeyRelease(inputs::Key key) const
{ {
#ifdef ENGINE_BUILD_VULKAN return m_keyboard.deltas[static_cast<int>(key)] == ButtonDelta::RELEASED;
unsigned int sdlExtensionCount = 0; }
SDL_Vulkan_GetInstanceExtensions(m_handle, &sdlExtensionCount, nullptr);
std::vector<const char*> requiredExtensions(sdlExtensionCount);
SDL_Vulkan_GetInstanceExtensions(m_handle, &sdlExtensionCount, requiredExtensions.data());
return requiredExtensions; // TODO mouse input
#else
return std::vector<const char*>{};
#endif
}
/* STATIC METHODS */ bool Window::getButton(inputs::MouseButton button) const
{
return m_mouse.buttons[static_cast<int>(button)];
}
// Display an error message box bool Window::getButtonPress(inputs::MouseButton button) const
void Window::errorBox(const std::string& message) {
{ return m_mouse.deltas[static_cast<int>(button)] == ButtonDelta::PRESSED;
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Game Error", message.c_str(), NULL); }
}
bool Window::getButtonRelease(inputs::MouseButton button) const
{
return m_mouse.deltas[static_cast<int>(button)] == ButtonDelta::RELEASED;
}
int Window::getMouseX() const
{
return static_cast<int>(m_mouse.x);
}
int Window::getMouseY() const
{
return static_cast<int>(m_mouse.y);
}
float Window::getMouseNormX() const
{
return ((float)m_mouse.x * 2.0f / (float)m_winSize.x) - 1.0f;
}
float Window::getMouseNormY() const
{
return ((float)m_mouse.y * -2.0f / (float)m_winSize.y) + 1.0f;
}
int Window::getMouseDX() const
{
return static_cast<int>(m_mouse.dx);
}
int Window::getMouseDY() const
{
return static_cast<int>(m_mouse.dy);
}
float Window::getMouseScrollX() const
{
return m_mouse.xscroll;
}
float Window::getMouseScrollY() const
{
return m_mouse.yscroll;
}
// TODO game pad
// get timer value
uint64_t Window::getNanos() const
{
uint64_t count;
count = SDL_GetPerformanceCounter();
if (m_counterFreq == BILLION) {
return count;
}
else {
return count * (BILLION / m_counterFreq);
}
}
uint64_t Window::getLastFrameStamp() const
{
return m_lastFrameStamp;
}
uint64_t Window::getFrameCount() const
{
return m_frames;
}
uint64_t Window::getStartTime() const
{
return m_startTime;
}
float Window::dt() const
{
return (float)m_lastFrameTime / (float)BILLION;
}
uint64_t Window::getFPS() const
{
if (m_lastFrameTime == 0) return 0;
return BILLION / m_lastFrameTime;
}
uint64_t Window::getAvgFPS() const
{
uint64_t delta_t = getNanos() - m_avgFpsStart;
if (delta_t == 0) return 0;
return BILLION * (m_frames - m_avgFpsStartCount) / delta_t;
}
void Window::resetAvgFPS()
{
m_avgFpsStart = getNanos();
m_avgFpsStartCount = getFrameCount();
}
bool Window::infoBox(const std::string& title, const std::string& msg)
{
if (isFullscreen() == false) {
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_INFORMATION, title.c_str(), msg.c_str(), m_handle);
return true;
}
else {
return false;
}
}
/* STATIC METHODS */
// Display an error message box
void Window::errorBox(const std::string& message)
{
SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Game Error", message.c_str(), NULL);
}
}