refactor(engine): 重构引擎代码结构,优化类型系统和核心模块
- 将核心类型和数学工具移动到types目录下,按功能分类 - 重构引用计数和智能指针实现,提高线程安全性 - 新增调度器和服务管理器,提供统一的更新机制 - 移除旧的输入系统和窗口管理代码 - 优化日志系统,移除冗余代码 - 添加TBB依赖,支持并行任务调度 - 统一代码风格,使用更简洁的命名规范
This commit is contained in:
parent
3b031666ae
commit
71eeeac033
|
|
@ -1,24 +1,14 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <core/types.h>
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <memory>
|
#include <types/base/types.h>
|
||||||
#include <platform/window.h>
|
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
// 前向声明
|
|
||||||
class Input;
|
|
||||||
class TimerManager;
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// Application 配置
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
enum class PlatformType { Auto = 0, PC, Switch };
|
enum class PlatformType { Auto = 0, PC, Switch };
|
||||||
|
|
||||||
struct AppConfig {
|
struct AppConfig {
|
||||||
std::string title = "Easy2D Application";
|
std::string title = "Extra2D Application";
|
||||||
int width = 800;
|
int width = 800;
|
||||||
int height = 600;
|
int height = 600;
|
||||||
bool fullscreen = false;
|
bool fullscreen = false;
|
||||||
|
|
@ -31,9 +21,11 @@ struct AppConfig {
|
||||||
bool enableDpiScale = false;
|
bool enableDpiScale = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
// ============================================================================
|
/**
|
||||||
// Application 单例 - 应用主控
|
* @brief 应用程序主控类
|
||||||
// ============================================================================
|
*
|
||||||
|
* 管理应用程序生命周期、窗口和主循环
|
||||||
|
*/
|
||||||
class Application {
|
class Application {
|
||||||
public:
|
public:
|
||||||
static Application& instance();
|
static Application& instance();
|
||||||
|
|
@ -51,10 +43,6 @@ public:
|
||||||
bool isPaused() const { return paused_; }
|
bool isPaused() const { return paused_; }
|
||||||
bool isRunning() const { return running_; }
|
bool isRunning() const { return running_; }
|
||||||
|
|
||||||
Window &window() { return *window_; }
|
|
||||||
Input &input();
|
|
||||||
TimerManager &timers();
|
|
||||||
|
|
||||||
float deltaTime() const { return deltaTime_; }
|
float deltaTime() const { return deltaTime_; }
|
||||||
float totalTime() const { return totalTime_; }
|
float totalTime() const { return totalTime_; }
|
||||||
int fps() const { return currentFps_; }
|
int fps() const { return currentFps_; }
|
||||||
|
|
@ -70,9 +58,6 @@ private:
|
||||||
|
|
||||||
AppConfig config_;
|
AppConfig config_;
|
||||||
|
|
||||||
UniquePtr<Window> window_;
|
|
||||||
UniquePtr<TimerManager> timerManager_;
|
|
||||||
|
|
||||||
bool initialized_ = false;
|
bool initialized_ = false;
|
||||||
bool running_ = false;
|
bool running_ = false;
|
||||||
bool paused_ = false;
|
bool paused_ = false;
|
||||||
|
|
@ -86,4 +71,6 @@ private:
|
||||||
int currentFps_ = 0;
|
int currentFps_ = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define APP extra2d::Application::instance()
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,25 +1,30 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
// Easy2D v3.0 - 统一入口头文件
|
// Extra2D v3.0 - 统一入口头文件
|
||||||
// 包含所有公共 API
|
// 包含所有公共 API
|
||||||
|
|
||||||
// Core
|
// Types
|
||||||
#include <core/color.h>
|
#include <types/base/types.h>
|
||||||
#include <core/rect.h>
|
#include <types/const/priority.h>
|
||||||
#include <core/size.h>
|
#include <types/ptr/intrusive_ptr.h>
|
||||||
#include <core/transform.h>
|
#include <types/ptr/ref_counted.h>
|
||||||
#include <core/types.h>
|
|
||||||
#include <core/vec2.h>
|
|
||||||
#include <core/vec3.h>
|
|
||||||
|
|
||||||
// Platform
|
// Math
|
||||||
#include <platform/input.h>
|
#include <types/math/color.h>
|
||||||
#include <platform/window.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
|
// Utils
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
#include <utils/random.h>
|
#include <utils/random.h>
|
||||||
#include <utils/timer.h>
|
|
||||||
|
|
||||||
// Application
|
// Application
|
||||||
#include <app/application.h>
|
#include <app/application.h>
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -1,11 +1,10 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdarg>
|
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
#include <core/types.h>
|
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
|
#include <types/base/types.h>
|
||||||
|
|
||||||
// SDL2 日志头文件
|
// SDL2 日志头文件
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <core/types.h>
|
|
||||||
#include <random>
|
#include <random>
|
||||||
|
#include <types/base/types.h>
|
||||||
|
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
#include <app/application.h>
|
#include <app/application.h>
|
||||||
#include <platform/input.h>
|
#include <core/director.h>
|
||||||
#include <platform/window.h>
|
|
||||||
#include <utils/logger.h>
|
#include <utils/logger.h>
|
||||||
#include <utils/timer.h>
|
|
||||||
|
|
||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
@ -13,15 +11,11 @@
|
||||||
|
|
||||||
namespace extra2d {
|
namespace extra2d {
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief 获取当前时间(秒)
|
|
||||||
*/
|
|
||||||
static double getTimeSeconds() {
|
static double getTimeSeconds() {
|
||||||
#ifdef __SWITCH__
|
#ifdef __SWITCH__
|
||||||
struct timespec ts;
|
struct timespec ts;
|
||||||
clock_gettime(CLOCK_MONOTONIC, &ts);
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
return static_cast<double>(ts.tv_sec) +
|
return static_cast<double>(ts.tv_sec) + static_cast<double>(ts.tv_nsec) / 1000000000.0;
|
||||||
static_cast<double>(ts.tv_nsec) / 1000000000.0;
|
|
||||||
#else
|
#else
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
auto now = steady_clock::now();
|
auto now = steady_clock::now();
|
||||||
|
|
@ -35,7 +29,9 @@ Application &Application::instance() {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::~Application() { shutdown(); }
|
Application::~Application() {
|
||||||
|
shutdown();
|
||||||
|
}
|
||||||
|
|
||||||
bool Application::init(const AppConfig& config) {
|
bool Application::init(const AppConfig& config) {
|
||||||
if (initialized_) {
|
if (initialized_) {
|
||||||
|
|
@ -61,45 +57,21 @@ bool Application::init(const AppConfig &config) {
|
||||||
if (R_SUCCEEDED(rc)) {
|
if (R_SUCCEEDED(rc)) {
|
||||||
E2D_LOG_INFO("RomFS initialized successfully");
|
E2D_LOG_INFO("RomFS initialized successfully");
|
||||||
} else {
|
} else {
|
||||||
E2D_LOG_WARN("romfsInit failed: {:#08X}, will use regular filesystem",
|
E2D_LOG_WARN("romfsInit failed: {:#08X}", rc);
|
||||||
rc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = socketInitializeDefault();
|
rc = socketInitializeDefault();
|
||||||
if (R_FAILED(rc)) {
|
if (R_FAILED(rc)) {
|
||||||
E2D_LOG_WARN(
|
E2D_LOG_WARN("socketInitializeDefault failed");
|
||||||
"socketInitializeDefault failed, nxlink will not be available");
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
window_ = unique<Window>();
|
if (!DIRECTOR.init()) {
|
||||||
WindowConfig winConfig;
|
E2D_LOG_ERROR("Failed to initialize Director");
|
||||||
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
timerManager_ = unique<TimerManager>();
|
|
||||||
|
|
||||||
initialized_ = true;
|
initialized_ = true;
|
||||||
running_ = true;
|
running_ = true;
|
||||||
|
|
||||||
|
|
@ -108,17 +80,11 @@ bool Application::init(const AppConfig &config) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::shutdown() {
|
void Application::shutdown() {
|
||||||
if (!initialized_)
|
if (!initialized_) return;
|
||||||
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;
|
PlatformType platform = config_.platform;
|
||||||
if (platform == PlatformType::Auto) {
|
if (platform == PlatformType::Auto) {
|
||||||
|
|
@ -128,6 +94,7 @@ void Application::shutdown() {
|
||||||
platform = PlatformType::PC;
|
platform = PlatformType::PC;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (platform == PlatformType::Switch) {
|
if (platform == PlatformType::Switch) {
|
||||||
#ifdef __SWITCH__
|
#ifdef __SWITCH__
|
||||||
romfsExit();
|
romfsExit();
|
||||||
|
|
@ -149,15 +116,9 @@ void Application::run() {
|
||||||
|
|
||||||
lastFrameTime_ = getTimeSeconds();
|
lastFrameTime_ = getTimeSeconds();
|
||||||
|
|
||||||
#ifdef __SWITCH__
|
while (running_) {
|
||||||
while (running_ && !window_->shouldClose()) {
|
|
||||||
mainLoop();
|
mainLoop();
|
||||||
}
|
}
|
||||||
#else
|
|
||||||
while (running_ && !window_->shouldClose()) {
|
|
||||||
mainLoop();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::quit() {
|
void Application::quit() {
|
||||||
|
|
@ -168,6 +129,7 @@ void Application::quit() {
|
||||||
void Application::pause() {
|
void Application::pause() {
|
||||||
if (!paused_) {
|
if (!paused_) {
|
||||||
paused_ = true;
|
paused_ = true;
|
||||||
|
DIRECTOR.pause();
|
||||||
E2D_LOG_INFO("Application paused");
|
E2D_LOG_INFO("Application paused");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -175,6 +137,7 @@ void Application::pause() {
|
||||||
void Application::resume() {
|
void Application::resume() {
|
||||||
if (paused_) {
|
if (paused_) {
|
||||||
paused_ = false;
|
paused_ = false;
|
||||||
|
DIRECTOR.resume();
|
||||||
lastFrameTime_ = getTimeSeconds();
|
lastFrameTime_ = getTimeSeconds();
|
||||||
E2D_LOG_INFO("Application resumed");
|
E2D_LOG_INFO("Application resumed");
|
||||||
}
|
}
|
||||||
|
|
@ -195,33 +158,22 @@ void Application::mainLoop() {
|
||||||
fpsTimer_ -= 1.0f;
|
fpsTimer_ -= 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
window_->pollEvents();
|
|
||||||
|
|
||||||
if (!paused_) {
|
if (!paused_) {
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
window_->swapBuffers();
|
|
||||||
|
|
||||||
if (!config_.vsync && config_.fpsLimit > 0) {
|
if (!config_.vsync && config_.fpsLimit > 0) {
|
||||||
double frameEndTime = getTimeSeconds();
|
double frameEndTime = getTimeSeconds();
|
||||||
double frameTime = frameEndTime - currentTime;
|
double frameTime = frameEndTime - currentTime;
|
||||||
double target = 1.0 / static_cast<double>(config_.fpsLimit);
|
double target = 1.0 / static_cast<double>(config_.fpsLimit);
|
||||||
if (frameTime < target) {
|
if (frameTime < target) {
|
||||||
auto sleepSeconds = target - frameTime;
|
std::this_thread::sleep_for(std::chrono::duration<double>(target - frameTime));
|
||||||
std::this_thread::sleep_for(std::chrono::duration<double>(sleepSeconds));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Application::update() {
|
void Application::update() {
|
||||||
if (timerManager_) {
|
DIRECTOR.mainLoop(deltaTime_);
|
||||||
timerManager_->update(deltaTime_);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Input &Application::input() { return *window_->getInput(); }
|
|
||||||
|
|
||||||
TimerManager &Application::timers() { return *timerManager_; }
|
|
||||||
|
|
||||||
} // namespace extra2d
|
} // namespace extra2d
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
@ -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
|
|
@ -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.
|
|
||||||
------------------------------------------------------------------------------
|
|
||||||
*/
|
|
||||||
|
|
@ -8,43 +8,70 @@ local function get_current_plat()
|
||||||
return get_config("plat") or os.host()
|
return get_config("plat") or os.host()
|
||||||
end
|
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 引擎库目标
|
-- 定义 Extra2D 引擎库目标
|
||||||
function define_extra2d_engine()
|
function define_extra2d_engine()
|
||||||
|
define_tbb_target()
|
||||||
|
|
||||||
target("extra2d")
|
target("extra2d")
|
||||||
set_kind("static")
|
set_kind("static")
|
||||||
|
|
||||||
-- 引擎源文件
|
add_deps("tbb")
|
||||||
|
|
||||||
add_files("src/**.cpp")
|
add_files("src/**.cpp")
|
||||||
add_files("third_party/glad/src/glad.c")
|
add_files("third_party/glad/src/glad.c")
|
||||||
|
|
||||||
-- 头文件路径
|
|
||||||
add_includedirs("include", {public = true})
|
add_includedirs("include", {public = true})
|
||||||
|
|
||||||
-- 第三方库头文件路径
|
|
||||||
add_includedirs("third_party/glad/include", {public = true})
|
add_includedirs("third_party/glad/include", {public = true})
|
||||||
add_includedirs("third_party", {public = true})
|
add_includedirs("third_party", {public = true})
|
||||||
|
|
||||||
-- 平台配置
|
add_defines("TBB_USE_PROFILING_TOOLS=0")
|
||||||
|
|
||||||
local plat = get_current_plat()
|
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"
|
local devkitPro = os.getenv("DEVKITPRO") or "C:/devkitPro"
|
||||||
add_includedirs(devkitPro .. "/portlibs/switch/include", {public = true})
|
add_includedirs(devkitPro .. "/portlibs/switch/include", {public = true})
|
||||||
add_linkdirs(devkitPro .. "/portlibs/switch/lib")
|
add_linkdirs(devkitPro .. "/portlibs/switch/lib")
|
||||||
add_syslinks("SDL2_mixer", "SDL2", "opusfile", "opus", "vorbisidec", "ogg",
|
add_syslinks("SDL2_mixer", "SDL2", "opusfile", "opus", "vorbisidec", "ogg",
|
||||||
"modplug", "mpg123", "FLAC", "GLESv2", "EGL", "glapi", "drm_nouveau",
|
"modplug", "mpg123", "FLAC", "GLESv2", "EGL", "glapi", "drm_nouveau",
|
||||||
{public = true})
|
{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
|
end
|
||||||
|
|
||||||
-- 编译器标志 (C 和 C++ 共用)
|
|
||||||
add_cxflags("-Wall", "-Wextra", {force = true})
|
add_cxflags("-Wall", "-Wextra", {force = true})
|
||||||
add_cxflags("-Wno-unused-variable", "-Wno-unused-function", "-Wno-unused-parameter", {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-strict-aliasing", "-Wno-implicit-fallthrough", {force = true})
|
||||||
add_cxflags("-Wno-missing-field-initializers", {force = true})
|
add_cxflags("-Wno-missing-field-initializers", {force = true})
|
||||||
|
|
||||||
-- C++ 专用编译器标志
|
|
||||||
add_cxxflags("-Wno-deprecated-copy", "-Wno-class-memaccess", {force = true})
|
add_cxxflags("-Wno-deprecated-copy", "-Wno-class-memaccess", {force = true})
|
||||||
|
|
||||||
if is_mode("debug") then
|
if is_mode("debug") then
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue