refactor(engine): 重构引擎代码结构,优化类型系统和核心模块

- 将核心类型和数学工具移动到types目录下,按功能分类
- 重构引用计数和智能指针实现,提高线程安全性
- 新增调度器和服务管理器,提供统一的更新机制
- 移除旧的输入系统和窗口管理代码
- 优化日志系统,移除冗余代码
- 添加TBB依赖,支持并行任务调度
- 统一代码风格,使用更简洁的命名规范
This commit is contained in:
ChestnutYueyue 2026-02-27 20:46:16 +08:00
parent 3b031666ae
commit 71eeeac033
41 changed files with 1356 additions and 15167 deletions

View File

@ -1,89 +1,76 @@
#pragma once
#include <core/types.h>
#include <string>
#include <memory>
#include <platform/window.h>
#include <types/base/types.h>
namespace extra2d {
// 前向声明
class Input;
class TimerManager;
// ============================================================================
// Application 配置
// ============================================================================
enum class PlatformType { Auto = 0, PC, Switch };
struct AppConfig {
std::string title = "Easy2D Application";
int width = 800;
int height = 600;
bool fullscreen = false;
bool resizable = true;
bool vsync = true;
int fpsLimit = 0;
int msaaSamples = 0;
PlatformType platform = PlatformType::Auto;
bool enableCursors = true;
bool enableDpiScale = false;
std::string title = "Extra2D Application";
int width = 800;
int height = 600;
bool fullscreen = false;
bool resizable = true;
bool vsync = true;
int fpsLimit = 0;
int msaaSamples = 0;
PlatformType platform = PlatformType::Auto;
bool enableCursors = true;
bool enableDpiScale = false;
};
// ============================================================================
// Application 单例 - 应用主控
// ============================================================================
/**
* @brief
*
*
*/
class Application {
public:
static Application &instance();
static Application& instance();
Application(const Application &) = delete;
Application &operator=(const Application &) = delete;
Application(const Application&) = delete;
Application& operator=(const Application&) = delete;
bool init(const AppConfig &config);
void shutdown();
void run();
void quit();
bool init(const AppConfig& config);
void shutdown();
void run();
void quit();
void pause();
void resume();
bool isPaused() const { return paused_; }
bool isRunning() const { return running_; }
void pause();
void resume();
bool isPaused() const { return paused_; }
bool isRunning() const { return running_; }
Window &window() { return *window_; }
Input &input();
TimerManager &timers();
float deltaTime() const { return deltaTime_; }
float totalTime() const { return totalTime_; }
int fps() const { return currentFps_; }
float deltaTime() const { return deltaTime_; }
float totalTime() const { return totalTime_; }
int fps() const { return currentFps_; }
const AppConfig &getConfig() const { return config_; }
const AppConfig& getConfig() const { return config_; }
private:
Application() = default;
~Application();
Application() = default;
~Application();
void mainLoop();
void update();
void mainLoop();
void update();
AppConfig config_;
AppConfig config_;
UniquePtr<Window> window_;
UniquePtr<TimerManager> timerManager_;
bool initialized_ = false;
bool running_ = false;
bool paused_ = false;
bool shouldQuit_ = false;
bool initialized_ = false;
bool running_ = false;
bool paused_ = false;
bool shouldQuit_ = false;
float deltaTime_ = 0.0f;
float totalTime_ = 0.0f;
double lastFrameTime_ = 0.0;
int frameCount_ = 0;
float fpsTimer_ = 0.0f;
int currentFps_ = 0;
float deltaTime_ = 0.0f;
float totalTime_ = 0.0f;
double lastFrameTime_ = 0.0;
int frameCount_ = 0;
float fpsTimer_ = 0.0f;
int currentFps_ = 0;
};
#define APP extra2d::Application::instance()
} // namespace extra2d

View File

@ -1,156 +0,0 @@
#pragma once
#include <algorithm>
#include <core/types.h>
#include <glm/vec4.hpp>
namespace extra2d {
/// RGB 颜色(字节,每通道 0-255
struct Color3B {
uint8_t r = 255;
uint8_t g = 255;
uint8_t b = 255;
constexpr Color3B() = default;
constexpr Color3B(uint8_t r, uint8_t g, uint8_t b) : r(r), g(g), b(b) {}
constexpr bool operator==(const Color3B &other) const {
return r == other.r && g == other.g && b == other.b;
}
constexpr bool operator!=(const Color3B &other) const {
return !(*this == other);
}
Color3B operator+(const Color3B &other) const {
return Color3B(
static_cast<uint8_t>(std::min(255, static_cast<int>(r) + other.r)),
static_cast<uint8_t>(std::min(255, static_cast<int>(g) + other.g)),
static_cast<uint8_t>(std::min(255, static_cast<int>(b) + other.b)));
}
Color3B operator-(const Color3B &other) const {
return Color3B(
static_cast<uint8_t>(std::max(0, static_cast<int>(r) - other.r)),
static_cast<uint8_t>(std::max(0, static_cast<int>(g) - other.g)),
static_cast<uint8_t>(std::max(0, static_cast<int>(b) - other.b)));
}
};
/// RGBA 颜色(浮点数,每通道 0.0 - 1.0
struct Color {
float r = 0.0f;
float g = 0.0f;
float b = 0.0f;
float a = 1.0f;
constexpr Color() = default;
constexpr Color(float r, float g, float b, float a = 1.0f)
: r(r), g(g), b(b), a(a) {}
/// 从 0xRRGGBB 整数构造
constexpr explicit Color(uint32_t rgb, float a = 1.0f)
: r(static_cast<float>((rgb >> 16) & 0xFF) / 255.0f),
g(static_cast<float>((rgb >> 8) & 0xFF) / 255.0f),
b(static_cast<float>((rgb) & 0xFF) / 255.0f), a(a) {}
/// 从 0-255 整数构造
static constexpr Color fromRGBA(uint8_t r, uint8_t g, uint8_t b,
uint8_t a = 255) {
return Color(r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f);
}
/// 转换为 glm::vec4
glm::vec4 toVec4() const { return {r, g, b, a}; }
/// 线性插值
static Color lerp(const Color &a, const Color &b, float t) {
t = std::clamp(t, 0.0f, 1.0f);
return Color(a.r + (b.r - a.r) * t, a.g + (b.g - a.g) * t,
a.b + (b.b - a.b) * t, a.a + (b.a - a.a) * t);
}
bool operator==(const Color &other) const {
return r == other.r && g == other.g && b == other.b && a == other.a;
}
bool operator!=(const Color &other) const { return !(*this == other); }
// 算术运算符
Color operator+(const Color &other) const {
return Color(r + other.r, g + other.g, b + other.b, a + other.a);
}
Color operator-(const Color &other) const {
return Color(r - other.r, g - other.g, b - other.b, a - other.a);
}
Color operator*(float scalar) const {
return Color(r * scalar, g * scalar, b * scalar, a * scalar);
}
Color operator/(float scalar) const {
return Color(r / scalar, g / scalar, b / scalar, a / scalar);
}
Color &operator+=(const Color &other) {
r += other.r;
g += other.g;
b += other.b;
a += other.a;
return *this;
}
Color &operator-=(const Color &other) {
r -= other.r;
g -= other.g;
b -= other.b;
a -= other.a;
return *this;
}
Color &operator*=(float scalar) {
r *= scalar;
g *= scalar;
b *= scalar;
a *= scalar;
return *this;
}
Color &operator/=(float scalar) {
r /= scalar;
g /= scalar;
b /= scalar;
a /= scalar;
return *this;
}
};
// 命名颜色常量
namespace Colors {
inline constexpr Color White{1.0f, 1.0f, 1.0f, 1.0f};
inline constexpr Color Black{0.0f, 0.0f, 0.0f, 1.0f};
inline constexpr Color Red{1.0f, 0.0f, 0.0f, 1.0f};
inline constexpr Color Green{0.0f, 1.0f, 0.0f, 1.0f};
inline constexpr Color Blue{0.0f, 0.0f, 1.0f, 1.0f};
inline constexpr Color Yellow{1.0f, 1.0f, 0.0f, 1.0f};
inline constexpr Color Cyan{0.0f, 1.0f, 1.0f, 1.0f};
inline constexpr Color Magenta{1.0f, 0.0f, 1.0f, 1.0f};
inline constexpr Color Orange{1.0f, 0.647f, 0.0f, 1.0f};
inline constexpr Color Purple{0.502f, 0.0f, 0.502f, 1.0f};
inline constexpr Color Pink{1.0f, 0.753f, 0.796f, 1.0f};
inline constexpr Color Gray{0.502f, 0.502f, 0.502f, 1.0f};
inline constexpr Color LightGray{0.827f, 0.827f, 0.827f, 1.0f};
inline constexpr Color DarkGray{0.412f, 0.412f, 0.412f, 1.0f};
inline constexpr Color Brown{0.647f, 0.165f, 0.165f, 1.0f};
inline constexpr Color Gold{1.0f, 0.843f, 0.0f, 1.0f};
inline constexpr Color Silver{0.753f, 0.753f, 0.753f, 1.0f};
inline constexpr Color SkyBlue{0.529f, 0.808f, 0.922f, 1.0f};
inline constexpr Color LimeGreen{0.196f, 0.804f, 0.196f, 1.0f};
inline constexpr Color Coral{1.0f, 0.498f, 0.314f, 1.0f};
inline constexpr Color Transparent{0.0f, 0.0f, 0.0f, 0.0f};
} // namespace Colors
} // namespace extra2d

53
include/core/director.h Normal file
View File

@ -0,0 +1,53 @@
#pragma once
#include <core/scheduler.h>
#include <core/service.h>
namespace extra2d {
/**
* @brief
*
*
*/
class Director {
public:
static Director& inst();
bool init();
void shutdown();
void mainLoop(float dt);
void mainLoopParallel(float dt);
Scheduler& sched() { return *sched_; }
SvcMgr& svcs() { return *svcMgr_; }
float dt() const { return dt_; }
float totalTime() const { return totalTime_; }
uint64 frameCount() const { return frameCount_; }
void pause();
void resume();
bool isPaused() const { return paused_; }
void setTimeScale(float scale);
private:
Director() = default;
Unique<Scheduler> sched_;
Unique<SvcMgr> svcMgr_;
float dt_ = 0.0f;
float totalTime_ = 0.0f;
float fixedAccumulator_ = 0.0f;
float fixedDt_ = 1.0f / 60.0f;
uint64 frameCount_ = 0;
bool paused_ = false;
bool inited_ = false;
};
#define DIRECTOR extra2d::Director::inst()
} // namespace extra2d

View File

@ -1,172 +0,0 @@
#pragma once
#include <core/ref_counted.h>
#include <functional>
#include <utility>
namespace extra2d {
// ============================================================================
// 侵入式智能指针
// 参考 Cocos2d-x 的 IntrusivePtr 设计
// ============================================================================
template <class T>
class IntrusivePtr {
public:
using element_type = T;
// 默认构造函数
IntrusivePtr() : _ptr(nullptr) {}
// 从原始指针构造
IntrusivePtr(T* p) : _ptr(p) {
if (_ptr) {
_ptr->addRef();
}
}
// 拷贝构造函数
IntrusivePtr(const IntrusivePtr<T>& r) : _ptr(r._ptr) {
if (_ptr) {
_ptr->addRef();
}
}
// 从派生类拷贝构造
template <typename U>
IntrusivePtr(const IntrusivePtr<U>& r) : _ptr(r.get()) {
if (_ptr) {
_ptr->addRef();
}
}
// 移动构造函数
IntrusivePtr(IntrusivePtr<T>&& r) noexcept : _ptr(r.release()) {}
// 从派生类移动构造
template <typename U>
IntrusivePtr(IntrusivePtr<U>&& r) noexcept : _ptr(r.release()) {}
// 析构函数
~IntrusivePtr() {
if (_ptr) {
_ptr->release();
}
}
// 获取原始指针
T* get() const { return _ptr; }
// 解引用操作符
T& operator*() const { return *_ptr; }
// 箭头操作符
T* operator->() const { return _ptr; }
// 转换为原始指针(隐式转换)
operator T*() const { return _ptr; }
// 赋值操作符 - 原始指针
IntrusivePtr<T>& operator=(T* p) {
reset(p);
return *this;
}
// 赋值操作符 - 同类型
IntrusivePtr<T>& operator=(const IntrusivePtr<T>& r) {
return *this = r._ptr;
}
// 赋值操作符 - 派生类
template <typename U>
IntrusivePtr<T>& operator=(const IntrusivePtr<U>& r) {
return *this = r.get();
}
// 移动赋值
IntrusivePtr<T>& operator=(IntrusivePtr<T>&& r) noexcept {
IntrusivePtr<T>(std::move(r)).swap(*this);
return *this;
}
// 从派生类移动赋值
template <typename U>
IntrusivePtr<T>& operator=(IntrusivePtr<U>&& r) noexcept {
IntrusivePtr<T>(std::move(r)).swap(*this);
return *this;
}
// 比较操作符
bool operator==(std::nullptr_t) const { return _ptr == nullptr; }
bool operator==(T* r) const { return _ptr == r; }
bool operator!=(std::nullptr_t) const { return _ptr != nullptr; }
bool operator!=(T* r) const { return _ptr != r; }
bool operator<(const IntrusivePtr<T>& r) const { return _ptr < r._ptr; }
// 重置指针
void reset() noexcept {
if (_ptr) {
_ptr->release();
}
_ptr = nullptr;
}
void reset(T* p) {
// 先增加新指针的引用计数,再释放旧指针
// 这样可以处理自赋值的情况
if (p) {
p->addRef();
}
if (_ptr) {
_ptr->release();
}
_ptr = p;
}
// 交换指针
void swap(T** pp) noexcept {
T* p = _ptr;
_ptr = *pp;
*pp = p;
}
void swap(IntrusivePtr<T>& r) noexcept {
swap(&r._ptr);
}
// 释放指针所有权(不减少引用计数)
T* release() {
T* retVal = _ptr;
_ptr = nullptr;
return retVal;
}
private:
T* _ptr;
};
// ============================================================================
// 辅助函数
// ============================================================================
// 创建 IntrusivePtr 的便捷函数
template <typename T, typename... Args>
inline IntrusivePtr<T> makePtr(Args&&... args) {
return IntrusivePtr<T>(new T(std::forward<Args>(args)...));
}
} // namespace extra2d
// ============================================================================
// std::hash 特化
// ============================================================================
namespace std {
template <class T>
struct hash<extra2d::IntrusivePtr<T>> {
size_t operator()(const extra2d::IntrusivePtr<T>& val) const noexcept {
return hash<T*>{}(val.get());
}
};
} // namespace std

View File

@ -1,77 +0,0 @@
#pragma once
#include <algorithm>
#include <core/vec2.h>
#include <core/size.h>
namespace extra2d {
// ---------------------------------------------------------------------------
// 2D 矩形
// ---------------------------------------------------------------------------
struct Rect {
Point origin;
Size size;
constexpr Rect() = default;
constexpr Rect(float x, float y, float w, float h)
: origin(x, y), size(w, h) {}
constexpr Rect(const Point &o, const Size &s) : origin(o), size(s) {}
float left() const { return origin.x; }
float top() const { return origin.y; }
float right() const { return origin.x + size.width; }
float bottom() const { return origin.y + size.height; }
float width() const { return size.width; }
float height() const { return size.height; }
Point center() const {
return {origin.x + size.width * 0.5f, origin.y + size.height * 0.5f};
}
bool empty() const { return size.empty(); }
bool containsPoint(const Point &p) const {
return p.x >= left() && p.x <= right() && p.y >= top() && p.y <= bottom();
}
bool contains(const Rect &r) const {
return r.left() >= left() && r.right() <= right() && r.top() >= top() &&
r.bottom() <= bottom();
}
bool intersects(const Rect &r) const {
return !(left() > r.right() || right() < r.left() || top() > r.bottom() ||
bottom() < r.top());
}
Rect intersection(const Rect &r) const {
float l = std::max(left(), r.left());
float t = std::max(top(), r.top());
float ri = std::min(right(), r.right());
float b = std::min(bottom(), r.bottom());
if (l < ri && t < b)
return {l, t, ri - l, b - t};
return {};
}
Rect unionWith(const Rect &r) const {
if (empty())
return r;
if (r.empty())
return *this;
float l = std::min(left(), r.left());
float t = std::min(top(), r.top());
float ri = std::max(right(), r.right());
float b = std::max(bottom(), r.bottom());
return {l, t, ri - l, b - t};
}
bool operator==(const Rect &r) const {
return origin == r.origin && size == r.size;
}
bool operator!=(const Rect &r) const { return !(*this == r); }
static constexpr Rect Zero() { return {0, 0, 0, 0}; }
};
} // namespace extra2d

