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