mirror of
https://github.com/bailwillharr/engine.git
synced 2024-09-21 04:51:18 +00:00
Initial commit
This commit is contained in:
commit
834e2fd3ac
10
.gitignore
vendored
Normal file
10
.gitignore
vendored
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
.ccls-cache/
|
||||||
|
.vscode/
|
||||||
|
.vs/
|
||||||
|
out/
|
||||||
|
build/
|
||||||
|
Debug/
|
||||||
|
Release/
|
||||||
|
MinSizeRel/
|
||||||
|
CMakeSettings.json
|
||||||
|
compile_commands.json
|
18
.gitmodules
vendored
Normal file
18
.gitmodules
vendored
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
[submodule "dependencies/SDL"]
|
||||||
|
path = dependencies/SDL
|
||||||
|
url = https://github.com/libsdl-org/SDL
|
||||||
|
[submodule "dependencies/stb"]
|
||||||
|
path = dependencies/stb
|
||||||
|
url = https://github.com/nothings/stb
|
||||||
|
[submodule "dependencies/glm"]
|
||||||
|
path = dependencies/glm
|
||||||
|
url = https://github.com/g-truc/glm
|
||||||
|
[submodule "dependencies/glad"]
|
||||||
|
path = dependencies/glad
|
||||||
|
url = https://github.com/dav1dde/glad
|
||||||
|
[submodule "dependencies/spdlog"]
|
||||||
|
path = dependencies/spdlog
|
||||||
|
url = https://github.com/gabime/spdlog
|
||||||
|
[submodule "dependencies/freetype"]
|
||||||
|
path = dependencies/freetype
|
||||||
|
url = https://gitlab.freedesktop.org/freetype/freetype.git
|
142
CMakeLists.txt
Normal file
142
CMakeLists.txt
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.12)
|
||||||
|
|
||||||
|
# options
|
||||||
|
|
||||||
|
project(engine LANGUAGES CXX
|
||||||
|
VERSION "0.1.0"
|
||||||
|
)
|
||||||
|
|
||||||
|
add_library(${PROJECT_NAME} SHARED
|
||||||
|
|
||||||
|
"src/window.cpp"
|
||||||
|
|
||||||
|
"src/input.cpp" #TODO make input_manager
|
||||||
|
|
||||||
|
"src/object.cpp"
|
||||||
|
"src/sceneroot.cpp"
|
||||||
|
|
||||||
|
"src/components/component.cpp"
|
||||||
|
"src/components/transform.cpp" # TODO move functionality into "Object" class
|
||||||
|
"src/components/camera.cpp"
|
||||||
|
"src/components/mesh_renderer.cpp"
|
||||||
|
"src/components/text_ui_renderer.cpp"
|
||||||
|
"src/components/custom.cpp"
|
||||||
|
|
||||||
|
"src/resources/resource.cpp"
|
||||||
|
"src/resources/mesh.cpp"
|
||||||
|
"src/resources/shader.cpp"
|
||||||
|
"src/resources/texture.cpp"
|
||||||
|
"src/resources/font.cpp"
|
||||||
|
|
||||||
|
"src/resource_manager.cpp"
|
||||||
|
|
||||||
|
# PUBLIC API
|
||||||
|
|
||||||
|
"include/log.hpp"
|
||||||
|
|
||||||
|
"include/window.hpp"
|
||||||
|
|
||||||
|
"include/inputs/keyboard.hpp"
|
||||||
|
"include/inputs/mouse.hpp"
|
||||||
|
|
||||||
|
"include/input.hpp"
|
||||||
|
|
||||||
|
"include/object.hpp"
|
||||||
|
"include/sceneroot.hpp"
|
||||||
|
|
||||||
|
"include/components/component.hpp"
|
||||||
|
"include/components/transform.hpp"
|
||||||
|
"include/components/camera.hpp"
|
||||||
|
"include/components/mesh_renderer.hpp"
|
||||||
|
"include/components/text_ui_renderer.hpp"
|
||||||
|
"include/components/custom.hpp"
|
||||||
|
|
||||||
|
"include/resources/resource.hpp"
|
||||||
|
"include/resources/mesh.hpp"
|
||||||
|
"include/resources/shader.hpp"
|
||||||
|
"include/resources/texture.hpp"
|
||||||
|
"include/resources/font.hpp"
|
||||||
|
|
||||||
|
"include/resource_manager.hpp"
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
# compiling options:
|
||||||
|
|
||||||
|
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD 20)
|
||||||
|
set_property(TARGET ${PROJECT_NAME} PROPERTY CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
if (MSVC)
|
||||||
|
target_compile_options(${PROJECT_NAME} PRIVATE /W3)
|
||||||
|
target_compile_options(${PROJECT_NAME} PRIVATE /M3)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
target_include_directories(${PROJECT_NAME} PUBLIC include)
|
||||||
|
target_include_directories(${PROJECT_NAME} PRIVATE src)
|
||||||
|
|
||||||
|
# Pass some project information into the source code
|
||||||
|
configure_file(config.h.in config.h)
|
||||||
|
target_include_directories(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
|
# libraries:
|
||||||
|
|
||||||
|
# MinGW library if using it
|
||||||
|
if (MINGW)
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC mingw32)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# SDL2:
|
||||||
|
set(SDL2_DISABLE_INSTALL ON)
|
||||||
|
set(SDL_SHARED ON)
|
||||||
|
set(SDL_STATIC OFF)
|
||||||
|
set(SDL_TEST OFF)
|
||||||
|
add_subdirectory(dependencies/SDL)
|
||||||
|
target_include_directories(${PROJECT_NAME} PUBLIC dependencies/SDL/include)
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC SDL2::SDL2)
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC SDL2::SDL2main)
|
||||||
|
# copy over SDL2 library:
|
||||||
|
add_custom_command(
|
||||||
|
TARGET ${PROJECT_NAME} PRE_BUILD
|
||||||
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
|
$<TARGET_FILE:SDL2::SDL2> $<TARGET_FILE_DIR:${PROJECT_NAME}>)
|
||||||
|
|
||||||
|
# GLM:
|
||||||
|
add_subdirectory(dependencies/glm)
|
||||||
|
target_include_directories(${PROJECT_NAME} PUBLIC dependencies/glm)
|
||||||
|
|
||||||
|
# GLAD:
|
||||||
|
set(GLAD_PROFILE "core" CACHE INTERNAL "" FORCE) # TODO remove this "CACHE INTERNAL..." thing
|
||||||
|
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)
|
||||||
|
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
|
||||||
|
set(SPDLOG_BUILD_SHARED ON CACHE INTERNAL "" FORCE)
|
||||||
|
add_subdirectory(dependencies/spdlog)
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC spdlog)
|
||||||
|
target_include_directories(${PROJECT_NAME} PUBLIC dependencies/spdlog/include)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# freetype
|
||||||
|
set(BUILD_SHARED_LIBS ON)
|
||||||
|
add_subdirectory(dependencies/freetype)
|
||||||
|
target_link_libraries(${PROJECT_NAME} PRIVATE freetype)
|
||||||
|
target_include_directories(${PROJECT_NAME} PRIVATE dependencies/freetype/include)
|
||||||
|
set(BUILD_SHARED_LIBS OFF)
|
||||||
|
|
||||||
|
# stb
|
||||||
|
target_include_directories(${PROJECT_NAME} PRIVATE dependencies/stb)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# public API:
|
||||||
|
|
3
config.h.in
Normal file
3
config.h.in
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#define SDLTEST_VERSION @PROJECT_VERSION@
|
1
dependencies/SDL
vendored
Submodule
1
dependencies/SDL
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 284769633864df4b1395f164dc16d03163973a63
|
1
dependencies/freetype
vendored
Submodule
1
dependencies/freetype
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit dd91f6e7f5a051818070c49715125fb72074023e
|
1
dependencies/glad
vendored
Submodule
1
dependencies/glad
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit ea756f7cc5e11dcef3cafdab87d45b3b528c875d
|
1
dependencies/glm
vendored
Submodule
1
dependencies/glm
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit cc98465e3508535ba8c7f6208df934c156a018dc
|
1
dependencies/spdlog
vendored
Submodule
1
dependencies/spdlog
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit f44fa31f5110331af196d0ed7f00ae1c7b8ef3cc
|
1
dependencies/stb
vendored
Submodule
1
dependencies/stb
vendored
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit af1a5bc352164740c1cc1354942b1c6b72eacb8a
|
53
include/components/camera.hpp
Normal file
53
include/components/camera.hpp
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "component.hpp"
|
||||||
|
|
||||||
|
#include "object.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <glm/ext.hpp>
|
||||||
|
|
||||||
|
namespace components {
|
||||||
|
|
||||||
|
class Camera : public Component {
|
||||||
|
|
||||||
|
public:
|
||||||
|
Camera(Object* parent);
|
||||||
|
~Camera();
|
||||||
|
|
||||||
|
// called every frame, don't call manually
|
||||||
|
void updateCam(glm::mat4 transform);
|
||||||
|
|
||||||
|
void makeActive();
|
||||||
|
bool isActive();
|
||||||
|
|
||||||
|
void usePerspective(float fovDeg);
|
||||||
|
void useOrtho();
|
||||||
|
|
||||||
|
bool m_noClear = false;
|
||||||
|
|
||||||
|
glm::vec3 m_lightPos = { 0.0f, 100.0f, 0.0f };
|
||||||
|
|
||||||
|
glm::vec4 clearColor{0.1f, 0.1f, 0.1f, 1.0f};
|
||||||
|
|
||||||
|
glm::mat4 m_projMatrix{ 1.0f };
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
enum class Modes {
|
||||||
|
PERSPECTIVE,
|
||||||
|
ORTHOGRAPHIC
|
||||||
|
};
|
||||||
|
|
||||||
|
Modes m_mode = Modes::ORTHOGRAPHIC;
|
||||||
|
|
||||||
|
// perspective mode settings
|
||||||
|
float m_fovDeg = 90.0f;
|
||||||
|
|
||||||
|
static glm::vec4 s_clearColor;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
41
include/components/component.hpp
Normal file
41
include/components/component.hpp
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
class Object;
|
||||||
|
class Window;
|
||||||
|
class Input;
|
||||||
|
class ResourceManager;
|
||||||
|
|
||||||
|
class 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:
|
||||||
|
Window& win;
|
||||||
|
Input& inp;
|
||||||
|
ResourceManager& res;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static int s_next_component_id;
|
||||||
|
|
||||||
|
int m_id = s_next_component_id;
|
||||||
|
TypeEnum m_type;
|
||||||
|
|
||||||
|
};
|
20
include/components/custom.hpp
Normal file
20
include/components/custom.hpp
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "component.hpp"
|
||||||
|
|
||||||
|
#include <glm/mat4x4.hpp>
|
||||||
|
|
||||||
|
namespace components {
|
||||||
|
|
||||||
|
class CustomComponent : public Component {
|
||||||
|
|
||||||
|
public:
|
||||||
|
CustomComponent(Object* parent);
|
||||||
|
virtual ~CustomComponent() = 0;
|
||||||
|
|
||||||
|
virtual void onInit();
|
||||||
|
virtual void onUpdate(glm::mat4 t);
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
39
include/components/mesh_renderer.hpp
Normal file
39
include/components/mesh_renderer.hpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "component.hpp"
|
||||||
|
|
||||||
|
#include "resources/shader.hpp"
|
||||||
|
#include "resources/mesh.hpp"
|
||||||
|
#include "resources/texture.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace components {
|
||||||
|
|
||||||
|
class Renderer : public Component {
|
||||||
|
|
||||||
|
public:
|
||||||
|
Renderer(Object*);
|
||||||
|
~Renderer() override;
|
||||||
|
|
||||||
|
// called every frame, do not call manually
|
||||||
|
void render(glm::mat4 transform);
|
||||||
|
|
||||||
|
void setMesh(const std::string& name);
|
||||||
|
void setTexture(const std::string& name);
|
||||||
|
|
||||||
|
std::shared_ptr<resources::Mesh> m_mesh = nullptr;
|
||||||
|
std::shared_ptr<resources::Texture> m_texture;
|
||||||
|
|
||||||
|
glm::vec3 m_color = { 1.0f, 1.0f, 1.0f };
|
||||||
|
glm::vec3 m_emission = { 0.0f, 0.0f, 0.0f };
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::shared_ptr<resources::Shader> m_shader;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
38
include/components/text_ui_renderer.hpp
Normal file
38
include/components/text_ui_renderer.hpp
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "component.hpp"
|
||||||
|
|
||||||
|
#include "resources/font.hpp"
|
||||||
|
#include "resources/mesh.hpp"
|
||||||
|
|
||||||
|
#include <glm/mat4x4.hpp>
|
||||||
|
|
||||||
|
namespace components {
|
||||||
|
|
||||||
|
class UI : public Component {
|
||||||
|
|
||||||
|
public:
|
||||||
|
UI(Object*);
|
||||||
|
~UI() override;
|
||||||
|
|
||||||
|
// called every frame, do not call manually
|
||||||
|
void render(glm::mat4 transform);
|
||||||
|
|
||||||
|
std::string m_text = "hello world";
|
||||||
|
glm::vec3 m_color = {1.0f, 1.0f, 1.0f};
|
||||||
|
|
||||||
|
enum class Alignment {
|
||||||
|
NONE = 0,
|
||||||
|
LEFT = 1,
|
||||||
|
RIGHT = 2
|
||||||
|
};
|
||||||
|
Alignment m_alignment = Alignment::LEFT;
|
||||||
|
bool m_scaled = true;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::shared_ptr<resources::Font> m_font;
|
||||||
|
std::shared_ptr<resources::Shader> m_shader;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
32
include/components/transform.hpp
Normal file
32
include/components/transform.hpp
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "component.hpp"
|
||||||
|
|
||||||
|
#include <glm/mat4x4.hpp>
|
||||||
|
#include <glm/vec3.hpp>
|
||||||
|
#include <glm/ext/quaternion_float.hpp>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace components {
|
||||||
|
|
||||||
|
class Transform : public Component {
|
||||||
|
|
||||||
|
// Scale, rotate (XYZ), translate
|
||||||
|
|
||||||
|
private:
|
||||||
|
glm::mat4 m_transformMatrix{1.0f};
|
||||||
|
|
||||||
|
public:
|
||||||
|
Transform(Object*);
|
||||||
|
~Transform() override;
|
||||||
|
|
||||||
|
glm::vec3 position{0.0f};
|
||||||
|
glm::quat rotation{};
|
||||||
|
glm::vec3 scale{1.0f};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
87
include/input.hpp
Normal file
87
include/input.hpp
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "inputs/mouse.hpp"
|
||||||
|
#include "inputs/keyboard.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class Window;
|
||||||
|
|
||||||
|
enum class InputDevice : int {
|
||||||
|
MOUSE,
|
||||||
|
KEYBOARD,
|
||||||
|
CONTROLLER,
|
||||||
|
SIZE
|
||||||
|
};
|
||||||
|
|
||||||
|
// This class should be used to get platform/input-device independent input
|
||||||
|
class Input {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
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);
|
||||||
|
|
||||||
|
};
|
374
include/inputs/keyboard.hpp
Normal file
374
include/inputs/keyboard.hpp
Normal file
@ -0,0 +1,374 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// Keyboard scancodes, taken from SDL_scancode.h
|
||||||
|
|
||||||
|
namespace inputs {
|
||||||
|
|
||||||
|
enum class Key : int {
|
||||||
|
UNKNOWN = 0,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \name Usage page 0x07
|
||||||
|
*
|
||||||
|
* These values are from usage page 0x07 (USB keyboard page).
|
||||||
|
*/
|
||||||
|
/* @{ */
|
||||||
|
|
||||||
|
A = 4,
|
||||||
|
B = 5,
|
||||||
|
C = 6,
|
||||||
|
D = 7,
|
||||||
|
E = 8,
|
||||||
|
F = 9,
|
||||||
|
G = 10,
|
||||||
|
H = 11,
|
||||||
|
I = 12,
|
||||||
|
J = 13,
|
||||||
|
K = 14,
|
||||||
|
L = 15,
|
||||||
|
M = 16,
|
||||||
|
N = 17,
|
||||||
|
O = 18,
|
||||||
|
P = 19,
|
||||||
|
Q = 20,
|
||||||
|
R = 21,
|
||||||
|
S = 22,
|
||||||
|
T = 23,
|
||||||
|
U = 24,
|
||||||
|
V = 25,
|
||||||
|
W = 26,
|
||||||
|
X = 27,
|
||||||
|
Y = 28,
|
||||||
|
Z = 29,
|
||||||
|
|
||||||
|
N1 = 30,
|
||||||
|
N2 = 31,
|
||||||
|
N3 = 32,
|
||||||
|
N4 = 33,
|
||||||
|
N5 = 34,
|
||||||
|
N6 = 35,
|
||||||
|
N7 = 36,
|
||||||
|
N8 = 37,
|
||||||
|
N9 = 38,
|
||||||
|
N0 = 39,
|
||||||
|
|
||||||
|
RETURN = 40,
|
||||||
|
ESCAPE = 41,
|
||||||
|
BACKSPACE = 42,
|
||||||
|
TAB = 43,
|
||||||
|
SPACE = 44,
|
||||||
|
|
||||||
|
MINUS = 45,
|
||||||
|
EQUALS = 46,
|
||||||
|
LEFTBRACKET = 47,
|
||||||
|
RIGHTBRACKET = 48,
|
||||||
|
BACKSLASH = 49, /**< Located at the lower left of the return
|
||||||
|
* key on ISO keyboards and at the right end
|
||||||
|
* of the QWERTY row on ANSI keyboards.
|
||||||
|
* Produces REVERSE SOLIDUS (backslash) and
|
||||||
|
* VERTICAL LINE in a US layout, REVERSE
|
||||||
|
* SOLIDUS and VERTICAL LINE in a UK Mac
|
||||||
|
* layout, NUMBER SIGN and TILDE in a UK
|
||||||
|
* Windows layout, DOLLAR SIGN and POUND SIGN
|
||||||
|
* in a Swiss German layout, NUMBER SIGN and
|
||||||
|
* APOSTROPHE in a German layout, GRAVE
|
||||||
|
* ACCENT and POUND SIGN in a French Mac
|
||||||
|
* layout, and ASTERISK and MICRO SIGN in a
|
||||||
|
* French Windows layout.
|
||||||
|
*/
|
||||||
|
NONUSHASH = 50, /**< ISO USB keyboards actually use this code
|
||||||
|
* instead of 49 for the same key, but all
|
||||||
|
* OSes I've seen treat the two codes
|
||||||
|
* identically. So, as an implementor, unless
|
||||||
|
* your keyboard generates both of those
|
||||||
|
* codes and your OS treats them differently,
|
||||||
|
* you should generate BACKSLASH
|
||||||
|
* instead of this code. As a user, you
|
||||||
|
* should not rely on this code because SDL
|
||||||
|
* will never generate it with most (all?)
|
||||||
|
* keyboards.
|
||||||
|
*/
|
||||||
|
SEMICOLON = 51,
|
||||||
|
APOSTROPHE = 52,
|
||||||
|
GRAVE = 53, /**< Located in the top left corner (on both ANSI
|
||||||
|
* and ISO keyboards). Produces GRAVE ACCENT and
|
||||||
|
* TILDE in a US Windows layout and in US and UK
|
||||||
|
* Mac layouts on ANSI keyboards, GRAVE ACCENT
|
||||||
|
* and NOT SIGN in a UK Windows layout, SECTION
|
||||||
|
* SIGN and PLUS-MINUS SIGN in US and UK Mac
|
||||||
|
* layouts on ISO keyboards, SECTION SIGN and
|
||||||
|
* DEGREE SIGN in a Swiss German layout (Mac:
|
||||||
|
* only on ISO keyboards), CIRCUMFLEX ACCENT and
|
||||||
|
* DEGREE SIGN in a German layout (Mac: only on
|
||||||
|
* ISO keyboards), SUPERSCRIPT TWO and TILDE in a
|
||||||
|
* French Windows layout, COMMERCIAL AT and
|
||||||
|
* NUMBER SIGN in a French Mac layout on ISO
|
||||||
|
* keyboards, and LESS-THAN SIGN and GREATER-THAN
|
||||||
|
* SIGN in a Swiss German, German, or French Mac
|
||||||
|
* layout on ANSI keyboards.
|
||||||
|
*/
|
||||||
|
COMMA = 54,
|
||||||
|
PERIOD = 55,
|
||||||
|
SLASH = 56,
|
||||||
|
|
||||||
|
CAPSLOCK = 57,
|
||||||
|
|
||||||
|
F1 = 58,
|
||||||
|
F2 = 59,
|
||||||
|
F3 = 60,
|
||||||
|
F4 = 61,
|
||||||
|
F5 = 62,
|
||||||
|
F6 = 63,
|
||||||
|
F7 = 64,
|
||||||
|
F8 = 65,
|
||||||
|
F9 = 66,
|
||||||
|
F10 = 67,
|
||||||
|
F11 = 68,
|
||||||
|
F12 = 69,
|
||||||
|
|
||||||
|
PRINTSCREEN = 70,
|
||||||
|
SCROLLLOCK = 71,
|
||||||
|
PAUSE = 72,
|
||||||
|
INSERT = 73, /**< insert on PC, help on some Mac keyboards (but
|
||||||
|
does send code 73, not 117) */
|
||||||
|
HOME = 74,
|
||||||
|
PAGEUP = 75,
|
||||||
|
DELETE = 76,
|
||||||
|
END = 77,
|
||||||
|
PAGEDOWN = 78,
|
||||||
|
RIGHT = 79,
|
||||||
|
LEFT = 80,
|
||||||
|
DOWN = 81,
|
||||||
|
UP = 82,
|
||||||
|
|
||||||
|
NUMLOCKCLEAR = 83, /**< num lock on PC, clear on Mac keyboards
|
||||||
|
*/
|
||||||
|
KP_DIVIDE = 84,
|
||||||
|
KP_MULTIPLY = 85,
|
||||||
|
KP_MINUS = 86,
|
||||||
|
KP_PLUS = 87,
|
||||||
|
KP_ENTER = 88,
|
||||||
|
KP_1 = 89,
|
||||||
|
KP_2 = 90,
|
||||||
|
KP_3 = 91,
|
||||||
|
KP_4 = 92,
|
||||||
|
KP_5 = 93,
|
||||||
|
KP_6 = 94,
|
||||||
|
KP_7 = 95,
|
||||||
|
KP_8 = 96,
|
||||||
|
KP_9 = 97,
|
||||||
|
KP_0 = 98,
|
||||||
|
KP_PERIOD = 99,
|
||||||
|
|
||||||
|
NONUSBACKSLASH = 100, /**< This is the additional key that ISO
|
||||||
|
* keyboards have over ANSI ones,
|
||||||
|
* located between left shift and Y.
|
||||||
|
* Produces GRAVE ACCENT and TILDE in a
|
||||||
|
* US or UK Mac layout, REVERSE SOLIDUS
|
||||||
|
* (backslash) and VERTICAL LINE in a
|
||||||
|
* US or UK Windows layout, and
|
||||||
|
* LESS-THAN SIGN and GREATER-THAN SIGN
|
||||||
|
* in a Swiss German, German, or French
|
||||||
|
* layout. */
|
||||||
|
APPLICATION = 101, /**< windows contextual menu, compose */
|
||||||
|
POWER = 102, /**< The USB document says this is a status flag,
|
||||||
|
* not a physical key - but some Mac keyboards
|
||||||
|
* do have a power key. */
|
||||||
|
KP_EQUALS = 103,
|
||||||
|
F13 = 104,
|
||||||
|
F14 = 105,
|
||||||
|
F15 = 106,
|
||||||
|
F16 = 107,
|
||||||
|
F17 = 108,
|
||||||
|
F18 = 109,
|
||||||
|
F19 = 110,
|
||||||
|
F20 = 111,
|
||||||
|
F21 = 112,
|
||||||
|
F22 = 113,
|
||||||
|
F23 = 114,
|
||||||
|
F24 = 115,
|
||||||
|
EXECUTE = 116,
|
||||||
|
HELP = 117,
|
||||||
|
MENU = 118,
|
||||||
|
SELECT = 119,
|
||||||
|
STOP = 120,
|
||||||
|
AGAIN = 121, /**< redo */
|
||||||
|
UNDO = 122,
|
||||||
|
CUT = 123,
|
||||||
|
COPY = 124,
|
||||||
|
PASTE = 125,
|
||||||
|
FIND = 126,
|
||||||
|
MUTE = 127,
|
||||||
|
VOLUMEUP = 128,
|
||||||
|
VOLUMEDOWN = 129,
|
||||||
|
/* not sure whether there's a reason to enable these */
|
||||||
|
/* LOCKINGCAPSLOCK = 130, */
|
||||||
|
/* LOCKINGNUMLOCK = 131, */
|
||||||
|
/* LOCKINGSCROLLLOCK = 132, */
|
||||||
|
KP_COMMA = 133,
|
||||||
|
KP_EQUALSAS400 = 134,
|
||||||
|
|
||||||
|
INTERNATIONAL1 = 135, /**< used on Asian keyboards, see
|
||||||
|
footnotes in USB doc */
|
||||||
|
INTERNATIONAL2 = 136,
|
||||||
|
INTERNATIONAL3 = 137, /**< Yen */
|
||||||
|
INTERNATIONAL4 = 138,
|
||||||
|
INTERNATIONAL5 = 139,
|
||||||
|
INTERNATIONAL6 = 140,
|
||||||
|
INTERNATIONAL7 = 141,
|
||||||
|
INTERNATIONAL8 = 142,
|
||||||
|
INTERNATIONAL9 = 143,
|
||||||
|
LANG1 = 144, /**< Hangul/English toggle */
|
||||||
|
LANG2 = 145, /**< Hanja conversion */
|
||||||
|
LANG3 = 146, /**< Katakana */
|
||||||
|
LANG4 = 147, /**< Hiragana */
|
||||||
|
LANG5 = 148, /**< Zenkaku/Hankaku */
|
||||||
|
LANG6 = 149, /**< reserved */
|
||||||
|
LANG7 = 150, /**< reserved */
|
||||||
|
LANG8 = 151, /**< reserved */
|
||||||
|
LANG9 = 152, /**< reserved */
|
||||||
|
|
||||||
|
ALTERASE = 153, /**< Erase-Eaze */
|
||||||
|
SYSREQ = 154,
|
||||||
|
CANCEL = 155,
|
||||||
|
CLEAR = 156,
|
||||||
|
PRIOR = 157,
|
||||||
|
RETURN2 = 158,
|
||||||
|
SEPARATOR = 159,
|
||||||
|
OUT = 160,
|
||||||
|
OPER = 161,
|
||||||
|
CLEARAGAIN = 162,
|
||||||
|
CRSEL = 163,
|
||||||
|
EXSEL = 164,
|
||||||
|
|
||||||
|
KP_00 = 176,
|
||||||
|
KP_000 = 177,
|
||||||
|
THOUSANDSSEPARATOR = 178,
|
||||||
|
DECIMALSEPARATOR = 179,
|
||||||
|
CURRENCYUNIT = 180,
|
||||||
|
CURRENCYSUBUNIT = 181,
|
||||||
|
KP_LEFTPAREN = 182,
|
||||||
|
KP_RIGHTPAREN = 183,
|
||||||
|
KP_LEFTBRACE = 184,
|
||||||
|
KP_RIGHTBRACE = 185,
|
||||||
|
KP_TAB = 186,
|
||||||
|
KP_BACKSPACE = 187,
|
||||||
|
KP_A = 188,
|
||||||
|
KP_B = 189,
|
||||||
|
KP_C = 190,
|
||||||
|
KP_D = 191,
|
||||||
|
KP_E = 192,
|
||||||
|
KP_F = 193,
|
||||||
|
KP_XOR = 194,
|
||||||
|
KP_POWER = 195,
|
||||||
|
KP_PERCENT = 196,
|
||||||
|
KP_LESS = 197,
|
||||||
|
KP_GREATER = 198,
|
||||||
|
KP_AMPERSAND = 199,
|
||||||
|
KP_DBLAMPERSAND = 200,
|
||||||
|
KP_VERTICALBAR = 201,
|
||||||
|
KP_DBLVERTICALBAR = 202,
|
||||||
|
KP_COLON = 203,
|
||||||
|
KP_HASH = 204,
|
||||||
|
KP_SPACE = 205,
|
||||||
|
KP_AT = 206,
|
||||||
|
KP_EXCLAM = 207,
|
||||||
|
KP_MEMSTORE = 208,
|
||||||
|
KP_MEMRECALL = 209,
|
||||||
|
KP_MEMCLEAR = 210,
|
||||||
|
KP_MEMADD = 211,
|
||||||
|
KP_MEMSUBTRACT = 212,
|
||||||
|
KP_MEMMULTIPLY = 213,
|
||||||
|
KP_MEMDIVIDE = 214,
|
||||||
|
KP_PLUSMINUS = 215,
|
||||||
|
KP_CLEAR = 216,
|
||||||
|
KP_CLEARENTRY = 217,
|
||||||
|
KP_BINARY = 218,
|
||||||
|
KP_OCTAL = 219,
|
||||||
|
KP_DECIMAL = 220,
|
||||||
|
KP_HEXADECIMAL = 221,
|
||||||
|
|
||||||
|
LCTRL = 224,
|
||||||
|
LSHIFT = 225,
|
||||||
|
LALT = 226, /**< alt, option */
|
||||||
|
LGUI = 227, /**< windows, command (apple), meta */
|
||||||
|
RCTRL = 228,
|
||||||
|
RSHIFT = 229,
|
||||||
|
RALT = 230, /**< alt gr, option */
|
||||||
|
RGUI = 231, /**< windows, command (apple), meta */
|
||||||
|
|
||||||
|
MODE = 257, /**< I'm not sure if this is really not covered
|
||||||
|
* by any of the above, but since there's a
|
||||||
|
* special KMOD_MODE for it I'm adding it here
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* @} *//* Usage page 0x07 */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \name Usage page 0x0C
|
||||||
|
*
|
||||||
|
* These values are mapped from usage page 0x0C (USB consumer page).
|
||||||
|
*/
|
||||||
|
/* @{ */
|
||||||
|
|
||||||
|
AUDIONEXT = 258,
|
||||||
|
AUDIOPREV = 259,
|
||||||
|
AUDIOSTOP = 260,
|
||||||
|
AUDIOPLAY = 261,
|
||||||
|
AUDIOMUTE = 262,
|
||||||
|
MEDIASELECT = 263,
|
||||||
|
WWW = 264,
|
||||||
|
MAIL = 265,
|
||||||
|
CALCULATOR = 266,
|
||||||
|
COMPUTER = 267,
|
||||||
|
AC_SEARCH = 268,
|
||||||
|
AC_HOME = 269,
|
||||||
|
AC_BACK = 270,
|
||||||
|
AC_FORWARD = 271,
|
||||||
|
AC_STOP = 272,
|
||||||
|
AC_REFRESH = 273,
|
||||||
|
AC_BOOKMARKS = 274,
|
||||||
|
|
||||||
|
/* @} *//* Usage page 0x0C */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \name Walther keys
|
||||||
|
*
|
||||||
|
* These are values that Christian Walther added (for mac keyboard?).
|
||||||
|
*/
|
||||||
|
/* @{ */
|
||||||
|
|
||||||
|
BRIGHTNESSDOWN = 275,
|
||||||
|
BRIGHTNESSUP = 276,
|
||||||
|
DISPLAYSWITCH = 277, /**< display mirroring/dual display
|
||||||
|
switch, video mode switch */
|
||||||
|
KBDILLUMTOGGLE = 278,
|
||||||
|
KBDILLUMDOWN = 279,
|
||||||
|
KBDILLUMUP = 280,
|
||||||
|
EJECT = 281,
|
||||||
|
SLEEP = 282,
|
||||||
|
|
||||||
|
APP1 = 283,
|
||||||
|
APP2 = 284,
|
||||||
|
|
||||||
|
/* @} *//* Walther keys */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \name Usage page 0x0C (additional media keys)
|
||||||
|
*
|
||||||
|
* These values are mapped from usage page 0x0C (USB consumer page).
|
||||||
|
*/
|
||||||
|
/* @{ */
|
||||||
|
|
||||||
|
AUDIOREWIND = 285,
|
||||||
|
AUDIOFASTFORWARD = 286,
|
||||||
|
|
||||||
|
/* @} *//* Usage page 0x0C (additional media keys) */
|
||||||
|
|
||||||
|
/* Add any other keys here. */
|
||||||
|
|
||||||
|
NUM_SCANCODES = 512 /**< not a key, just marks the number of scancodes
|
||||||
|
for array bounds */
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
22
include/inputs/mouse.hpp
Normal file
22
include/inputs/mouse.hpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace inputs {
|
||||||
|
|
||||||
|
enum class MouseButton : int {
|
||||||
|
M_LEFT,
|
||||||
|
M_MIDDLE,
|
||||||
|
M_RIGHT,
|
||||||
|
M_X1,
|
||||||
|
M_X2,
|
||||||
|
M_INVALID=7,
|
||||||
|
M_SIZE=7
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class MouseAxis : int {
|
||||||
|
X,
|
||||||
|
Y,
|
||||||
|
X_SCR,
|
||||||
|
Y_SCR
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
16
include/log.hpp
Normal file
16
include/log.hpp
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef NDEBUG
|
||||||
|
#define SPDLOG_ACTIVE_LEVEL 2 // info
|
||||||
|
#else
|
||||||
|
#define SPDLOG_ACTIVE_LEVEL 0 // trace
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <spdlog/spdlog.h>
|
||||||
|
|
||||||
|
#define TRACE SPDLOG_TRACE
|
||||||
|
#define DEBUG SPDLOG_DEBUG
|
||||||
|
#define INFO SPDLOG_INFO
|
||||||
|
#define WARN SPDLOG_WARN
|
||||||
|
#define ERROR SPDLOG_ERROR
|
||||||
|
#define CRITICAL SPDLOG_CRITICAL
|
136
include/object.hpp
Normal file
136
include/object.hpp
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <glm/mat4x4.hpp>
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <memory>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
class Window;
|
||||||
|
class Input;
|
||||||
|
class ResourceManager;
|
||||||
|
|
||||||
|
class SceneRoot;
|
||||||
|
class Component;
|
||||||
|
|
||||||
|
namespace components {
|
||||||
|
class Transform;
|
||||||
|
class Camera;
|
||||||
|
class Renderer;
|
||||||
|
class UI;
|
||||||
|
class CustomComponent;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GameIO {
|
||||||
|
Window * const win;
|
||||||
|
Input * const input;
|
||||||
|
ResourceManager * const 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 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);
|
||||||
|
|
||||||
|
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) {
|
||||||
|
T* derived = dynamic_cast<T*>(component.get());
|
||||||
|
if (derived != nullptr) {
|
||||||
|
return derived;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
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'");
|
||||||
|
}
|
||||||
|
if (std::is_same<T, components::Transform>::value) {
|
||||||
|
throw std::runtime_error("deleteComponent() error: attempt to remove the 'Transform' 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.");
|
||||||
|
}
|
79
include/resource_manager.hpp
Normal file
79
include/resource_manager.hpp
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "resources/resource.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
// Doesn't own resources, only holds weak_ptrs
|
||||||
|
|
||||||
|
class ResourceManager {
|
||||||
|
|
||||||
|
public:
|
||||||
|
ResourceManager();
|
||||||
|
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>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
return castedSharedPtr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Resource in map but no longer exists. Delete it.
|
||||||
|
m_resources.erase(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return create<T>(name);
|
||||||
|
|
||||||
|
}
|
33
include/resources/font.hpp
Normal file
33
include/resources/font.hpp
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "resource.hpp"
|
||||||
|
|
||||||
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
#include <glm/vec2.hpp>
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace resources {
|
||||||
|
|
||||||
|
class Font : public Resource {
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
struct Character {
|
||||||
|
unsigned int textureID; // openGL texture handle
|
||||||
|
glm::ivec2 size;
|
||||||
|
glm::ivec2 bearing; // offset from baseline to top-left of glyph
|
||||||
|
long advance; // offset to the next glyph
|
||||||
|
};
|
||||||
|
|
||||||
|
Font(const std::filesystem::path& resPath);
|
||||||
|
~Font() override;
|
||||||
|
Character getChar(char c);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::map<char, Character> m_characters;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
54
include/resources/mesh.hpp
Normal file
54
include/resources/mesh.hpp
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "resource.hpp"
|
||||||
|
|
||||||
|
#include "resources/shader.hpp"
|
||||||
|
|
||||||
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
#include <glm/vec3.hpp>
|
||||||
|
#include <glm/vec2.hpp>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
struct Vertex {
|
||||||
|
glm::vec3 pos;
|
||||||
|
glm::vec3 norm;
|
||||||
|
glm::vec2 uv;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace resources {
|
||||||
|
|
||||||
|
class Mesh : public Resource {
|
||||||
|
|
||||||
|
public:
|
||||||
|
Mesh(const std::vector<Vertex>& vertices);
|
||||||
|
Mesh(const std::vector<Vertex>& vertices, const std::vector<unsigned int>& indices);
|
||||||
|
Mesh(const std::filesystem::path& resPath);
|
||||||
|
~Mesh() override;
|
||||||
|
|
||||||
|
void drawMesh(const Shader& shader);
|
||||||
|
|
||||||
|
static void invalidate()
|
||||||
|
{
|
||||||
|
s_active_vao = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Vertex> m_vertices;
|
||||||
|
std::vector<unsigned int> m_indices;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static int s_active_vao;
|
||||||
|
|
||||||
|
GLuint m_vao;
|
||||||
|
GLuint m_vbo;
|
||||||
|
GLuint m_ebo;
|
||||||
|
|
||||||
|
void bindVAO() const;
|
||||||
|
|
||||||
|
void initMesh();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
22
include/resources/resource.hpp
Normal file
22
include/resources/resource.hpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
class Resource {
|
||||||
|
|
||||||
|
public:
|
||||||
|
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();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::filesystem::path m_resourcePath;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::string m_type;
|
||||||
|
|
||||||
|
};
|
75
include/resources/shader.hpp
Normal file
75
include/resources/shader.hpp
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "resource.hpp"
|
||||||
|
|
||||||
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
#include <glm/mat4x4.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
namespace resources {
|
||||||
|
|
||||||
|
class Shader : public Resource {
|
||||||
|
|
||||||
|
public:
|
||||||
|
Shader(const std::filesystem::path& resPath);
|
||||||
|
~Shader() override;
|
||||||
|
|
||||||
|
enum class UniformType {
|
||||||
|
FLOAT_MAT4 = GL_FLOAT_MAT4,
|
||||||
|
FLOAT_VEC2 = GL_FLOAT_VEC2,
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
27
include/resources/texture.hpp
Normal file
27
include/resources/texture.hpp
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "resource.hpp"
|
||||||
|
|
||||||
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
namespace resources {
|
||||||
|
|
||||||
|
class Texture : public Resource {
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
static GLuint s_binded_texture;
|
||||||
|
|
||||||
|
GLuint m_texture;
|
||||||
|
|
||||||
|
|
||||||
|
public:
|
||||||
|
Texture(const std::filesystem::path& resPath);
|
||||||
|
~Texture() override;
|
||||||
|
|
||||||
|
void bindTexture() const;
|
||||||
|
|
||||||
|
static void invalidate();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
27
include/sceneroot.hpp
Normal file
27
include/sceneroot.hpp
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "object.hpp"
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
|
// Holds everything you would expect to find in a game scene
|
||||||
|
class SceneRoot : public Object {
|
||||||
|
|
||||||
|
public:
|
||||||
|
// create a new empty scene
|
||||||
|
SceneRoot(struct GameIO things);
|
||||||
|
SceneRoot(const std::filesystem::path& file, struct GameIO things);
|
||||||
|
|
||||||
|
SceneRoot(const SceneRoot&) = delete;
|
||||||
|
SceneRoot& operator=(const SceneRoot&) = delete;
|
||||||
|
~SceneRoot();
|
||||||
|
|
||||||
|
void updateStuff();
|
||||||
|
|
||||||
|
void activateCam(int id);
|
||||||
|
void deactivateCam(int id);
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::vector<int> m_activeCameras{};
|
||||||
|
|
||||||
|
};
|
213
include/window.hpp
Normal file
213
include/window.hpp
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "inputs/keyboard.hpp"
|
||||||
|
#include "inputs/mouse.hpp"
|
||||||
|
|
||||||
|
#pragma warning (push, 0)
|
||||||
|
#include <SDL.h>
|
||||||
|
#pragma warning (pop)
|
||||||
|
|
||||||
|
#include <glm/vec2.hpp>
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
extern const uint64_t BILLION;
|
||||||
|
|
||||||
|
class Window {
|
||||||
|
|
||||||
|
public:
|
||||||
|
Window(const std::string& title);
|
||||||
|
Window(const Window&) = delete;
|
||||||
|
Window& operator=(const Window&) = delete;
|
||||||
|
~Window();
|
||||||
|
|
||||||
|
// Return the title name
|
||||||
|
std::string getTitle() const;
|
||||||
|
|
||||||
|
// Make this window the current OpenGL context.
|
||||||
|
// This is already done in window initialisation.
|
||||||
|
void makeContextCurrent();
|
||||||
|
|
||||||
|
// Tell the GPU to render the back buffer to the screen.
|
||||||
|
// Run this on every frame.
|
||||||
|
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
|
||||||
|
// monitor's refresh rate.
|
||||||
|
void setVSync(bool enable);
|
||||||
|
// Returns true if VSync is enabled.
|
||||||
|
bool getVSync() const;
|
||||||
|
|
||||||
|
glm::ivec2 getViewportSize();
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Sets the close flag, check this with shouldClose()
|
||||||
|
void setCloseFlag();
|
||||||
|
// Returns true if the window should remain open
|
||||||
|
bool isRunning() const;
|
||||||
|
|
||||||
|
void setFullscreen(bool fullscreen, bool exclusive=true);
|
||||||
|
void toggleFullscreen();
|
||||||
|
|
||||||
|
bool isFullscreen() const;
|
||||||
|
|
||||||
|
// Relative mouse mode captures the cursor for FPS style use. Returns false if unsupported.
|
||||||
|
bool setRelativeMouseMode(bool enabled);
|
||||||
|
|
||||||
|
// returns true if relative mouse mode is enabled
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
// retrieves x coordinate of the mouse
|
||||||
|
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;
|
||||||
|
uint64_t getNanos() const;
|
||||||
|
// get the time recorded at the end of the last frame
|
||||||
|
uint64_t getLastFrameStamp() const;
|
||||||
|
|
||||||
|
// returns the number of frames elapsed since window creation
|
||||||
|
uint64_t getFrameCount() const;
|
||||||
|
uint64_t getStartTime() const;;
|
||||||
|
float dt() const; // returns delta time in seconds
|
||||||
|
uint64_t getFPS() const;
|
||||||
|
uint64_t getAvgFPS() const;
|
||||||
|
|
||||||
|
void resetAvgFPS();
|
||||||
|
|
||||||
|
bool infoBox(const std::string& title, const std::string& msg);
|
||||||
|
|
||||||
|
/* STATIC METHODS */
|
||||||
|
static void errorBox(const std::string& message);
|
||||||
|
|
||||||
|
private:
|
||||||
|
SDL_Window* m_handle;
|
||||||
|
SDL_GLContext m_glContext;
|
||||||
|
|
||||||
|
bool m_shouldClose = false;
|
||||||
|
|
||||||
|
std::string m_title;
|
||||||
|
|
||||||
|
bool m_fullscreen = false;
|
||||||
|
bool m_justResized = false;
|
||||||
|
bool m_keyboardFocus = true;
|
||||||
|
|
||||||
|
// size in screen coordinates
|
||||||
|
glm::ivec2 m_winSize = glm::vec2(640, 480);
|
||||||
|
// actual framebuffer size
|
||||||
|
glm::ivec2 m_fbSize;
|
||||||
|
|
||||||
|
// performance counter frequency
|
||||||
|
uint64_t m_counterFreq;
|
||||||
|
|
||||||
|
// number of frames swapped
|
||||||
|
uint64_t m_frames = 0;
|
||||||
|
// frame count offset for fpsAvg
|
||||||
|
uint64_t m_avgFpsStartCount = 0;
|
||||||
|
// in nanoseconds
|
||||||
|
uint64_t m_startTime;
|
||||||
|
// in nanoseconds
|
||||||
|
uint64_t m_lastFrameStamp;
|
||||||
|
// in nanoseconds; elapsed time between frames
|
||||||
|
uint64_t m_lastFrameTime = 1; // not 0 to avoid division by zero
|
||||||
|
// in nanoseconds
|
||||||
|
uint64_t m_avgFpsStart;
|
||||||
|
|
||||||
|
// input stuff
|
||||||
|
|
||||||
|
enum class ButtonDelta {
|
||||||
|
SAME = 0,
|
||||||
|
PRESSED,
|
||||||
|
RELEASED
|
||||||
|
};
|
||||||
|
|
||||||
|
struct {
|
||||||
|
std::array<bool, SDL_NUM_SCANCODES> keys;
|
||||||
|
std::array<enum ButtonDelta, SDL_NUM_SCANCODES> deltas;
|
||||||
|
} m_keyboard{ };
|
||||||
|
|
||||||
|
struct {
|
||||||
|
std::array<bool, static_cast<int>(inputs::MouseButton::M_SIZE)> buttons;
|
||||||
|
std::array<enum ButtonDelta, 8> deltas;
|
||||||
|
Sint32 x;
|
||||||
|
Sint32 y;
|
||||||
|
Sint32 dx;
|
||||||
|
Sint32 dy;
|
||||||
|
float xscroll;
|
||||||
|
float yscroll;
|
||||||
|
bool captured = false;
|
||||||
|
} m_mouse{ };
|
||||||
|
|
||||||
|
// private methods
|
||||||
|
|
||||||
|
void onResize(Sint32 width, Sint32 height);
|
||||||
|
void resetInputDeltas();
|
||||||
|
|
||||||
|
// event methods (like callbacks)
|
||||||
|
|
||||||
|
void onWindowEvent(SDL_WindowEvent& e);
|
||||||
|
void onKeyEvent(SDL_KeyboardEvent& e);
|
||||||
|
void onMouseButtonEvent(SDL_MouseButtonEvent& e);
|
||||||
|
void onMouseMotionEvent(SDL_MouseMotionEvent& e);
|
||||||
|
void onMouseWheelEvent(SDL_MouseWheelEvent& e);
|
||||||
|
|
||||||
|
};
|
101
src/components/camera.cpp
Normal file
101
src/components/camera.cpp
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
#include "components/camera.hpp"
|
||||||
|
|
||||||
|
#include "resource_manager.hpp"
|
||||||
|
#include "resources/shader.hpp"
|
||||||
|
|
||||||
|
#include "sceneroot.hpp"
|
||||||
|
|
||||||
|
#include "window.hpp"
|
||||||
|
|
||||||
|
#include "log.hpp"
|
||||||
|
|
||||||
|
static const std::string VIEW_MAT_UNIFORM = "viewMat";
|
||||||
|
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};
|
||||||
|
|
||||||
|
Camera::Camera(Object* parent) : Component(parent, TypeEnum::CAMERA)
|
||||||
|
{
|
||||||
|
parent->root.activateCam(getID());
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
|
glDisable(GL_STENCIL_TEST);
|
||||||
|
|
||||||
|
glEnable(GL_CULL_FACE);
|
||||||
|
}
|
||||||
|
|
||||||
|
Camera::~Camera()
|
||||||
|
{
|
||||||
|
parent.root.deactivateCam(getID());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::updateCam(glm::mat4 transform)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (parent.win.getWindowResized()) {
|
||||||
|
if (m_mode == Modes::PERSPECTIVE) {
|
||||||
|
usePerspective(m_fovDeg);
|
||||||
|
} else {
|
||||||
|
useOrtho();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::mat4 viewMatrix = glm::inverse(transform);
|
||||||
|
|
||||||
|
using namespace resources;
|
||||||
|
|
||||||
|
auto resPtrs = parent.res.getAllResourcesOfType("shader");
|
||||||
|
for (const auto& resPtr : resPtrs) {
|
||||||
|
// shader ref count increased by 1, but only in this scope
|
||||||
|
auto lockedPtr = resPtr.lock();
|
||||||
|
auto shader = dynamic_cast<Shader*>(lockedPtr.get());
|
||||||
|
shader->setUniform_m4(VIEW_MAT_UNIFORM, viewMatrix);
|
||||||
|
shader->setUniform_m4(PROJ_MAT_UNIFORM, m_projMatrix);
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
GLint64 viewportParams[4];
|
||||||
|
glGetInteger64v(GL_VIEWPORT, viewportParams);
|
||||||
|
return { viewportParams[2], viewportParams[3] };
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::usePerspective(float fovDeg)
|
||||||
|
{
|
||||||
|
constexpr float NEAR = 0.1f;
|
||||||
|
constexpr float FAR = 1000.0f;
|
||||||
|
|
||||||
|
m_mode = Modes::PERSPECTIVE;
|
||||||
|
m_fovDeg = fovDeg;
|
||||||
|
|
||||||
|
glm::vec2 viewportDim = getViewportSize();
|
||||||
|
|
||||||
|
float fovRad = glm::radians(fovDeg);
|
||||||
|
m_projMatrix = glm::perspectiveFov(fovRad, viewportDim.x, viewportDim.y, NEAR, FAR);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Camera::useOrtho()
|
||||||
|
{
|
||||||
|
m_mode = Modes::ORTHOGRAPHIC;
|
||||||
|
|
||||||
|
glm::vec2 viewportDim = getViewportSize();
|
||||||
|
float aspect = viewportDim.x / viewportDim.y;
|
||||||
|
|
||||||
|
m_projMatrix = glm::ortho(-10.0f * aspect, 10.0f * aspect, -10.0f, 10.0f, -100.0f, 100.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
30
src/components/component.cpp
Normal file
30
src/components/component.cpp
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
#include "components/component.hpp"
|
||||||
|
|
||||||
|
#include "object.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
int Component::s_next_component_id = 0;
|
||||||
|
|
||||||
|
Component::Component(Object* parent, TypeEnum type) :
|
||||||
|
m_type(type), parent(*parent),
|
||||||
|
win(parent->win),
|
||||||
|
inp(parent->inp),
|
||||||
|
res(parent->res)
|
||||||
|
{
|
||||||
|
s_next_component_id++;
|
||||||
|
}
|
||||||
|
|
||||||
|
Component::~Component()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int Component::getID()
|
||||||
|
{
|
||||||
|
return m_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
Component::TypeEnum Component::getType()
|
||||||
|
{
|
||||||
|
return m_type;
|
||||||
|
}
|
24
src/components/custom.cpp
Normal file
24
src/components/custom.cpp
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#include "components/custom.hpp"
|
||||||
|
|
||||||
|
namespace components {
|
||||||
|
|
||||||
|
CustomComponent::CustomComponent(Object* parent) : Component(parent, TypeEnum::CUSTOM)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
CustomComponent::~CustomComponent()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CustomComponent::onInit()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void CustomComponent::onUpdate(glm::mat4 t)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
50
src/components/mesh_renderer.cpp
Normal file
50
src/components/mesh_renderer.cpp
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#include "components/mesh_renderer.hpp"
|
||||||
|
|
||||||
|
#include "object.hpp"
|
||||||
|
|
||||||
|
#include "resource_manager.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace components {
|
||||||
|
|
||||||
|
Renderer::Renderer(Object* parent) : Component(parent, TypeEnum::RENDERER)
|
||||||
|
{
|
||||||
|
m_shader = this->parent.res.get<resources::Shader>("shaders/basic.glsl");
|
||||||
|
m_texture = this->parent.res.get<resources::Texture>("textures/missing.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
Renderer::~Renderer()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::render(glm::mat4 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)
|
||||||
|
{
|
||||||
|
m_mesh = parent.res.get<resources::Mesh>(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::setTexture(const std::string& name)
|
||||||
|
{
|
||||||
|
m_texture = parent.res.get<resources::Texture>(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
81
src/components/text_ui_renderer.cpp
Normal file
81
src/components/text_ui_renderer.cpp
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
#include "components/text_ui_renderer.hpp"
|
||||||
|
|
||||||
|
#include "object.hpp"
|
||||||
|
#include "resource_manager.hpp"
|
||||||
|
#include "resources/texture.hpp"
|
||||||
|
|
||||||
|
namespace components {
|
||||||
|
|
||||||
|
UI::UI(Object* parent) : Component(parent, TypeEnum::UI)
|
||||||
|
{
|
||||||
|
const std::string FONTFILE{ "fonts/LiberationMono-Regular.ttf" };
|
||||||
|
m_font = parent->res.get<resources::Font>(FONTFILE);
|
||||||
|
m_shader = parent->res.get<resources::Shader>("shaders/font.glsl");
|
||||||
|
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
}
|
||||||
|
|
||||||
|
UI::~UI()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void UI::render(glm::mat4 transform)
|
||||||
|
{
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
m_shader->setUniform_m4("modelMat", transform);
|
||||||
|
|
||||||
|
m_shader->setUniform_v3("textColor", m_color);
|
||||||
|
m_shader->setUniform_i("textScaling", (int)m_scaled);
|
||||||
|
|
||||||
|
std::vector<resources::Font::Character> glyphs;
|
||||||
|
for (char c : m_text) {
|
||||||
|
glyphs.push_back(m_font->getChar(c));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr float scale = 0.002f;
|
||||||
|
float x = 0.0f;
|
||||||
|
|
||||||
|
if (m_alignment == Alignment::RIGHT) {
|
||||||
|
|
||||||
|
// first find the length of the text
|
||||||
|
float len = 0.0f;
|
||||||
|
for (const auto& glyph : glyphs) {
|
||||||
|
len += (glyph.advance >> 6) * scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
x = -len;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& glyph : glyphs) {
|
||||||
|
|
||||||
|
float xpos = x + glyph.bearing.x * scale;
|
||||||
|
float ypos = (glyph.bearing.y - glyph.size.y) * scale;
|
||||||
|
|
||||||
|
float w = glyph.size.x * scale;
|
||||||
|
float h = glyph.size.y * scale;
|
||||||
|
|
||||||
|
resources::Mesh mesh({
|
||||||
|
{{xpos, ypos + h, 0.0f}, {}, {0.0f, 0.0f}},
|
||||||
|
{{xpos, ypos , 0.0f}, {}, {0.0f, 1.0f}},
|
||||||
|
{{xpos + w, ypos, 0.0f}, {}, {1.0f, 1.0f}},
|
||||||
|
{{xpos, ypos + h, 0.0f}, {}, {0.0f, 0.0f}},
|
||||||
|
{{xpos + w, ypos, 0.0f}, {}, {1.0f, 1.0f}},
|
||||||
|
{{xpos + w, ypos + h, 0.0f}, {}, {1.0f, 0.0f}},
|
||||||
|
});
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, glyph.textureID);
|
||||||
|
|
||||||
|
mesh.drawMesh(*m_shader);
|
||||||
|
|
||||||
|
x += (glyph.advance >> 6) * scale;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
resources::Texture::invalidate();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
16
src/components/transform.cpp
Normal file
16
src/components/transform.cpp
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#include "components/transform.hpp"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace components {
|
||||||
|
|
||||||
|
Transform::Transform(Object* parent) : Component(parent, TypeEnum::TRANSFORM)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Transform::~Transform()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
232
src/input.cpp
Normal file
232
src/input.cpp
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
#include "input.hpp"
|
||||||
|
|
||||||
|
#include "window.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
Input::Input(const Window &win) : m_win(win)
|
||||||
|
{
|
||||||
|
m_enabledDevices.fill(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
Input::~Input()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// private methods
|
||||||
|
|
||||||
|
float Input::getDeviceAxis(enum InputDevice device, int axis) const
|
||||||
|
{
|
||||||
|
switch (device) {
|
||||||
|
case InputDevice::MOUSE:
|
||||||
|
switch (static_cast<inputs::MouseAxis>(axis)) {
|
||||||
|
case inputs::MouseAxis::X:
|
||||||
|
return static_cast<float>(m_win.getMouseDX());
|
||||||
|
case inputs::MouseAxis::Y:
|
||||||
|
return static_cast<float>(m_win.getMouseDY());
|
||||||
|
case inputs::MouseAxis::X_SCR:
|
||||||
|
return m_win.getMouseScrollX();
|
||||||
|
case inputs::MouseAxis::Y_SCR:
|
||||||
|
return m_win.getMouseScrollY();
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case InputDevice::KEYBOARD:
|
||||||
|
break;
|
||||||
|
case InputDevice::CONTROLLER:
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Error getting device axis");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Input::getDeviceButton(enum InputDevice device, int button) const
|
||||||
|
{
|
||||||
|
switch (device) {
|
||||||
|
case InputDevice::MOUSE:
|
||||||
|
return m_win.getButton(static_cast<inputs::MouseButton>(button));
|
||||||
|
case InputDevice::KEYBOARD:
|
||||||
|
return m_win.getKey(static_cast<inputs::Key>(button));
|
||||||
|
case InputDevice::CONTROLLER:
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Error getting device button");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Input::getDeviceButtonDown(enum InputDevice device, int button) const
|
||||||
|
{
|
||||||
|
switch (device) {
|
||||||
|
case InputDevice::MOUSE:
|
||||||
|
return m_win.getButtonPress(static_cast<enum inputs::MouseButton>(button));
|
||||||
|
case InputDevice::KEYBOARD:
|
||||||
|
return m_win.getKeyPress(static_cast<enum inputs::Key>(button));
|
||||||
|
case InputDevice::CONTROLLER:
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Error getting device button");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Input::getDeviceButtonUp(enum InputDevice device, int button) const
|
||||||
|
{
|
||||||
|
switch (device) {
|
||||||
|
case InputDevice::MOUSE:
|
||||||
|
return m_win.getButtonRelease(static_cast<enum inputs::MouseButton>(button));
|
||||||
|
case InputDevice::KEYBOARD:
|
||||||
|
return m_win.getKeyRelease(static_cast<enum inputs::Key>(button));
|
||||||
|
case InputDevice::CONTROLLER:
|
||||||
|
break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
throw std::runtime_error("Error getting device button");
|
||||||
|
}
|
||||||
|
|
||||||
|
float Input::getButtonAxis(enum InputDevice device, int high, int low) const
|
||||||
|
{
|
||||||
|
float value = 0.0f;
|
||||||
|
if (getDeviceButton(device, high)) value += 1.0f;
|
||||||
|
if (low != 0) {
|
||||||
|
if (getDeviceButton(device, low)) value += -1.0f;
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// public methods
|
||||||
|
|
||||||
|
void Input::addInputButton(const std::string& name, InputDevice device, int button)
|
||||||
|
{
|
||||||
|
m_buttonEntries.push_back( { name, device, button } );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Input::addInputAxis(const std::string& name, InputDevice device, int axis)
|
||||||
|
{
|
||||||
|
m_axisEntries.push_back( { name, device, axis, false, 0, 0 } );
|
||||||
|
}
|
||||||
|
|
||||||
|
void Input::addInputButtonAsAxis(const std::string& name, InputDevice device, int high, int low)
|
||||||
|
{
|
||||||
|
m_axisEntries.push_back( { name, device, 0, true, high, low } );
|
||||||
|
}
|
||||||
|
|
||||||
|
// OVERLOADS:
|
||||||
|
|
||||||
|
// Add a mouse input
|
||||||
|
void Input::addInputButton(const std::string& name, inputs::MouseButton button)
|
||||||
|
{
|
||||||
|
addInputButton(name, InputDevice::MOUSE, static_cast<int>(button));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Input::addInputAxis(const std::string& name, inputs::MouseAxis axis)
|
||||||
|
{
|
||||||
|
addInputAxis(name, InputDevice::MOUSE, static_cast<int>(axis));
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a keyboard input (TODO: add KeyboardButton enum class)
|
||||||
|
void Input::addInputButton(const std::string& name, inputs::Key button)
|
||||||
|
{
|
||||||
|
addInputButton(name, InputDevice::KEYBOARD, static_cast<int>(button));
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Input::delInputButton(int index)
|
||||||
|
{
|
||||||
|
std::vector<struct ButtonEntry>::iterator it = m_buttonEntries.begin();
|
||||||
|
std::advance(it, index);
|
||||||
|
m_buttonEntries.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Input::delInputAxis(int index)
|
||||||
|
{
|
||||||
|
std::vector<struct AxisEntry>::iterator it = m_axisEntries.begin();
|
||||||
|
std::advance(it, index);
|
||||||
|
m_axisEntries.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Input::setDeviceActive(enum InputDevice device, bool active)
|
||||||
|
{
|
||||||
|
m_enabledDevices[static_cast<int>(device)] = active;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Input::getDeviceActive(enum InputDevice device) const
|
||||||
|
{
|
||||||
|
return m_enabledDevices[static_cast<int>(device)];
|
||||||
|
}
|
||||||
|
|
||||||
|
float Input::getAxis(const std::string& axisName) const
|
||||||
|
{
|
||||||
|
for (const AxisEntry& e : m_axisEntries) {
|
||||||
|
if (e.name == axisName) {
|
||||||
|
if (m_enabledDevices[static_cast<int>(e.device)]) {
|
||||||
|
if (e.isButtonAxis) {
|
||||||
|
return getButtonAxis(e.device, e.high, e.low);
|
||||||
|
} 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Input::getButton(const std::string& buttonName) const
|
||||||
|
{
|
||||||
|
bool isDown = false;
|
||||||
|
|
||||||
|
for (const ButtonEntry& e : m_buttonEntries) {
|
||||||
|
if (e.name == buttonName) {
|
||||||
|
if (m_enabledDevices[static_cast<int>(e.device)]) {
|
||||||
|
if (getDeviceButton(e.device, e.button) == true) {
|
||||||
|
isDown = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isDown;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Input::getButtonPress(const std::string& buttonName) const
|
||||||
|
{
|
||||||
|
bool isPressed = false;
|
||||||
|
|
||||||
|
for (const ButtonEntry& e : m_buttonEntries) {
|
||||||
|
if (e.name == buttonName) {
|
||||||
|
if (m_enabledDevices[static_cast<int>(e.device)]) {
|
||||||
|
if (getDeviceButtonDown(e.device, e.button) == true) {
|
||||||
|
isPressed = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isPressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Input::getButtonRelease(const std::string& buttonName) const
|
||||||
|
{
|
||||||
|
bool isReleased = false;
|
||||||
|
|
||||||
|
for (const ButtonEntry& e : m_buttonEntries) {
|
||||||
|
if (e.name == buttonName) {
|
||||||
|
if (m_enabledDevices[static_cast<int>(e.device)]) {
|
||||||
|
if (getDeviceButtonUp(e.device, e.button) == true) {
|
||||||
|
isReleased = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isReleased;
|
||||||
|
}
|
136
src/object.cpp
Normal file
136
src/object.cpp
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
#include "object.hpp"
|
||||||
|
|
||||||
|
#include "components/transform.hpp"
|
||||||
|
#include "components/camera.hpp"
|
||||||
|
#include "components/mesh_renderer.hpp"
|
||||||
|
#include "components/text_ui_renderer.hpp"
|
||||||
|
#include "components/custom.hpp"
|
||||||
|
|
||||||
|
#include <log.hpp>
|
||||||
|
|
||||||
|
int Object::s_object_count = 0;
|
||||||
|
|
||||||
|
Object::Object(std::string name, Object* parent, SceneRoot& root, struct GameIO things)
|
||||||
|
: m_name(name), m_parent(parent), root(root),
|
||||||
|
m_gameIO(things),
|
||||||
|
win(*things.win),
|
||||||
|
inp(*things.input),
|
||||||
|
res(*things.resMan)
|
||||||
|
{
|
||||||
|
s_object_count++;
|
||||||
|
// all objects come with at least a transform component
|
||||||
|
createComponent<components::Transform>();
|
||||||
|
}
|
||||||
|
|
||||||
|
Object::~Object()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Object::getName()
|
||||||
|
{
|
||||||
|
return m_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Object::getAllSubComponents(struct CompList& compList, glm::mat4 parentTransform)
|
||||||
|
{
|
||||||
|
using namespace components;
|
||||||
|
|
||||||
|
glm::mat4 objTransform{1.0f};
|
||||||
|
|
||||||
|
auto t = getComponent<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);
|
||||||
|
}
|
||||||
|
}
|
71
src/resource_manager.cpp
Normal file
71
src/resource_manager.cpp
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#include "resource_manager.hpp"
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <windows.h>
|
||||||
|
#define MAX_PATH 260
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ResourceManager::ResourceManager()
|
||||||
|
{
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
CHAR exeDirBuf[MAX_PATH + 1];
|
||||||
|
GetModuleFileNameA(NULL, exeDirBuf, MAX_PATH + 1);
|
||||||
|
std::filesystem::path cwd = std::filesystem::path(exeDirBuf).parent_path();
|
||||||
|
#else
|
||||||
|
std::filesystem::path cwd = std::filesystem::current_path();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (std::filesystem::is_directory(cwd / "res")) {
|
||||||
|
m_resourcesPath = std::filesystem::absolute("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");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
std::vector<std::weak_ptr<Resource>> ResourceManager::getAllResourcesOfType(const std::string& type)
|
||||||
|
{
|
||||||
|
std::vector<std::weak_ptr<Resource>> resources;
|
||||||
|
for (const auto& [name, ptr] : m_resources) {
|
||||||
|
if (ptr.expired() == false) {
|
||||||
|
if (ptr.lock()->getType() == type) {
|
||||||
|
resources.push_back(ptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resources;
|
||||||
|
}
|
||||||
|
|
||||||
|
// private
|
||||||
|
|
||||||
|
std::filesystem::path ResourceManager::getFilePath(const std::string& name)
|
||||||
|
{
|
||||||
|
return m_resourcesPath / name;
|
||||||
|
}
|
96
src/resources/font.cpp
Normal file
96
src/resources/font.cpp
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
#include "resources/font.hpp"
|
||||||
|
|
||||||
|
#include <ft2build.h>
|
||||||
|
#include FT_FREETYPE_H
|
||||||
|
|
||||||
|
namespace resources {
|
||||||
|
|
||||||
|
Font::Font(const std::filesystem::path& resPath) : Resource(resPath, "font")
|
||||||
|
{
|
||||||
|
|
||||||
|
FT_Library library;
|
||||||
|
FT_Face face;
|
||||||
|
int err;
|
||||||
|
|
||||||
|
err = FT_Init_FreeType(&library);
|
||||||
|
if (err) {
|
||||||
|
throw std::runtime_error("Failed to initialise freetype library");
|
||||||
|
}
|
||||||
|
|
||||||
|
err = FT_New_Face(library, resPath.string().c_str(), 0, &face);
|
||||||
|
if (err == FT_Err_Unknown_File_Format) {
|
||||||
|
FT_Done_FreeType(library);
|
||||||
|
throw std::runtime_error("Unknown file format for font '" + resPath.string() + "'");
|
||||||
|
}
|
||||||
|
else if (err != 0) {
|
||||||
|
FT_Done_FreeType(library);
|
||||||
|
throw std::runtime_error("Unable to open font '" + resPath.string() + "'");
|
||||||
|
}
|
||||||
|
|
||||||
|
err = FT_Set_Pixel_Sizes(face, 0, 64);
|
||||||
|
if (err) {
|
||||||
|
FT_Done_Face(face);
|
||||||
|
FT_Done_FreeType(library);
|
||||||
|
throw std::runtime_error("Attempt to set pixel size to one unavailable in the font");
|
||||||
|
}
|
||||||
|
|
||||||
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||||
|
|
||||||
|
for (unsigned char c = 0; c < 128; c++) {
|
||||||
|
|
||||||
|
err = FT_Load_Char(face, c, FT_LOAD_RENDER);
|
||||||
|
if (err) {
|
||||||
|
FT_Done_Face(face);
|
||||||
|
FT_Done_FreeType(library);
|
||||||
|
throw std::runtime_error("Unable to load char glyph");
|
||||||
|
}
|
||||||
|
|
||||||
|
// generate texture
|
||||||
|
unsigned int texture;
|
||||||
|
glGenTextures(1, &texture);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, texture);
|
||||||
|
glTexImage2D(
|
||||||
|
GL_TEXTURE_2D,
|
||||||
|
0,
|
||||||
|
GL_RED,
|
||||||
|
face->glyph->bitmap.width,
|
||||||
|
face->glyph->bitmap.rows,
|
||||||
|
0,
|
||||||
|
GL_RED,
|
||||||
|
GL_UNSIGNED_BYTE,
|
||||||
|
face->glyph->bitmap.buffer
|
||||||
|
);
|
||||||
|
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
|
||||||
|
Character character = {
|
||||||
|
texture,
|
||||||
|
glm::ivec2{face->glyph->bitmap.width, face->glyph->bitmap.rows}, // Size of Glyph
|
||||||
|
glm::ivec2{face->glyph->bitmap_left, face->glyph->bitmap_top}, // Offset from baseline (bottom-left) to top-left of glyph
|
||||||
|
face->glyph->advance.x
|
||||||
|
};
|
||||||
|
|
||||||
|
m_characters.insert(std::make_pair(c, character));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // reset alignment settings
|
||||||
|
|
||||||
|
FT_Done_Face(face);
|
||||||
|
FT_Done_FreeType(library);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Font::~Font()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Font::Character Font::getChar(char c)
|
||||||
|
{
|
||||||
|
return m_characters.at(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
119
src/resources/mesh.cpp
Normal file
119
src/resources/mesh.cpp
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
#include "resources/mesh.hpp"
|
||||||
|
|
||||||
|
namespace resources {
|
||||||
|
|
||||||
|
struct MeshFileHeader {
|
||||||
|
unsigned int vertex_count;
|
||||||
|
unsigned int index_count;
|
||||||
|
int material;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void loadMeshFromFile(const std::filesystem::path& path, std::vector<Vertex>* vertices, std::vector<unsigned int>* indices)
|
||||||
|
{
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// Replace this aberation with something that's readable and doesn't use FILE*
|
||||||
|
|
||||||
|
struct MeshFileHeader header{};
|
||||||
|
|
||||||
|
FILE* fp = fopen(path.string().c_str(), "rb");
|
||||||
|
if (fp == NULL) {
|
||||||
|
throw std::runtime_error("Unable to open mesh file " + path.string());
|
||||||
|
}
|
||||||
|
|
||||||
|
fread(&header, sizeof(struct MeshFileHeader), 1, fp);
|
||||||
|
|
||||||
|
indices->resize(header.index_count);
|
||||||
|
vertices->resize(header.vertex_count);
|
||||||
|
|
||||||
|
fread(&(*indices)[0], sizeof(unsigned int) * header.index_count, 1, fp);
|
||||||
|
fread(&((*vertices)[0].pos[0]), sizeof(float) * 8 * header.vertex_count, 1, 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()
|
||||||
|
{
|
||||||
|
glGenVertexArrays(1, &m_vao);
|
||||||
|
bindVAO();
|
||||||
|
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
|
||||||
|
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));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mesh::drawMesh(const Shader& shader)
|
||||||
|
{
|
||||||
|
bindVAO();
|
||||||
|
shader.makeActive();
|
||||||
|
#ifndef SDLTEST_NOGFX
|
||||||
|
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(m_indices.size()), GL_UNSIGNED_INT, 0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
Mesh::Mesh(const std::vector<Vertex>& vertices) : Resource("", "mesh")
|
||||||
|
{
|
||||||
|
// constructor for custom meshes without an index array
|
||||||
|
m_vertices = vertices; // COPY over vertices
|
||||||
|
for (int i = 0; i < m_vertices.size(); i++) {
|
||||||
|
m_indices.push_back(i);
|
||||||
|
}
|
||||||
|
initMesh();
|
||||||
|
}
|
||||||
|
|
||||||
|
Mesh::Mesh(const std::vector<Vertex>& vertices, const std::vector<unsigned int>& indices) : Resource("", "mesh")
|
||||||
|
{
|
||||||
|
m_vertices = vertices; // COPY over vertices
|
||||||
|
m_indices = indices; // COPY over indices;
|
||||||
|
initMesh();
|
||||||
|
}
|
||||||
|
|
||||||
|
// To be used with the resource manager
|
||||||
|
Mesh::Mesh(const std::filesystem::path& resPath) : Resource(resPath, "mesh")
|
||||||
|
{
|
||||||
|
loadMeshFromFile(resPath, &m_vertices, &m_indices);
|
||||||
|
initMesh();
|
||||||
|
}
|
||||||
|
|
||||||
|
Mesh::~Mesh()
|
||||||
|
{
|
||||||
|
glDeleteVertexArrays(1, &m_vao);
|
||||||
|
glDeleteBuffers(1, &m_vbo);
|
||||||
|
glDeleteBuffers(1, &m_ebo);
|
||||||
|
if (s_active_vao == m_vao) {
|
||||||
|
s_active_vao = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
20
src/resources/resource.cpp
Normal file
20
src/resources/resource.cpp
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
#include "resources/resource.hpp"
|
||||||
|
|
||||||
|
#include <log.hpp>
|
||||||
|
|
||||||
|
Resource::Resource(const std::filesystem::path& resPath, const std::string& type) : m_resourcePath(resPath), m_type(type)
|
||||||
|
{
|
||||||
|
if (m_type != "mesh")
|
||||||
|
TRACE("Creating {} resource: {}", type, resPath.filename().string());
|
||||||
|
}
|
||||||
|
|
||||||
|
Resource::~Resource()
|
||||||
|
{
|
||||||
|
if (m_type != "mesh")
|
||||||
|
TRACE("Destroyed {} resource: {}", m_type, m_resourcePath.filename().string());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string Resource::getType()
|
||||||
|
{
|
||||||
|
return m_type;
|
||||||
|
}
|
223
src/resources/shader.cpp
Normal file
223
src/resources/shader.cpp
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
#include "resources/shader.hpp"
|
||||||
|
|
||||||
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
#include <log.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
#include <vector>
|
||||||
|
#include <span>
|
||||||
|
|
||||||
|
static std::unique_ptr<std::vector<char>> readFile(const char * path)
|
||||||
|
{
|
||||||
|
std::ifstream file(path, std::ios::binary | std::ios::ate);
|
||||||
|
//file.exceptions(std::ifstream::failbit); // throw exception if file cannot be opened
|
||||||
|
if (file.fail()) {
|
||||||
|
throw std::runtime_error("Failed to open file for reading: " + std::string(path));
|
||||||
|
}
|
||||||
|
size_t size = file.tellg();
|
||||||
|
file.seekg(0, std::ios::beg);
|
||||||
|
auto buf = std::make_unique<std::vector<char>>();
|
||||||
|
buf->resize(size);
|
||||||
|
file.rdbuf()->sgetn(buf->data(), size);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
static GLuint compile(const char *path, GLenum type)
|
||||||
|
{
|
||||||
|
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")
|
||||||
|
{
|
||||||
|
|
||||||
|
const std::string vertexShaderPath = (resPath.parent_path()/std::filesystem::path(resPath.stem().string() + ".vert")).string();
|
||||||
|
const std::string fragmentShaderPath = (resPath.parent_path()/std::filesystem::path(resPath.stem().string() + ".frag")).string();
|
||||||
|
GLuint vs = compile(vertexShaderPath.c_str(), GL_VERTEX_SHADER);
|
||||||
|
GLuint fs = compile(fragmentShaderPath.c_str(), GL_FRAGMENT_SHADER);
|
||||||
|
m_program = glCreateProgram();
|
||||||
|
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
|
||||||
|
glDeleteShader(vs);
|
||||||
|
glDeleteShader(fs);
|
||||||
|
|
||||||
|
GLint linked, validated;
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
glDeleteProgram(m_program);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
131
src/resources/texture.cpp
Normal file
131
src/resources/texture.cpp
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
#include "resources/texture.hpp"
|
||||||
|
|
||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
|
#include "stb_image.h"
|
||||||
|
|
||||||
|
#include <log.hpp>
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace resources {
|
||||||
|
|
||||||
|
// -1 means invalid / no bind
|
||||||
|
GLuint Texture::s_binded_texture = -1;
|
||||||
|
|
||||||
|
void Texture::invalidate()
|
||||||
|
{
|
||||||
|
s_binded_texture = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns false if unable to open file
|
||||||
|
static bool readPNG(const std::string& path, std::vector<uint8_t>& texbuf, int *width, int *height, bool *isRGBA)
|
||||||
|
{
|
||||||
|
int x, y, n;
|
||||||
|
unsigned char *data = stbi_load(path.c_str(), &x, &y, &n, 0);
|
||||||
|
|
||||||
|
|
||||||
|
if (data == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t size = x * y * n;
|
||||||
|
|
||||||
|
texbuf.resize(size);
|
||||||
|
memcpy(texbuf.data(), data, size);
|
||||||
|
|
||||||
|
*width = x;
|
||||||
|
*height = y;
|
||||||
|
if (n == 4) {
|
||||||
|
*isRGBA = true;
|
||||||
|
} else {
|
||||||
|
*isRGBA = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
stbi_image_free(data);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool readGLRaw(const std::string& path, std::vector<uint8_t>& texbuf, int *width, int *height, bool *isRGBA)
|
||||||
|
{
|
||||||
|
FILE *fp = fopen(path.c_str(), "rb");
|
||||||
|
if (!fp) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fseek(fp, 0x02, SEEK_SET);
|
||||||
|
uint64_t tex_data_offset;
|
||||||
|
fread(&tex_data_offset, sizeof(uint64_t), 1, fp);
|
||||||
|
|
||||||
|
fseek(fp, 0L, SEEK_END);
|
||||||
|
uint64_t end = ftell(fp);
|
||||||
|
|
||||||
|
texbuf.resize(end);
|
||||||
|
fseek(fp, tex_data_offset, SEEK_SET);
|
||||||
|
fread(texbuf.data(), 1, end, fp);
|
||||||
|
|
||||||
|
fclose(fp);
|
||||||
|
|
||||||
|
*width = 4096;
|
||||||
|
*height = 4096;
|
||||||
|
*isRGBA = false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture::Texture(const std::filesystem::path& resPath) : Resource(resPath, "texture")
|
||||||
|
{
|
||||||
|
|
||||||
|
std::vector<uint8_t> texbuf;
|
||||||
|
|
||||||
|
int width, height;
|
||||||
|
bool isRGBA, success;
|
||||||
|
|
||||||
|
if (resPath.extension() == ".png") {
|
||||||
|
success = readPNG(resPath.string(), texbuf, &width, &height, &isRGBA);
|
||||||
|
} else {
|
||||||
|
success = readGLRaw(resPath.string(), texbuf, &width, &height, &isRGBA);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!success) {
|
||||||
|
throw std::runtime_error("Unable to open texture: " + resPath.string());
|
||||||
|
}
|
||||||
|
|
||||||
|
glGenTextures(1, &m_texture);
|
||||||
|
|
||||||
|
bindTexture(); // glBindTexture
|
||||||
|
|
||||||
|
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
||||||
|
|
||||||
|
if (isRGBA) {
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, texbuf.data());
|
||||||
|
} else {
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, texbuf.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
|
|
||||||
|
DEBUG("loaded texture {} width: {} height: {} size: {}", resPath.filename().string(), width, height, texbuf.size());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture::~Texture()
|
||||||
|
{
|
||||||
|
if (s_binded_texture == m_texture) {
|
||||||
|
s_binded_texture = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Texture::bindTexture() const
|
||||||
|
{
|
||||||
|
if (s_binded_texture != m_texture) {
|
||||||
|
glBindTexture(GL_TEXTURE_2D, m_texture);
|
||||||
|
s_binded_texture = m_texture;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
91
src/sceneroot.cpp
Normal file
91
src/sceneroot.cpp
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
#include "sceneroot.hpp"
|
||||||
|
|
||||||
|
#include "window.hpp"
|
||||||
|
|
||||||
|
#include "components/custom.hpp"
|
||||||
|
#include "components/camera.hpp"
|
||||||
|
#include "components/transform.hpp"
|
||||||
|
#include "components/mesh_renderer.hpp"
|
||||||
|
#include "components/text_ui_renderer.hpp"
|
||||||
|
|
||||||
|
#include <glm/mat4x4.hpp>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "log.hpp"
|
||||||
|
|
||||||
|
SceneRoot::SceneRoot(struct GameIO things) : Object("root", nullptr, *this, things)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
SceneRoot::SceneRoot(const std::filesystem::path& file, struct GameIO things) : SceneRoot(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
|
||||||
|
|
||||||
|
for (const auto& [c, t] : compList.cameras) {
|
||||||
|
for (int id : m_activeCameras) {
|
||||||
|
if (c->getID() == id) {
|
||||||
|
c->updateCam(t);
|
||||||
|
for (const auto& [c, t] : compList.renderers) {
|
||||||
|
c->render(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
494
src/window.cpp
Normal file
494
src/window.cpp
Normal file
@ -0,0 +1,494 @@
|
|||||||
|
#include "window.hpp"
|
||||||
|
|
||||||
|
#include <glad/glad.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
const uint64_t BILLION = 1000000000;
|
||||||
|
|
||||||
|
Window::Window(const std::string& title) : m_title(title)
|
||||||
|
{
|
||||||
|
|
||||||
|
// init SDL
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_counterFreq = SDL_GetPerformanceFrequency();
|
||||||
|
m_startTime = getNanos();
|
||||||
|
m_lastFrameStamp = m_startTime - 1;
|
||||||
|
m_avgFpsStart = m_startTime;
|
||||||
|
|
||||||
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||||
|
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
|
||||||
|
m_handle = SDL_CreateWindow(
|
||||||
|
m_title.c_str(),
|
||||||
|
SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
|
||||||
|
static_cast<int>(m_winSize.x),
|
||||||
|
static_cast<int>(m_winSize.y),
|
||||||
|
SDL_WINDOW_RESIZABLE | SDL_WINDOW_OPENGL | SDL_WINDOW_ALLOW_HIGHDPI);
|
||||||
|
if (m_handle == NULL) {
|
||||||
|
SDL_Quit();
|
||||||
|
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);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Window::~Window()
|
||||||
|
{
|
||||||
|
SDL_GL_DeleteContext(m_glContext);
|
||||||
|
SDL_DestroyWindow(m_handle);
|
||||||
|
SDL_Quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
// private methods
|
||||||
|
|
||||||
|
void Window::onResize(Sint32 width, Sint32 height)
|
||||||
|
{
|
||||||
|
// get window size
|
||||||
|
m_winSize.x = static_cast<int>(width);
|
||||||
|
m_winSize.y = static_cast<int>(height);
|
||||||
|
|
||||||
|
// get framebuffer size
|
||||||
|
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:
|
||||||
|
onResize(e.data1, e.data2);
|
||||||
|
break;
|
||||||
|
case SDL_WINDOWEVENT_FOCUS_GAINED:
|
||||||
|
m_keyboardFocus = true;
|
||||||
|
break;
|
||||||
|
case SDL_WINDOWEVENT_FOCUS_LOST:
|
||||||
|
m_keyboardFocus = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::onKeyEvent(SDL_KeyboardEvent &e)
|
||||||
|
{
|
||||||
|
bool keyWasDown = m_keyboard.keys[e.keysym.scancode];
|
||||||
|
bool keyIsDown = (e.state == SDL_PRESSED);
|
||||||
|
m_keyboard.keys[e.keysym.scancode] = keyIsDown;
|
||||||
|
if (keyIsDown != keyWasDown) { // (if key was pressed or released)
|
||||||
|
m_keyboard.deltas[e.keysym.scancode] = keyIsDown ? ButtonDelta::PRESSED : ButtonDelta::RELEASED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::onMouseButtonEvent(SDL_MouseButtonEvent &e)
|
||||||
|
{
|
||||||
|
enum inputs::MouseButton button = inputs::MouseButton::M_INVALID;
|
||||||
|
switch (e.button) {
|
||||||
|
case SDL_BUTTON_LEFT:
|
||||||
|
button = inputs::MouseButton::M_LEFT;
|
||||||
|
break;
|
||||||
|
case SDL_BUTTON_MIDDLE:
|
||||||
|
button = inputs::MouseButton::M_MIDDLE;
|
||||||
|
break;
|
||||||
|
case SDL_BUTTON_RIGHT:
|
||||||
|
button = inputs::MouseButton::M_RIGHT;
|
||||||
|
break;
|
||||||
|
case SDL_BUTTON_X1:
|
||||||
|
button = inputs::MouseButton::M_X1;
|
||||||
|
break;
|
||||||
|
case SDL_BUTTON_X2:
|
||||||
|
button = inputs::MouseButton::M_X2;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool buttonWasDown = m_mouse.buttons[static_cast<int>(button)];
|
||||||
|
bool buttonIsDown = (e.state == SDL_PRESSED);
|
||||||
|
m_mouse.buttons[static_cast<int>(button)] = buttonIsDown;
|
||||||
|
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)
|
||||||
|
if (m_mouse.deltas[static_cast<int>(button)] == ButtonDelta::SAME) {
|
||||||
|
m_mouse.deltas[static_cast<int>(button)] = buttonIsDown ? ButtonDelta::PRESSED : ButtonDelta::RELEASED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::onMouseMotionEvent(SDL_MouseMotionEvent &e)
|
||||||
|
{
|
||||||
|
m_mouse.x = e.x;
|
||||||
|
m_mouse.y = e.y;
|
||||||
|
m_mouse.dx = e.xrel;
|
||||||
|
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
|
||||||
|
|
||||||
|
std::string Window::getTitle() const
|
||||||
|
{
|
||||||
|
return m_title;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::makeContextCurrent()
|
||||||
|
{
|
||||||
|
if (SDL_GL_MakeCurrent(m_handle, m_glContext) != 0) {
|
||||||
|
throw std::runtime_error("Failed to make GL context current");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::swapBuffers()
|
||||||
|
{
|
||||||
|
#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()
|
||||||
|
{
|
||||||
|
|
||||||
|
resetInputDeltas();
|
||||||
|
|
||||||
|
// loop through all available events
|
||||||
|
SDL_Event e;
|
||||||
|
while (SDL_PollEvent(&e)) {
|
||||||
|
switch (e.type) {
|
||||||
|
|
||||||
|
case SDL_QUIT:
|
||||||
|
setCloseFlag();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_WINDOWEVENT:
|
||||||
|
onWindowEvent(e.window);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_KEYDOWN: // FALL THROUGH
|
||||||
|
case SDL_KEYUP:
|
||||||
|
onKeyEvent(e.key);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_MOUSEBUTTONDOWN: // FALL THROUGH
|
||||||
|
case SDL_MOUSEBUTTONUP:
|
||||||
|
onMouseButtonEvent(e.button);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_MOUSEMOTION:
|
||||||
|
onMouseMotionEvent(e.motion);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SDL_MOUSEWHEEL:
|
||||||
|
onMouseWheelEvent(e.wheel);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::setVSync(bool enable)
|
||||||
|
{
|
||||||
|
if (SDL_GL_SetSwapInterval(enable ? 1 : 0) != 0) {
|
||||||
|
throw std::runtime_error("Failed to set swap interval");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Window::getVSync() const
|
||||||
|
{
|
||||||
|
return SDL_GL_GetSwapInterval() == 0 ? false : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
int width, height;
|
||||||
|
SDL_GetWindowSize(m_handle, &width, &height);
|
||||||
|
onResize(width, height);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::toggleFullscreen()
|
||||||
|
{
|
||||||
|
setFullscreen(!m_fullscreen);
|
||||||
|
}
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
return m_mouse.captured;
|
||||||
|
}
|
||||||
|
|
||||||
|
// getting input
|
||||||
|
|
||||||
|
bool Window::getKey(inputs::Key key) const
|
||||||
|
{
|
||||||
|
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
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user