View File

@ -1,41 +0,0 @@
#pragma once
#include <atomic>
#include <cstdint>
namespace extra2d {
// ============================================================================
// 侵入式引用计数基类
// 参考 Cocos2d-x 的 RefCounted 设计
// ============================================================================
class RefCounted {
public:
virtual ~RefCounted() = default;
// 增加引用计数
void addRef() {
++_referenceCount;
}
// 减少引用计数当计数为0时删除对象
void release() {
if (--_referenceCount == 0) {
delete this;
}
}
// 获取当前引用计数
uint32_t getRefCount() const {
return _referenceCount.load();
}
protected:
// 构造函数初始引用计数为1
RefCounted() : _referenceCount(1) {}
private:
std::atomic<uint32_t> _referenceCount;
};
} // namespace extra2d

122
include/core/scheduler.h Normal file
View File

@ -0,0 +1,122 @@
#pragma once
#include <types/base/types.h>
#include <types/ptr/ref_counted.h>
#include <types/ptr/intrusive_ptr.h>
#include <types/const/priority.h>
#include <tbb/concurrent_vector.h>
#include <tbb/concurrent_hash_map.h>
#include <tbb/concurrent_priority_queue.h>
#include <atomic>
namespace extra2d {
using TimerHdl = uint32;
constexpr TimerHdl INVALID_HDL = 0;
/**
* @brief
*
* update
*/
class TimerTarget {
public:
virtual ~TimerTarget() = default;
virtual void update(float dt) = 0;
};
/**
* @brief
*/
class Timer : public RefCounted {
public:
virtual ~Timer() = default;
virtual void update(float dt) = 0;
virtual void trigger() = 0;
bool isPaused() const { return paused_; }
bool isDone() const { return done_; }
TimerHdl hdl() const { return hdl_; }
void pause() { paused_ = true; }
void resume() { paused_ = false; }
void cancel() { done_ = true; }
protected:
Timer() = default;
float elapsed_ = -1.0f;
float interval_ = 0.0f;
float delay_ = 0.0f;
uint32 repeat_ = 0;
uint32 timesExecuted_ = 0;
bool useDelay_ = false;
bool runForever_ = false;
bool paused_ = false;
bool done_ = false;
TimerHdl hdl_ = INVALID_HDL;
friend class Scheduler;
};
/**
* @brief
*
* TBB 线 update
*/
class Scheduler {
public:
using Cb = Fn<void(float)>;
using VoidCb = Fn<void()>;
static Scheduler& inst();
TimerHdl scheduleUpdate(TimerTarget* target, int pri = 0);
void unscheduleUpdate(TimerTarget* target);
TimerHdl schedule(Cb cb, float interval, uint32 repeat = 0, float delay = 0.0f);
TimerHdl scheduleOnce(VoidCb cb, float delay);
TimerHdl scheduleForever(Cb cb, float interval);
void unschedule(TimerHdl hdl);
void unscheduleAll();
void pause(TimerHdl hdl);
void resume(TimerHdl hdl);
void setTimeScale(float scale) { timeScale_ = scale; }
float timeScale() const { return timeScale_; }
void update(float dt);
void updateParallel(float dt);
bool isScheduled(TimerHdl hdl) const;
size_t count() const;
private:
Scheduler() = default;
struct UpdateEntry {
TimerTarget* target;
int pri;
bool paused;
bool markedForDel;
bool operator<(const UpdateEntry& o) const { return pri > o.pri; }
};
tbb::concurrent_vector<UpdateEntry> updates_;
tbb::concurrent_hash_map<TimerTarget*, size_t> updateIndex_;
tbb::concurrent_hash_map<TimerHdl, Ptr<Timer>> timers_;
tbb::concurrent_priority_queue<UpdateEntry> updateQueue_;
std::atomic<TimerHdl> nextHdl_{1};
std::atomic<float> timeScale_{1.0f};
std::atomic<bool> locked_{false};
TimerHdl genHdl();
};
#define SCHED extra2d::Scheduler::inst()
} // namespace extra2d

82
include/core/service.h Normal file
View File

@ -0,0 +1,82 @@
#pragma once
#include <types/base/types.h>
#include <types/ptr/ref_counted.h>
#include <types/ptr/intrusive_ptr.h>
#include <types/const/priority.h>
#include <string>
#include <vector>
#include <tbb/concurrent_hash_map.h>
namespace extra2d {
/**
* @brief
*
*
*/
class IService : public RefCounted {
public:
virtual ~IService() = default;
virtual bool init() { return true; }
virtual void shutdown() {}
virtual void update(float dt) {}
virtual void lateUpdate(float dt) {}
virtual void fixedUpdate(float dt) {}
virtual const char* name() const = 0;
virtual int pri() const { return Pri::Default; }
bool isInited() const { return inited_; }
bool isEnabled() const { return enabled_; }
void setEnabled(bool v) { enabled_ = v; }
protected:
IService() = default;
bool inited_ = false;
bool enabled_ = true;
};
/**
* @brief
*
*
*/
class SvcMgr {
public:
static SvcMgr& inst();
void reg(Ptr<IService> svc);
void unreg(const char* name);
Ptr<IService> get(const char* name);
template<typename T>
Ptr<T> getAs(const char* name) {
return static_cast<T*>(get(name).get());
}
bool initAll();
void shutdownAll();
void updateAll(float dt);
void lateUpdateAll(float dt);
void fixedUpdateAll(float dt);
bool has(const char* name) const;
size_t count() const;
private:
SvcMgr() = default;
using SvcMap = tbb::concurrent_hash_map<std::string, Ptr<IService>>;
SvcMap svcMap_;
std::vector<Ptr<IService>> sortedSvcs_;
void sortSvcs();
};
#define SVC_MGR extra2d::SvcMgr::inst()
} // namespace extra2d

View File

@ -1,26 +0,0 @@
#pragma once
namespace extra2d {
// ---------------------------------------------------------------------------
// 2D 尺寸
// ---------------------------------------------------------------------------
struct Size {
float width = 0.0f;
float height = 0.0f;
constexpr Size() = default;
constexpr Size(float w, float h) : width(w), height(h) {}
bool operator==(const Size &s) const {
return width == s.width && height == s.height;
}
bool operator!=(const Size &s) const { return !(*this == s); }
float area() const { return width * height; }
bool empty() const { return width <= 0.0f || height <= 0.0f; }
static constexpr Size Zero() { return {0.0f, 0.0f}; }
};
} // namespace extra2d

View File

@ -1,87 +0,0 @@
#pragma once
#include <core/vec2.h>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/mat4x4.hpp>
namespace extra2d {
// ---------------------------------------------------------------------------
// 2D 变换矩阵(基于 glm::mat4兼容 OpenGL
// ---------------------------------------------------------------------------
struct Transform2D {
glm::mat4 matrix{1.0f}; // 单位矩阵
Transform2D() = default;
explicit Transform2D(const glm::mat4 &m) : matrix(m) {}
static Transform2D identity() { return Transform2D{}; }
static Transform2D translation(float x, float y) {
Transform2D t;
t.matrix = glm::translate(glm::mat4(1.0f), glm::vec3(x, y, 0.0f));
return t;
}
static Transform2D translation(const Vec2 &v) {
return translation(v.x, v.y);
}
static Transform2D rotation(float degrees) {
Transform2D t;
t.matrix = glm::rotate(glm::mat4(1.0f), degrees * DEG_TO_RAD,
glm::vec3(0.0f, 0.0f, 1.0f));
return t;
}
static Transform2D scaling(float sx, float sy) {
Transform2D t;
t.matrix = glm::scale(glm::mat4(1.0f), glm::vec3(sx, sy, 1.0f));
return t;
}
static Transform2D scaling(float s) { return scaling(s, s); }
static Transform2D skewing(float skewX, float skewY) {
Transform2D t;
t.matrix = glm::mat4(1.0f);
t.matrix[1][0] = std::tan(skewX * DEG_TO_RAD);
t.matrix[0][1] = std::tan(skewY * DEG_TO_RAD);
return t;
}
Transform2D operator*(const Transform2D &other) const {
return Transform2D(matrix * other.matrix);
}
Transform2D &operator*=(const Transform2D &other) {
matrix *= other.matrix;
return *this;
}
Vec2 transformPoint(const Vec2 &p) const {
glm::vec4 result = matrix * glm::vec4(p.x, p.y, 0.0f, 1.0f);
return {result.x, result.y};
}
Transform2D inverse() const { return Transform2D(glm::inverse(matrix)); }
};
// ---------------------------------------------------------------------------
// 数学工具函数
// ---------------------------------------------------------------------------
namespace math {
inline float clamp(float value, float minVal, float maxVal) {
return value < minVal ? minVal : (value > maxVal ? maxVal : value);
}
inline float lerp(float a, float b, float t) { return a + (b - a) * t; }
inline float degrees(float radians) { return radians * RAD_TO_DEG; }
inline float radians(float degrees) { return degrees * DEG_TO_RAD; }
} // namespace math
} // namespace extra2d

View File

@ -1,44 +0,0 @@
#pragma once
#include <core/intrusive_ptr.h>
#include <cstdint>
#include <functional>
#include <memory>
namespace extra2d {
// ---------------------------------------------------------------------------
// 智能指针别名
// ---------------------------------------------------------------------------
template <typename T> using UniquePtr = std::unique_ptr<T>;
/// 创建 IntrusivePtr 的便捷函数
template <typename T, typename... Args>
inline IntrusivePtr<T> makeRef(Args &&...args) {
return makePtr<T>(std::forward<Args>(args)...);
}
/// 创建 unique_ptr 的便捷函数
template <typename T, typename... Args>
inline UniquePtr<T> unique(Args &&...args) {
return std::make_unique<T>(std::forward<Args>(args)...);
}
// ---------------------------------------------------------------------------
// 函数别名
// ---------------------------------------------------------------------------
template <typename Sig> using Function = std::function<Sig>;
// ---------------------------------------------------------------------------
// 基础类型别名
// ---------------------------------------------------------------------------
using int8 = std::int8_t;
using int16 = std::int16_t;
using int32 = std::int32_t;
using int64 = std::int64_t;
using uint8 = std::uint8_t;
using uint16 = std::uint16_t;
using uint32 = std::uint32_t;
using uint64 = std::uint64_t;
} // namespace extra2d

View File

@ -1,86 +0,0 @@
#pragma once
#include <cmath>
namespace extra2d {
// ---------------------------------------------------------------------------
// 常量
// ---------------------------------------------------------------------------
constexpr float PI_F = 3.14159265358979323846f;
constexpr float DEG_TO_RAD = PI_F / 180.0f;
constexpr float RAD_TO_DEG = 180.0f / PI_F;
// ---------------------------------------------------------------------------
// 2D 向量
// ---------------------------------------------------------------------------
struct Vec2 {
float x = 0.0f;
float y = 0.0f;
constexpr Vec2() = default;
constexpr Vec2(float x, float y) : x(x), y(y) {}
// 基础运算
Vec2 operator+(const Vec2 &v) const { return {x + v.x, y + v.y}; }
Vec2 operator-(const Vec2 &v) const { return {x - v.x, y - v.y}; }
Vec2 operator*(float s) const { return {x * s, y * s}; }
Vec2 operator/(float s) const { return {x / s, y / s}; }
Vec2 operator-() const { return {-x, -y}; }
Vec2 &operator+=(const Vec2 &v) {
x += v.x;
y += v.y;
return *this;
}
Vec2 &operator-=(const Vec2 &v) {
x -= v.x;
y -= v.y;
return *this;
}
Vec2 &operator*=(float s) {
x *= s;
y *= s;
return *this;
}
Vec2 &operator/=(float s) {
x /= s;
y /= s;
return *this;
}
bool operator==(const Vec2 &v) const { return x == v.x && y == v.y; }
bool operator!=(const Vec2 &v) const { return !(*this == v); }
// 向量运算
float length() const { return std::sqrt(x * x + y * y); }
float lengthSquared() const { return x * x + y * y; }
Vec2 normalized() const {
float len = length();
if (len > 0.0f)
return {x / len, y / len};
return {0.0f, 0.0f};
}
float dot(const Vec2 &v) const { return x * v.x + y * v.y; }
float cross(const Vec2 &v) const { return x * v.y - y * v.x; }
float distance(const Vec2 &v) const { return (*this - v).length(); }
float angle() const { return std::atan2(y, x) * RAD_TO_DEG; }
static Vec2 lerp(const Vec2 &a, const Vec2 &b, float t) {
return a + (b - a) * t;
}
static constexpr Vec2 Zero() { return {0.0f, 0.0f}; }
static constexpr Vec2 One() { return {1.0f, 1.0f}; }
static constexpr Vec2 UnitX() { return {1.0f, 0.0f}; }
static constexpr Vec2 UnitY() { return {0.0f, 1.0f}; }
};
inline Vec2 operator*(float s, const Vec2 &v) { return v * s; }
using Point = Vec2;
} // namespace extra2d

View File

@ -1,76 +0,0 @@
#pragma once
#include <cmath>
namespace extra2d {
// ---------------------------------------------------------------------------
// 3D 向量 (用于3D动作)
// ---------------------------------------------------------------------------
struct Vec3 {
float x = 0.0f;
float y = 0.0f;
float z = 0.0f;
constexpr Vec3() = default;
constexpr Vec3(float x, float y, float z) : x(x), y(y), z(z) {}
Vec3 operator+(const Vec3 &v) const { return {x + v.x, y + v.y, z + v.z}; }
Vec3 operator-(const Vec3 &v) const { return {x - v.x, y - v.y, z - v.z}; }
Vec3 operator*(float s) const { return {x * s, y * s, z * s}; }
Vec3 operator/(float s) const { return {x / s, y / s, z / s}; }
Vec3 operator-() const { return {-x, -y, -z}; }
Vec3 &operator+=(const Vec3 &v) {
x += v.x;
y += v.y;
z += v.z;
return *this;
}
Vec3 &operator-=(const Vec3 &v) {
x -= v.x;
y -= v.y;
z -= v.z;
return *this;
}
Vec3 &operator*=(float s) {
x *= s;
y *= s;
z *= s;
return *this;
}
Vec3 &operator/=(float s) {
x /= s;
y /= s;
z /= s;
return *this;
}
bool operator==(const Vec3 &v) const {
return x == v.x && y == v.y && z == v.z;
}
bool operator!=(const Vec3 &v) const { return !(*this == v); }
float length() const { return std::sqrt(x * x + y * y + z * z); }
float lengthSquared() const { return x * x + y * y + z * z; }
Vec3 normalized() const {
float len = length();
if (len > 0.0f)
return {x / len, y / len, z / len};
return {0.0f, 0.0f, 0.0f};
}
float dot(const Vec3 &v) const { return x * v.x + y * v.y + z * v.z; }
static Vec3 lerp(const Vec3 &a, const Vec3 &b, float t) {
return a + (b - a) * t;
}
static constexpr Vec3 Zero() { return {0.0f, 0.0f, 0.0f}; }
static constexpr Vec3 One() { return {1.0f, 1.0f, 1.0f}; }
};
inline Vec3 operator*(float s, const Vec3 &v) { return v * s; }
} // namespace extra2d

View File

