Implement Engine, World, and system initialization

Signed-off-by: Daniel Henry <iamdanhenry@gmail.com>
This commit is contained in:
2025-09-06 19:48:55 -05:00
parent 301e862438
commit d71f3bfab7
10 changed files with 355 additions and 0 deletions

45
.clang-format Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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