Initial engine repository

This commit is contained in:
2026-06-08 19:58:35 +08:00
commit 7a925c3736
137 changed files with 162421 additions and 0 deletions
@@ -0,0 +1,178 @@
#pragma once
#include <utility>
#include <type_traits>
namespace frostbite2D {
template <typename T, typename RefPolicy>
class RefBasePtr : protected RefPolicy {
public:
using value_type = T;
using pointer_type = T*;
using const_pointer_type = const T*;
using reference_type = T&;
using const_reference_type = const T&;
RefBasePtr() noexcept : ptr_(nullptr) {}
RefBasePtr(std::nullptr_t) noexcept : ptr_(nullptr) {}
RefBasePtr(pointer_type p) : ptr_(p) {
RefPolicy::Retain(ptr_);
}
RefBasePtr(const RefBasePtr& other) : ptr_(other.ptr_) {
RefPolicy::Retain(ptr_);
}
RefBasePtr(RefBasePtr&& other) noexcept : ptr_(nullptr) {
Swap(other);
}
~RefBasePtr() {
Tidy();
}
template <typename U,
typename std::enable_if<std::is_convertible<U*, T*>::value, int>::type = 0>
RefBasePtr(const RefBasePtr<U, RefPolicy>& other) {
ptr_ = dynamic_cast<pointer_type>(other.Get());
RefPolicy::Retain(ptr_);
}
pointer_type Get() const noexcept { return ptr_; }
pointer_type* GetAddressOfAndRelease() {
Tidy();
return &ptr_;
}
void Reset(pointer_type ptr = nullptr) {
if (ptr) {
RefBasePtr(ptr).Swap(*this);
} else {
Tidy();
}
}
void Swap(RefBasePtr& other) noexcept {
std::swap(ptr_, other.ptr_);
}
pointer_type operator->() { return ptr_; }
const_pointer_type operator->() const { return ptr_; }
reference_type operator*() { return *ptr_; }
const_reference_type operator*() const { return *ptr_; }
pointer_type* operator&() { return this->GetAddressOfAndRelease(); }
operator bool() const noexcept { return ptr_ != nullptr; }
bool operator!() const noexcept { return ptr_ == nullptr; }
RefBasePtr& operator=(const RefBasePtr& other) {
if (other.ptr_ != ptr_) {
RefBasePtr(other).Swap(*this);
}
return *this;
}
RefBasePtr& operator=(RefBasePtr&& other) noexcept {
if (other.ptr_ != ptr_) {
other.Swap(*this);
}
return *this;
}
RefBasePtr& operator=(pointer_type p) {
if (p != ptr_) {
RefBasePtr(p).Swap(*this);
}
return *this;
}
template <typename U,
typename std::enable_if<std::is_convertible<U*, T*>::value, int>::type = 0>
RefBasePtr& operator=(const RefBasePtr<U, RefPolicy>& other) {
if (other.Get() != ptr_) {
RefBasePtr(dynamic_cast<pointer_type>(other.Get())).Swap(*this);
}
return *this;
}
RefBasePtr& operator=(std::nullptr_t) {
Tidy();
return *this;
}
private:
void Tidy() {
RefPolicy::Release(ptr_);
ptr_ = nullptr;
}
pointer_type ptr_;
};
template <class T, class U, class RefPolicy>
bool operator==(const RefBasePtr<T, RefPolicy>& lhs,
const RefBasePtr<U, RefPolicy>& rhs) noexcept {
return lhs.Get() == rhs.Get();
}
template <class T, class RefPolicy>
bool operator==(const RefBasePtr<T, RefPolicy>& lhs, T* rhs) noexcept {
return lhs.Get() == rhs;
}
template <class T, class RefPolicy>
bool operator==(T* lhs, const RefBasePtr<T, RefPolicy>& rhs) noexcept {
return lhs == rhs.Get();
}
template <class T, class RefPolicy>
bool operator==(const RefBasePtr<T, RefPolicy>& lhs, std::nullptr_t) noexcept {
return !static_cast<bool>(lhs);
}
template <class T, class RefPolicy>
bool operator==(std::nullptr_t, const RefBasePtr<T, RefPolicy>& rhs) noexcept {
return !static_cast<bool>(rhs);
}
template <class T, class U, class RefPolicy>
bool operator!=(const RefBasePtr<T, RefPolicy>& lhs,
const RefBasePtr<U, RefPolicy>& rhs) noexcept {
return !(lhs == rhs);
}
template <class T, class RefPolicy>
bool operator!=(const RefBasePtr<T, RefPolicy>& lhs, T* rhs) noexcept {
return lhs.Get() != rhs;
}
template <class T, class RefPolicy>
bool operator!=(T* lhs, const RefBasePtr<T, RefPolicy>& rhs) noexcept {
return lhs != rhs.Get();
}
template <class T, class RefPolicy>
bool operator!=(const RefBasePtr<T, RefPolicy>& lhs, std::nullptr_t) noexcept {
return static_cast<bool>(lhs);
}
template <class T, class RefPolicy>
bool operator!=(std::nullptr_t, const RefBasePtr<T, RefPolicy>& rhs) noexcept {
return static_cast<bool>(rhs);
}
template <class T, class U, class RefPolicy>
bool operator<(const RefBasePtr<T, RefPolicy>& lhs,
const RefBasePtr<U, RefPolicy>& rhs) noexcept {
return lhs.Get() < rhs.Get();
}
template <class T, class RefPolicy>
void swap(RefBasePtr<T, RefPolicy>& lhs, RefBasePtr<T, RefPolicy>& rhs) noexcept {
lhs.Swap(rhs);
}
}
@@ -0,0 +1,272 @@
#pragma once
#include <frostbite2D/core/window.h>
#include <frostbite2D/graphics/camera.h>
#include <frostbite2D/graphics/render_style.h>
#include <frostbite2D/graphics/render_resolution.h>
#include <frostbite2D/types/type_alias.h>
#include <frostbite2D/event/event.h>
#include <string>
#include <memory>
namespace frostbite2D {
/**
* @brief 应用配置结构体
* 仅包含应用级别的配置项,模块配置由各模块自行管理
*/
struct AppConfig {
/**
* @brief 应用名称
*/
std::string appName = "frostbite2D App";
/**
* @brief 应用版本号
*/
std::string appVersion = "1.0.0";
/**
* @brief 组织名称
*/
std::string organization = "frostbite";
/**
* @brief 窗口配置
*/
WindowConfig windowConfig;
/**
* @brief 目标平台
*/
PlatformType targetPlatform = PlatformType::Auto;
/**
* @brief 是否启用虚拟分辨率
*/
bool useVirtualResolution = false;
/**
* @brief 虚拟分辨率宽度
*/
int virtualWidth = 0;
/**
* @brief 虚拟分辨率高度
*/
int virtualHeight = 0;
/**
* @brief 虚拟分辨率缩放模式
*/
ResolutionScaleMode resolutionMode = ResolutionScaleMode::Fit;
bool useSeparateUIVirtualResolution = false;
int uiVirtualWidth = 0;
int uiVirtualHeight = 0;
ResolutionScaleMode uiResolutionMode = ResolutionScaleMode::Fit;
/**
* @brief 默认 2D 渲染风格预设
* 启动时作为世界/UI 渲染的基础风格,Scene 可按需覆盖
*/
RenderStyleProfileId renderStyleProfile = RenderStyleProfileId::Hybrid2D;
/**
* @brief 最大帧率,0 表示不限制
*/
int maxFps = 0;
/**
* @brief 创建默认配置
* @return 默认的应用配置实例
*/
static AppConfig createDefault() {
AppConfig config;
config.appName = "Frostbite2D App";
config.appVersion = "1.0.0";
config.organization = "frostbite";
config.targetPlatform = PlatformType::Auto;
config.windowConfig = WindowConfig();
config.renderStyleProfile = RenderStyleProfileId::Hybrid2D;
config.useSeparateUIVirtualResolution = false;
config.uiVirtualWidth = 0;
config.uiVirtualHeight = 0;
config.uiResolutionMode = ResolutionScaleMode::Fit;
config.maxFps = 0;
return config;
}
};
/**
* @brief 应用程序类
*/
class Application {
public:
using StartCallback = Function<void()>;
/**
* @brief 获取单例实例
* @return 应用程序实例引用
*/
static Application& get();
Application(const Application&) = delete;
Application& operator=(const Application&) = delete;
/**
* @brief 使用默认配置初始化
* @return 初始化成功返回 true
*/
bool init();
/**
* @brief 使用指定配置初始化
* @param config 应用配置
* @return 初始化成功返回 true
*/
bool init(const AppConfig& config);
/**
* @brief 关闭应用程序
*/
void shutdown();
/**
* @brief 运行主循环
* @param callback 主循环开始前执行一次的启动回调
*/
void run(StartCallback callback);
/**
* @brief 请求退出
*/
void quit();
/**
* @brief 暂停应用程序
*/
void pause();
/**
* @brief 恢复应用程序
*/
void resume();
/**
* @brief 检查是否暂停
* @return 暂停状态返回 true
*/
bool isPaused() const { return paused_; }
/**
* @brief 检查是否运行中
* @return 运行中返回 true
*/
bool isRunning() const { return running_; }
/**
* @brief 获取总运行时间
* @return 总运行时间(秒)
*/
float totalTime() const { return totalTime_; }
/**
* @brief 获取当前帧率
* @return 帧率
*/
int fps() const { return currentFps_; }
/**
* @brief 设置最大帧率
* @param maxFps 0 表示不限制,>0 表示帧率上限
*/
void setMaxFps(int maxFps);
/**
* @brief 获取最大帧率设置
* @return 0 表示不限制,>0 表示帧率上限
*/
int maxFps() const { return maxFps_; }
void setTextInputEnabled(bool enabled);
bool isTextInputEnabled() const { return textInputEnabled_; }
void requestTextInput();
void releaseTextInput();
void setImmEnabled(bool enabled);
bool isImmEnabled() const;
/**
* @brief 获取应用配置
* @return 应用配置常量引用
*/
const AppConfig& getConfig() const;
/**
* @brief 获取窗口
* @return 窗口指针
*/
Window* getWindow() const { return window_; }
/**
* @brief 获取渲染器
* @return 渲染器指针
*/
class Renderer* getRenderer() const { return renderer_; }
/**
* @brief 获取 delta time
* @return 帧间隔时间(秒)
*/
float deltaTime() const { return deltaTime_; }
private:
Application() = default;
~Application();
/**
* @brief 初始化核心模块
* @return 初始化成功返回 true
*/
bool initCoreModules();
/**
* @brief 主循环
*/
void mainLoop();
/**
* @brief 更新
*/
void update();
/**
* @brief 渲染
*/
void render();
std::unique_ptr<Event> convertSDLEvent(const SDL_Event& sdlEvent);
void dispatchEvent(const Event& event);
Window* window_ = nullptr;
class Renderer* renderer_ = nullptr;
Camera* camera_ = nullptr;
AppConfig config_;
bool initialized_ = false;
bool running_ = false;
bool paused_ = false;
bool shouldQuit_ = false;
bool textInputEnabled_ = false;
bool immEnabled_ = false;
int textInputRequestCount_ = 0;
float deltaTime_ = 0.0f;
float totalTime_ = 0.0f;
double lastFrameTime_ = 0.0;
int frameCount_ = 0;
float fpsTimer_ = 0.0f;
int currentFps_ = 0;
int maxFps_ = 0;
double targetFrameDuration_ = 0.0;
const float fpsUpdateInterval_ = 1.0f;
};
}
@@ -0,0 +1,142 @@
#pragma once
#include <atomic>
#include <condition_variable>
#include <exception>
#include <future>
#include <memory>
#include <mutex>
#include <queue>
#include <thread>
#include <type_traits>
#include <utility>
#include <vector>
#include <frostbite2D/types/type_alias.h>
namespace frostbite2D {
struct TaskSystemConfig {
uint32 workerCount = 0;
uint32 maxMainThreadCallbacksPerFrame = 0;
uint32 threadStackSize = 0;
};
class TaskSystem {
public:
static TaskSystem& get();
TaskSystem(const TaskSystem&) = delete;
TaskSystem& operator=(const TaskSystem&) = delete;
bool init(const TaskSystemConfig& config = {});
void shutdown();
bool isInitialized() const { return initialized_; }
bool isMainThread() const;
size_t workerCount() const;
uint32 maxMainThreadCallbacksPerFrame() const;
template <typename F>
void post(F&& task) {
enqueueWorkerTask(Function<void()>(std::forward<F>(task)));
}
template <typename F>
auto submit(F&& task) -> std::future<std::invoke_result_t<F>> {
using Result = std::invoke_result_t<F>;
auto packagedTask =
std::make_shared<std::packaged_task<Result()>>(std::forward<F>(task));
std::future<Result> future = packagedTask->get_future();
enqueueWorkerTask([packagedTask]() mutable { (*packagedTask)(); });
return future;
}
template <typename F>
void postToMainThread(F&& callback) {
enqueueMainThreadTask(Function<void()>(std::forward<F>(callback)));
}
template <typename Task, typename OnComplete>
void submitThen(Task&& task, OnComplete&& onComplete) {
using Result = std::invoke_result_t<Task>;
using Storage = std::decay_t<Result>;
auto taskFn = std::make_shared<std::decay_t<Task>>(std::forward<Task>(task));
auto completeFn =
std::make_shared<std::decay_t<OnComplete>>(std::forward<OnComplete>(onComplete));
enqueueWorkerTask([this, taskFn, completeFn]() mutable {
try {
if constexpr (std::is_void_v<Result>) {
std::invoke(*taskFn);
enqueueMainThreadTask([completeFn]() mutable { std::invoke(*completeFn); });
} else {
auto result = std::make_shared<Storage>(std::invoke(*taskFn));
enqueueMainThreadTask([completeFn, result]() mutable {
std::invoke(*completeFn, std::move(*result));
});
}
} catch (...) {
logUnhandledTaskException(std::current_exception());
}
});
}
template <typename Task, typename OnComplete, typename OnError>
void submitThen(Task&& task, OnComplete&& onComplete, OnError&& onError) {
using Result = std::invoke_result_t<Task>;
using Storage = std::decay_t<Result>;
auto taskFn = std::make_shared<std::decay_t<Task>>(std::forward<Task>(task));
auto completeFn =
std::make_shared<std::decay_t<OnComplete>>(std::forward<OnComplete>(onComplete));
auto errorFn =
std::make_shared<std::decay_t<OnError>>(std::forward<OnError>(onError));
enqueueWorkerTask([this, taskFn, completeFn, errorFn]() mutable {
try {
if constexpr (std::is_void_v<Result>) {
std::invoke(*taskFn);
enqueueMainThreadTask([completeFn]() mutable { std::invoke(*completeFn); });
} else {
auto result = std::make_shared<Storage>(std::invoke(*taskFn));
enqueueMainThreadTask([completeFn, result]() mutable {
std::invoke(*completeFn, std::move(*result));
});
}
} catch (...) {
auto error = std::make_shared<std::exception_ptr>(std::current_exception());
enqueueMainThreadTask([errorFn, error]() mutable { std::invoke(*errorFn, *error); });
}
});
}
void drainMainThreadTasks(uint32 maxTasks = 0);
private:
TaskSystem() = default;
~TaskSystem();
void enqueueWorkerTask(Function<void()> task);
void enqueueMainThreadTask(Function<void()> task);
size_t resolveWorkerCount(uint32 requestedCount) const;
static void logUnhandledTaskException(std::exception_ptr error);
TaskSystemConfig config_;
std::vector<std::thread> workers_;
std::queue<Function<void()>> workerTasks_;
std::queue<Function<void()>> mainThreadTasks_;
mutable std::mutex workerMutex_;
mutable std::mutex mainThreadMutex_;
std::condition_variable workerCondition_;
std::atomic<bool> initialized_ = false;
std::atomic<bool> acceptingTasks_ = false;
std::thread::id mainThreadId_;
};
} // namespace frostbite2D
@@ -0,0 +1,137 @@
#pragma once
#include <SDL2/SDL.h>
#include <frostbite2D/types/type_alias.h>
#include <frostbite2D/types/type_math.h>
#include <functional>
#include <string>
namespace frostbite2D {
enum class CursorType {
Arrow,
TextInput,
Hand,
SizeAll,
SizeWE,
SizeNS,
SizeNESW,
SizeNWSE,
No,
};
struct Resolution {
uint32_t width = 0;
uint32_t height = 0;
uint32_t refresh_rate = 0;
Resolution() = default;
Resolution(uint32_t width, uint32_t height, uint32_t refresh_rate)
: width(width), height(height), refresh_rate(refresh_rate) {}
};
struct Icon {
Icon() = default;
Icon(std::string file_path) : file_path(file_path) {}
std::string file_path;
#if defined(_WIN32)
uint32_t resource_id = 0;
Icon(uint32_t resource_id) : resource_id(resource_id) {}
#endif
};
struct WindowConfig {
uint32_t width = 640;
uint32_t height = 480;
std::string title = "frostbite2D Game";
Icon icon;
bool resizable = false;
bool fullscreen = false;
bool borderless = false;
bool decorated = true;
int multisamples = 0;
bool centered = true;
bool vsync = true;
bool showCursor = true;
};
class Window {
public:
Window() = default;
~Window() = default;
virtual bool create(const WindowConfig& cfg);
virtual void destroy();
virtual void poll();
virtual void swap();
virtual void close();
virtual void setTitle(const std::string& title);
virtual void setSize(int w, int h);
virtual void setPos(int x, int y);
virtual void setFullscreen(bool fs);
virtual void setVSync(bool vsync);
virtual void setVisible(bool visible);
virtual int width() const;
virtual int height() const;
virtual int drawableWidth() const;
virtual int drawableHeight() const;
virtual Size size() const;
virtual Vec2 pos() const;
virtual bool fullscreen() const;
virtual bool vsync() const;
virtual bool focused() const;
virtual bool minimized() const;
virtual void setImmEnabled(bool enabled);
virtual bool isImmEnabled() const;
virtual float scaleX() const;
virtual float scaleY() const;
virtual bool refreshMetrics(bool emitResize = false);
virtual void setCursor(CursorType cursor);
virtual void showCursor(bool show);
virtual void lockCursor(bool lock);
using ResizeCb = std::function<void(int, int)>;
using CloseCb = std::function<void()>;
using FocusCb = std::function<void(bool)>;
virtual void onResize(ResizeCb cb);
virtual void onClose(CloseCb cb);
virtual void onFocus(FocusCb cb);
virtual void* native() const;
SDL_Window* sdlWindow() const { return sdlWindow_; }
SDL_GLContext glContext() const { return glContext_; }
private:
SDL_Window* sdlWindow_ = nullptr;
SDL_GLContext glContext_ = nullptr;
int width_ = 1280;
int height_ = 720;
int drawableWidth_ = 1280;
int drawableHeight_ = 720;
bool fullscreen_ = false;
bool vsync_ = true;
bool focused_ = true;
bool minimized_ = false;
bool shouldClose_ = false;
float scaleX_ = 1.0f;
float scaleY_ = 1.0f;
bool cursorVisible_ = true;
bool cursorLocked_ = false;
bool immEnabled_ = false;
bool immDisabledByApp_ = false;
void* immRestoreContext_ = nullptr;
ResizeCb resizeCb_;
CloseCb closeCb_;
FocusCb focusCb_;
};
} // namespace frostbite2D