@ -1,25 +1,30 @@
#pragma once
// Easy2D v3.0 - 统一入口头文件
// Extra2D v3.0 - 统一入口头文件
// 包含所有公共 API
// Core
#include <core/color.h>
#include <core/rect.h>
#include <core/size.h>
#include <core/transform.h>
#include <core/types.h>
#include <core/vec2.h>
#include <core/vec3.h>
// Types
#include <types/base/types.h>
#include <types/const/priority.h>
#include <types/ptr/intrusive_ptr.h>
#include <types/ptr/ref_counted.h>
// Platform
#include <platform/input.h>
#include <platform/window.h>
// Math
#include <types/math/color.h>
#include <types/math/rect.h>
#include <types/math/size.h>
#include <types/math/transform.h>
#include <types/math/vec2.h>
#include <types/math/vec3.h>
// Core
#include <core/director.h>
#include <core/scheduler.h>
#include <core/service.h>
// Utils
#include <utils/logger.h>
#include <utils/random.h>
#include <utils/timer.h>
// Application
#include <app/application.h>

View File

@ -1,105 +0,0 @@
#pragma once
#include <array>
#include <core/types.h>
#include <core/vec2.h>
#include <platform/input_codes.h>
#include <SDL.h>
namespace extra2d {
enum class MouseButton {
Left = 0,
Right = 1,
Middle = 2,
Button4 = 3,
Button5 = 4,
Button6 = 5,
Button7 = 6,
Button8 = 7,
Count = 8
};
class Input {
public:
Input();
~Input();
void init();
void shutdown();
void update();
bool isKeyDown(int keyCode) const;
bool isKeyPressed(int keyCode) const;
bool isKeyReleased(int keyCode) const;
bool isButtonDown(int button) const;
bool isButtonPressed(int button) const;
bool isButtonReleased(int button) const;
Vec2 getLeftStick() const;
Vec2 getRightStick() const;
bool isMouseDown(MouseButton button) const;
bool isMousePressed(MouseButton button) const;
bool isMouseReleased(MouseButton button) const;
Vec2 getMousePosition() const;
Vec2 getMouseDelta() const;
float getMouseScroll() const { return mouseScroll_; }
float getMouseScrollDelta() const { return mouseScroll_ - prevMouseScroll_; }
void setMousePosition(const Vec2 &position);
void setMouseVisible(bool visible);
void setMouseLocked(bool locked);
bool isTouching() const { return touching_; }
Vec2 getTouchPosition() const { return touchPosition_; }
int getTouchCount() const { return touchCount_; }
bool isAnyKeyDown() const;
bool isAnyMouseDown() const;
void onControllerAdded(int deviceIndex);
void onControllerRemoved(int instanceId);
void onMouseWheel(float x, float y);
private:
static constexpr int MAX_BUTTONS = SDL_CONTROLLER_BUTTON_MAX;
static constexpr int MAX_KEYS = SDL_NUM_SCANCODES;
SDL_GameController *controller_;
std::array<bool, MAX_KEYS> keysDown_;
std::array<bool, MAX_KEYS> prevKeysDown_;
std::array<bool, MAX_BUTTONS> buttonsDown_;
std::array<bool, MAX_BUTTONS> prevButtonsDown_;
float leftStickX_;
float leftStickY_;
float rightStickX_;
float rightStickY_;
Vec2 mousePosition_;
Vec2 prevMousePosition_;
float mouseScroll_;
float prevMouseScroll_;
std::array<bool, 8> mouseButtonsDown_;
std::array<bool, 8> prevMouseButtonsDown_;
bool touching_;
bool prevTouching_;
Vec2 touchPosition_;
Vec2 prevTouchPosition_;
int touchCount_;
void updateKeyboard();
void updateMouse();
void updateGamepad();
void updateTouch();
};
} // namespace extra2d

View File

@ -1,212 +0,0 @@
#pragma once
// SDL2 键码定义
#include <SDL2/SDL.h>
namespace extra2d {
// ============================================================================
// 键盘按键码 (基于 SDL2)
// ============================================================================
namespace Key {
enum : int {
Unknown = SDLK_UNKNOWN,
Space = SDLK_SPACE,
Apostrophe = SDLK_QUOTE,
Comma = SDLK_COMMA,
Minus = SDLK_MINUS,
Period = SDLK_PERIOD,
Slash = SDLK_SLASH,
Num0 = SDLK_0,
Num1 = SDLK_1,
Num2 = SDLK_2,
Num3 = SDLK_3,
Num4 = SDLK_4,
Num5 = SDLK_5,
Num6 = SDLK_6,
Num7 = SDLK_7,
Num8 = SDLK_8,
Num9 = SDLK_9,
Semicolon = SDLK_SEMICOLON,
Equal = SDLK_EQUALS,
A = SDLK_a,
B = SDLK_b,
C = SDLK_c,
D = SDLK_d,
E = SDLK_e,
F = SDLK_f,
G = SDLK_g,
H = SDLK_h,
I = SDLK_i,
J = SDLK_j,
K = SDLK_k,
L = SDLK_l,
M = SDLK_m,
N = SDLK_n,
O = SDLK_o,
P = SDLK_p,
Q = SDLK_q,
R = SDLK_r,
S = SDLK_s,
T = SDLK_t,
U = SDLK_u,
V = SDLK_v,
W = SDLK_w,
X = SDLK_x,
Y = SDLK_y,
Z = SDLK_z,
LeftBracket = SDLK_LEFTBRACKET,
Backslash = SDLK_BACKSLASH,
RightBracket = SDLK_RIGHTBRACKET,
GraveAccent = SDLK_BACKQUOTE,
Escape = SDLK_ESCAPE,
Enter = SDLK_RETURN,
Tab = SDLK_TAB,
Backspace = SDLK_BACKSPACE,
Insert = SDLK_INSERT,
Delete = SDLK_DELETE,
Right = SDLK_RIGHT,
Left = SDLK_LEFT,
Down = SDLK_DOWN,
Up = SDLK_UP,
PageUp = SDLK_PAGEUP,
PageDown = SDLK_PAGEDOWN,
Home = SDLK_HOME,
End = SDLK_END,
CapsLock = SDLK_CAPSLOCK,
ScrollLock = SDLK_SCROLLLOCK,
NumLock = SDLK_NUMLOCKCLEAR,
PrintScreen = SDLK_PRINTSCREEN,
Pause = SDLK_PAUSE,
F1 = SDLK_F1,
F2 = SDLK_F2,
F3 = SDLK_F3,
F4 = SDLK_F4,
F5 = SDLK_F5,
F6 = SDLK_F6,
F7 = SDLK_F7,
F8 = SDLK_F8,
F9 = SDLK_F9,
F10 = SDLK_F10,
F11 = SDLK_F11,
F12 = SDLK_F12,
F13 = SDLK_F13,
F14 = SDLK_F14,
F15 = SDLK_F15,
F16 = SDLK_F16,
F17 = SDLK_F17,
F18 = SDLK_F18,
F19 = SDLK_F19,
F20 = SDLK_F20,
F21 = SDLK_F21,
F22 = SDLK_F22,
F23 = SDLK_F23,
F24 = SDLK_F24,
KP0 = SDLK_KP_0,
KP1 = SDLK_KP_1,
KP2 = SDLK_KP_2,
KP3 = SDLK_KP_3,
KP4 = SDLK_KP_4,
KP5 = SDLK_KP_5,
KP6 = SDLK_KP_6,
KP7 = SDLK_KP_7,
KP8 = SDLK_KP_8,
KP9 = SDLK_KP_9,
KPDecimal = SDLK_KP_PERIOD,
KPDivide = SDLK_KP_DIVIDE,
KPMultiply = SDLK_KP_MULTIPLY,
KPSubtract = SDLK_KP_MINUS,
KPAdd = SDLK_KP_PLUS,
KPEnter = SDLK_KP_ENTER,
KPEqual = SDLK_KP_EQUALS,
LeftShift = SDLK_LSHIFT,
LeftControl = SDLK_LCTRL,
LeftAlt = SDLK_LALT,
LeftSuper = SDLK_LGUI,
RightShift = SDLK_RSHIFT,
RightControl = SDLK_RCTRL,
RightAlt = SDLK_RALT,
RightSuper = SDLK_RGUI,
Menu = SDLK_MENU,
Last = SDLK_MENU
};
}
// ============================================================================
// 修饰键
// ============================================================================
namespace Mod {
enum : int {
Shift = KMOD_SHIFT,
Control = KMOD_CTRL,
Alt = KMOD_ALT,
Super = KMOD_GUI,
CapsLock = KMOD_CAPS,
NumLock = KMOD_NUM
};
}
// ============================================================================
// 鼠标按键码
// ============================================================================
namespace Mouse {
enum : int {
Button1 = 0,
Button2 = 1,
Button3 = 2,
Button4 = 3,
Button5 = 4,
Button6 = 5,
Button7 = 6,
Button8 = 7,
ButtonLast = Button8,
ButtonLeft = Button1,
ButtonRight = Button2,
ButtonMiddle = Button3
};
}
// ============================================================================
// 游戏手柄按键
// ============================================================================
namespace GamepadButton {
enum : int {
A = SDL_CONTROLLER_BUTTON_A,
B = SDL_CONTROLLER_BUTTON_B,
X = SDL_CONTROLLER_BUTTON_X,
Y = SDL_CONTROLLER_BUTTON_Y,
LeftBumper = SDL_CONTROLLER_BUTTON_LEFTSHOULDER,
RightBumper = SDL_CONTROLLER_BUTTON_RIGHTSHOULDER,
Back = SDL_CONTROLLER_BUTTON_BACK,
Start = SDL_CONTROLLER_BUTTON_START,
Guide = SDL_CONTROLLER_BUTTON_GUIDE,
LeftThumb = SDL_CONTROLLER_BUTTON_LEFTSTICK,
RightThumb = SDL_CONTROLLER_BUTTON_RIGHTSTICK,
DPadUp = SDL_CONTROLLER_BUTTON_DPAD_UP,
DPadRight = SDL_CONTROLLER_BUTTON_DPAD_RIGHT,
DPadDown = SDL_CONTROLLER_BUTTON_DPAD_DOWN,
DPadLeft = SDL_CONTROLLER_BUTTON_DPAD_LEFT,
Last = SDL_CONTROLLER_BUTTON_DPAD_LEFT,
Cross = A,
Circle = B,
Square = X,
Triangle = Y
};
}
// ============================================================================
// 游戏手柄轴
// ============================================================================
namespace GamepadAxis {
enum : int {
LeftX = SDL_CONTROLLER_AXIS_LEFTX,
LeftY = SDL_CONTROLLER_AXIS_LEFTY,
RightX = SDL_CONTROLLER_AXIS_RIGHTX,
RightY = SDL_CONTROLLER_AXIS_RIGHTY,
LeftTrigger = SDL_CONTROLLER_AXIS_TRIGGERLEFT,
RightTrigger = SDL_CONTROLLER_AXIS_TRIGGERRIGHT,
Last = SDL_CONTROLLER_AXIS_TRIGGERRIGHT
};
}
} // namespace extra2d

View File

@ -1,153 +0,0 @@
#pragma once
#include <core/size.h>
#include <core/types.h>
#include <core/vec2.h>
#include <string>
#include <functional>
#include <SDL.h>
namespace extra2d {
// 前向声明
class Input;
// ============================================================================
// 窗口配置
// ============================================================================
struct WindowConfig {
std::string title = "Extra2D Application";
int width = 1280;
int height = 720;
bool fullscreen = true;
bool resizable = false;
bool vsync = true;
int msaaSamples = 0;
bool centerWindow = true;
bool enableCursors = true;
bool enableDpiScale = true;
bool fullscreenDesktop =
true; // true: SDL_WINDOW_FULLSCREEN_DESKTOP, false: SDL_WINDOW_FULLSCREEN
};
// ============================================================================
// 鼠标光标形状枚举
// ============================================================================
enum class CursorShape {
Arrow,
IBeam,
Crosshair,
Hand,
HResize,
VResize,
ResizeAll,
ResizeNWSE,
ResizeNESW
};
// ============================================================================
// Window 类 - SDL2 Window + GLES 3.2 封装
// 支持平台: Nintendo Switch, Windows, Linux, macOS
// ============================================================================
class Window {
public:
Window();
~Window();
// 创建窗口
bool create(const WindowConfig &config);
void destroy();
// 窗口操作
void pollEvents();
void swapBuffers();
bool shouldClose() const;
void setShouldClose(bool close);
// 窗口属性
void setTitle(const std::string &title);
void setSize(int width, int height);
void setPosition(int x, int y);
void setFullscreen(bool fullscreen);
void setVSync(bool enabled);
void setResizable(bool resizable);
// 获取窗口属性
int width() const { return width_; }
int height() const { return height_; }
Size size() const {
return Size(static_cast<float>(width_), static_cast<float>(height_));
}
Vec2 pos() const;
bool isFullscreen() const { return fullscreen_; }
bool isVSync() const { return vsync_; }
// DPI 缩放 (PC 端自动检测Switch 固定 1.0)
float getContentScaleX() const;
float getContentScaleY() const;
Vec2 getContentScale() const;
// 窗口状态
bool isFocused() const { return focused_; }
bool isMinimized() const;
bool isMaximized() const;
// 获取 SDL2 窗口和 GL 上下文
SDL_Window *getSDLWindow() const { return sdlWindow_; }
SDL_GLContext getGLContext() const { return glContext_; }
// 设置/获取用户数据
void setUserData(void *data) { userData_ = data; }
void *getUserData() const { return userData_; }
// 获取输入管理器
Input *getInput() const { return input_.get(); }
// 光标操作 (PC 端有效Switch 上为空操作)
void setCursor(CursorShape shape);
void resetCursor();
void setMouseVisible(bool visible);
// 窗口回调
using ResizeCallback = std::function<void(int width, int height)>;
using FocusCallback = std::function<void(bool focused)>;
using CloseCallback = std::function<void()>;
void setResizeCallback(ResizeCallback callback) {
resizeCallback_ = callback;
}
void setFocusCallback(FocusCallback callback) { focusCallback_ = callback; }
void setCloseCallback(CloseCallback callback) { closeCallback_ = callback; }
private:
// SDL2 状态
SDL_Window *sdlWindow_;
SDL_GLContext glContext_;
SDL_Cursor *sdlCursors_[9]; // 光标缓存
SDL_Cursor *currentCursor_;
int width_;
int height_;
bool vsync_;
bool shouldClose_;
bool fullscreen_;
bool focused_;
float contentScaleX_;
float contentScaleY_;
bool enableDpiScale_;
void *userData_;
UniquePtr<Input> input_;
ResizeCallback resizeCallback_;
FocusCallback focusCallback_;
CloseCallback closeCallback_;
bool initSDL(const WindowConfig &config);
void deinitSDL();
void initCursors();
void deinitCursors();
void updateContentScale();
};
} // namespace extra2d

View File

@ -0,0 +1,26 @@
#pragma once
#include <cstdint>
#include <functional>
#include <memory>
namespace extra2d {
using int8 = std::int8_t;
using int16 = std::int16_t;
using int32 = std::int32_t;
using int64 = std::int64_t;
using uint8 = std::uint8_t;
using uint16 = std::uint16_t;
using uint32 = std::uint32_t;
using uint64 = std::uint64_t;
template <typename Sig> using Fn = std::function<Sig>;
template <typename T> using Unique = std::unique_ptr<T>;
template <typename T, typename... Args>
inline Unique<T> makeUnique(Args&&... args) {
return std::make_unique<T>(std::forward<Args>(args)...);
}
} // namespace extra2d

View File

@ -0,0 +1,20 @@
#pragma once
#include <types/base/types.h>
#include <climits>
namespace extra2d {
namespace Pri {
constexpr int32 Min = INT32_MIN;
constexpr int32 System = -1000;
constexpr int32 Input = -100;
constexpr int32 Default = 0;
constexpr int32 Logic = 100;
constexpr int32 Anim = 200;
constexpr int32 Physics = 300;
constexpr int32 Render = 1000;
constexpr int32 Max = INT32_MAX;
}
} // namespace extra2d

View File

