Implement Engine, World, and system initialization
Signed-off-by: Daniel Henry <iamdanhenry@gmail.com>
This commit is contained in:
45
.clang-format
Normal file
45
.clang-format
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
BasedOnStyle: LLVM
|
||||||
|
IndentWidth: 2
|
||||||
|
TabWidth: 2
|
||||||
|
UseTab: Never
|
||||||
|
BreakStringLiterals: false
|
||||||
|
ColumnLimit: 0
|
||||||
|
|
||||||
|
|
||||||
|
# Stop collapsing things to one line
|
||||||
|
# AllowShortFunctionsOnASingleLine: Empty # or 'Empty' if you want only {} allowed
|
||||||
|
AllowShortIfStatementsOnASingleLine: false
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortBlocksOnASingleLine: false
|
||||||
|
|
||||||
|
# Top-level switches
|
||||||
|
AlignTrailingComments: true # align // comments across consecutive lines
|
||||||
|
BreakTemplateDeclarations: Yes
|
||||||
|
|
||||||
|
# Align declarations in neat columns
|
||||||
|
AlignConsecutiveDeclarations:
|
||||||
|
Enabled: true
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: false
|
||||||
|
AlignFunctionDeclarations: true # optional, aligns return types for funcs
|
||||||
|
AlignFunctionPointers: true # optional
|
||||||
|
|
||||||
|
# Align assignments too (and pad short ops so columns line up)
|
||||||
|
AlignConsecutiveAssignments:
|
||||||
|
Enabled: true
|
||||||
|
AcrossEmptyLines: false
|
||||||
|
AcrossComments: false
|
||||||
|
AlignCompound: true # include +=, -=, etc.
|
||||||
|
PadOperators: true # only valid here
|
||||||
|
|
||||||
|
SortIncludes: true
|
||||||
|
IncludeBlocks: Merge
|
||||||
|
IncludeCategories:
|
||||||
|
- Regex: '^".*"'
|
||||||
|
Priority: 1
|
||||||
|
- Regex: '^<.*>'
|
||||||
|
Priority: 2
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
47
CMakeLists.txt
Normal file
47
CMakeLists.txt
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.24)
|
||||||
|
|
||||||
|
project(CraftEngine
|
||||||
|
VERSION 0.1.0
|
||||||
|
LANGUAGES C CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17)
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
set(CMAKE_CXX_EXTENSIONS OFF)
|
||||||
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
|
add_library(CraftEngine STATIC
|
||||||
|
include/Craft/Engine.h
|
||||||
|
src/Craft/Engine.cpp
|
||||||
|
|
||||||
|
include/Craft/World.h
|
||||||
|
src/Craft/World.cpp
|
||||||
|
|
||||||
|
include/Craft/Graphics.h
|
||||||
|
src/Craft/Graphics.cpp
|
||||||
|
|
||||||
|
include/Craft/Common.h
|
||||||
|
)
|
||||||
|
|
||||||
|
target_include_directories(CraftEngine PUBLIC include)
|
||||||
|
|
||||||
|
# Fetch SDL3
|
||||||
|
include(FetchContent)
|
||||||
|
FetchContent_Declare(
|
||||||
|
SDL3
|
||||||
|
GIT_REPOSITORY https://github.com/libsdl-org/SDL.git
|
||||||
|
GIT_TAG release-3.2.22
|
||||||
|
GIT_SHALLOW TRUE
|
||||||
|
)
|
||||||
|
FetchContent_MakeAvailable(SDL3)
|
||||||
|
|
||||||
|
target_link_libraries(CraftEngine PUBLIC SDL3::SDL3)
|
||||||
|
|
||||||
|
# FetchContent_Declare(
|
||||||
|
# SDL3_image
|
||||||
|
# GIT_REPOSITORY https://github.com/libsdl-org/SDL_image
|
||||||
|
# GIT_TAG release-3.2.4
|
||||||
|
# GIT_SHALLOW TRUE
|
||||||
|
# )
|
||||||
|
# FetchContent_MakeAvailable(SDL3_image)
|
||||||
|
# target_link_libraries(CraftEngine PUBLIC SDL3_image:SDL3_image)
|
||||||
|
|
||||||
6
include/Craft.h
Normal file
6
include/Craft.h
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
#include "Craft/Engine.h" // IWYU pragma: keep
|
||||||
|
#include "Craft/World.h" // IWYU pragma: keep
|
||||||
|
|
||||||
|
// Convenience Macros
|
||||||
|
#define GetEngine() craft::Engine::Get()
|
||||||
|
#define GetWorld() craft::Engine::Get().GetWorld()
|
||||||
22
include/Craft/Common.h
Normal file
22
include/Craft/Common.h
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdio> // IWYU pragma: keep
|
||||||
|
#include <memory>
|
||||||
|
#include <unordered_map>
|
||||||
|
namespace craft {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using unique = std::unique_ptr<T>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using shared = std::shared_ptr<T>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
using weak = std::weak_ptr<T>;
|
||||||
|
|
||||||
|
template <typename keyType, typename valueType, typename Hasher = std::hash<keyType>>
|
||||||
|
using Dict = std::unordered_map<keyType, valueType, Hasher>;
|
||||||
|
|
||||||
|
#define LOG(M, ...) std::printf(M "\n", ##__VA_ARGS__)
|
||||||
|
|
||||||
|
} // namespace craft
|
||||||
113
include/Craft/Engine.h
Normal file
113
include/Craft/Engine.h
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Craft/Common.h"
|
||||||
|
#include "Craft/Graphics.h"
|
||||||
|
#include "Craft/World.h"
|
||||||
|
#include "SDL3/SDL_timer.h"
|
||||||
|
#include <functional>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace craft {
|
||||||
|
|
||||||
|
int main();
|
||||||
|
|
||||||
|
class Engine {
|
||||||
|
public:
|
||||||
|
////////// ENGINE
|
||||||
|
friend int main();
|
||||||
|
static Engine &Get() {
|
||||||
|
static Engine instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////// WORLD
|
||||||
|
template <typename T>
|
||||||
|
void LoadWorld() {
|
||||||
|
static_assert(std::is_base_of_v<World, T>, "LoadWorld<T>: T MUST inherit from craft::World!");
|
||||||
|
mLoadedWorld = std::make_unique<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void RegisterInitialWorld(std::function<unique<World>()> factory) {
|
||||||
|
Get().initialWorldFactory = factory;
|
||||||
|
}
|
||||||
|
|
||||||
|
World &GetWorld() {
|
||||||
|
return *mLoadedWorld;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Engine() : mRunning{true}, mTargetFixedFrameRate(60.f) {}
|
||||||
|
|
||||||
|
unique<Graphics> mGraphics = std::make_unique<Graphics>();
|
||||||
|
|
||||||
|
bool mRunning;
|
||||||
|
|
||||||
|
float mTargetFixedFrameRate;
|
||||||
|
|
||||||
|
void Run() {
|
||||||
|
|
||||||
|
static uint64_t lastTime = SDL_GetPerformanceCounter();
|
||||||
|
static uint64_t frequency = SDL_GetPerformanceFrequency();
|
||||||
|
static float accumulatedTime = 0.0f;
|
||||||
|
|
||||||
|
uint64_t currentTime = SDL_GetPerformanceCounter();
|
||||||
|
float frameDeltaTime = (currentTime - lastTime) / (float)frequency;
|
||||||
|
|
||||||
|
lastTime = currentTime;
|
||||||
|
|
||||||
|
accumulatedTime += frameDeltaTime;
|
||||||
|
|
||||||
|
while (mRunning) {
|
||||||
|
// Begin the world if needed
|
||||||
|
if (!GetWorld().HasBegun()) {
|
||||||
|
GetWorld().Begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_Event event;
|
||||||
|
while (SDL_PollEvent(&event)) {
|
||||||
|
if (!mGraphics->HandleEvents(event)) {
|
||||||
|
mRunning = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
float targetDeltaTime = 1.f / mTargetFixedFrameRate;
|
||||||
|
while (accumulatedTime >= targetDeltaTime) {
|
||||||
|
accumulatedTime -= targetDeltaTime;
|
||||||
|
mLoadedWorld->FixedTick();
|
||||||
|
}
|
||||||
|
|
||||||
|
mGraphics->Clear();
|
||||||
|
|
||||||
|
mLoadedWorld->Tick(frameDeltaTime);
|
||||||
|
|
||||||
|
mGraphics->Render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// CRAZY WORLD INIT SHIT
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//////////
|
||||||
|
std::function<unique<World>()> initialWorldFactory;
|
||||||
|
|
||||||
|
unique<World> mLoadedWorld;
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Explain this shit, no shot I remember how this works in 3 days.
|
||||||
|
|
||||||
|
#define CRAFT_INITIALIZE(WorldClass) \
|
||||||
|
namespace { \
|
||||||
|
static int __craft_init_##WorldClass = []() { \
|
||||||
|
static_assert(std::is_base_of_v<craft::World, WorldClass>, \
|
||||||
|
"CRAFT_INITIALIZE: " #WorldClass " must inherit from craft::World!"); \
|
||||||
|
craft::Engine::RegisterInitialWorld([]() -> std::unique_ptr<craft::World> { \
|
||||||
|
return std::make_unique<WorldClass>(); \
|
||||||
|
}); \
|
||||||
|
return 0; \
|
||||||
|
}(); \
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace craft
|
||||||
23
include/Craft/Graphics.h
Normal file
23
include/Craft/Graphics.h
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
|
||||||
|
namespace craft {
|
||||||
|
|
||||||
|
class Graphics {
|
||||||
|
public:
|
||||||
|
Graphics();
|
||||||
|
~Graphics();
|
||||||
|
void Clear();
|
||||||
|
void Render();
|
||||||
|
|
||||||
|
inline SDL_Window *GetWindow() const { return mWindow; }
|
||||||
|
inline SDL_Renderer *GetRenderer() const { return mRenderer; }
|
||||||
|
|
||||||
|
bool HandleEvents(SDL_Event &event);
|
||||||
|
|
||||||
|
private:
|
||||||
|
SDL_Window *mWindow;
|
||||||
|
SDL_Renderer *mRenderer;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace craft
|
||||||
16
include/Craft/World.h
Normal file
16
include/Craft/World.h
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace craft {
|
||||||
|
class World {
|
||||||
|
public:
|
||||||
|
World();
|
||||||
|
virtual void Begin();
|
||||||
|
virtual void Tick(float deltaTime);
|
||||||
|
virtual void FixedTick();
|
||||||
|
|
||||||
|
inline bool HasBegun() const { return mHasBegun; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool mHasBegun;
|
||||||
|
};
|
||||||
|
} // namespace craft
|
||||||
19
src/Craft/Engine.cpp
Normal file
19
src/Craft/Engine.cpp
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
#include "Craft/Engine.h"
|
||||||
|
|
||||||
|
int craft::main() {
|
||||||
|
|
||||||
|
Engine &engine = Engine::Get();
|
||||||
|
|
||||||
|
if (engine.initialWorldFactory) {
|
||||||
|
engine.mLoadedWorld = engine.initialWorldFactory();
|
||||||
|
} else {
|
||||||
|
LOG("Error: No initial world registered! Use CRAFT_INITIALIZE(YourWorldClass)");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
Engine::Get().Run();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
craft::main();
|
||||||
|
}
|
||||||
51
src/Craft/Graphics.cpp
Normal file
51
src/Craft/Graphics.cpp
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
#include "Craft/Graphics.h"
|
||||||
|
|
||||||
|
namespace craft {
|
||||||
|
Graphics::Graphics() : mWindow{nullptr}, mRenderer{nullptr} {
|
||||||
|
SDL_Init(SDL_INIT_VIDEO);
|
||||||
|
|
||||||
|
mWindow = SDL_CreateWindow("Craft Engine", 800, 600, 0);
|
||||||
|
|
||||||
|
// If in a debug build and on Apple, use OpenGL.
|
||||||
|
// Otherwise just let SDL decide.
|
||||||
|
#if defined(__APPLE__) && !defined(NDEBUG)
|
||||||
|
static constexpr const char *kRendererName = "opengl";
|
||||||
|
#else
|
||||||
|
static constexpr const char *kRendererName = nullptr;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
mRenderer = SDL_CreateRenderer(mWindow, kRendererName);
|
||||||
|
|
||||||
|
// Not sure if this is needed on non-Apple machines, but
|
||||||
|
// on my Mac it loads behind everything else.
|
||||||
|
SDL_RaiseWindow(mWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
Graphics::~Graphics() {
|
||||||
|
if (mRenderer) {
|
||||||
|
SDL_DestroyRenderer(mRenderer);
|
||||||
|
}
|
||||||
|
if (mWindow) {
|
||||||
|
SDL_DestroyWindow(mWindow);
|
||||||
|
}
|
||||||
|
SDL_Quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Graphics::Clear() {
|
||||||
|
SDL_SetRenderDrawColor(mRenderer, 0, 0, 0, 255);
|
||||||
|
SDL_RenderClear(mRenderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Graphics::Render() {
|
||||||
|
SDL_RenderPresent(mRenderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Graphics::HandleEvents(SDL_Event &event) {
|
||||||
|
|
||||||
|
if (event.type == SDL_EVENT_QUIT) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} // namespace craft
|
||||||
13
src/Craft/World.cpp
Normal file
13
src/Craft/World.cpp
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#include "Craft/World.h"
|
||||||
|
|
||||||
|
namespace craft {
|
||||||
|
|
||||||
|
World::World() {}
|
||||||
|
|
||||||
|
void World::Begin() {
|
||||||
|
mHasBegun = true;
|
||||||
|
}
|
||||||
|
void World::Tick(float deltaTime) {}
|
||||||
|
void World::FixedTick() {}
|
||||||
|
|
||||||
|
} // namespace craft
|
||||||
Reference in New Issue
Block a user