Compare commits
19 Commits
e373badf39
...
flecs
| Author | SHA1 | Date | |
|---|---|---|---|
| 293f2bacc5 | |||
| b78c214229 | |||
| d643288e56 | |||
| 077191dc10 | |||
| ba6a560129 | |||
| e590203234 | |||
| 82ddc88246 | |||
| ecad49ee32 | |||
| 942d792ba7 | |||
| 1ee0407d16 | |||
| 94434d373c | |||
| 997e45d8af | |||
| e4378dcddc | |||
| 991093d352 | |||
| 5331c3d002 | |||
| 45a1c77e90 | |||
| 2a43299a15 | |||
| aa3dd28fb3 | |||
| 125e19e0be |
@@ -1,5 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.20)
|
||||
cmake_policy(SET CMP0135 NEW)
|
||||
cmake_minimum_required(VERSION 3.24)
|
||||
|
||||
project(
|
||||
Fall-Fever
|
||||
@@ -12,43 +11,46 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)
|
||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
||||
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
|
||||
include(FetchContent)
|
||||
|
||||
# EnTT
|
||||
FetchContent_Declare(
|
||||
entt
|
||||
URL https://github.com/skypjack/entt/archive/refs/tags/v3.12.0.tar.gz
|
||||
)
|
||||
|
||||
FetchContent_MakeAvailable(entt)
|
||||
|
||||
# GLFW
|
||||
FetchContent_Declare(
|
||||
glfw
|
||||
URL https://github.com/glfw/glfw/releases/download/3.3.8/glfw-3.3.8.zip
|
||||
)
|
||||
|
||||
option(GLFW_BUILD_DOCS "" OFF)
|
||||
option(GLFW_BUILD_EXAMPLES "" OFF)
|
||||
option(GLFW_BUILD_TESTS "" OFF)
|
||||
option(GLFW_INSTALL "" OFF)
|
||||
FetchContent_MakeAvailable(glfw)
|
||||
|
||||
|
||||
# fx-gltf
|
||||
FetchContent_Declare(
|
||||
fx-gltf
|
||||
URL https://github.com/jessey-git/fx-gltf/archive/refs/tags/v2.0.0.tar.gz
|
||||
)
|
||||
|
||||
option(FX_GLTF_BUILD_TESTS "" OFF)
|
||||
option(FX_GLTF_INSTALL "" OFF)
|
||||
FetchContent_MakeAvailable(fx-gltf)
|
||||
|
||||
find_package(glm REQUIRED)
|
||||
find_package(flecs CONFIG REQUIRED)
|
||||
find_package(glm CONFIG REQUIRED)
|
||||
find_package(glfw3 REQUIRED)
|
||||
find_package(spdlog REQUIRED)
|
||||
find_package(fx-gltf REQUIRED)
|
||||
|
||||
add_subdirectory(${PROJECT_SOURCE_DIR}/lib)
|
||||
add_subdirectory(${PROJECT_SOURCE_DIR}/src)
|
||||
|
||||
add_library(fever_core
|
||||
src/asset/asset.cpp
|
||||
src/transform/transform.cpp
|
||||
src/core/camera.cpp
|
||||
src/core/glad.cpp
|
||||
src/core/graphics/framebuffer.cpp
|
||||
src/core/graphics/image.cpp
|
||||
src/core/graphics/material.cpp
|
||||
src/core/graphics/mesh.cpp
|
||||
src/core/light.cpp
|
||||
src/core/render.cpp
|
||||
src/core/shader.cpp
|
||||
src/core/time.cpp
|
||||
src/input/input.cpp
|
||||
src/scene/gltf.cpp
|
||||
src/scene/gltf_loader.cpp
|
||||
src/log/log.cpp
|
||||
src/window/window.cpp
|
||||
)
|
||||
|
||||
target_compile_features(fever_core PUBLIC cxx_std_20)
|
||||
target_include_directories(fever_core PUBLIC ${CMAKE_SOURCE_DIR}/src)
|
||||
|
||||
target_link_libraries(
|
||||
fever_core PUBLIC
|
||||
glad
|
||||
stb
|
||||
glfw
|
||||
flecs::flecs
|
||||
spdlog::spdlog
|
||||
glm::glm
|
||||
fx-gltf::fx-gltf
|
||||
)
|
||||
|
||||
add_subdirectory(${PROJECT_SOURCE_DIR}/apps)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"version": 2,
|
||||
"version": 3,
|
||||
"cmakeMinimumRequired": {
|
||||
"major": 3,
|
||||
"minor": 14,
|
||||
"minor": 24,
|
||||
"patch": 0
|
||||
},
|
||||
"configurePresets": [
|
||||
@@ -26,17 +26,26 @@
|
||||
"CMAKE_CXX_STANDARD_REQUIRED": "ON"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "vcpkg",
|
||||
"hidden": true,
|
||||
"cacheVariables": {
|
||||
"CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake",
|
||||
"VCPKG_INSTALL_OPTIONS": "--no-print-usage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "dev",
|
||||
"generator": "Ninja",
|
||||
"binaryDir": "${sourceDir}/build",
|
||||
"inherits": [
|
||||
"std"
|
||||
"std",
|
||||
"vcpkg"
|
||||
],
|
||||
"cacheVariables": {
|
||||
"CMAKE_BUILD_TYPE": "Debug",
|
||||
"CMAKE_CXX_FLAGS": "-Wall -Wextra -Wpedantic"
|
||||
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
|
||||
"CMAKE_CXX_FLAGS": "-Wall -Wextra -Wpedantic -fdiagnostics-color=always"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
12
README.md
12
README.md
@@ -1,3 +1,13 @@
|
||||
A work in progress game engine written in C++ using OpenGL and [entt](https://github.com/skypjack/entt).
|
||||
A work in progress game engine written in C++ using OpenGL and [flecs](https://flecs.dev).
|
||||
|
||||

|
||||
|
||||
## Controls
|
||||
| Button | Action |
|
||||
| ------------ | -------------------------- |
|
||||
| W, A, S, D | Horizontal Camera Movement |
|
||||
| Left Shift | Camera Down |
|
||||
| Space | Camera Up |
|
||||
| Left Alt | Accelerate |
|
||||
| Left Control | Catch Mouse |
|
||||
| Escape | Quit |
|
||||
|
||||
2
apps/CMakeLists.txt
Normal file
2
apps/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
add_subdirectory(fall-fever)
|
||||
add_subdirectory(flecs-test)
|
||||
9
apps/fall-fever/CMakeLists.txt
Normal file
9
apps/fall-fever/CMakeLists.txt
Normal file
@@ -0,0 +1,9 @@
|
||||
find_package(cxxopts CONFIG)
|
||||
|
||||
add_executable(Fall-Fever
|
||||
main.cpp
|
||||
controller.cpp
|
||||
flycam.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(Fall-Fever PRIVATE fever_core cxxopts::cxxopts)
|
||||
@@ -1,40 +1,28 @@
|
||||
#include "controller.h"
|
||||
#include "flycam.h"
|
||||
|
||||
#include "components/name.h"
|
||||
#include "components/transform.h"
|
||||
#include "transform/transform.h"
|
||||
#include "core/camera.h"
|
||||
#include "core/light.h"
|
||||
#include "window/window.h"
|
||||
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <flecs.h>
|
||||
|
||||
using namespace entt::literals;
|
||||
|
||||
Controller::Controller()
|
||||
Controller::Controller(std::string_view path)
|
||||
{
|
||||
std::filesystem::path document_path("WaterBottle/glTF-Binary/WaterBottle.glb");
|
||||
entt::hashed_string document_hash(document_path.c_str());
|
||||
spdlog::info("Open {}", path);
|
||||
std::filesystem::path document_path(path);
|
||||
entt::hashed_string document_hash(document_path.string().c_str());
|
||||
|
||||
entt::resource<Gltf> gltf_document =
|
||||
gltf_cache.load(document_hash, document_path).first->second;
|
||||
|
||||
gltf_document->spawn_default_scene(registry(), gltf_node_cache);
|
||||
|
||||
// Convert meshes
|
||||
auto mesh_view = registry().view<entt::resource<Mesh>>();
|
||||
for (auto [entity, mesh] : mesh_view.each()) {
|
||||
registry().emplace<GpuMesh>(entity, GpuMesh(mesh));
|
||||
|
||||
// Remove Mesh resource as it is no longer needed.
|
||||
registry().erase<entt::resource<Mesh>>(entity);
|
||||
}
|
||||
|
||||
// Convert materials
|
||||
auto material_view = registry().view<entt::resource<Material>>();
|
||||
for (auto [entity, material] : material_view.each()) {
|
||||
registry().emplace<GpuMaterial>(entity, GpuMaterial(material));
|
||||
|
||||
// Remove Material resource as it is no longer needed.
|
||||
registry().erase<entt::resource<Material>>(entity);
|
||||
}
|
||||
|
||||
// Spawn default lights
|
||||
auto directional_light = registry().create();
|
||||
registry().emplace<Name>(directional_light, "Directional Light");
|
||||
@@ -53,13 +41,24 @@ Controller::Controller()
|
||||
registry().emplace<GlobalTransform>(point_light, GlobalTransform{});
|
||||
registry().emplace<PointLight>(point_light,
|
||||
PointLight{.intensity = PointLight::DEFAULT_INTENSITY});
|
||||
|
||||
// Spawn default camera
|
||||
auto camera_view = registry().view<Camera const>();
|
||||
if (camera_view.empty()) {
|
||||
auto entity = registry().create();
|
||||
registry().emplace<Name>(entity, "Camera");
|
||||
registry().emplace<Transform>(entity, Transform{.translation = glm::vec3(0.0, 0.25, -1.0)});
|
||||
registry().emplace<GlobalTransform>(entity, GlobalTransform{});
|
||||
registry().emplace<Camera>(entity, Camera{.projection = Camera::Perspective{}});
|
||||
registry().emplace<Flycam>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
void Controller::update()
|
||||
{
|
||||
Camera::keyboard_movement(registry());
|
||||
Flycam::keyboard_movement(registry());
|
||||
|
||||
if (registry().ctx().get<Window::MouseCatched>().catched) {
|
||||
Camera::mouse_orientation(registry());
|
||||
if (registry().ctx().get<WindowDeprecated::MouseCatched>().catched) {
|
||||
Flycam::mouse_orientation(registry());
|
||||
}
|
||||
}
|
||||
13
apps/fall-fever/controller.h
Normal file
13
apps/fall-fever/controller.h
Normal file
@@ -0,0 +1,13 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/application.h"
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
class Controller : public FeverCore::Application
|
||||
{
|
||||
public:
|
||||
Controller(std::string_view path);
|
||||
void update() override;
|
||||
};
|
||||
98
apps/fall-fever/flycam.cpp
Normal file
98
apps/fall-fever/flycam.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
#include "flycam.h"
|
||||
|
||||
#include "core/camera.h"
|
||||
#include "core/time.h"
|
||||
#include "input/input.h"
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
void Flycam::keyboard_movement(entt::registry& registry)
|
||||
{
|
||||
struct KeyboardMovementContext
|
||||
{
|
||||
bool accelerate{};
|
||||
};
|
||||
|
||||
auto& movement_context = registry.ctx().emplace<KeyboardMovementContext>();
|
||||
auto const& key_state = registry.ctx().get<Input::ButtonInput<Input::KeyCode>>();
|
||||
auto const& delta_time = registry.ctx().get<Time::Delta>();
|
||||
|
||||
auto camera_view =
|
||||
registry.view<Flycam const, Camera const, Transform, GlobalTransform const>();
|
||||
auto camera_entity = camera_view.front();
|
||||
|
||||
if (camera_entity == entt::null) {
|
||||
spdlog::debug("No camera entity found");
|
||||
return;
|
||||
}
|
||||
|
||||
auto [camera, camera_transform, camera_global_transform] = camera_view.get(camera_entity);
|
||||
|
||||
glm::vec3 front_vec = Camera::front_vector(camera_global_transform);
|
||||
front_vec.y = 0;
|
||||
|
||||
glm::vec3 delta_pos = glm::vec3(0., 0., 0.);
|
||||
float acceleration = movement_context.accelerate ? ACCELERATION : 1.0F;
|
||||
float delta_factor = static_cast<float>(delta_time.delta.count()) * SPEED * acceleration;
|
||||
movement_context.accelerate = false;
|
||||
|
||||
if (key_state.pressed(Input::KeyCode{GLFW_KEY_W})) {
|
||||
delta_pos += delta_factor * glm::normalize(front_vec);
|
||||
}
|
||||
if (key_state.pressed(Input::KeyCode{GLFW_KEY_S})) {
|
||||
delta_pos -= delta_factor * glm::normalize(front_vec);
|
||||
}
|
||||
if (key_state.pressed(Input::KeyCode{GLFW_KEY_A})) {
|
||||
delta_pos -= delta_factor * glm::normalize(glm::cross(front_vec, Camera::UP_VECTOR));
|
||||
}
|
||||
if (key_state.pressed(Input::KeyCode{GLFW_KEY_D})) {
|
||||
delta_pos += delta_factor * glm::normalize(glm::cross(front_vec, Camera::UP_VECTOR));
|
||||
}
|
||||
if (key_state.pressed(Input::KeyCode{GLFW_KEY_SPACE})) {
|
||||
delta_pos += delta_factor * Camera::UP_VECTOR;
|
||||
}
|
||||
if (key_state.pressed(Input::KeyCode{GLFW_KEY_LEFT_SHIFT})) {
|
||||
delta_pos -= delta_factor * Camera::UP_VECTOR;
|
||||
}
|
||||
if (key_state.pressed(Input::KeyCode{GLFW_KEY_LEFT_ALT})) {
|
||||
movement_context.accelerate = true;
|
||||
}
|
||||
|
||||
camera_transform.translation += delta_pos;
|
||||
}
|
||||
|
||||
void Flycam::mouse_orientation(entt::registry& registry)
|
||||
{
|
||||
auto camera_view = registry.view<Flycam const, Camera, Transform>();
|
||||
auto camera_entity = camera_view.front();
|
||||
|
||||
if (camera_entity == entt::null) {
|
||||
spdlog::debug("No camera entity found");
|
||||
return;
|
||||
}
|
||||
|
||||
auto [camera, camera_transform] = camera_view.get(camera_entity);
|
||||
|
||||
auto const& mouse_cursor_input = registry.ctx().get<Input::MouseMotion>();
|
||||
auto delta_x = mouse_cursor_input.delta.x;
|
||||
auto delta_y = mouse_cursor_input.delta.y;
|
||||
|
||||
auto pitch = static_cast<float>(-delta_y);
|
||||
auto yaw = static_cast<float>(delta_x);
|
||||
|
||||
// Orthographic projection currently unsupported
|
||||
auto& camera_perspective = std::get<Camera::Perspective>(camera.projection);
|
||||
|
||||
camera_perspective.pitch += glm::radians(pitch);
|
||||
camera_perspective.yaw += glm::radians(yaw);
|
||||
|
||||
static constexpr float PITCH_CLIP = glm::radians(89.);
|
||||
camera_perspective.pitch =
|
||||
std::clamp(static_cast<float>(camera_perspective.pitch), -PITCH_CLIP, PITCH_CLIP);
|
||||
|
||||
camera_transform.orientation =
|
||||
glm::quat(glm::vec3(-camera_perspective.pitch, -camera_perspective.yaw, 0.0));
|
||||
}
|
||||
12
apps/fall-fever/flycam.h
Normal file
12
apps/fall-fever/flycam.h
Normal file
@@ -0,0 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
|
||||
struct Flycam
|
||||
{
|
||||
static constexpr float SPEED = 0.5;
|
||||
static constexpr float ACCELERATION = 5.0;
|
||||
|
||||
static void keyboard_movement(entt::registry& registry);
|
||||
static void mouse_orientation(entt::registry& registry);
|
||||
};
|
||||
49
apps/fall-fever/main.cpp
Normal file
49
apps/fall-fever/main.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "controller.h"
|
||||
#include "util/log.h"
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <cxxopts.hpp>
|
||||
#include <iostream>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
auto main(int argc, char* argv[]) -> int
|
||||
{
|
||||
Log::initialize();
|
||||
|
||||
cxxopts::Options options("Fall-Fever", "A brief description");
|
||||
|
||||
// clang-format off
|
||||
options.add_options()
|
||||
("model", "Model file to load", cxxopts::value<std::string>())
|
||||
("h,help", "Print usage")
|
||||
;
|
||||
// clang-format on
|
||||
|
||||
options.parse_positional({"model"});
|
||||
|
||||
auto result = options.parse(argc, argv);
|
||||
|
||||
if (result.count("help")) {
|
||||
std::cout << options.help() << std::endl;
|
||||
exit(0);
|
||||
}
|
||||
|
||||
std::string model;
|
||||
if (result.count("model"))
|
||||
model = result["model"].as<std::string>();
|
||||
|
||||
// Initialize GLFW
|
||||
if (glfwInit() == 0) {
|
||||
spdlog::critical("Could not initialize GLFW");
|
||||
return -1;
|
||||
}
|
||||
|
||||
{
|
||||
// Create controller
|
||||
Controller controller(model);
|
||||
controller.run();
|
||||
}
|
||||
|
||||
glfwTerminate();
|
||||
return 0;
|
||||
}
|
||||
5
apps/flecs-test/CMakeLists.txt
Normal file
5
apps/flecs-test/CMakeLists.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
add_executable(flecs-test
|
||||
main.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(flecs-test PRIVATE fever_core)
|
||||
67
apps/flecs-test/main.cpp
Normal file
67
apps/flecs-test/main.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
#include <asset/asset.h>
|
||||
#include <input/input.h>
|
||||
#include <log/log.h>
|
||||
#include <transform/transform.h>
|
||||
#include <window/window.h>
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <flecs.h>
|
||||
#include <iostream>
|
||||
|
||||
int main()
|
||||
{
|
||||
Log::initialize();
|
||||
|
||||
flecs::world world;
|
||||
|
||||
world.import <Window::WindowModule>();
|
||||
world.import <Input::InputModule>();
|
||||
world.import <Asset::AssetModule>();
|
||||
world.import <TransformModule>();
|
||||
|
||||
#ifndef NDEBUG
|
||||
world.import <flecs::stats>();
|
||||
world.set<flecs::Rest>({});
|
||||
#endif
|
||||
|
||||
world.system<Window::Window, Input::ButtonInput<Input::KeyCode> const>("CatchMouse")
|
||||
.each([](flecs::entity e,
|
||||
Window::Window,
|
||||
Input::ButtonInput<Input::KeyCode> const& button_input) {
|
||||
if (button_input.just_pressed(Input::KeyCode(GLFW_KEY_LEFT_CONTROL))) {
|
||||
auto glfw_window = e.get<std::shared_ptr<GLFWwindow>>();
|
||||
auto input_mode = glfwGetInputMode(glfw_window->get(), GLFW_CURSOR);
|
||||
|
||||
if (input_mode == GLFW_CURSOR_NORMAL) {
|
||||
glfwSetInputMode(glfw_window->get(), GLFW_CURSOR, GLFW_CURSOR_HIDDEN);
|
||||
} else {
|
||||
glfwSetInputMode(glfw_window->get(), GLFW_CURSOR, GLFW_CURSOR_NORMAL);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
world.system<Window::Window, Input::ButtonInput<Input::KeyCode> const>("CloseOnEsc")
|
||||
.each([](flecs::entity e,
|
||||
Window::Window,
|
||||
Input::ButtonInput<Input::KeyCode> const& button_input) {
|
||||
if (button_input.just_pressed(Input::KeyCode(GLFW_KEY_ESCAPE))) {
|
||||
e.world().quit();
|
||||
}
|
||||
});
|
||||
|
||||
world.system("PollEvents").kind(flecs::PreUpdate).run([](flecs::iter&) { glfwPollEvents(); });
|
||||
|
||||
world.system<std::shared_ptr<GLFWwindow>>("Render")
|
||||
.kind(flecs::PostUpdate)
|
||||
.each([](std::shared_ptr<GLFWwindow>& glfw_window) { glfwSwapBuffers(glfw_window.get()); });
|
||||
|
||||
auto parent = world.entity("TestParent").add<Transform>().add<GlobalTransform>();
|
||||
parent.get_mut<Transform>()->translation += glm::vec3(1.0);
|
||||
|
||||
auto child = world.entity("TestChild").add<Transform>().add<GlobalTransform>();
|
||||
child.get_mut<Transform>()->translation += glm::vec3(1.0);
|
||||
child.child_of(parent);
|
||||
|
||||
while (world.progress()) {
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
add_library(fever_engine
|
||||
components/transform.cpp
|
||||
core/graphics/framebuffer.cpp
|
||||
core/graphics/image.cpp
|
||||
core/graphics/material.cpp
|
||||
core/graphics/mesh.cpp
|
||||
core/camera.cpp
|
||||
core/game_loop.cpp
|
||||
core/glad.cpp
|
||||
core/light.cpp
|
||||
core/render.cpp
|
||||
core/shader.cpp
|
||||
core/time.cpp
|
||||
input/input.cpp
|
||||
scene/gltf.cpp
|
||||
scene/gltf_loader.cpp
|
||||
util/log.cpp
|
||||
window/window.cpp
|
||||
)
|
||||
|
||||
target_compile_features(fever_engine PUBLIC cxx_std_20)
|
||||
target_include_directories(fever_engine PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_compile_definitions(fever_engine PRIVATE SPDLOG_FMT_EXTERNAL)
|
||||
|
||||
target_link_libraries(
|
||||
fever_engine PUBLIC
|
||||
glad
|
||||
glfw
|
||||
EnTT::EnTT
|
||||
fmt
|
||||
pthread
|
||||
spdlog
|
||||
fx-gltf::fx-gltf
|
||||
stb
|
||||
)
|
||||
|
||||
add_executable(Fall-Fever
|
||||
bin/main.cpp
|
||||
bin/controller.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(Fall-Fever PRIVATE fever_engine)
|
||||
9
src/asset/asset.cpp
Normal file
9
src/asset/asset.cpp
Normal file
@@ -0,0 +1,9 @@
|
||||
#include "asset.h"
|
||||
|
||||
namespace Asset {
|
||||
|
||||
AssetModule::AssetModule([[maybe_unused]] flecs::world& world)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace Asset
|
||||
42
src/asset/asset.h
Normal file
42
src/asset/asset.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include <flecs.h>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Asset {
|
||||
|
||||
struct AssetModule
|
||||
{
|
||||
AssetModule(flecs::world& world);
|
||||
};
|
||||
|
||||
template <typename T> struct AssetCache
|
||||
{
|
||||
std::unordered_map<std::string, std::shared_ptr<T>> cache;
|
||||
};
|
||||
|
||||
template <typename T> struct AssetLoader
|
||||
{
|
||||
template <typename Args> std::shared_ptr<T> operator()(Args&&...)
|
||||
{
|
||||
static_assert(sizeof(T) == 0, "AssetLoader not implemented for this type.");
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> std::shared_ptr<T> load(flecs::world& world, std::string asset_path)
|
||||
{
|
||||
auto& asset_cache = world.ensure<AssetCache<T>>();
|
||||
|
||||
auto asset_it = asset_cache.cache.find(asset_path);
|
||||
if (asset_it != asset_cache.cache.end())
|
||||
return asset_it->second;
|
||||
|
||||
AssetLoader<T> asset_loader;
|
||||
auto loaded_asset = asset_loader(asset_path);
|
||||
|
||||
asset_cache.cache.emplace(asset_path, loaded_asset);
|
||||
return loaded_asset;
|
||||
}
|
||||
} // namespace Asset
|
||||
@@ -1,16 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "core/game_loop.h"
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
class Controller : public GameLoop
|
||||
{
|
||||
public:
|
||||
Controller();
|
||||
void update() override;
|
||||
};
|
||||
@@ -1,26 +0,0 @@
|
||||
#include "controller.h"
|
||||
#include "util/log.h"
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <iostream>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
auto main() -> int
|
||||
{
|
||||
Log::initialize();
|
||||
|
||||
// Initialize GLFW
|
||||
if (glfwInit() == 0) {
|
||||
spdlog::critical("Could not initialize GLFW");
|
||||
return -1;
|
||||
}
|
||||
|
||||
{
|
||||
// Create controller
|
||||
Controller controller;
|
||||
controller.run();
|
||||
}
|
||||
|
||||
glfwTerminate();
|
||||
return 0;
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
struct Name
|
||||
{
|
||||
std::string name;
|
||||
};
|
||||
@@ -1,37 +0,0 @@
|
||||
#include "transform.h"
|
||||
#include "relationship.h"
|
||||
|
||||
void GlobalTransform::update(entt::registry ®istry)
|
||||
{
|
||||
// Update GlobalTransform components
|
||||
// TODO: Only do this when the Transform changed.
|
||||
auto root_transform_view =
|
||||
registry.view<Transform const, GlobalTransform>(entt::exclude<Parent>);
|
||||
auto transform_view = registry.view<Transform const, GlobalTransform, Parent const>();
|
||||
|
||||
for (auto [entity, transform, global_transform] : root_transform_view.each()) {
|
||||
global_transform = transform;
|
||||
|
||||
auto parent_global_transform = global_transform;
|
||||
if (auto *children = registry.try_get<Children>(entity)) {
|
||||
for (auto child : children->children) {
|
||||
std::function<void(entt::entity entity, GlobalTransform parent_global_transform)>
|
||||
transform_propagate =
|
||||
[®istry, &transform_propagate, &transform_view](
|
||||
entt::entity entity, GlobalTransform parent_global_transform) {
|
||||
auto [transform, global_transform, parent] = transform_view.get(entity);
|
||||
global_transform.transform = parent_global_transform.transform *
|
||||
GlobalTransform(transform).transform;
|
||||
|
||||
if (auto *children = registry.try_get<Children>(entity)) {
|
||||
for (auto child : children->children) {
|
||||
transform_propagate(child, global_transform);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
transform_propagate(child, parent_global_transform);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
struct Transform
|
||||
{
|
||||
glm::vec3 translation{};
|
||||
glm::quat orientation{};
|
||||
glm::vec3 scale{1.0, 1.0, 1.0};
|
||||
};
|
||||
|
||||
struct GlobalTransform
|
||||
{
|
||||
GlobalTransform() = default;
|
||||
GlobalTransform(Transform const &transform)
|
||||
{
|
||||
// Translate * Rotate * Scale * vertex_vec;
|
||||
// First scaling, then rotation, then translation
|
||||
|
||||
// Translate
|
||||
glm::mat4 translation_matrix = glm::translate(glm::mat4(1.0F), transform.translation);
|
||||
|
||||
// Rotate
|
||||
glm::mat4 rotation_matrix = glm::toMat4(transform.orientation);
|
||||
|
||||
// Scale
|
||||
glm::mat4 scale_matrix = glm::scale(glm::mat4(1.0F), transform.scale);
|
||||
|
||||
this->transform = translation_matrix * rotation_matrix * scale_matrix;
|
||||
}
|
||||
|
||||
glm::mat4 transform{};
|
||||
|
||||
[[nodiscard]] auto position() const -> glm::vec3 { return transform[3]; };
|
||||
|
||||
static void update(entt::registry ®istry);
|
||||
};
|
||||
@@ -1,30 +1,26 @@
|
||||
#include "game_loop.h"
|
||||
#include "application.h"
|
||||
|
||||
#include "core/camera.h"
|
||||
#include "core/light.h"
|
||||
#include "core/render.h"
|
||||
#include "core/shader.h"
|
||||
#include "core/time.h"
|
||||
#include "input/input.h"
|
||||
#include "scene/scene.h"
|
||||
#include "window/window.h"
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <fx/gltf.h>
|
||||
#include <glad/gl.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <iostream>
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
|
||||
GameLoop::~GameLoop() = default;
|
||||
namespace FeverCore {
|
||||
|
||||
GameLoop::GameLoop() :
|
||||
game_window(std::make_shared<Window>(event_dispatcher)),
|
||||
Application::~Application() = default;
|
||||
|
||||
Application::Application() :
|
||||
game_window(std::make_shared<Window>(&_world)),
|
||||
post_processing_framebuffer(game_window->physical_dimensions()),
|
||||
key_listener{.registry = entt_registry},
|
||||
cursor_listener{.registry = entt_registry},
|
||||
@@ -38,13 +34,13 @@ GameLoop::GameLoop() :
|
||||
{
|
||||
register_context_variables();
|
||||
|
||||
event_dispatcher.sink<Window::ResizeEvent>().connect<&GameLoop::recreate_framebuffer>(this);
|
||||
event_dispatcher.sink<Window::ResizeEvent>().connect<&Application::recreate_framebuffer>(this);
|
||||
event_dispatcher.sink<Input::KeyInput>().connect<&Input::KeyListener::key_event>(key_listener);
|
||||
event_dispatcher.sink<Input::MouseMotion>().connect<&Input::CursorListener::cursor_event>(
|
||||
cursor_listener);
|
||||
}
|
||||
|
||||
void GameLoop::run()
|
||||
void Application::run()
|
||||
{
|
||||
entt::hashed_string shader_hash(Material::SHADER_NAME.data());
|
||||
auto standard_material_shader =
|
||||
@@ -70,9 +66,9 @@ void GameLoop::run()
|
||||
GlobalTransform::update(entt_registry);
|
||||
Camera::aspect_ratio_update(entt_registry);
|
||||
|
||||
update();
|
||||
this->update();
|
||||
|
||||
Input::State<Input::KeyCode>::update_state(entt_registry);
|
||||
// Input::State<Input::KeyCode>::update_state(entt_registry);
|
||||
Input::reset_mouse_motion(entt_registry);
|
||||
|
||||
// --- Render and buffer swap ---
|
||||
@@ -91,14 +87,16 @@ void GameLoop::run()
|
||||
}
|
||||
}
|
||||
|
||||
void GameLoop::register_context_variables()
|
||||
void Application::register_context_variables()
|
||||
{
|
||||
entt_registry.ctx().emplace<Input::State<Input::KeyCode>>();
|
||||
entt_registry.ctx().emplace<Input::MouseMotion>();
|
||||
}
|
||||
|
||||
void GameLoop::recreate_framebuffer()
|
||||
void Application::recreate_framebuffer()
|
||||
{
|
||||
auto dimensions = game_window->physical_dimensions();
|
||||
post_processing_framebuffer = Framebuffer(dimensions);
|
||||
}
|
||||
|
||||
} // namespace FeverCore
|
||||
@@ -2,36 +2,35 @@
|
||||
|
||||
#include "core/graphics/framebuffer.h"
|
||||
#include "core/shader.h"
|
||||
#include "entt/entity/fwd.hpp"
|
||||
#include "entt/signal/fwd.hpp"
|
||||
#include "input/input.h"
|
||||
#include "scene/gltf_loader.h"
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
#include <flecs.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
class Camera;
|
||||
class Window;
|
||||
class WindowDeprecated;
|
||||
|
||||
class GameLoop
|
||||
namespace FeverCore {
|
||||
|
||||
class Application
|
||||
{
|
||||
public:
|
||||
GameLoop();
|
||||
Application();
|
||||
|
||||
virtual ~GameLoop();
|
||||
GameLoop(GameLoop const&) = delete;
|
||||
GameLoop(GameLoop&&) = delete;
|
||||
auto operator=(GameLoop const&) -> GameLoop& = delete;
|
||||
auto operator=(GameLoop&&) -> GameLoop& = delete;
|
||||
virtual ~Application();
|
||||
Application(Application const&) = delete;
|
||||
Application(Application&&) = delete;
|
||||
auto operator=(Application const&) -> Application& = delete;
|
||||
auto operator=(Application&&) -> Application& = delete;
|
||||
|
||||
auto registry() -> entt::registry& { return entt_registry; }
|
||||
auto registry() const -> entt::registry const& { return entt_registry; }
|
||||
|
||||
auto dispatcher() -> entt::dispatcher& { return event_dispatcher; }
|
||||
auto dispatcher() const -> entt::dispatcher const& { return event_dispatcher; }
|
||||
auto world() -> flecs::world& { return _world; }
|
||||
auto world() const -> flecs::world const& { return _world; }
|
||||
|
||||
void run();
|
||||
|
||||
@@ -41,12 +40,13 @@ protected:
|
||||
virtual void register_context_variables();
|
||||
void recreate_framebuffer();
|
||||
|
||||
std::shared_ptr<Window> game_window;
|
||||
std::shared_ptr<WindowDeprecated> game_window;
|
||||
|
||||
Shader post_processing_shader{"post_processing", "data/shaders"};
|
||||
Framebuffer post_processing_framebuffer;
|
||||
|
||||
entt::registry entt_registry;
|
||||
flecs::world _world;
|
||||
|
||||
entt::dispatcher event_dispatcher{};
|
||||
Input::KeyListener key_listener;
|
||||
@@ -62,4 +62,6 @@ protected:
|
||||
|
||||
GltfLoader gltf_loader;
|
||||
entt::resource_cache<Gltf, GltfLoader> gltf_cache;
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace FeverCore
|
||||
@@ -1,10 +1,7 @@
|
||||
#include "camera.h"
|
||||
#include "core/time.h"
|
||||
#include "input/input.h"
|
||||
#include "window/window.h"
|
||||
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <algorithm>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
@@ -33,108 +30,15 @@ auto Camera::front_vector(GlobalTransform const& transform) -> glm::vec3
|
||||
return glm::normalize(transform.transform * glm::vec4(0.0, 0.0, 1.0, 0.0));
|
||||
}
|
||||
|
||||
void Camera::keyboard_movement(entt::registry& registry)
|
||||
{
|
||||
struct KeyboardMovementContext
|
||||
{
|
||||
bool accelerate{};
|
||||
};
|
||||
|
||||
auto& movement_context = registry.ctx().emplace<KeyboardMovementContext>();
|
||||
auto const& key_state = registry.ctx().get<Input::State<Input::KeyCode>>();
|
||||
auto const& delta_time = registry.ctx().get<Time::Delta>();
|
||||
|
||||
auto camera_view = registry.view<Camera const, Transform, GlobalTransform const>();
|
||||
auto camera_entity = camera_view.front();
|
||||
|
||||
if (camera_entity == entt::null) {
|
||||
spdlog::debug("No camera entity found");
|
||||
return;
|
||||
}
|
||||
|
||||
auto [camera, camera_transform, camera_global_transform] = camera_view.get(camera_entity);
|
||||
|
||||
glm::vec3 front_vec = front_vector(camera_global_transform);
|
||||
front_vec.y = 0;
|
||||
|
||||
glm::vec3 delta_pos = glm::vec3(0., 0., 0.);
|
||||
float acceleration = movement_context.accelerate ? ACCELERATION : 1.0F;
|
||||
float delta_factor = static_cast<float>(delta_time.delta.count()) * SPEED * acceleration;
|
||||
movement_context.accelerate = false;
|
||||
|
||||
if (key_state.pressed(Input::KeyCode{GLFW_KEY_W})) {
|
||||
delta_pos += delta_factor * glm::normalize(front_vec);
|
||||
}
|
||||
if (key_state.pressed(Input::KeyCode{GLFW_KEY_S})) {
|
||||
delta_pos -= delta_factor * glm::normalize(front_vec);
|
||||
}
|
||||
if (key_state.pressed(Input::KeyCode{GLFW_KEY_A})) {
|
||||
delta_pos -= delta_factor * glm::normalize(glm::cross(front_vec, Camera::UP_VECTOR));
|
||||
}
|
||||
if (key_state.pressed(Input::KeyCode{GLFW_KEY_D})) {
|
||||
delta_pos += delta_factor * glm::normalize(glm::cross(front_vec, Camera::UP_VECTOR));
|
||||
}
|
||||
if (key_state.pressed(Input::KeyCode{GLFW_KEY_SPACE})) {
|
||||
delta_pos += delta_factor * UP_VECTOR;
|
||||
}
|
||||
if (key_state.pressed(Input::KeyCode{GLFW_KEY_LEFT_SHIFT})) {
|
||||
delta_pos -= delta_factor * UP_VECTOR;
|
||||
}
|
||||
if (key_state.pressed(Input::KeyCode{GLFW_KEY_LEFT_ALT})) {
|
||||
movement_context.accelerate = true;
|
||||
}
|
||||
|
||||
camera_transform.translation += delta_pos;
|
||||
}
|
||||
|
||||
void Camera::mouse_orientation(entt::registry& registry)
|
||||
{
|
||||
auto camera_view = registry.view<Camera, Transform>();
|
||||
auto camera_entity = camera_view.front();
|
||||
|
||||
if (camera_entity == entt::null) {
|
||||
spdlog::debug("No camera entity found");
|
||||
return;
|
||||
}
|
||||
|
||||
auto [camera, camera_transform] = camera_view.get(camera_entity);
|
||||
|
||||
auto const& mouse_cursor_input = registry.ctx().get<Input::MouseMotion>();
|
||||
auto delta_x = mouse_cursor_input.delta.x;
|
||||
auto delta_y = mouse_cursor_input.delta.y;
|
||||
|
||||
auto pitch = static_cast<float>(-delta_y);
|
||||
auto yaw = static_cast<float>(delta_x);
|
||||
|
||||
// Orthographic projection currently unsupported
|
||||
auto& camera_perspective = std::get<Perspective>(camera.projection);
|
||||
|
||||
camera_perspective.pitch += glm::radians(pitch);
|
||||
camera_perspective.yaw += glm::radians(yaw);
|
||||
|
||||
static constexpr float PITCH_CLIP = glm::radians(89.);
|
||||
camera_perspective.pitch =
|
||||
std::clamp(static_cast<float>(camera_perspective.pitch), -PITCH_CLIP, PITCH_CLIP);
|
||||
|
||||
camera_transform.orientation =
|
||||
glm::quat(glm::vec3(-camera_perspective.pitch, -camera_perspective.yaw, 0.0));
|
||||
}
|
||||
|
||||
void Camera::aspect_ratio_update(entt::registry& registry)
|
||||
{
|
||||
float aspect_ratio = registry.ctx().get<Window::Descriptor>().aspect_ratio;
|
||||
// float aspect_ratio = registry.ctx().get<WindowDeprecated::Descriptor>().aspect_ratio;
|
||||
|
||||
auto camera_view = registry.view<Camera>();
|
||||
auto camera_entity = camera_view.front();
|
||||
// auto camera_view = registry.view<Camera>();
|
||||
|
||||
if (camera_entity == entt::null) {
|
||||
spdlog::debug("No camera entity found");
|
||||
return;
|
||||
}
|
||||
|
||||
auto [camera] = camera_view.get(camera_entity);
|
||||
|
||||
// Orthographic projection currently unsupported
|
||||
auto& camera_perspective = std::get<Perspective>(camera.projection);
|
||||
camera_perspective.aspect_ratio = aspect_ratio;
|
||||
// for (auto [entity, camera] : camera_view.each()) {
|
||||
// // Orthographic projection currently unsupported
|
||||
// auto& camera_perspective = std::get<Perspective>(camera.projection);
|
||||
// camera_perspective.aspect_ratio = aspect_ratio;
|
||||
// }
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "components/transform.h"
|
||||
#include "transform/transform.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <entt/entt.hpp>
|
||||
#include <glm/glm.hpp>
|
||||
#include <variant>
|
||||
@@ -15,11 +14,6 @@ struct Camera
|
||||
|
||||
static constexpr glm::vec3 UP_VECTOR = glm::vec3(0.0, 1.0, 0.0);
|
||||
|
||||
static constexpr float SPEED = 0.5;
|
||||
static constexpr float ACCELERATION = 5.0;
|
||||
|
||||
static constexpr glm::vec3 DEFAULT_POSITION = glm::vec3(0.0, 0.25, -1.0);
|
||||
|
||||
struct Perspective
|
||||
{
|
||||
float fov = DEFAULT_FOV;
|
||||
|
||||
@@ -128,7 +128,7 @@ void init_glad()
|
||||
// Initialize GLAD
|
||||
if (gladLoadGL(glfwGetProcAddress) == 0) {
|
||||
spdlog::critical("Failed to initialize GLAD");
|
||||
std::quick_exit(-1);
|
||||
std::abort();
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "light.h"
|
||||
#include "components/transform.h"
|
||||
#include "transform/transform.h"
|
||||
|
||||
static auto light_active(float illuminance) -> bool
|
||||
{
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "shader.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <fstream>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
@@ -4,36 +4,42 @@
|
||||
|
||||
namespace Input {
|
||||
|
||||
void KeyListener::key_event(KeyInput const& key_input_event)
|
||||
{
|
||||
auto& key_state = registry.ctx().get<State<KeyCode>>();
|
||||
void keyboard_input_observer(KeyboardInput const& keyboard_input);
|
||||
template <typename T> void clear_button_input(ButtonInput<T>& state);
|
||||
|
||||
if (key_input_event.action == static_cast<Action>(GLFW_PRESS)) {
|
||||
key_state.press(key_input_event.key_code);
|
||||
} else if (key_input_event.action == static_cast<Action>(GLFW_RELEASE)) {
|
||||
key_state.release(key_input_event.key_code);
|
||||
InputModule::InputModule(flecs::world& world)
|
||||
{
|
||||
world.component<KeyboardInput>();
|
||||
world.component<MouseButtonInput>();
|
||||
world.component<MouseMotion>();
|
||||
world.component<ButtonInput<KeyCode>>();
|
||||
|
||||
world.system<ButtonInput<KeyCode>>("ClearButtonInput")
|
||||
.kind(flecs::PostUpdate)
|
||||
.each(clear_button_input<KeyCode>);
|
||||
|
||||
world.observer<KeyboardInput>("KeyboardInputObserver")
|
||||
.event(flecs::OnSet)
|
||||
.each(keyboard_input_observer);
|
||||
}
|
||||
|
||||
void keyboard_input_observer(KeyboardInput const& keyboard_input)
|
||||
{
|
||||
auto* key_state = keyboard_input.window.get_mut<ButtonInput<KeyCode>>();
|
||||
|
||||
if (keyboard_input.action == static_cast<Input::Action>(GLFW_PRESS)) {
|
||||
key_state->press(keyboard_input.key_code);
|
||||
} else if (keyboard_input.action == static_cast<Input::Action>(GLFW_RELEASE)) {
|
||||
key_state->release(keyboard_input.key_code);
|
||||
}
|
||||
}
|
||||
|
||||
void CursorListener::cursor_event(MouseMotion const& mouse_motion_event)
|
||||
template <typename T> void clear_button_input(ButtonInput<T>& state)
|
||||
{
|
||||
auto& mouse_motion = registry.ctx().get<MouseMotion>();
|
||||
mouse_motion.delta += mouse_motion_event.delta;
|
||||
}
|
||||
|
||||
void reset_mouse_motion(entt::registry& registry)
|
||||
{
|
||||
auto& mouse_motion = registry.ctx().get<MouseMotion>();
|
||||
mouse_motion = {};
|
||||
}
|
||||
|
||||
template <typename T> void State<T>::update_state(entt::registry& registry)
|
||||
{
|
||||
auto& state = registry.ctx().get<State<T>>();
|
||||
state.just_pressed_keys.clear();
|
||||
state.just_released_keys.clear();
|
||||
}
|
||||
|
||||
template class State<KeyCode>;
|
||||
template class ButtonInput<KeyCode>;
|
||||
|
||||
} // namespace Input
|
||||
|
||||
@@ -1,13 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "entt/entity/fwd.hpp"
|
||||
#include <entt/entt.hpp>
|
||||
#include <flecs.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
|
||||
namespace Input {
|
||||
|
||||
struct InputModule
|
||||
{
|
||||
InputModule(flecs::world& world);
|
||||
};
|
||||
|
||||
enum class KeyCode : int
|
||||
{
|
||||
};
|
||||
@@ -16,10 +19,18 @@ enum class Action : int
|
||||
{
|
||||
};
|
||||
|
||||
struct KeyInput
|
||||
struct KeyboardInput
|
||||
{
|
||||
KeyCode key_code;
|
||||
Action action;
|
||||
flecs::entity window;
|
||||
};
|
||||
|
||||
struct MouseButtonInput
|
||||
{
|
||||
KeyCode key_code;
|
||||
Action action;
|
||||
flecs::entity window;
|
||||
};
|
||||
|
||||
struct MouseMotion
|
||||
@@ -27,16 +38,14 @@ struct MouseMotion
|
||||
glm::vec2 delta{};
|
||||
};
|
||||
|
||||
template <typename T> class State
|
||||
template <typename T> class ButtonInput
|
||||
{
|
||||
public:
|
||||
auto pressed(T input) const -> bool { return pressed_keys.contains(input); }
|
||||
auto just_pressed(T input) const -> bool { return just_pressed_keys.contains(input); }
|
||||
auto just_released(T input) const -> bool { return just_pressed_keys.contains(input); }
|
||||
|
||||
static void update_state(entt::registry& registry);
|
||||
|
||||
private:
|
||||
// private:
|
||||
void press(T input)
|
||||
{
|
||||
if (pressed_keys.insert(input).second) {
|
||||
@@ -54,23 +63,6 @@ private:
|
||||
std::set<T> pressed_keys;
|
||||
std::set<T> just_pressed_keys;
|
||||
std::set<T> just_released_keys;
|
||||
|
||||
friend class KeyListener;
|
||||
friend class CursorListener;
|
||||
};
|
||||
|
||||
struct KeyListener
|
||||
{
|
||||
entt::registry& registry;
|
||||
void key_event(KeyInput const& key_input_event);
|
||||
};
|
||||
|
||||
struct CursorListener
|
||||
{
|
||||
entt::registry& registry;
|
||||
void cursor_event(MouseMotion const& mouse_motion_event);
|
||||
};
|
||||
|
||||
void reset_mouse_motion(entt::registry& registry);
|
||||
|
||||
}; // namespace Input
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "gltf.h"
|
||||
#include "components/name.h"
|
||||
#include "components/relationship.h"
|
||||
#include "core/camera.h"
|
||||
|
||||
@@ -38,7 +37,7 @@ auto Gltf::spawn_scene(std::size_t index,
|
||||
std::function<entt::entity(GltfNode const&, entt::entity)> spawn_node =
|
||||
[®istry, &spawn_node](GltfNode const& node, entt::entity parent) {
|
||||
auto entity = registry.create();
|
||||
registry.emplace<Name>(entity, node.name);
|
||||
// registry.emplace<Name>(entity, node.name);
|
||||
registry.emplace<Transform>(entity, node.transform);
|
||||
registry.emplace<GlobalTransform>(entity, GlobalTransform{});
|
||||
registry.emplace<Parent>(entity, Parent{.parent = parent});
|
||||
@@ -84,16 +83,6 @@ auto Gltf::spawn_scene(std::size_t index,
|
||||
registry.get<Children>(scene_entity).children.push_back(node_entity);
|
||||
}
|
||||
|
||||
auto camera_view = registry.view<Camera const>();
|
||||
if (camera_view.empty()) {
|
||||
// Spawn default camera
|
||||
auto entity = registry.create();
|
||||
registry.emplace<Name>(entity, "Camera");
|
||||
registry.emplace<Transform>(entity, Transform{.translation = Camera::DEFAULT_POSITION});
|
||||
registry.emplace<GlobalTransform>(entity, GlobalTransform{});
|
||||
registry.emplace<Camera>(entity, Camera{.projection = Camera::Perspective{}});
|
||||
}
|
||||
|
||||
return scene_entity;
|
||||
}
|
||||
|
||||
@@ -113,12 +102,32 @@ auto Gltf::spawn_scene(std::string_view name,
|
||||
return entt::null;
|
||||
}
|
||||
|
||||
auto Gltf::spawn_default_scene(entt::registry& registry, entt::resource_cache<GltfNode>& node_cache)
|
||||
-> entt::entity
|
||||
auto Gltf::spawn_default_scene(entt::registry& registry,
|
||||
entt::resource_cache<GltfNode>& node_cache) -> entt::entity
|
||||
{
|
||||
if (document.scene != -1) {
|
||||
return spawn_scene(document.scene, registry, node_cache);
|
||||
if (document.scene == -1) {
|
||||
return entt::null;
|
||||
}
|
||||
|
||||
return entt::null;
|
||||
auto scene = spawn_scene(document.scene, registry, node_cache);
|
||||
|
||||
// Convert meshes
|
||||
auto mesh_view = registry.view<entt::resource<Mesh>>();
|
||||
for (auto [entity, mesh] : mesh_view.each()) {
|
||||
registry.emplace<GpuMesh>(entity, GpuMesh(mesh));
|
||||
|
||||
// Remove Mesh resource as it is no longer needed.
|
||||
registry.erase<entt::resource<Mesh>>(entity);
|
||||
}
|
||||
|
||||
// Convert materials
|
||||
auto material_view = registry.view<entt::resource<Material>>();
|
||||
for (auto [entity, material] : material_view.each()) {
|
||||
registry.emplace<GpuMaterial>(entity, GpuMaterial(material));
|
||||
|
||||
// Remove Material resource as it is no longer needed.
|
||||
registry.erase<entt::resource<Material>>(entity);
|
||||
}
|
||||
|
||||
return scene;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "components/transform.h"
|
||||
#include "transform/transform.h"
|
||||
#include "core/graphics/material.h"
|
||||
#include "core/graphics/mesh.h"
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#include "gltf_loader.h"
|
||||
#include "components/name.h"
|
||||
#include "components/relationship.h"
|
||||
#include "core/camera.h"
|
||||
#include "entt/entity/fwd.hpp"
|
||||
@@ -78,7 +77,7 @@ static auto load_texture(fx::gltf::Texture const& texture,
|
||||
std::istreambuf_iterator<char>(),
|
||||
std::back_inserter(image_data));
|
||||
|
||||
entt::hashed_string const image_hash(image_path.c_str());
|
||||
entt::hashed_string const image_hash(image_path.string().c_str());
|
||||
|
||||
return image_cache.load(image_hash, image_data, colorFormat).first->second;
|
||||
}
|
||||
@@ -389,103 +388,6 @@ auto GltfLoader::operator()(std::filesystem::path const& document_path) -> resul
|
||||
nodes.push_back(node.second);
|
||||
}
|
||||
|
||||
// // Load scenes
|
||||
// std::vector<entt::resource<Scene>> scenes;
|
||||
// for (auto const& gltf_scene : gltf.scenes) {
|
||||
// // Get nodes by hash
|
||||
// std::vector<entt::resource<GltfNode>> nodes;
|
||||
// nodes.reserve(gltf_scene.nodes.size());
|
||||
|
||||
// for (auto node_id : gltf_scene.nodes) {
|
||||
// auto const& node = gltf.nodes.at(node_id);
|
||||
// entt::hashed_string node_hash(node.name.c_str());
|
||||
// nodes.push_back(gltf_node_cache[node_hash]);
|
||||
// }
|
||||
|
||||
// if (gltf_scene.name.empty()) {
|
||||
// spdlog::warn("glTF scene has no name.");
|
||||
// }
|
||||
|
||||
// // Spawn an entity for every node in scene
|
||||
// for (auto const& node : nodes) {
|
||||
// std::function<entt::entity(GltfNode const&, std::optional<entt::entity>)> spawn_node =
|
||||
// [this, &spawn_node](GltfNode const& node, std::optional<entt::entity> parent) {
|
||||
// auto entity = registry.create();
|
||||
// registry.emplace<Name>(entity, node.name);
|
||||
// registry.emplace<Transform>(entity, node.transform);
|
||||
// registry.emplace<GlobalTransform>(entity, GlobalTransform{});
|
||||
|
||||
// if (parent.has_value()) {
|
||||
// registry.emplace<Parent>(entity, Parent{.parent = parent.value()});
|
||||
// }
|
||||
|
||||
// std::vector<entt::entity> child_entities;
|
||||
|
||||
// auto mesh = node.mesh;
|
||||
// if (mesh.has_value()) {
|
||||
// for (auto const& primitive : mesh.value()->primitives) {
|
||||
// auto mesh_entity = registry.create();
|
||||
// registry.emplace<Parent>(mesh_entity, Parent{.parent = entity});
|
||||
// registry.emplace<Transform>(mesh_entity, Transform{});
|
||||
// registry.emplace<GlobalTransform>(mesh_entity, GlobalTransform{});
|
||||
// registry.emplace<entt::resource<Mesh>>(mesh_entity, primitive.mesh);
|
||||
// registry.emplace<entt::resource<Material>>(mesh_entity,
|
||||
// primitive.material);
|
||||
|
||||
// child_entities.push_back(mesh_entity);
|
||||
// }
|
||||
// }
|
||||
|
||||
// auto camera = node.camera;
|
||||
// if (camera.has_value()) {
|
||||
// auto perspective =
|
||||
// std::get<fx::gltf::Camera::Perspective>(camera.value().projection);
|
||||
// Camera::Perspective camera_perspective{.fov = perspective.yfov,
|
||||
// .aspect_ratio =
|
||||
// perspective.aspectRatio,
|
||||
// .near = perspective.znear,
|
||||
// .far = perspective.zfar};
|
||||
// registry.emplace<Camera>(entity, Camera{.projection = camera_perspective});
|
||||
// }
|
||||
|
||||
// // Spawn child nodes
|
||||
// for (auto const& child : node.children) {
|
||||
// auto child_entity = spawn_node(child, entity);
|
||||
// child_entities.push_back(child_entity);
|
||||
// }
|
||||
|
||||
// registry.emplace<Children>(entity, Children{.children = child_entities});
|
||||
// return entity;
|
||||
// };
|
||||
|
||||
// spawn_node(node, {});
|
||||
// }
|
||||
|
||||
// auto camera_view = registry.view<Camera const>();
|
||||
// if (camera_view.empty()) {
|
||||
// // Spawn default camera
|
||||
// auto entity = registry.create();
|
||||
// registry.emplace<Name>(entity, "Camera");
|
||||
// registry.emplace<Transform>(entity, Transform{.translation = Camera::DEFAULT_POSITION});
|
||||
// registry.emplace<GlobalTransform>(entity, GlobalTransform{});
|
||||
// registry.emplace<Camera>(entity, Camera{.projection = Camera::Perspective{}});
|
||||
// }
|
||||
|
||||
// entt::hashed_string scene_hash(gltf_scene.name.c_str());
|
||||
// entt::resource<Scene> scene_resource =
|
||||
// scene_cache.load(scene_hash, Scene{}).first->second;
|
||||
// scenes.push_back(scene_resource);
|
||||
// }
|
||||
|
||||
// // Default scene
|
||||
// auto default_scene = [&gltf, &scenes]() -> std::optional<entt::resource<Scene>> {
|
||||
// if (gltf.scene != -1) {
|
||||
// return scenes.at(gltf.scene);
|
||||
// }
|
||||
|
||||
// return {};
|
||||
// }();
|
||||
|
||||
return std::make_shared<Gltf>(Gltf{.materials = std::move(materials),
|
||||
.meshes = std::move(gltf_meshes),
|
||||
.nodes = std::move(nodes),
|
||||
|
||||
36
src/transform/transform.cpp
Normal file
36
src/transform/transform.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
#include "transform.h"
|
||||
|
||||
void GlobalTransform::update(entt::registry& registry)
|
||||
{
|
||||
// Update GlobalTransform components
|
||||
// TODO: Only do this when the Transform changed.
|
||||
// auto root_transform_view =
|
||||
// registry.view<Transform const, GlobalTransform>(entt::exclude<Parent>);
|
||||
// auto transform_view = registry.view<Transform const, GlobalTransform, Parent const>();
|
||||
|
||||
// for (auto [entity, transform, global_transform] : root_transform_view.each()) {
|
||||
// global_transform = transform;
|
||||
|
||||
// auto parent_global_transform = global_transform;
|
||||
// if (auto* children = registry.try_get<Children>(entity)) {
|
||||
// for (auto child : children->children) {
|
||||
// std::function<void(entt::entity entity, GlobalTransform parent_global_transform)>
|
||||
// transform_propagate =
|
||||
// [®istry, &transform_propagate, &transform_view](
|
||||
// entt::entity entity, GlobalTransform parent_global_transform) {
|
||||
// auto [transform, global_transform, parent] = transform_view.get(entity);
|
||||
// global_transform.transform = parent_global_transform.transform *
|
||||
// GlobalTransform(transform).transform;
|
||||
|
||||
// if (auto* children = registry.try_get<Children>(entity)) {
|
||||
// for (auto child : children->children) {
|
||||
// transform_propagate(child, global_transform);
|
||||
// }
|
||||
// }
|
||||
// };
|
||||
|
||||
// transform_propagate(child, parent_global_transform);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
68
src/transform/transform.h
Normal file
68
src/transform/transform.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
#include <flecs.h>
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/gtx/quaternion.hpp>
|
||||
|
||||
struct Transform
|
||||
{
|
||||
glm::vec3 translation{};
|
||||
glm::quat orientation{};
|
||||
glm::vec3 scale{1.0, 1.0, 1.0};
|
||||
};
|
||||
|
||||
struct GlobalTransform
|
||||
{
|
||||
GlobalTransform() = default;
|
||||
GlobalTransform(Transform const& transform)
|
||||
{
|
||||
// Translate * Rotate * Scale * vertex_vec;
|
||||
// First scaling, then rotation, then translation
|
||||
|
||||
// Translate
|
||||
glm::mat4 translation_matrix = glm::translate(glm::mat4(1.0F), transform.translation);
|
||||
|
||||
// Rotate
|
||||
glm::mat4 rotation_matrix = glm::toMat4(transform.orientation);
|
||||
|
||||
// Scale
|
||||
glm::mat4 scale_matrix = glm::scale(glm::mat4(1.0F), transform.scale);
|
||||
|
||||
this->transform = translation_matrix * rotation_matrix * scale_matrix;
|
||||
}
|
||||
|
||||
glm::mat4 transform{};
|
||||
|
||||
[[nodiscard]] auto position() const -> glm::vec3 { return transform[3]; };
|
||||
|
||||
static void update(entt::registry& registry);
|
||||
};
|
||||
|
||||
// namespace Transform {
|
||||
|
||||
struct TransformModule
|
||||
{
|
||||
TransformModule(flecs::world& world)
|
||||
{
|
||||
world.system<GlobalTransform, Transform const>("PropagateTransform")
|
||||
.kind(flecs::PostUpdate)
|
||||
.each([](flecs::entity e,
|
||||
GlobalTransform& global_transform,
|
||||
Transform const& local_transform) {
|
||||
// There is no guarantee that the parent is iterated first. So there could be a two
|
||||
// frame delay.
|
||||
if (e.parent() == flecs::entity::null()) {
|
||||
global_transform.transform = GlobalTransform(local_transform).transform;
|
||||
} else {
|
||||
auto parent_global_transform = e.parent().get_mut<GlobalTransform>();
|
||||
global_transform.transform = parent_global_transform->transform *
|
||||
GlobalTransform(local_transform).transform;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// } // namespace Transform
|
||||
@@ -7,10 +7,33 @@
|
||||
#include <GLFW/glfw3.h>
|
||||
#include <spdlog/spdlog.h>
|
||||
|
||||
namespace Window {
|
||||
|
||||
static constexpr unsigned INIT_WINDOW_WIDTH = 1280;
|
||||
static constexpr unsigned INIT_WINDOW_HEIGHT = 720;
|
||||
static constexpr double MOUSE_SENSITIVITY = 0.15;
|
||||
|
||||
Window::Window(entt::dispatcher& event_dispatcher) : event_dispatcher(event_dispatcher)
|
||||
static void glfw_error_callback(int error, char const* description);
|
||||
static void framebuffer_size_callback(GLFWwindow* glfw_window, int width, int height);
|
||||
static void window_size_callback(GLFWwindow* glfw_window, int width, int height);
|
||||
static void window_close_callback(GLFWwindow* glfw_window);
|
||||
static void key_callback(GLFWwindow* glfw_window, int key, int scancode, int action, int mods);
|
||||
static void mouse_cursor_callback(GLFWwindow* glfw_window, double xpos, double ypos);
|
||||
|
||||
WindowModule::WindowModule(flecs::world& world)
|
||||
{
|
||||
glfwInit();
|
||||
world.component<Window>();
|
||||
world.component<PrimaryWindow>();
|
||||
world.component<std::shared_ptr<GLFWwindow>>();
|
||||
world.component<std::optional<CursorPosition>>();
|
||||
world.component<LogicalSize>();
|
||||
world.component<PhysicalSize>();
|
||||
|
||||
spawn_window(world);
|
||||
}
|
||||
|
||||
flecs::entity spawn_window(flecs::world& world)
|
||||
{
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||
@@ -24,7 +47,7 @@ Window::Window(entt::dispatcher& event_dispatcher) : event_dispatcher(event_disp
|
||||
glfwWindowHint(GLFW_MAXIMIZED, GLFW_TRUE);
|
||||
#endif
|
||||
|
||||
glfw_window = std::shared_ptr<GLFWwindow>(
|
||||
auto glfw_window = std::shared_ptr<GLFWwindow>(
|
||||
glfwCreateWindow(INIT_WINDOW_WIDTH, INIT_WINDOW_HEIGHT, "OpenGL", nullptr, nullptr),
|
||||
[](GLFWwindow* window) { glfwDestroyWindow(window); });
|
||||
|
||||
@@ -36,114 +59,100 @@ Window::Window(entt::dispatcher& event_dispatcher) : event_dispatcher(event_disp
|
||||
glfwMakeContextCurrent(glfw_window.get());
|
||||
|
||||
// Callbacks
|
||||
glfwSetWindowUserPointer(glfw_window.get(), this);
|
||||
glfwSetWindowUserPointer(glfw_window.get(), &world);
|
||||
glfwSetKeyCallback(glfw_window.get(), key_callback);
|
||||
glfwSetCursorPosCallback(glfw_window.get(), mouse_cursor_callback);
|
||||
glfwSetWindowSizeCallback(glfw_window.get(), window_size_callback);
|
||||
glfwSetWindowCloseCallback(glfw_window.get(), window_close_callback);
|
||||
glfwSetFramebufferSizeCallback(glfw_window.get(), framebuffer_size_callback);
|
||||
|
||||
init_glad();
|
||||
|
||||
int window_width, window_height;
|
||||
glfwGetWindowSize(glfw_window.get(), &window_width, &window_height);
|
||||
|
||||
int framebuffer_width, framebuffer_height;
|
||||
glfwGetFramebufferSize(glfw_window.get(), &framebuffer_width, &framebuffer_height);
|
||||
glViewport(0, 0, framebuffer_width, framebuffer_height);
|
||||
|
||||
flecs::entity window = world.entity("PrimaryWindow");
|
||||
window.add<Window>();
|
||||
window.add<Input::ButtonInput<Input::KeyCode>>();
|
||||
window.add<std::optional<CursorPosition>>();
|
||||
window.set<std::shared_ptr<GLFWwindow>>(glfw_window);
|
||||
window.set<LogicalSize>({window_width, window_height});
|
||||
window.set<PhysicalSize>({framebuffer_width, framebuffer_height});
|
||||
|
||||
world.set<PrimaryWindow>({window});
|
||||
|
||||
return window;
|
||||
}
|
||||
|
||||
void Window::glfw_error_callback(int error, char const* description)
|
||||
static void glfw_error_callback(int error, char const* description)
|
||||
{
|
||||
spdlog::warn("GLFW [{:d}]: {:s}\n", error, description);
|
||||
}
|
||||
|
||||
void Window::framebuffer_size_callback(GLFWwindow* glfw_window, int width, int height)
|
||||
void window_size_callback(GLFWwindow* glfw_window, int width, int height)
|
||||
{
|
||||
auto& window = *static_cast<Window*>(glfwGetWindowUserPointer(glfw_window));
|
||||
auto* world = static_cast<flecs::world*>(glfwGetWindowUserPointer(glfw_window));
|
||||
auto window = world->get<PrimaryWindow>()->window;
|
||||
|
||||
window.set<LogicalSize>({width, height});
|
||||
}
|
||||
|
||||
void window_close_callback(GLFWwindow* glfw_window)
|
||||
{
|
||||
auto* world = static_cast<flecs::world*>(glfwGetWindowUserPointer(glfw_window));
|
||||
world->quit();
|
||||
}
|
||||
|
||||
void framebuffer_size_callback(GLFWwindow* glfw_window, int width, int height)
|
||||
{
|
||||
auto* world = static_cast<flecs::world*>(glfwGetWindowUserPointer(glfw_window));
|
||||
auto window = world->get<PrimaryWindow>()->window;
|
||||
|
||||
window.set<PhysicalSize>({width, height});
|
||||
|
||||
glViewport(0, 0, width, height);
|
||||
|
||||
window.event_dispatcher.enqueue<ResizeEvent>();
|
||||
}
|
||||
|
||||
void Window::key_callback(GLFWwindow* glfw_window,
|
||||
int key,
|
||||
[[maybe_unused]] int scancode,
|
||||
int action,
|
||||
[[maybe_unused]] int mods)
|
||||
void key_callback(GLFWwindow* glfw_window,
|
||||
int key,
|
||||
[[maybe_unused]] int scancode,
|
||||
int action,
|
||||
[[maybe_unused]] int mods)
|
||||
{
|
||||
auto& window = *static_cast<Window*>(glfwGetWindowUserPointer(glfw_window));
|
||||
auto* world = static_cast<flecs::world*>(glfwGetWindowUserPointer(glfw_window));
|
||||
|
||||
window.event_dispatcher.enqueue<Input::KeyInput>(
|
||||
Input::KeyInput{.key_code = static_cast<Input::KeyCode>(key),
|
||||
.action = static_cast<Input::Action>(action)});
|
||||
// Maybe lookup with the glfw pointer instead?
|
||||
auto window = world->get<PrimaryWindow>()->window;
|
||||
|
||||
world->component<Input::KeyboardInput>().set<Input::KeyboardInput>(
|
||||
{.key_code = static_cast<Input::KeyCode>(key),
|
||||
.action = static_cast<Input::Action>(action),
|
||||
.window = window});
|
||||
}
|
||||
|
||||
void Window::mouse_cursor_callback(GLFWwindow* glfw_window, double xpos, double ypos)
|
||||
void mouse_cursor_callback(GLFWwindow* glfw_window, double xpos, double ypos)
|
||||
{
|
||||
auto& window = *static_cast<Window*>(glfwGetWindowUserPointer(glfw_window));
|
||||
auto* world = static_cast<flecs::world*>(glfwGetWindowUserPointer(glfw_window));
|
||||
auto window = world->get<PrimaryWindow>()->window;
|
||||
|
||||
glm::vec2 delta{xpos - window.last_cursor_pos.x, ypos - window.last_cursor_pos.y};
|
||||
auto cursor_position = window.get_mut<std::optional<CursorPosition>>();
|
||||
bool had_value = cursor_position->has_value();
|
||||
|
||||
*cursor_position = {xpos, ypos};
|
||||
|
||||
// Prevent invalid delta events when the cursor position wasn't valid
|
||||
if (!had_value)
|
||||
return;
|
||||
|
||||
glm::vec2 delta{xpos - cursor_position->value().x, ypos - cursor_position->value().y};
|
||||
|
||||
window.last_cursor_pos = {xpos, ypos};
|
||||
delta *= MOUSE_SENSITIVITY;
|
||||
|
||||
// Check if this is the first VALID mouse event after window being resized
|
||||
if (window.first_mouse_input && (delta.x >= std::numeric_limits<double>::epsilon() ||
|
||||
delta.y >= std::numeric_limits<double>::epsilon())) {
|
||||
window.first_mouse_input = false;
|
||||
return;
|
||||
}
|
||||
|
||||
window.event_dispatcher.enqueue<Input::MouseMotion>(Input::MouseMotion{.delta = delta});
|
||||
world->component<Input::MouseMotion>().set<Input::MouseMotion>({delta});
|
||||
}
|
||||
|
||||
auto Window::logical_dimensions() const -> glm::u32vec2
|
||||
{
|
||||
int width{};
|
||||
int height{};
|
||||
glfwGetWindowSize(glfw_window.get(), &width, &height);
|
||||
return {width, height};
|
||||
}
|
||||
|
||||
auto Window::physical_dimensions() const -> glm::u32vec2
|
||||
{
|
||||
int width{};
|
||||
int height{};
|
||||
glfwGetFramebufferSize(glfw_window.get(), &width, &height);
|
||||
return {width, height};
|
||||
}
|
||||
|
||||
void Window::mouse_catching(entt::registry& registry) const
|
||||
{
|
||||
if (!registry.ctx().contains<MouseCatched>()) {
|
||||
bool catched = true;
|
||||
|
||||
#ifndef NDEBUG
|
||||
catched = false;
|
||||
#endif
|
||||
|
||||
registry.ctx().emplace<MouseCatched>(MouseCatched{.catched = catched});
|
||||
}
|
||||
|
||||
auto& mouse_catched = registry.ctx().get<MouseCatched>();
|
||||
auto const& key_state = registry.ctx().get<Input::State<Input::KeyCode>>();
|
||||
|
||||
if (key_state.just_pressed(Input::KeyCode{GLFW_KEY_LEFT_CONTROL})) {
|
||||
mouse_catched.catched = !mouse_catched.catched;
|
||||
|
||||
glfwSetInputMode(glfw_window.get(),
|
||||
GLFW_CURSOR,
|
||||
mouse_catched.catched ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
void Window::close_on_esc(entt::registry& registry) const
|
||||
{
|
||||
auto const& key_state = registry.ctx().get<Input::State<Input::KeyCode>>();
|
||||
|
||||
if (key_state.just_pressed(Input::KeyCode{GLFW_KEY_ESCAPE})) {
|
||||
glfwSetWindowShouldClose(glfw_window.get(), GLFW_TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
void Window::update_descriptor(entt::registry& registry) const
|
||||
{
|
||||
auto dimensions = logical_dimensions();
|
||||
|
||||
registry.ctx().erase<Descriptor>();
|
||||
registry.ctx().emplace<Descriptor>(Descriptor{
|
||||
.logical_dimensions = dimensions,
|
||||
.aspect_ratio = static_cast<float>(dimensions.x) / static_cast<float>(dimensions.y)});
|
||||
}
|
||||
} // namespace Window
|
||||
|
||||
@@ -1,52 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include <entt/entt.hpp>
|
||||
#include <input/input.h>
|
||||
|
||||
#include <flecs.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
class GLFWwindow;
|
||||
|
||||
class Window
|
||||
namespace Window {
|
||||
|
||||
struct WindowModule
|
||||
{
|
||||
public:
|
||||
struct MouseCatched
|
||||
{
|
||||
bool catched = true;
|
||||
};
|
||||
|
||||
struct Descriptor
|
||||
{
|
||||
glm::u32vec2 logical_dimensions;
|
||||
float aspect_ratio{};
|
||||
};
|
||||
|
||||
struct ResizeEvent
|
||||
{};
|
||||
|
||||
Window(entt::dispatcher& event_dispatcher);
|
||||
|
||||
[[nodiscard]] auto handle() -> GLFWwindow& { return *glfw_window; }
|
||||
[[nodiscard]] auto physical_dimensions() const -> glm::u32vec2;
|
||||
[[nodiscard]] auto logical_dimensions() const -> glm::u32vec2;
|
||||
|
||||
void update_descriptor(entt::registry& registry) const;
|
||||
void mouse_catching(entt::registry& registry) const;
|
||||
void close_on_esc(entt::registry& registry) const;
|
||||
|
||||
private:
|
||||
static void key_callback(GLFWwindow* glfw_window, int key, int scancode, int action, int mods);
|
||||
static void mouse_cursor_callback(GLFWwindow* glfw_window, double xpos, double ypos);
|
||||
static void framebuffer_size_callback(GLFWwindow* glfw_window, int width, int height);
|
||||
static void glfw_error_callback(int error, char const* description);
|
||||
|
||||
static constexpr float MOUSE_SENSITIVITY = 0.15F;
|
||||
|
||||
std::shared_ptr<GLFWwindow> glfw_window;
|
||||
|
||||
entt::dispatcher& event_dispatcher;
|
||||
|
||||
glm::vec2 last_cursor_pos{};
|
||||
bool first_mouse_input = true;
|
||||
WindowModule(flecs::world& world);
|
||||
};
|
||||
|
||||
struct Window
|
||||
{};
|
||||
|
||||
struct PrimaryWindow
|
||||
{
|
||||
flecs::entity window;
|
||||
};
|
||||
|
||||
struct CursorPosition
|
||||
{
|
||||
double x;
|
||||
double y;
|
||||
};
|
||||
|
||||
struct LogicalSize
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
struct PhysicalSize
|
||||
{
|
||||
int width;
|
||||
int height;
|
||||
};
|
||||
|
||||
flecs::entity spawn_window(flecs::world& world);
|
||||
|
||||
} // namespace Window
|
||||
|
||||
24
vcpkg.json
Normal file
24
vcpkg.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"vcpkg-configuration": {
|
||||
"overlay-ports": [
|
||||
"./vcpkg"
|
||||
]
|
||||
},
|
||||
"dependencies": [
|
||||
"cxxopts",
|
||||
"entt",
|
||||
"fmt",
|
||||
"fx-gltf",
|
||||
{
|
||||
"name": "glfw3",
|
||||
"features": [
|
||||
"wayland"
|
||||
]
|
||||
},
|
||||
"glm",
|
||||
"spdlog",
|
||||
"fx-gltf",
|
||||
"flecs"
|
||||
],
|
||||
"builtin-baseline": "ce613c41372b23b1f51333815feb3edd87ef8a8b"
|
||||
}
|
||||
16
vcpkg/fx-gltf/portfile.cmake
Normal file
16
vcpkg/fx-gltf/portfile.cmake
Normal file
@@ -0,0 +1,16 @@
|
||||
vcpkg_from_github(
|
||||
OUT_SOURCE_PATH SOURCE_PATH
|
||||
REPO jessey-git/fx-gltf
|
||||
REF v2.0.0
|
||||
SHA512 0731732b5919a55045df2fe06bda6ea2a4d4d889556d738d0ade5acf39341a0748deb13bb130cbd6226146c70c2566ebfa67e6ac398157be00fefcf1f071a9bd
|
||||
HEAD_REF main
|
||||
)
|
||||
|
||||
vcpkg_configure_cmake(
|
||||
SOURCE_PATH ${SOURCE_PATH}
|
||||
PREFER_NINJA
|
||||
OPTIONS
|
||||
-DFX_GLTF_BUILD_TESTS=OFF
|
||||
)
|
||||
|
||||
vcpkg_install_cmake()
|
||||
7
vcpkg/fx-gltf/vcpkg.json
Normal file
7
vcpkg/fx-gltf/vcpkg.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "fx-gltf",
|
||||
"version": "2.0.0",
|
||||
"description": "A C++ library for loading and saving glTF 2.0 files.",
|
||||
"homepage": "https://github.com/jessey-git/fx-gltf",
|
||||
"dependencies": []
|
||||
}
|
||||
Reference in New Issue
Block a user