@ -0,0 +1,62 @@
#pragma once
#include <types/base/types.h>
namespace extra2d {
struct Color {
float r, g, b, a;
Color() : r(1.0f), g(1.0f), b(1.0f), a(1.0f) {}
Color(float r, float g, float b, float a = 1.0f) : r(r), g(g), b(b), a(a) {}
explicit Color(float v) : r(v), g(v), b(v), a(1.0f) {}
Color operator+(const Color& o) const { return {r + o.r, g + o.g, b + o.b, a + o.a}; }
Color operator-(const Color& o) const { return {r - o.r, g - o.g, b - o.b, a - o.a}; }
Color operator*(float s) const { return {r * s, g * s, b * s, a * s}; }
Color operator*(const Color& o) const { return {r * o.r, g * o.g, b * o.b, a * o.a}; }
Color& operator+=(const Color& o) { r += o.r; g += o.g; b += o.b; a += o.a; return *this; }
Color& operator-=(const Color& o) { r -= o.r; g -= o.g; b -= o.b; a -= o.a; return *this; }
Color& operator*=(float s) { r *= s; g *= s; b *= s; a *= s; return *this; }
Color& operator*=(const Color& o) { r *= o.r; g *= o.g; b *= o.b; a *= o.a; return *this; }
bool operator==(const Color& o) const { return r == o.r && g == o.g && b == o.b && a == o.a; }
bool operator!=(const Color& o) const { return !(*this == o); }
static Color fromRGBA(uint8 r, uint8 g, uint8 b, uint8 a = 255) {
return {r / 255.0f, g / 255.0f, b / 255.0f, a / 255.0f};
}
static Color fromHex(uint32 hex) {
return {
((hex >> 24) & 0xFF) / 255.0f,
((hex >> 16) & 0xFF) / 255.0f,
((hex >> 8) & 0xFF) / 255.0f,
(hex & 0xFF) / 255.0f
};
}
static Color lerp(const Color& a, const Color& b, float t) {
return {
a.r + (b.r - a.r) * t,
a.g + (b.g - a.g) * t,
a.b + (b.b - a.b) * t,
a.a + (b.a - a.a) * t
};
}
static const Color White;
static const Color Black;
static const Color Red;
static const Color Green;
static const Color Blue;
static const Color Yellow;
static const Color Cyan;
static const Color Magenta;
static const Color Transparent;
};
inline Color operator*(float s, const Color& c) { return c * s; }
} // namespace extra2d

67
include/types/math/rect.h Normal file
View File

@ -0,0 +1,67 @@
#pragma once
#include <types/base/types.h>
#include <types/math/vec2.h>
#include <types/math/size.h>
namespace extra2d {
struct Rect {
float x, y, w, h;
Rect() : x(0.0f), y(0.0f), w(0.0f), h(0.0f) {}
Rect(float x, float y, float w, float h) : x(x), y(y), w(w), h(h) {}
Rect(const Vec2& pos, const Size& sz) : x(pos.x), y(pos.y), w(sz.w), h(sz.h) {}
Vec2 pos() const { return {x, y}; }
Vec2 center() const { return {x + w * 0.5f, y + h * 0.5f}; }
Size size() const { return {w, h}; }
float left() const { return x; }
float right() const { return x + w; }
float top() const { return y; }
float bottom() const { return y + h; }
float area() const { return w * h; }
bool empty() const { return w <= 0.0f || h <= 0.0f; }
bool contains(const Vec2& p) const {
return p.x >= x && p.x <= x + w && p.y >= y && p.y <= y + h;
}
bool contains(const Rect& o) const {
return o.x >= x && o.y >= y && o.right() <= right() && o.bottom() <= bottom();
}
bool intersects(const Rect& o) const {
return x < o.right() && right() > o.x && y < o.bottom() && bottom() > o.y;
}
Rect intersection(const Rect& o) const {
if (!intersects(o)) return Rect();
float nx = (std::max)(x, o.x);
float ny = (std::max)(y, o.y);
float nw = (std::min)(right(), o.right()) - nx;
float nh = (std::min)(bottom(), o.bottom()) - ny;
return {nx, ny, nw, nh};
}
Rect unite(const Rect& o) const {
float nx = (std::min)(x, o.x);
float ny = (std::min)(y, o.y);
float nr = (std::max)(right(), o.right());
float nb = (std::max)(bottom(), o.bottom());
return {nx, ny, nr - nx, nb - ny};
}
void setPos(const Vec2& p) { x = p.x; y = p.y; }
void setSize(const Size& s) { w = s.w; h = s.h; }
void setCenter(const Vec2& c) { x = c.x - w * 0.5f; y = c.y - h * 0.5f; }
bool operator==(const Rect& o) const { return x == o.x && y == o.y && w == o.w && h == o.h; }
bool operator!=(const Rect& o) const { return !(*this == o); }
static const Rect Zero;
};
} // namespace extra2d

36
include/types/math/size.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include <types/base/types.h>
namespace extra2d {
struct Size {
float w, h;
Size() : w(0.0f), h(0.0f) {}
Size(float w, float h) : w(w), h(h) {}
explicit Size(float v) : w(v), h(v) {}
Size operator+(const Size& o) const { return {w + o.w, h + o.h}; }
Size operator-(const Size& o) const { return {w - o.w, h - o.h}; }
Size operator*(float s) const { return {w * s, h * s}; }
Size operator/(float s) const { return {w / s, h / s}; }
Size& operator+=(const Size& o) { w += o.w; h += o.h; return *this; }
Size& operator-=(const Size& o) { w -= o.w; h -= o.h; return *this; }
Size& operator*=(float s) { w *= s; h *= s; return *this; }
Size& operator/=(float s) { w /= s; h /= s; return *this; }
bool operator==(const Size& o) const { return w == o.w && h == o.h; }
bool operator!=(const Size& o) const { return !(*this == o); }
float area() const { return w * h; }
bool empty() const { return w <= 0.0f || h <= 0.0f; }
static const Size Zero;
static const Size One;
};
inline Size operator*(float s, const Size& sz) { return sz * s; }
} // namespace extra2d

View File

@ -0,0 +1,31 @@
#pragma once
#include <types/base/types.h>
#include <types/math/vec2.h>
namespace extra2d {
struct Transform {
Vec2 pos;
Vec2 scale;
float rot;
Transform() : pos(0.0f, 0.0f), scale(1.0f, 1.0f), rot(0.0f) {}
Transform(const Vec2& pos, const Vec2& scale, float rot)
: pos(pos), scale(scale), rot(rot) {}
void reset() {
pos = Vec2(0.0f, 0.0f);
scale = Vec2(1.0f, 1.0f);
rot = 0.0f;
}
bool operator==(const Transform& o) const {
return pos == o.pos && scale == o.scale && rot == o.rot;
}
bool operator!=(const Transform& o) const { return !(*this == o); }
static const Transform Identity;
};
} // namespace extra2d

61
include/types/math/vec2.h Normal file
View File

@ -0,0 +1,61 @@
#pragma once
#include <types/base/types.h>
#include <cmath>
namespace extra2d {
struct Vec2 {
float x, y;
Vec2() : x(0.0f), y(0.0f) {}
Vec2(float x, float y) : x(x), y(y) {}
explicit Vec2(float v) : x(v), y(v) {}
Vec2 operator+(const Vec2& o) const { return {x + o.x, y + o.y}; }
Vec2 operator-(const Vec2& o) const { return {x - o.x, y - o.y}; }
Vec2 operator*(float s) const { return {x * s, y * s}; }
Vec2 operator/(float s) const { return {x / s, y / s}; }
Vec2 operator*(const Vec2& o) const { return {x * o.x, y * o.y}; }
Vec2 operator/(const Vec2& o) const { return {x / o.x, y / o.y}; }
Vec2& operator+=(const Vec2& o) { x += o.x; y += o.y; return *this; }
Vec2& operator-=(const Vec2& o) { x -= o.x; y -= o.y; return *this; }
Vec2& operator*=(float s) { x *= s; y *= s; return *this; }
Vec2& operator/=(float s) { x /= s; y /= s; return *this; }
Vec2 operator-() const { return {-x, -y}; }
bool operator==(const Vec2& o) const { return x == o.x && y == o.y; }
bool operator!=(const Vec2& o) const { return !(*this == o); }
float len() const { return std::sqrt(x * x + y * y); }
float lenSq() const { return x * x + y * y; }
Vec2 norm() const {
float l = len();
return l > 0.0f ? *this / l : Vec2(0.0f);
}
void normalize() {
float l = len();
if (l > 0.0f) { x /= l; y /= l; }
}
float dot(const Vec2& o) const { return x * o.x + y * o.y; }
float cross(const Vec2& o) const { return x * o.y - y * o.x; }
static float dist(const Vec2& a, const Vec2& b) { return (a - b).len(); }
static Vec2 lerp(const Vec2& a, const Vec2& b, float t) {
return {a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t};
}
static const Vec2 Zero;
static const Vec2 One;
static const Vec2 UnitX;
static const Vec2 UnitY;
};
inline Vec2 operator*(float s, const Vec2& v) { return v * s; }
} // namespace extra2d

64
include/types/math/vec3.h Normal file
View File

@ -0,0 +1,64 @@
#pragma once
#include <types/base/types.h>
#include <cmath>
namespace extra2d {
struct Vec3 {
float x, y, z;
Vec3() : x(0.0f), y(0.0f), z(0.0f) {}
Vec3(float x, float y, float z) : x(x), y(y), z(z) {}
explicit Vec3(float v) : x(v), y(v), z(v) {}
Vec3 operator+(const Vec3& o) const { return {x + o.x, y + o.y, z + o.z}; }
Vec3 operator-(const Vec3& o) const { return {x - o.x, y - o.y, z - o.z}; }
Vec3 operator*(float s) const { return {x * s, y * s, z * s}; }
Vec3 operator/(float s) const { return {x / s, y / s, z / s}; }
Vec3 operator*(const Vec3& o) const { return {x * o.x, y * o.y, z * o.z}; }
Vec3 operator/(const Vec3& o) const { return {x / o.x, y / o.y, z / o.z}; }
Vec3& operator+=(const Vec3& o) { x += o.x; y += o.y; z += o.z; return *this; }
Vec3& operator-=(const Vec3& o) { x -= o.x; y -= o.y; z -= o.z; return *this; }
Vec3& operator*=(float s) { x *= s; y *= s; z *= s; return *this; }
Vec3& operator/=(float s) { x /= s; y /= s; z /= s; return *this; }
Vec3 operator-() const { return {-x, -y, -z}; }
bool operator==(const Vec3& o) const { return x == o.x && y == o.y && z == o.z; }
bool operator!=(const Vec3& o) const { return !(*this == o); }
float len() const { return std::sqrt(x * x + y * y + z * z); }
float lenSq() const { return x * x + y * y + z * z; }
Vec3 norm() const {
float l = len();
return l > 0.0f ? *this / l : Vec3(0.0f);
}
void normalize() {
float l = len();
if (l > 0.0f) { x /= l; y /= l; z /= l; }
}
float dot(const Vec3& o) const { return x * o.x + y * o.y + z * o.z; }
Vec3 cross(const Vec3& o) const {
return {y * o.z - z * o.y, z * o.x - x * o.z, x * o.y - y * o.x};
}
static float dist(const Vec3& a, const Vec3& b) { return (a - b).len(); }
static Vec3 lerp(const Vec3& a, const Vec3& b, float t) {
return {a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t};
}
static const Vec3 Zero;
static const Vec3 One;
static const Vec3 UnitX;
static const Vec3 UnitY;
static const Vec3 UnitZ;
};
inline Vec3 operator*(float s, const Vec3& v) { return v * s; }
} // namespace extra2d

View File

@ -0,0 +1,47 @@
#pragma once
#include <types/ptr/ref_counted.h>
#include <utility>
namespace extra2d {
template <class T>
class Ptr {
public:
using element_type = T;
Ptr() : p_(nullptr) {}
Ptr(T* p) : p_(p) { if (p_) p_->addRef(); }
Ptr(const Ptr<T>& r) : p_(r.p_) { if (p_) p_->addRef(); }
template <typename U> Ptr(const Ptr<U>& r) : p_(r.get()) { if (p_) p_->addRef(); }
Ptr(Ptr<T>&& r) noexcept : p_(r.release()) {}
~Ptr() { if (p_) p_->release(); }
T* get() const { return p_; }
T& operator*() const { return *p_; }
T* operator->() const { return p_; }
operator T*() const { return p_; }
Ptr<T>& operator=(T* p) { reset(p); return *this; }
Ptr<T>& operator=(const Ptr<T>& r) { return *this = r.p_; }
Ptr<T>& operator=(Ptr<T>&& r) noexcept { Ptr<T>(std::move(r)).swap(*this); return *this; }
bool operator==(std::nullptr_t) const { return p_ == nullptr; }
bool operator!=(std::nullptr_t) const { return p_ != nullptr; }
void reset() noexcept { if (p_) p_->release(); p_ = nullptr; }
void reset(T* p) { if (p) p->addRef(); if (p_) p_->release(); p_ = p; }
T* release() { T* ret = p_; p_ = nullptr; return ret; }
void swap(Ptr<T>& r) noexcept { T* tmp = p_; p_ = r.p_; r.p_ = tmp; }
private:
T* p_;
};
template <typename T, typename... Args>
inline Ptr<T> makePtr(Args&&... args) {
return Ptr<T>(new T(std::forward<Args>(args)...));
}
} // namespace extra2d

View File

@ -0,0 +1,31 @@
#pragma once
#include <cstdint>
#include <atomic>
namespace extra2d {
class RefCounted {
public:
RefCounted() : refCount_(0) {}
virtual ~RefCounted() = default;
void addRef() const { refCount_.fetch_add(1, std::memory_order_relaxed); }
void release() const {
if (refCount_.fetch_sub(1, std::memory_order_acq_rel) == 1) {
delete this;
}
}
uint32 refCount() const { return refCount_.load(std::memory_order_relaxed); }
protected:
RefCounted(const RefCounted&) : refCount_(0) {}
RefCounted& operator=(const RefCounted&) { return *this; }
private:
mutable std::atomic<uint32> refCount_;
};
} // namespace extra2d

View File

@ -1,11 +1,10 @@
#pragma once
#include <cstdarg>
#include <cstdio>
#include <core/types.h>
#include <sstream>
#include <string>
#include <type_traits>
#include <types/base/types.h>
// SDL2 日志头文件
#include <SDL.h>
@ -16,13 +15,13 @@ namespace extra2d {
// 日志级别枚举 - 映射到 SDL_LogPriority
// ============================================================================
enum class LogLevel {
Trace = SDL_LOG_PRIORITY_VERBOSE, // SDL 详细日志
Debug = SDL_LOG_PRIORITY_DEBUG, // SDL 调试日志
Info = SDL_LOG_PRIORITY_INFO, // SDL 信息日志
Warn = SDL_LOG_PRIORITY_WARN, // SDL 警告日志
Error = SDL_LOG_PRIORITY_ERROR, // SDL 错误日志
Fatal = SDL_LOG_PRIORITY_CRITICAL, // SDL 严重日志
Off = SDL_LOG_PRIORITY_CRITICAL + 1 // 关闭日志 (使用 Critical+1 作为关闭标记)
Trace = SDL_LOG_PRIORITY_VERBOSE, // SDL 详细日志
Debug = SDL_LOG_PRIORITY_DEBUG, // SDL 调试日志
Info = SDL_LOG_PRIORITY_INFO, // SDL 信息日志
Warn = SDL_LOG_PRIORITY_WARN, // SDL 警告日志
Error = SDL_LOG_PRIORITY_ERROR, // SDL 错误日志
Fatal = SDL_LOG_PRIORITY_CRITICAL, // SDL 严重日志
Off = SDL_LOG_PRIORITY_CRITICAL + 1 // 关闭日志 (使用 Critical+1 作为关闭标记)
};
// ============================================================================

View File

@ -1,7 +1,8 @@
#pragma once
#include <core/types.h>
#include <random>
#include <types/base/types.h>
namespace extra2d {

View File

@ -1,99 +0,0 @@
#pragma once
#include <chrono>
#include <core/types.h>
#include <map>
#include <memory>
#include <vector>
namespace extra2d {
// ============================================================================
// Timer 类 - 单次/重复计时器
// ============================================================================
class Timer {
public:
using Clock = std::chrono::steady_clock;
using TimePoint = Clock::time_point;
using Duration = Clock::duration;
using Callback = Function<void()>;
Timer(float interval, bool repeat, Callback callback);
/// 更新计时器,返回 true 如果触发了回调
bool update(float deltaTime);
/// 重置计时器
void reset();
/// 暂停计时器
void pause();
/// 恢复计时器
void resume();
/// 取消计时器(标记为无效)
void cancel();
/// 是否有效
bool isValid() const { return valid_; }
/// 是否暂停
bool isPaused() const { return paused_; }
/// 获取剩余时间(秒)
float getRemaining() const;
/// 获取唯一ID
uint32 getId() const { return id_; }
private:
uint32 id_;
float interval_;
float elapsed_;
bool repeat_;
bool paused_;
bool valid_;
Callback callback_;
static uint32 nextId_;
};
// ============================================================================
// TimerManager 类 - 管理所有计时器
// ============================================================================
class TimerManager {
public:
TimerManager() = default;
~TimerManager() = default;
/// 创建单次计时器返回计时器ID
uint32 addTimer(float delay, Timer::Callback callback);
/// 创建重复计时器返回计时器ID
uint32 addRepeatingTimer(float interval, Timer::Callback callback);
/// 取消指定ID的计时器
void cancelTimer(uint32 timerId);
/// 暂停指定ID的计时器
void pauseTimer(uint32 timerId);
/// 恢复指定ID的计时器
void resumeTimer(uint32 timerId);
/// 更新所有计时器(每帧调用)
void update(float deltaTime);
/// 清除所有计时器
void clear();
/// 获取计时器数量
size_t getTimerCount() const { return timers_.size(); }
private:
std::map<uint32, std::unique_ptr<Timer>> timers_;
std::vector<uint32> timersToRemove_;
};
} // namespace extra2d

View File

@ -1,8 +1,6 @@
#include <app/application.h>
#include <platform/input.h>
#include <platform/window.h>
#include <core/director.h>
#include <utils/logger.h>
#include <utils/timer.h>
#include <chrono>
#include <thread>
@ -13,215 +11,169 @@
namespace extra2d {
/**
* @brief
*/
static double getTimeSeconds() {
#ifdef __SWITCH__
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return static_cast<double>(ts.tv_sec) +
static_cast<double>(ts.tv_nsec) / 1000000000.0;
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return static_cast<double>(ts.tv_sec) + static_cast<double>(ts.tv_nsec) / 1000000000.0;
#else
using namespace std::chrono;
auto now = steady_clock::now();
auto duration = now.time_since_epoch();
return duration_cast<std::chrono::duration<double>>(duration).count();
using namespace std::chrono;
auto now = steady_clock::now();
auto duration = now.time_since_epoch();
return duration_cast<std::chrono::duration<double>>(duration).count();
#endif
}
Application &Application::instance() {
static Application instance;
return instance;
Application& Application::instance() {
static Application instance;
return instance;
}
Application::~Application() { shutdown(); }
Application::~Application() {
shutdown();
}
bool Application::init(const AppConfig &config) {
if (initialized_) {
E2D_LOG_WARN("Application already initialized");
bool Application::init(const AppConfig& config) {
if (initialized_) {
E2D_LOG_WARN("Application already initialized");
return true;
}
config_ = config;
PlatformType platform = config_.platform;
if (platform == PlatformType::Auto) {
#ifdef __SWITCH__
platform = PlatformType::Switch;
#else
platform = PlatformType::PC;
#endif
}
if (platform == PlatformType::Switch) {
#ifdef __SWITCH__
Result rc;
rc = romfsInit();
if (R_SUCCEEDED(rc)) {
E2D_LOG_INFO("RomFS initialized successfully");
} else {
E2D_LOG_WARN("romfsInit failed: {:#08X}", rc);
}
rc = socketInitializeDefault();
if (R_FAILED(rc)) {
E2D_LOG_WARN("socketInitializeDefault failed");
}
#endif
}
if (!DIRECTOR.init()) {
E2D_LOG_ERROR("Failed to initialize Director");
return false;
}
initialized_ = true;
running_ = true;
E2D_LOG_INFO("Application initialized successfully");
return true;
}
config_ = config;
PlatformType platform = config_.platform;
if (platform == PlatformType::Auto) {
#ifdef __SWITCH__
platform = PlatformType::Switch;
#else
platform = PlatformType::PC;
#endif
}
if (platform == PlatformType::Switch) {
#ifdef __SWITCH__
Result rc;
rc = romfsInit();
if (R_SUCCEEDED(rc)) {
E2D_LOG_INFO("RomFS initialized successfully");
} else {
E2D_LOG_WARN("romfsInit failed: {:#08X}, will use regular filesystem",
rc);
}
rc = socketInitializeDefault();
if (R_FAILED(rc)) {
E2D_LOG_WARN(
"socketInitializeDefault failed, nxlink will not be available");
}
#endif
}
window_ = unique<Window>();
WindowConfig winConfig;
winConfig.title = config.title;
winConfig.width = config.width;
winConfig.height = config.height;
if (platform == PlatformType::Switch) {
winConfig.fullscreen = true;
winConfig.fullscreenDesktop = false;
winConfig.resizable = false;
winConfig.enableCursors = false;
winConfig.enableDpiScale = false;
} else {
winConfig.fullscreen = config.fullscreen;
winConfig.resizable = config.resizable;
winConfig.enableCursors = config.enableCursors;
winConfig.enableDpiScale = config.enableDpiScale;
}
winConfig.vsync = config.vsync;
winConfig.msaaSamples = config.msaaSamples;
if (!window_->create(winConfig)) {
E2D_LOG_ERROR("Failed to create window");
return false;
}
timerManager_ = unique<TimerManager>();
initialized_ = true;
running_ = true;
E2D_LOG_INFO("Application initialized successfully");
return true;
}
void Application::shutdown() {
if (!initialized_)
return;
if (!initialized_) return;
E2D_LOG_INFO("Shutting down application...");
E2D_LOG_INFO("Shutting down application...");
timerManager_.reset();
DIRECTOR.shutdown();
if (window_) {
window_->destroy();
window_.reset();
}
PlatformType platform = config_.platform;
if (platform == PlatformType::Auto) {
PlatformType platform = config_.platform;
if (platform == PlatformType::Auto) {
#ifdef __SWITCH__
platform = PlatformType::Switch;
platform = PlatformType::Switch;
#else
platform = PlatformType::PC;
platform = PlatformType::PC;
#endif
}
if (platform == PlatformType::Switch) {
}
if (platform == PlatformType::Switch) {
#ifdef __SWITCH__
romfsExit();
socketExit();
romfsExit();
socketExit();
#endif
}
}
initialized_ = false;
running_ = false;
initialized_ = false;
running_ = false;
E2D_LOG_INFO("Application shutdown complete");
E2D_LOG_INFO("Application shutdown complete");
}
void Application::run() {
if (!initialized_) {
E2D_LOG_ERROR("Application not initialized");
return;
}
if (!initialized_) {
E2D_LOG_ERROR("Application not initialized");
return;
}
lastFrameTime_ = getTimeSeconds();
lastFrameTime_ = getTimeSeconds();
#ifdef __SWITCH__
while (running_ && !window_->shouldClose()) {
mainLoop();
}
#else
while (running_ && !window_->shouldClose()) {
mainLoop();
}
#endif
while (running_) {
mainLoop();
}
}
void Application::quit() {
shouldQuit_ = true;
running_ = false;
shouldQuit_ = true;
running_ = false;
}
void Application::pause() {
if (!paused_) {
paused_ = true;
E2D_LOG_INFO("Application paused");
}
if (!paused_) {
paused_ = true;
DIRECTOR.pause();
E2D_LOG_INFO("Application paused");
}
}
void Application::resume() {
if (paused_) {
paused_ = false;
lastFrameTime_ = getTimeSeconds();
E2D_LOG_INFO("Application resumed");
}
if (paused_) {
paused_ = false;
DIRECTOR.resume();
lastFrameTime_ = getTimeSeconds();
E2D_LOG_INFO("Application resumed");
}
}
void Application::mainLoop() {
double currentTime = getTimeSeconds();
deltaTime_ = static_cast<float>(currentTime - lastFrameTime_);
lastFrameTime_ = currentTime;
double currentTime = getTimeSeconds();
deltaTime_ = static_cast<float>(currentTime - lastFrameTime_);
lastFrameTime_ = currentTime;
totalTime_ += deltaTime_;
totalTime_ += deltaTime_;
frameCount_++;
fpsTimer_ += deltaTime_;
if (fpsTimer_ >= 1.0f) {
currentFps_ = frameCount_;
frameCount_ = 0;
fpsTimer_ -= 1.0f;
}
window_->pollEvents();
if (!paused_) {
update();
}
window_->swapBuffers();
if (!config_.vsync && config_.fpsLimit > 0) {
double frameEndTime = getTimeSeconds();
double frameTime = frameEndTime - currentTime;
double target = 1.0 / static_cast<double>(config_.fpsLimit);
if (frameTime < target) {
auto sleepSeconds = target - frameTime;
std::this_thread::sleep_for(std::chrono::duration<double>(sleepSeconds));
frameCount_++;
fpsTimer_ += deltaTime_;
if (fpsTimer_ >= 1.0f) {
currentFps_ = frameCount_;
frameCount_ = 0;
fpsTimer_ -= 1.0f;
}
if (!paused_) {
update();
}
if (!config_.vsync && config_.fpsLimit > 0) {
double frameEndTime = getTimeSeconds();
double frameTime = frameEndTime - currentTime;
double target = 1.0 / static_cast<double>(config_.fpsLimit);
if (frameTime < target) {
std::this_thread::sleep_for(std::chrono::duration<double>(target - frameTime));
}
}
}
}
void Application::update() {
if (timerManager_) {
timerManager_->update(deltaTime_);
}
DIRECTOR.mainLoop(deltaTime_);
}
Input &Application::input() { return *window_->getInput(); }
TimerManager &Application::timers() { return *timerManager_; }
} // namespace extra2d

84
src/core/director.cpp Normal file
View File

@ -0,0 +1,84 @@
#include <core/director.h>
namespace extra2d {
Director& Director::inst() {
static Director instance;
return instance;
}
bool Director::init() {
if (inited_) return true;
sched_ = makeUnique<Scheduler>();
svcMgr_ = makeUnique<SvcMgr>();
inited_ = true;
return true;
}
void Director::shutdown() {
if (!inited_) return;
svcMgr_->shutdownAll();
sched_->unscheduleAll();
svcMgr_.reset();
sched_.reset();
inited_ = false;
}
void Director::mainLoop(float dt) {
if (paused_) return;
dt_ = dt;
totalTime_ += dt;
frameCount_++;
svcMgr_->updateAll(dt);
fixedAccumulator_ += dt;
while (fixedAccumulator_ >= fixedDt_) {
svcMgr_->fixedUpdateAll(fixedDt_);
fixedAccumulator_ -= fixedDt_;
}
sched_->update(dt);
svcMgr_->lateUpdateAll(dt);
}
void Director::mainLoopParallel(float dt) {
if (paused_) return;
dt_ = dt;
totalTime_ += dt;
frameCount_++;
svcMgr_->updateAll(dt);
fixedAccumulator_ += dt;
while (fixedAccumulator_ >= fixedDt_) {
svcMgr_->fixedUpdateAll(fixedDt_);
fixedAccumulator_ -= fixedDt_;
}
sched_->updateParallel(dt);
svcMgr_->lateUpdateAll(dt);
}
void Director::pause() {
paused_ = true;
}
void Director::resume() {
paused_ = false;
}
void Director::setTimeScale(float scale) {
sched_->setTimeScale(scale);
}
} // namespace extra2d

240
src/core/scheduler.cpp Normal file
View File

@ -0,0 +1,240 @@
#include <core/scheduler.h>
#include <algorithm>
namespace extra2d {
namespace {
class IntervalTimer : public Timer {
public:
IntervalTimer(Scheduler::Cb cb, float interval, uint32 repeat, float delay)
: cb_(std::move(cb)) {
interval_ = interval;
repeat_ = repeat;
delay_ = delay;
useDelay_ = delay > 0.0f;
runForever_ = repeat == 0;
elapsed_ = useDelay_ ? 0.0f : -interval_;
}
void update(float dt) override {
if (paused_ || done_) return;
elapsed_ += dt;
if (useDelay_) {
if (elapsed_ < delay_) return;
elapsed_ -= delay_;
useDelay_ = false;
}
if (elapsed_ >= interval_) {
trigger();
elapsed_ -= interval_;
if (!runForever_) {
timesExecuted_++;
if (timesExecuted_ >= repeat_) {
done_ = true;
}
}
}
}
void trigger() override {
if (cb_) cb_(elapsed_);
}
private:
Scheduler::Cb cb_;
};
class OnceTimer : public Timer {
public:
OnceTimer(Scheduler::VoidCb cb, float delay)
: cb_(std::move(cb)) {
delay_ = delay;
elapsed_ = 0.0f;
}
void update(float dt) override {
if (paused_ || done_) return;
elapsed_ += dt;
if (elapsed_ >= delay_) {
trigger();
done_ = true;
}
}
void trigger() override {
if (cb_) cb_();
}
private:
Scheduler::VoidCb cb_;
};
}
Scheduler& Scheduler::inst() {
static Scheduler instance;
return instance;
}
TimerHdl Scheduler::scheduleUpdate(TimerTarget* target, int pri) {
if (!target) return INVALID_HDL;
UpdateEntry entry{target, pri, false, false};
updates_.push_back(entry);
updateIndex_.insert({target, updates_.size() - 1});
return genHdl();
}
void Scheduler::unscheduleUpdate(TimerTarget* target) {
if (!target) return;
tbb::concurrent_hash_map<TimerTarget*, size_t>::accessor acc;
if (updateIndex_.find(acc, target)) {
size_t idx = acc->second;
if (idx < updates_.size()) {
updates_[idx].markedForDel = true;
}
updateIndex_.erase(acc);
}
}
TimerHdl Scheduler::schedule(Cb cb, float interval, uint32 repeat, float delay) {
if (!cb) return INVALID_HDL;
auto timer = makePtr<IntervalTimer>(std::move(cb), interval, repeat, delay);
TimerHdl hdl = genHdl();
timer->hdl_ = hdl;
timers_.insert({hdl, timer});
return hdl;
}
TimerHdl Scheduler::scheduleOnce(VoidCb cb, float delay) {
if (!cb) return INVALID_HDL;
auto timer = makePtr<OnceTimer>(std::move(cb), delay);
TimerHdl hdl = genHdl();
timer->hdl_ = hdl;
timers_.insert({hdl, timer});
return hdl;
}
TimerHdl Scheduler::scheduleForever(Cb cb, float interval) {
return schedule(std::move(cb), interval, 0, 0.0f);
}
void Scheduler::unschedule(TimerHdl hdl) {
timers_.erase(hdl);
}
void Scheduler::unscheduleAll() {
timers_.clear();
updates_.clear();
updateIndex_.clear();
}
void Scheduler::pause(TimerHdl hdl) {
tbb::concurrent_hash_map<TimerHdl, Ptr<Timer>>::accessor acc;
if (timers_.find(acc, hdl)) {
acc->second->pause();
}
}
void Scheduler::resume(TimerHdl hdl) {
tbb::concurrent_hash_map<TimerHdl, Ptr<Timer>>::accessor acc;
if (timers_.find(acc, hdl)) {
acc->second->resume();
}
}
void Scheduler::update(float dt) {
float scaledDt = dt * timeScale_.load();
locked_ = true;
for (auto& entry : updates_) {
if (!entry.markedForDel && !entry.paused && entry.target) {
entry.target->update(scaledDt);
}
}
updates_.erase(
std::remove_if(updates_.begin(), updates_.end(),
[](const UpdateEntry& e) { return e.markedForDel; }),
updates_.end()
);
std::vector<TimerHdl> toRemove;
for (auto it = timers_.begin(); it != timers_.end(); ++it) {
auto& timer = it->second;
timer->update(scaledDt);
if (timer->isDone()) {
toRemove.push_back(it->first);
}
}
for (auto hdl : toRemove) {
timers_.erase(hdl);
}
locked_ = false;
}
void Scheduler::updateParallel(float dt) {
float scaledDt = dt * timeScale_.load();
locked_ = true;
tbb::parallel_for(tbb::blocked_range<size_t>(0, updates_.size()),
[this, scaledDt](const tbb::blocked_range<size_t>& range) {
for (size_t i = range.begin(); i != range.end(); ++i) {
auto& entry = updates_[i];
if (!entry.markedForDel && !entry.paused && entry.target) {
entry.target->update(scaledDt);
}
}
});
updates_.erase(
std::remove_if(updates_.begin(), updates_.end(),
[](const UpdateEntry& e) { return e.markedForDel; }),
updates_.end()
);
std::vector<TimerHdl> toRemove;
for (auto it = timers_.begin(); it != timers_.end(); ++it) {
auto& timer = it->second;
timer->update(scaledDt);
if (timer->isDone()) {
toRemove.push_back(it->first);
}
}
for (auto hdl : toRemove) {
timers_.erase(hdl);
}
locked_ = false;
}
bool Scheduler::isScheduled(TimerHdl hdl) const {
tbb::concurrent_hash_map<TimerHdl, Ptr<Timer>>::const_accessor acc;
return timers_.find(acc, hdl);
}
size_t Scheduler::count() const {
return timers_.size();
}
TimerHdl Scheduler::genHdl() {
return nextHdl_.fetch_add(1, std::memory_order_relaxed);
}
} // namespace extra2d

97
src/core/service.cpp Normal file
View File

@ -0,0 +1,97 @@
#include <core/service.h>
#include <algorithm>
namespace extra2d {
SvcMgr& SvcMgr::inst() {
static SvcMgr instance;
return instance;
}
void SvcMgr::reg(Ptr<IService> svc) {
if (!svc) return;
SvcMap::accessor acc;
svcMap_.insert(acc, svc->name());
acc->second = svc;
sortSvcs();
}
void SvcMgr::unreg(const char* name) {
svcMap_.erase(name);
sortSvcs();
}
Ptr<IService> SvcMgr::get(const char* name) {
SvcMap::const_accessor acc;
if (svcMap_.find(acc, name)) {
return acc->second;
}
return nullptr;
}
bool SvcMgr::initAll() {
for (auto& svc : sortedSvcs_) {
if (svc && !svc->isInited()) {
if (!svc->init()) {
return false;
}
svc->inited_ = true;
}
}
return true;
}
void SvcMgr::shutdownAll() {
for (auto it = sortedSvcs_.rbegin(); it != sortedSvcs_.rend(); ++it) {
if (*it && (*it)->isInited()) {
(*it)->shutdown();
(*it)->inited_ = false;
}
}
}
void SvcMgr::updateAll(float dt) {
for (auto& svc : sortedSvcs_) {
if (svc && svc->isInited() && svc->isEnabled()) {
svc->update(dt);
}
}
}
void SvcMgr::lateUpdateAll(float dt) {
for (auto& svc : sortedSvcs_) {
if (svc && svc->isInited() && svc->isEnabled()) {
svc->lateUpdate(dt);
}
}
}
void SvcMgr::fixedUpdateAll(float dt) {
for (auto& svc : sortedSvcs_) {
if (svc && svc->isInited() && svc->isEnabled()) {
svc->fixedUpdate(dt);
}
}
}
bool SvcMgr::has(const char* name) const {
SvcMap::const_accessor acc;
return svcMap_.find(acc, name);
}
size_t SvcMgr::count() const {
return svcMap_.size();
}
void SvcMgr::sortSvcs() {
sortedSvcs_.clear();
for (auto it = svcMap_.begin(); it != svcMap_.end(); ++it) {
sortedSvcs_.push_back(it->second);
}
std::sort(sortedSvcs_.begin(), sortedSvcs_.end(),
[](const Ptr<IService>& a, const Ptr<IService>& b) {
return a->pri() < b->pri();
});
}
} // namespace extra2d

View File

@ -1,307 +0,0 @@
#include <platform/input.h>
#include <platform/input_codes.h>
#include <utils/logger.h>
namespace extra2d {
Input::Input()
: controller_(nullptr), leftStickX_(0.0f), leftStickY_(0.0f),
rightStickX_(0.0f), rightStickY_(0.0f), mouseScroll_(0.0f),
prevMouseScroll_(0.0f), touching_(false), prevTouching_(false),
touchCount_(0) {
keysDown_.fill(false);
prevKeysDown_.fill(false);
buttonsDown_.fill(false);
prevButtonsDown_.fill(false);
mouseButtonsDown_.fill(false);
prevMouseButtonsDown_.fill(false);
}
Input::~Input() { shutdown(); }
void Input::init() {
for (int i = 0; i < SDL_NumJoysticks(); ++i) {
if (SDL_IsGameController(i)) {
controller_ = SDL_GameControllerOpen(i);
if (controller_) {
E2D_LOG_INFO("GameController opened: {}",
SDL_GameControllerName(controller_));
break;
}
}
}
if (!controller_) {
E2D_LOG_WARN("No game controller found");
}
int mouseX, mouseY;
SDL_GetMouseState(&mouseX, &mouseY);
mousePosition_ = Vec2(static_cast<float>(mouseX), static_cast<float>(mouseY));
prevMousePosition_ = mousePosition_;
}
void Input::shutdown() {
if (controller_) {
SDL_GameControllerClose(controller_);
controller_ = nullptr;
}
}
void Input::update() {
prevKeysDown_ = keysDown_;
prevButtonsDown_ = buttonsDown_;
prevMouseButtonsDown_ = mouseButtonsDown_;
prevMousePosition_ = mousePosition_;
prevMouseScroll_ = mouseScroll_;
prevTouching_ = touching_;
prevTouchPosition_ = touchPosition_;
updateKeyboard();
updateMouse();
updateGamepad();
updateTouch();
}
void Input::updateKeyboard() {
const Uint8 *state = SDL_GetKeyboardState(nullptr);
for (int i = 0; i < MAX_KEYS; ++i) {
keysDown_[i] = state[i] != 0;
}
}
void Input::updateMouse() {
int mouseX, mouseY;
Uint32 buttonState = SDL_GetMouseState(&mouseX, &mouseY);
mousePosition_ = Vec2(static_cast<float>(mouseX), static_cast<float>(mouseY));
mouseButtonsDown_[0] = (buttonState & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0;
mouseButtonsDown_[1] = (buttonState & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0;
mouseButtonsDown_[2] = (buttonState & SDL_BUTTON(SDL_BUTTON_MIDDLE)) != 0;
mouseButtonsDown_[3] = (buttonState & SDL_BUTTON(SDL_BUTTON_X1)) != 0;
mouseButtonsDown_[4] = (buttonState & SDL_BUTTON(SDL_BUTTON_X2)) != 0;
}
void Input::updateGamepad() {
if (controller_) {
for (int i = 0; i < MAX_BUTTONS; ++i) {
buttonsDown_[i] =
SDL_GameControllerGetButton(
controller_, static_cast<SDL_GameControllerButton>(i)) != 0;
}
leftStickX_ = static_cast<float>(SDL_GameControllerGetAxis(
controller_, SDL_CONTROLLER_AXIS_LEFTX)) /
32767.0f;
leftStickY_ = static_cast<float>(SDL_GameControllerGetAxis(
controller_, SDL_CONTROLLER_AXIS_LEFTY)) /
32767.0f;
rightStickX_ = static_cast<float>(SDL_GameControllerGetAxis(
controller_, SDL_CONTROLLER_AXIS_RIGHTX)) /
32767.0f;
rightStickY_ = static_cast<float>(SDL_GameControllerGetAxis(
controller_, SDL_CONTROLLER_AXIS_RIGHTY)) /
32767.0f;
} else {
buttonsDown_.fill(false);
leftStickX_ = leftStickY_ = rightStickX_ = rightStickY_ = 0.0f;
}
}
void Input::updateTouch() {
SDL_TouchID touchId = SDL_GetTouchDevice(0);
if (touchId != 0) {
touchCount_ = SDL_GetNumTouchFingers(touchId);
if (touchCount_ > 0) {
SDL_Finger *finger = SDL_GetTouchFinger(touchId, 0);
if (finger) {
touching_ = true;
int windowWidth, windowHeight;
SDL_Window *window = SDL_GL_GetCurrentWindow();
if (window) {
SDL_GetWindowSize(window, &windowWidth, &windowHeight);
touchPosition_ =
Vec2(finger->x * windowWidth, finger->y * windowHeight);
} else {
touchPosition_ = Vec2(finger->x * 1280.0f, finger->y * 720.0f);
}
} else {
touching_ = false;
}
} else {
touching_ = false;
}
} else {
touchCount_ = 0;
touching_ = false;
}
}
// ============================================================================
// 键盘输入
// ============================================================================
bool Input::isKeyDown(int keyCode) const {
SDL_Scancode scancode = SDL_GetScancodeFromKey(keyCode);
if (scancode >= 0 && scancode < MAX_KEYS) {
return keysDown_[scancode];
}
return false;
}
bool Input::isKeyPressed(int keyCode) const {
SDL_Scancode scancode = SDL_GetScancodeFromKey(keyCode);
if (scancode >= 0 && scancode < MAX_KEYS) {
return keysDown_[scancode] && !prevKeysDown_[scancode];
}
return false;
}
bool Input::isKeyReleased(int keyCode) const {
SDL_Scancode scancode = SDL_GetScancodeFromKey(keyCode);
if (scancode >= 0 && scancode < MAX_KEYS) {
return !keysDown_[scancode] && prevKeysDown_[scancode];
}
return false;
}
// ============================================================================
// 手柄按钮
// ============================================================================
bool Input::isButtonDown(int button) const {
if (button < 0 || button >= MAX_BUTTONS)
return false;
return buttonsDown_[button];
}
bool Input::isButtonPressed(int button) const {
if (button < 0 || button >= MAX_BUTTONS)
return false;
return buttonsDown_[button] && !prevButtonsDown_[button];
}
bool Input::isButtonReleased(int button) const {
if (button < 0 || button >= MAX_BUTTONS)
return false;
return !buttonsDown_[button] && prevButtonsDown_[button];
}
Vec2 Input::getLeftStick() const { return Vec2(leftStickX_, leftStickY_); }
Vec2 Input::getRightStick() const { return Vec2(rightStickX_, rightStickY_); }
// ============================================================================
// 鼠标输入
// ============================================================================
bool Input::isMouseDown(MouseButton button) const {
int index = static_cast<int>(button);
if (index < 0 || index >= 8)
return false;
return mouseButtonsDown_[index];
}
bool Input::isMousePressed(MouseButton button) const {
int index = static_cast<int>(button);
if (index < 0 || index >= 8)
return false;
return mouseButtonsDown_[index] && !prevMouseButtonsDown_[index];
}
bool Input::isMouseReleased(MouseButton button) const {
int index = static_cast<int>(button);
if (index < 0 || index >= 8)
return false;
return !mouseButtonsDown_[index] && prevMouseButtonsDown_[index];
}
Vec2 Input::getMousePosition() const { return mousePosition_; }
Vec2 Input::getMouseDelta() const {
return mousePosition_ - prevMousePosition_;
}
void Input::setMousePosition(const Vec2 &position) {
SDL_WarpMouseInWindow(SDL_GL_GetCurrentWindow(), static_cast<int>(position.x),
static_cast<int>(position.y));
}
void Input::setMouseVisible(bool visible) {
SDL_ShowCursor(visible ? SDL_ENABLE : SDL_DISABLE);
}
void Input::setMouseLocked(bool locked) {
SDL_SetRelativeMouseMode(locked ? SDL_TRUE : SDL_FALSE);
}
// ============================================================================
// 便捷方法
// ============================================================================
bool Input::isAnyKeyDown() const {
for (int i = 0; i < MAX_KEYS; ++i) {
if (keysDown_[i])
return true;
}
return false;
}
bool Input::isAnyMouseDown() const {
for (int i = 0; i < 8; ++i) {
if (mouseButtonsDown_[i])
return true;
}
return false;
}
// ============================================================================
// 事件处理
// ============================================================================
void Input::onControllerAdded(int deviceIndex) {
if (SDL_IsGameController(deviceIndex)) {
SDL_GameController *controller = SDL_GameControllerOpen(deviceIndex);
if (controller) {
if (controller_) {
SDL_GameControllerClose(controller_);
}
controller_ = controller;
E2D_LOG_INFO("GameController connected: {}",
SDL_GameControllerName(controller_));
} else {
E2D_LOG_ERROR("Failed to open GameController: {}", SDL_GetError());
}
}
}
void Input::onControllerRemoved(int instanceId) {
if (controller_) {
SDL_Joystick *joystick = SDL_GameControllerGetJoystick(controller_);
if (joystick) {
SDL_JoystickID joyInstanceId = SDL_JoystickInstanceID(joystick);
if (joyInstanceId == instanceId) {
E2D_LOG_INFO("GameController disconnected");
SDL_GameControllerClose(controller_);
controller_ = nullptr;
for (int i = 0; i < SDL_NumJoysticks(); ++i) {
if (SDL_IsGameController(i)) {
controller_ = SDL_GameControllerOpen(i);
if (controller_) {
E2D_LOG_INFO("Switched to GameController: {}",
SDL_GameControllerName(controller_));
break;
}
}
}
}
}
}
}
void Input::onMouseWheel(float x, float y) { mouseScroll_ += y; }
} // namespace extra2d

View File

@ -1,393 +0,0 @@
#include <platform/input.h>
#include <platform/window.h>
#include <utils/logger.h>
#include <SDL.h>
#include <glad/glad.h>
namespace extra2d {
Window::Window()
: sdlWindow_(nullptr), glContext_(nullptr), currentCursor_(nullptr),
width_(1280), height_(720), vsync_(true), shouldClose_(false),
fullscreen_(true), focused_(true), contentScaleX_(1.0f),
contentScaleY_(1.0f), enableDpiScale_(true), userData_(nullptr) {
// 初始化光标数组
for (int i = 0; i < 9; ++i) {
sdlCursors_[i] = nullptr;
}
}
Window::~Window() { destroy(); }
bool Window::create(const WindowConfig &config) {
if (sdlWindow_ != nullptr) {
E2D_LOG_WARN("Window already created");
return false;
}
width_ = config.width;
height_ = config.height;
vsync_ = config.vsync;
fullscreen_ = config.fullscreen;
enableDpiScale_ = config.enableDpiScale;
// 初始化 SDL2 + 创建窗口 + GL 上下文
if (!initSDL(config)) {
E2D_LOG_ERROR("Failed to initialize SDL2");
return false;
}
// 创建输入管理器
input_ = unique<Input>();
input_->init();
// 初始化光标
if (config.enableCursors) {
initCursors();
}
E2D_LOG_INFO("Window created: {}x{}", width_, height_);
return true;
}
bool Window::initSDL(const WindowConfig &config) {
// SDL2 全局初始化(视频 + 游戏控制器 + 音频)
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_GAMECONTROLLER | SDL_INIT_AUDIO) !=
0) {
E2D_LOG_ERROR("SDL_Init failed: {}", SDL_GetError());
return false;
}
// 设置 OpenGL ES 3.2 上下文属性
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
// 颜色/深度/模板缓冲配置
SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8);
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8);
// 双缓冲
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
// 创建 SDL2 窗口
Uint32 windowFlags = SDL_WINDOW_OPENGL;
// 根据配置设置窗口模式
if (config.fullscreen) {
// Switch 平台使用 SDL_WINDOW_FULLSCREEN固定分辨率
// PC 平台使用 SDL_WINDOW_FULLSCREEN_DESKTOP桌面全屏
if (config.fullscreenDesktop) {
windowFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
} else {
windowFlags |= SDL_WINDOW_FULLSCREEN;
}
} else {
if (config.resizable) {
windowFlags |= SDL_WINDOW_RESIZABLE;
}
// 注意SDL_WINDOWPOS_CENTERED 是位置参数,不是窗口标志
// 窗口居中在 SDL_CreateWindow 的位置参数中处理
}
sdlWindow_ = SDL_CreateWindow(
config.title.c_str(),
config.centerWindow ? SDL_WINDOWPOS_CENTERED : SDL_WINDOWPOS_UNDEFINED,
config.centerWindow ? SDL_WINDOWPOS_CENTERED : SDL_WINDOWPOS_UNDEFINED,
width_, height_, windowFlags);
if (!sdlWindow_) {
E2D_LOG_ERROR("SDL_CreateWindow failed: {}", SDL_GetError());
SDL_Quit();
return false;
}
// 创建 OpenGL ES 上下文
glContext_ = SDL_GL_CreateContext(sdlWindow_);
if (!glContext_) {
E2D_LOG_ERROR("SDL_GL_CreateContext failed: {}", SDL_GetError());
SDL_DestroyWindow(sdlWindow_);
sdlWindow_ = nullptr;
SDL_Quit();
return false;
}
if (SDL_GL_MakeCurrent(sdlWindow_, glContext_) != 0) {
E2D_LOG_ERROR("SDL_GL_MakeCurrent failed: {}", SDL_GetError());
SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr;
SDL_DestroyWindow(sdlWindow_);
sdlWindow_ = nullptr;
SDL_Quit();
return false;
}
// 加载 OpenGL ES 函数指针
if (gladLoadGLES2Loader(
reinterpret_cast<GLADloadproc>(SDL_GL_GetProcAddress)) == 0) {
E2D_LOG_ERROR("gladLoadGLES2Loader failed");
SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr;
SDL_DestroyWindow(sdlWindow_);
sdlWindow_ = nullptr;
SDL_Quit();
return false;
}
// 设置 VSync
SDL_GL_SetSwapInterval(vsync_ ? 1 : 0);
// 更新 DPI 缩放
if (config.enableDpiScale) {
updateContentScale();
}
E2D_LOG_INFO("SDL2 + GLES 3.2 initialized successfully");
E2D_LOG_INFO("OpenGL Version: {}",
reinterpret_cast<const char *>(glGetString(GL_VERSION)));
E2D_LOG_INFO("OpenGL Renderer: {}",
reinterpret_cast<const char *>(glGetString(GL_RENDERER)));
return true;
}
void Window::deinitSDL() {
deinitCursors();
if (glContext_) {
SDL_GL_DeleteContext(glContext_);
glContext_ = nullptr;
}
if (sdlWindow_) {
SDL_DestroyWindow(sdlWindow_);
sdlWindow_ = nullptr;
}
SDL_Quit();
}
void Window::destroy() {
if (sdlWindow_ != nullptr) {
input_.reset();
deinitSDL();
E2D_LOG_INFO("Window destroyed");
}
}
void Window::pollEvents() {
// SDL2 事件循环
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
shouldClose_ = true;
if (closeCallback_) {
closeCallback_();
}
break;
case SDL_WINDOWEVENT:
switch (event.window.event) {
case SDL_WINDOWEVENT_RESIZED:
case SDL_WINDOWEVENT_SIZE_CHANGED:
width_ = event.window.data1;
height_ = event.window.data2;
updateContentScale();
if (resizeCallback_) {
resizeCallback_(width_, height_);
}
break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
focused_ = true;
if (focusCallback_) {
focusCallback_(true);
}
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
focused_ = false;
if (focusCallback_) {
focusCallback_(false);
}
break;
}
break;
case SDL_CONTROLLERDEVICEADDED:
if (input_) {
input_->onControllerAdded(event.cdevice.which);
}
break;
case SDL_CONTROLLERDEVICEREMOVED:
if (input_) {
input_->onControllerRemoved(event.cdevice.which);
}
break;
case SDL_CONTROLLERDEVICEREMAPPED:
E2D_LOG_INFO("Controller device remapped");
break;
case SDL_MOUSEWHEEL:
if (input_) {
input_->onMouseWheel(event.wheel.x, event.wheel.y);
}
break;
}
}
// 输入更新
if (input_) {
input_->update();
}
}
void Window::swapBuffers() {
if (sdlWindow_) {
SDL_GL_SwapWindow(sdlWindow_);
}
}
bool Window::shouldClose() const { return shouldClose_; }
void Window::setShouldClose(bool close) { shouldClose_ = close; }
void Window::setTitle(const std::string &title) {
if (sdlWindow_) {
SDL_SetWindowTitle(sdlWindow_, title.c_str());
}
}
void Window::setSize(int width, int height) {
if (sdlWindow_) {
SDL_SetWindowSize(sdlWindow_, width, height);
width_ = width;
height_ = height;
}
}
void Window::setPosition(int x, int y) {
if (sdlWindow_) {
SDL_SetWindowPosition(sdlWindow_, x, y);
}
}
void Window::setFullscreen(bool fullscreen) {
if (sdlWindow_) {
// 默认使用桌面全屏模式PC 平台)
Uint32 flags = fullscreen ? SDL_WINDOW_FULLSCREEN_DESKTOP : 0;
SDL_SetWindowFullscreen(sdlWindow_, flags);
fullscreen_ = fullscreen;
}
}
void Window::setVSync(bool enabled) {
vsync_ = enabled;
SDL_GL_SetSwapInterval(enabled ? 1 : 0);
}
void Window::setResizable(bool resizable) {
if (sdlWindow_) {
SDL_SetWindowResizable(sdlWindow_, resizable ? SDL_TRUE : SDL_FALSE);
}
}
Vec2 Window::pos() const {
if (sdlWindow_) {
int x, y;
SDL_GetWindowPosition(sdlWindow_, &x, &y);
return Vec2(static_cast<float>(x), static_cast<float>(y));
}
return Vec2::Zero();
}
float Window::getContentScaleX() const {
return enableDpiScale_ ? contentScaleX_ : 1.0f;
}
float Window::getContentScaleY() const {
return enableDpiScale_ ? contentScaleY_ : 1.0f;
}
Vec2 Window::getContentScale() const {
return Vec2(getContentScaleX(), getContentScaleY());
}
bool Window::isMinimized() const {
if (sdlWindow_) {
Uint32 flags = SDL_GetWindowFlags(sdlWindow_);
return (flags & SDL_WINDOW_MINIMIZED) != 0;
}
return false;
}
bool Window::isMaximized() const {
if (sdlWindow_) {
Uint32 flags = SDL_GetWindowFlags(sdlWindow_);
return (flags & SDL_WINDOW_MAXIMIZED) != 0;
}
return true;
}
void Window::initCursors() {
sdlCursors_[0] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_ARROW);
sdlCursors_[1] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_IBEAM);
sdlCursors_[2] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_CROSSHAIR);
sdlCursors_[3] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_HAND);
sdlCursors_[4] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEWE);
sdlCursors_[5] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENS);
sdlCursors_[6] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZEALL);
sdlCursors_[7] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENWSE);
sdlCursors_[8] = SDL_CreateSystemCursor(SDL_SYSTEM_CURSOR_SIZENESW);
}
void Window::deinitCursors() {
for (int i = 0; i < 9; ++i) {
if (sdlCursors_[i]) {
SDL_FreeCursor(sdlCursors_[i]);
sdlCursors_[i] = nullptr;
}
}
currentCursor_ = nullptr;
}
void Window::setCursor(CursorShape shape) {
int index = static_cast<int>(shape);
if (index >= 0 && index < 9 && sdlCursors_[index]) {
SDL_SetCursor(sdlCursors_[index]);
currentCursor_ = sdlCursors_[index];
}
}
void Window::resetCursor() {
SDL_SetCursor(SDL_GetDefaultCursor());
currentCursor_ = nullptr;
}
void Window::setMouseVisible(bool visible) {
SDL_ShowCursor(visible ? SDL_ENABLE : SDL_DISABLE);
}
void Window::updateContentScale() {
if (sdlWindow_) {
// 使用 DPI 计算内容缩放比例
int displayIndex = SDL_GetWindowDisplayIndex(sdlWindow_);
if (displayIndex >= 0) {
float ddpi, hdpi, vdpi;
if (SDL_GetDisplayDPI(displayIndex, &ddpi, &hdpi, &vdpi) == 0) {
// 假设标准 DPI 为 96
contentScaleX_ = hdpi / 96.0f;
contentScaleY_ = vdpi / 96.0f;
}
}
}
}
} // namespace extra2d

View File

@ -1,118 +0,0 @@
#include <algorithm>
#include <utils/timer.h>
namespace extra2d {
uint32 Timer::nextId_ = 1;
Timer::Timer(float interval, bool repeat, Callback callback)
: interval_(interval), elapsed_(0.0f), repeat_(repeat), paused_(false),
valid_(true), callback_(std::move(callback)) {
id_ = nextId_++;
}
bool Timer::update(float deltaTime) {
if (!valid_ || paused_) {
return false;
}
elapsed_ += deltaTime;
if (elapsed_ >= interval_) {
if (callback_) {
callback_();
}
if (repeat_) {
elapsed_ = 0.0f;
return true;
} else {
valid_ = false;
return true;
}
}
return false;
}
void Timer::reset() {
elapsed_ = 0.0f;
valid_ = true;
paused_ = false;
}
void Timer::pause() { paused_ = true; }
void Timer::resume() { paused_ = false; }
void Timer::cancel() { valid_ = false; }
float Timer::getRemaining() const {
if (!valid_ || paused_) {
return 0.0f;
}
return std::max(0.0f, interval_ - elapsed_);
}
// ============================================================================
// TimerManager 实现
// ============================================================================
uint32 TimerManager::addTimer(float delay, Timer::Callback callback) {
auto timer = std::make_unique<Timer>(delay, false, std::move(callback));
uint32 id = timer->getId();
timers_.emplace(id, std::move(timer));
return id;
}
uint32 TimerManager::addRepeatingTimer(float interval,
Timer::Callback callback) {
auto timer = std::make_unique<Timer>(interval, true, std::move(callback));
uint32 id = timer->getId();
timers_.emplace(id, std::move(timer));
return id;
}
void TimerManager::cancelTimer(uint32 timerId) {
auto it = timers_.find(timerId);
if (it != timers_.end()) {
it->second->cancel();
timersToRemove_.push_back(timerId);
}
}
void TimerManager::pauseTimer(uint32 timerId) {
auto it = timers_.find(timerId);
if (it != timers_.end()) {
it->second->pause();
}
}
void TimerManager::resumeTimer(uint32 timerId) {
auto it = timers_.find(timerId);
if (it != timers_.end()) {
it->second->resume();
}
}
void TimerManager::update(float deltaTime) {
timersToRemove_.clear();
for (auto &[id, timer] : timers_) {
timer->update(deltaTime);
if (!timer->isValid()) {
timersToRemove_.push_back(id);
}
}
for (uint32 id : timersToRemove_) {
timers_.erase(id);
}
}
void TimerManager::clear() {
timers_.clear();
timersToRemove_.clear();
}
} // namespace extra2d

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,428 +0,0 @@
// stb_perlin.h - v0.5 - perlin noise
// public domain single-file C implementation by Sean Barrett
//
// LICENSE
//
// See end of file.
//
//
// to create the implementation,
// #define STB_PERLIN_IMPLEMENTATION
// in *one* C/CPP file that includes this file.
//
//
// Documentation:
//
// float stb_perlin_noise3( float x,
// float y,
// float z,
// int x_wrap=0,
// int y_wrap=0,
// int z_wrap=0)
//
// This function computes a random value at the coordinate (x,y,z).
// Adjacent random values are continuous but the noise fluctuates
// its randomness with period 1, i.e. takes on wholly unrelated values
// at integer points. Specifically, this implements Ken Perlin's
// revised noise function from 2002.
//
// The "wrap" parameters can be used to create wraparound noise that
// wraps at powers of two. The numbers MUST be powers of two. Specify
// 0 to mean "don't care". (The noise always wraps every 256 due
// details of the implementation, even if you ask for larger or no
// wrapping.)
//
// float stb_perlin_noise3_seed( float x,
// float y,
// float z,
// int x_wrap=0,
// int y_wrap=0,
// int z_wrap=0,
// int seed)
//
// As above, but 'seed' selects from multiple different variations of the
// noise function. The current implementation only uses the bottom 8 bits
// of 'seed', but possibly in the future more bits will be used.
//
//
// Fractal Noise:
//
// Three common fractal noise functions are included, which produce
// a wide variety of nice effects depending on the parameters
// provided. Note that each function will call stb_perlin_noise3
// 'octaves' times, so this parameter will affect runtime.
//
// float stb_perlin_ridge_noise3(float x, float y, float z,
// float lacunarity, float gain, float offset, int octaves)
//
// float stb_perlin_fbm_noise3(float x, float y, float z,
// float lacunarity, float gain, int octaves)
//
// float stb_perlin_turbulence_noise3(float x, float y, float z,
// float lacunarity, float gain, int octaves)
//
// Typical values to start playing with:
// octaves = 6 -- number of "octaves" of noise3() to sum
// lacunarity = ~ 2.0 -- spacing between successive octaves (use exactly 2.0 for wrapping output)
// gain = 0.5 -- relative weighting applied to each successive octave
// offset = 1.0? -- used to invert the ridges, may need to be larger, not sure
//
//
// Contributors:
// Jack Mott - additional noise functions
// Jordan Peck - seeded noise
//
#ifdef __cplusplus
extern "C" {
#endif
extern float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap);
extern float stb_perlin_noise3_seed(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, int seed);
extern float stb_perlin_ridge_noise3(float x, float y, float z, float lacunarity, float gain, float offset, int octaves);
extern float stb_perlin_fbm_noise3(float x, float y, float z, float lacunarity, float gain, int octaves);
extern float stb_perlin_turbulence_noise3(float x, float y, float z, float lacunarity, float gain, int octaves);
extern float stb_perlin_noise3_wrap_nonpow2(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, unsigned char seed);
#ifdef __cplusplus
}
#endif
#ifdef STB_PERLIN_IMPLEMENTATION
#include <math.h> // fabs()
// not same permutation table as Perlin's reference to avoid copyright issues;
// Perlin's table can be found at http://mrl.nyu.edu/~perlin/noise/
static unsigned char stb__perlin_randtab[512] =
{
23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123,
152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72,
175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240,
8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57,
225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233,
94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172,
165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243,
65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122,
26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76,
250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246,
132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3,
91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231,
38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221,
131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62,
27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135,
61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5,
// and a second copy so we don't need an extra mask or static initializer
23, 125, 161, 52, 103, 117, 70, 37, 247, 101, 203, 169, 124, 126, 44, 123,
152, 238, 145, 45, 171, 114, 253, 10, 192, 136, 4, 157, 249, 30, 35, 72,
175, 63, 77, 90, 181, 16, 96, 111, 133, 104, 75, 162, 93, 56, 66, 240,
8, 50, 84, 229, 49, 210, 173, 239, 141, 1, 87, 18, 2, 198, 143, 57,
225, 160, 58, 217, 168, 206, 245, 204, 199, 6, 73, 60, 20, 230, 211, 233,
94, 200, 88, 9, 74, 155, 33, 15, 219, 130, 226, 202, 83, 236, 42, 172,
165, 218, 55, 222, 46, 107, 98, 154, 109, 67, 196, 178, 127, 158, 13, 243,
65, 79, 166, 248, 25, 224, 115, 80, 68, 51, 184, 128, 232, 208, 151, 122,
26, 212, 105, 43, 179, 213, 235, 148, 146, 89, 14, 195, 28, 78, 112, 76,
250, 47, 24, 251, 140, 108, 186, 190, 228, 170, 183, 139, 39, 188, 244, 246,
132, 48, 119, 144, 180, 138, 134, 193, 82, 182, 120, 121, 86, 220, 209, 3,
91, 241, 149, 85, 205, 150, 113, 216, 31, 100, 41, 164, 177, 214, 153, 231,
38, 71, 185, 174, 97, 201, 29, 95, 7, 92, 54, 254, 191, 118, 34, 221,
131, 11, 163, 99, 234, 81, 227, 147, 156, 176, 17, 142, 69, 12, 110, 62,
27, 255, 0, 194, 59, 116, 242, 252, 19, 21, 187, 53, 207, 129, 64, 135,
61, 40, 167, 237, 102, 223, 106, 159, 197, 189, 215, 137, 36, 32, 22, 5,
};
// perlin's gradient has 12 cases so some get used 1/16th of the time
// and some 2/16ths. We reduce bias by changing those fractions
// to 5/64ths and 6/64ths
// this array is designed to match the previous implementation
// of gradient hash: indices[stb__perlin_randtab[i]&63]
static unsigned char stb__perlin_randtab_grad_idx[512] =
{
7, 9, 5, 0, 11, 1, 6, 9, 3, 9, 11, 1, 8, 10, 4, 7,
8, 6, 1, 5, 3, 10, 9, 10, 0, 8, 4, 1, 5, 2, 7, 8,
7, 11, 9, 10, 1, 0, 4, 7, 5, 0, 11, 6, 1, 4, 2, 8,
8, 10, 4, 9, 9, 2, 5, 7, 9, 1, 7, 2, 2, 6, 11, 5,
5, 4, 6, 9, 0, 1, 1, 0, 7, 6, 9, 8, 4, 10, 3, 1,
2, 8, 8, 9, 10, 11, 5, 11, 11, 2, 6, 10, 3, 4, 2, 4,
9, 10, 3, 2, 6, 3, 6, 10, 5, 3, 4, 10, 11, 2, 9, 11,
1, 11, 10, 4, 9, 4, 11, 0, 4, 11, 4, 0, 0, 0, 7, 6,
10, 4, 1, 3, 11, 5, 3, 4, 2, 9, 1, 3, 0, 1, 8, 0,
6, 7, 8, 7, 0, 4, 6, 10, 8, 2, 3, 11, 11, 8, 0, 2,
4, 8, 3, 0, 0, 10, 6, 1, 2, 2, 4, 5, 6, 0, 1, 3,
11, 9, 5, 5, 9, 6, 9, 8, 3, 8, 1, 8, 9, 6, 9, 11,
10, 7, 5, 6, 5, 9, 1, 3, 7, 0, 2, 10, 11, 2, 6, 1,
3, 11, 7, 7, 2, 1, 7, 3, 0, 8, 1, 1, 5, 0, 6, 10,
11, 11, 0, 2, 7, 0, 10, 8, 3, 5, 7, 1, 11, 1, 0, 7,
9, 0, 11, 5, 10, 3, 2, 3, 5, 9, 7, 9, 8, 4, 6, 5,
// and a second copy so we don't need an extra mask or static initializer
7, 9, 5, 0, 11, 1, 6, 9, 3, 9, 11, 1, 8, 10, 4, 7,
8, 6, 1, 5, 3, 10, 9, 10, 0, 8, 4, 1, 5, 2, 7, 8,
7, 11, 9, 10, 1, 0, 4, 7, 5, 0, 11, 6, 1, 4, 2, 8,
8, 10, 4, 9, 9, 2, 5, 7, 9, 1, 7, 2, 2, 6, 11, 5,
5, 4, 6, 9, 0, 1, 1, 0, 7, 6, 9, 8, 4, 10, 3, 1,
2, 8, 8, 9, 10, 11, 5, 11, 11, 2, 6, 10, 3, 4, 2, 4,
9, 10, 3, 2, 6, 3, 6, 10, 5, 3, 4, 10, 11, 2, 9, 11,
1, 11, 10, 4, 9, 4, 11, 0, 4, 11, 4, 0, 0, 0, 7, 6,
10, 4, 1, 3, 11, 5, 3, 4, 2, 9, 1, 3, 0, 1, 8, 0,
6, 7, 8, 7, 0, 4, 6, 10, 8, 2, 3, 11, 11, 8, 0, 2,
4, 8, 3, 0, 0, 10, 6, 1, 2, 2, 4, 5, 6, 0, 1, 3,
11, 9, 5, 5, 9, 6, 9, 8, 3, 8, 1, 8, 9, 6, 9, 11,
10, 7, 5, 6, 5, 9, 1, 3, 7, 0, 2, 10, 11, 2, 6, 1,
3, 11, 7, 7, 2, 1, 7, 3, 0, 8, 1, 1, 5, 0, 6, 10,
11, 11, 0, 2, 7, 0, 10, 8, 3, 5, 7, 1, 11, 1, 0, 7,
9, 0, 11, 5, 10, 3, 2, 3, 5, 9, 7, 9, 8, 4, 6, 5,
};
static float stb__perlin_lerp(float a, float b, float t)
{
return a + (b-a) * t;
}
static int stb__perlin_fastfloor(float a)
{
int ai = (int) a;
return (a < ai) ? ai-1 : ai;
}
// different grad function from Perlin's, but easy to modify to match reference
static float stb__perlin_grad(int grad_idx, float x, float y, float z)
{
static float basis[12][4] =
{
{ 1, 1, 0 },
{ -1, 1, 0 },
{ 1,-1, 0 },
{ -1,-1, 0 },
{ 1, 0, 1 },
{ -1, 0, 1 },
{ 1, 0,-1 },
{ -1, 0,-1 },
{ 0, 1, 1 },
{ 0,-1, 1 },
{ 0, 1,-1 },
{ 0,-1,-1 },
};
float *grad = basis[grad_idx];
return grad[0]*x + grad[1]*y + grad[2]*z;
}
float stb_perlin_noise3_internal(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, unsigned char seed)
{
float u,v,w;
float n000,n001,n010,n011,n100,n101,n110,n111;
float n00,n01,n10,n11;
float n0,n1;
unsigned int x_mask = (x_wrap-1) & 255;
unsigned int y_mask = (y_wrap-1) & 255;
unsigned int z_mask = (z_wrap-1) & 255;
int px = stb__perlin_fastfloor(x);
int py = stb__perlin_fastfloor(y);
int pz = stb__perlin_fastfloor(z);
int x0 = px & x_mask, x1 = (px+1) & x_mask;
int y0 = py & y_mask, y1 = (py+1) & y_mask;
int z0 = pz & z_mask, z1 = (pz+1) & z_mask;
int r0,r1, r00,r01,r10,r11;
#define stb__perlin_ease(a) (((a*6-15)*a + 10) * a * a * a)
x -= px; u = stb__perlin_ease(x);
y -= py; v = stb__perlin_ease(y);
z -= pz; w = stb__perlin_ease(z);
r0 = stb__perlin_randtab[x0+seed];
r1 = stb__perlin_randtab[x1+seed];
r00 = stb__perlin_randtab[r0+y0];
r01 = stb__perlin_randtab[r0+y1];
r10 = stb__perlin_randtab[r1+y0];
r11 = stb__perlin_randtab[r1+y1];
n000 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z0], x , y , z );
n001 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z1], x , y , z-1 );
n010 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z0], x , y-1, z );
n011 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z1], x , y-1, z-1 );
n100 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z0], x-1, y , z );
n101 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z1], x-1, y , z-1 );
n110 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z0], x-1, y-1, z );
n111 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z1], x-1, y-1, z-1 );
n00 = stb__perlin_lerp(n000,n001,w);
n01 = stb__perlin_lerp(n010,n011,w);
n10 = stb__perlin_lerp(n100,n101,w);
n11 = stb__perlin_lerp(n110,n111,w);
n0 = stb__perlin_lerp(n00,n01,v);
n1 = stb__perlin_lerp(n10,n11,v);
return stb__perlin_lerp(n0,n1,u);
}
float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap)
{
return stb_perlin_noise3_internal(x,y,z,x_wrap,y_wrap,z_wrap,0);
}
float stb_perlin_noise3_seed(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, int seed)
{
return stb_perlin_noise3_internal(x,y,z,x_wrap,y_wrap,z_wrap, (unsigned char) seed);
}
float stb_perlin_ridge_noise3(float x, float y, float z, float lacunarity, float gain, float offset, int octaves)
{
int i;
float frequency = 1.0f;
float prev = 1.0f;
float amplitude = 0.5f;
float sum = 0.0f;
for (i = 0; i < octaves; i++) {
float r = stb_perlin_noise3_internal(x*frequency,y*frequency,z*frequency,0,0,0,(unsigned char)i);
r = offset - (float) fabs(r);
r = r*r;
sum += r*amplitude*prev;
prev = r;
frequency *= lacunarity;
amplitude *= gain;
}
return sum;
}
float stb_perlin_fbm_noise3(float x, float y, float z, float lacunarity, float gain, int octaves)
{
int i;
float frequency = 1.0f;
float amplitude = 1.0f;
float sum = 0.0f;
for (i = 0; i < octaves; i++) {
sum += stb_perlin_noise3_internal(x*frequency,y*frequency,z*frequency,0,0,0,(unsigned char)i)*amplitude;
frequency *= lacunarity;
amplitude *= gain;
}
return sum;
}
float stb_perlin_turbulence_noise3(float x, float y, float z, float lacunarity, float gain, int octaves)
{
int i;
float frequency = 1.0f;
float amplitude = 1.0f;
float sum = 0.0f;
for (i = 0; i < octaves; i++) {
float r = stb_perlin_noise3_internal(x*frequency,y*frequency,z*frequency,0,0,0,(unsigned char)i)*amplitude;
sum += (float) fabs(r);
frequency *= lacunarity;
amplitude *= gain;
}
return sum;
}
float stb_perlin_noise3_wrap_nonpow2(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap, unsigned char seed)
{
float u,v,w;
float n000,n001,n010,n011,n100,n101,n110,n111;
float n00,n01,n10,n11;
float n0,n1;
int px = stb__perlin_fastfloor(x);
int py = stb__perlin_fastfloor(y);
int pz = stb__perlin_fastfloor(z);
int x_wrap2 = (x_wrap ? x_wrap : 256);
int y_wrap2 = (y_wrap ? y_wrap : 256);
int z_wrap2 = (z_wrap ? z_wrap : 256);
int x0 = px % x_wrap2, x1;
int y0 = py % y_wrap2, y1;
int z0 = pz % z_wrap2, z1;
int r0,r1, r00,r01,r10,r11;
if (x0 < 0) x0 += x_wrap2;
if (y0 < 0) y0 += y_wrap2;
if (z0 < 0) z0 += z_wrap2;
x1 = (x0+1) % x_wrap2;
y1 = (y0+1) % y_wrap2;
z1 = (z0+1) % z_wrap2;
#define stb__perlin_ease(a) (((a*6-15)*a + 10) * a * a * a)
x -= px; u = stb__perlin_ease(x);
y -= py; v = stb__perlin_ease(y);
z -= pz; w = stb__perlin_ease(z);
r0 = stb__perlin_randtab[x0];
r0 = stb__perlin_randtab[r0+seed];
r1 = stb__perlin_randtab[x1];
r1 = stb__perlin_randtab[r1+seed];
r00 = stb__perlin_randtab[r0+y0];
r01 = stb__perlin_randtab[r0+y1];
r10 = stb__perlin_randtab[r1+y0];
r11 = stb__perlin_randtab[r1+y1];
n000 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z0], x , y , z );
n001 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r00+z1], x , y , z-1 );
n010 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z0], x , y-1, z );
n011 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r01+z1], x , y-1, z-1 );
n100 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z0], x-1, y , z );
n101 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r10+z1], x-1, y , z-1 );
n110 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z0], x-1, y-1, z );
n111 = stb__perlin_grad(stb__perlin_randtab_grad_idx[r11+z1], x-1, y-1, z-1 );
n00 = stb__perlin_lerp(n000,n001,w);
n01 = stb__perlin_lerp(n010,n011,w);
n10 = stb__perlin_lerp(n100,n101,w);
n11 = stb__perlin_lerp(n110,n111,w);
n0 = stb__perlin_lerp(n00,n01,v);
n1 = stb__perlin_lerp(n10,n11,v);
return stb__perlin_lerp(n0,n1,u);
}
#endif // STB_PERLIN_IMPLEMENTATION
/*
------------------------------------------------------------------------------
This software is available under 2 licenses -- choose whichever you prefer.
------------------------------------------------------------------------------
ALTERNATIVE A - MIT License
Copyright (c) 2017 Sean Barrett
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
------------------------------------------------------------------------------
ALTERNATIVE B - Public Domain (www.unlicense.org)
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
software, either in source code form or as a compiled binary, for any purpose,
commercial or non-commercial, and by any means.
In jurisdictions that recognize copyright laws, the author or authors of this
software dedicate any and all copyright interest in the software to the public
domain. We make this dedication for the benefit of the public at large and to
the detriment of our heirs and successors. We intend this dedication to be an
overt act of relinquishment in perpetuity of all present and future rights to
this software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
------------------------------------------------------------------------------
*/

View File

@ -8,43 +8,70 @@ local function get_current_plat()
return get_config("plat") or os.host()
end
-- 定义 TBB 源码编译目标
function define_tbb_target()
target("tbb")
set_kind("static")
set_languages("c++17")
add_includedirs("third_party/tbb/include", {public = true})
add_files("third_party/tbb/src/tbb/*.cpp")
add_defines("__TBB_BUILD=1")
add_defines("TBB_SUPPRESS_DEPRECATED_MESSAGES=1")
add_defines("TBB_USE_PROFILING_TOOLS=0")
local plat = get_current_plat()
if plat == "mingw" then
add_defines("_UNICODE", "UNICODE")
elseif plat == "switch" then
add_defines("__TBB_USE_THREAD_SANITIZER=0")
add_defines("TBB_USE_ASSERT=0")
add_defines("TBB_USE_DEBUG=0")
end
add_cxflags("-Wno-implicit-fallthrough", "-Wno-unused-function", "-Wno-unused-variable", {force = true})
target_end()
end
-- 定义 Extra2D 引擎库目标
function define_extra2d_engine()
define_tbb_target()
target("extra2d")
set_kind("static")
-- 引擎源文件
add_deps("tbb")
add_files("src/**.cpp")
add_files("third_party/glad/src/glad.c")
-- 头文件路径
add_includedirs("include", {public = true})
-- 第三方库头文件路径
add_includedirs("third_party/glad/include", {public = true})
add_includedirs("third_party", {public = true})
-- 平台配置
add_defines("TBB_USE_PROFILING_TOOLS=0")
local plat = get_current_plat()
if plat == "switch" then
if plat == "mingw" then
add_defines("_UNICODE", "UNICODE")
add_packages("glm", "libsdl2", "libsdl2_mixer", {public = true})
add_syslinks("opengl32", "glu32", "winmm", "imm32", "version", "setupapi", {public = true})
elseif plat == "switch" then
local devkitPro = os.getenv("DEVKITPRO") or "C:/devkitPro"
add_includedirs(devkitPro .. "/portlibs/switch/include", {public = true})
add_linkdirs(devkitPro .. "/portlibs/switch/lib")
add_syslinks("SDL2_mixer", "SDL2", "opusfile", "opus", "vorbisidec", "ogg",
"modplug", "mpg123", "FLAC", "GLESv2", "EGL", "glapi", "drm_nouveau",
{public = true})
elseif plat == "mingw" then
add_packages("glm", "libsdl2", "libsdl2_mixer", {public = true})
add_syslinks("opengl32", "glu32", "winmm", "imm32", "version", "setupapi", {public = true})
end
-- 编译器标志 (C 和 C++ 共用)
add_cxflags("-Wall", "-Wextra", {force = true})
add_cxflags("-Wno-unused-variable", "-Wno-unused-function", "-Wno-unused-parameter", {force = true})
add_cxflags("-Wno-strict-aliasing", "-Wno-implicit-fallthrough", {force = true})
add_cxflags("-Wno-missing-field-initializers", {force = true})
-- C++ 专用编译器标志
add_cxxflags("-Wno-deprecated-copy", "-Wno-class-memaccess", {force = true})
if is_mode("debug") then