commit f299d9e7653fc7b6949741c3e2eafda3e3b2186b Author: ChestnutYueyue <952134128@qq.com> Date: Wed Feb 11 19:40:26 2026 +0800 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3a2cb15 --- /dev/null +++ b/.gitignore @@ -0,0 +1,141 @@ +# ============================================ +# Easy2D 项目 Git 忽略配置 +# ============================================ + +# -------------------------------------------- +# 构建输出目录 +# -------------------------------------------- +/build/ +/bin/ +/obj/ +/out/ +/Debug/ +/Release/ +/x64/ +/x86/ +/.build/ +/.trae/ +# -------------------------------------------- +# xmake 构建系统 +# -------------------------------------------- +/.xmake/ +/.xmake.conf +/xmake.config + +# -------------------------------------------- +# IDE 和编辑器配置 +# -------------------------------------------- +# Visual Studio +.vs/ +*.sln +*.vcxproj +*.vcxproj.filters +*.vcxproj.user +*.suo +*.user +*.userosscache +*.sdf +*.opensdf +*.VC.db +*.VC.opendb +ipch/ + +# VS Code +.vscode/ +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# CLion / IntelliJ +.idea/ +*.iml +*.iws +*.ipr +cmake-build-*/ + +# -------------------------------------------- +# 编译缓存和索引 +# -------------------------------------------- +/.cache/ +/.clangd/ +/compile_commands.json +.clang-format +.clang-tidy + +# -------------------------------------------- +# 依赖和包管理 +# -------------------------------------------- +/packages/ +/deps/ +/third_party/ +/vendor/ + +# -------------------------------------------- +# 生成的文件 +# -------------------------------------------- +*.exe +*.dll +*.lib +*.a +*.so +*.dylib +*.pdb +*.ilk +*.exp +*.manifest +*.res +*.o +*.obj + +# -------------------------------------------- +# 日志和临时文件 +# -------------------------------------------- +*.log +*.tmp +*.temp +*.swp +*.swo +*~ +.DS_Store +Thumbs.db + +# -------------------------------------------- +# 测试和覆盖率 +# -------------------------------------------- +/test-results/ +/coverage/ +*.gcov +*.gcda +*.gcno + +# -------------------------------------------- +# 文档生成 +# -------------------------------------------- +/docs/html/ +/docs/latex/ +/doxygen/ + +# -------------------------------------------- +# 平台特定 +# -------------------------------------------- +# Windows +*.bat +*.cmd + +# macOS +.DS_Store +.AppleDouble +.LSOverride + +# Linux +*.d + +# -------------------------------------------- +# 其他 +# -------------------------------------------- +*.zip +*.tar.gz +*.rar +*.7z diff --git a/Extra2D/include/KHR/khrplatform.h b/Extra2D/include/KHR/khrplatform.h new file mode 100644 index 0000000..0164644 --- /dev/null +++ b/Extra2D/include/KHR/khrplatform.h @@ -0,0 +1,311 @@ +#ifndef __khrplatform_h_ +#define __khrplatform_h_ + +/* +** Copyright (c) 2008-2018 The Khronos Group Inc. +** +** Permission is hereby granted, free of charge, to any person obtaining a +** copy of this software and/or associated documentation files (the +** "Materials"), to deal in the Materials without restriction, including +** without limitation the rights to use, copy, modify, merge, publish, +** distribute, sublicense, and/or sell copies of the Materials, and to +** permit persons to whom the Materials are 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 Materials. +** +** THE MATERIALS ARE 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 +** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS. +*/ + +/* Khronos platform-specific types and definitions. + * + * The master copy of khrplatform.h is maintained in the Khronos EGL + * Registry repository at https://github.com/KhronosGroup/EGL-Registry + * The last semantic modification to khrplatform.h was at commit ID: + * 67a3e0864c2d75ea5287b9f3d2eb74a745936692 + * + * Adopters may modify this file to suit their platform. Adopters are + * encouraged to submit platform specific modifications to the Khronos + * group so that they can be included in future versions of this file. + * Please submit changes by filing pull requests or issues on + * the EGL Registry repository linked above. + * + * + * See the Implementer's Guidelines for information about where this file + * should be located on your system and for more details of its use: + * http://www.khronos.org/registry/implementers_guide.pdf + * + * This file should be included as + * #include + * by Khronos client API header files that use its types and defines. + * + * The types in khrplatform.h should only be used to define API-specific types. + * + * Types defined in khrplatform.h: + * khronos_int8_t signed 8 bit + * khronos_uint8_t unsigned 8 bit + * khronos_int16_t signed 16 bit + * khronos_uint16_t unsigned 16 bit + * khronos_int32_t signed 32 bit + * khronos_uint32_t unsigned 32 bit + * khronos_int64_t signed 64 bit + * khronos_uint64_t unsigned 64 bit + * khronos_intptr_t signed same number of bits as a pointer + * khronos_uintptr_t unsigned same number of bits as a pointer + * khronos_ssize_t signed size + * khronos_usize_t unsigned size + * khronos_float_t signed 32 bit floating point + * khronos_time_ns_t unsigned 64 bit time in nanoseconds + * khronos_utime_nanoseconds_t unsigned time interval or absolute time in + * nanoseconds + * khronos_stime_nanoseconds_t signed time interval in nanoseconds + * khronos_boolean_enum_t enumerated boolean type. This should + * only be used as a base type when a client API's boolean type is + * an enum. Client APIs which use an integer or other type for + * booleans cannot use this as the base type for their boolean. + * + * Tokens defined in khrplatform.h: + * + * KHRONOS_FALSE, KHRONOS_TRUE Enumerated boolean false/true values. + * + * KHRONOS_SUPPORT_INT64 is 1 if 64 bit integers are supported; otherwise 0. + * KHRONOS_SUPPORT_FLOAT is 1 if floats are supported; otherwise 0. + * + * Calling convention macros defined in this file: + * KHRONOS_APICALL + * KHRONOS_APIENTRY + * KHRONOS_APIATTRIBUTES + * + * These may be used in function prototypes as: + * + * KHRONOS_APICALL void KHRONOS_APIENTRY funcname( + * int arg1, + * int arg2) KHRONOS_APIATTRIBUTES; + */ + +#if defined(__SCITECH_SNAP__) && !defined(KHRONOS_STATIC) +# define KHRONOS_STATIC 1 +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APICALL + *------------------------------------------------------------------------- + * This precedes the return type of the function in the function prototype. + */ +#if defined(KHRONOS_STATIC) + /* If the preprocessor constant KHRONOS_STATIC is defined, make the + * header compatible with static linking. */ +# define KHRONOS_APICALL +#elif defined(_WIN32) +# define KHRONOS_APICALL __declspec(dllimport) +#elif defined (__SYMBIAN32__) +# define KHRONOS_APICALL IMPORT_C +#elif defined(__ANDROID__) +# define KHRONOS_APICALL __attribute__((visibility("default"))) +#else +# define KHRONOS_APICALL +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APIENTRY + *------------------------------------------------------------------------- + * This follows the return type of the function and precedes the function + * name in the function prototype. + */ +#if defined(_WIN32) && !defined(_WIN32_WCE) && !defined(__SCITECH_SNAP__) + /* Win32 but not WinCE */ +# define KHRONOS_APIENTRY __stdcall +#else +# define KHRONOS_APIENTRY +#endif + +/*------------------------------------------------------------------------- + * Definition of KHRONOS_APIATTRIBUTES + *------------------------------------------------------------------------- + * This follows the closing parenthesis of the function prototype arguments. + */ +#if defined (__ARMCC_2__) +#define KHRONOS_APIATTRIBUTES __softfp +#else +#define KHRONOS_APIATTRIBUTES +#endif + +/*------------------------------------------------------------------------- + * basic type definitions + *-----------------------------------------------------------------------*/ +#if (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__GNUC__) || defined(__SCO__) || defined(__USLC__) + + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 +/* + * To support platform where unsigned long cannot be used interchangeably with + * inptr_t (e.g. CHERI-extended ISAs), we can use the stdint.h intptr_t. + * Ideally, we could just use (u)intptr_t everywhere, but this could result in + * ABI breakage if khronos_uintptr_t is changed from unsigned long to + * unsigned long long or similar (this results in different C++ name mangling). + * To avoid changes for existing platforms, we restrict usage of intptr_t to + * platforms where the size of a pointer is larger than the size of long. + */ +#if defined(__SIZEOF_LONG__) && defined(__SIZEOF_POINTER__) +#if __SIZEOF_POINTER__ > __SIZEOF_LONG__ +#define KHRONOS_USE_INTPTR_T +#endif +#endif + +#elif defined(__VMS ) || defined(__sgi) + +/* + * Using + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(_WIN32) && !defined(__SCITECH_SNAP__) + +/* + * Win32 + */ +typedef __int32 khronos_int32_t; +typedef unsigned __int32 khronos_uint32_t; +typedef __int64 khronos_int64_t; +typedef unsigned __int64 khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif defined(__sun__) || defined(__digital__) + +/* + * Sun or Digital + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#if defined(__arch64__) || defined(_LP64) +typedef long int khronos_int64_t; +typedef unsigned long int khronos_uint64_t; +#else +typedef long long int khronos_int64_t; +typedef unsigned long long int khronos_uint64_t; +#endif /* __arch64__ */ +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#elif 0 + +/* + * Hypothetical platform with no float or int64 support + */ +typedef int khronos_int32_t; +typedef unsigned int khronos_uint32_t; +#define KHRONOS_SUPPORT_INT64 0 +#define KHRONOS_SUPPORT_FLOAT 0 + +#else + +/* + * Generic fallback + */ +#include +typedef int32_t khronos_int32_t; +typedef uint32_t khronos_uint32_t; +typedef int64_t khronos_int64_t; +typedef uint64_t khronos_uint64_t; +#define KHRONOS_SUPPORT_INT64 1 +#define KHRONOS_SUPPORT_FLOAT 1 + +#endif + + +/* + * Types that are (so far) the same on all platforms + */ +typedef signed char khronos_int8_t; +typedef unsigned char khronos_uint8_t; +typedef signed short int khronos_int16_t; +typedef unsigned short int khronos_uint16_t; + +/* + * Types that differ between LLP64 and LP64 architectures - in LLP64, + * pointers are 64 bits, but 'long' is still 32 bits. Win64 appears + * to be the only LLP64 architecture in current use. + */ +#ifdef KHRONOS_USE_INTPTR_T +typedef intptr_t khronos_intptr_t; +typedef uintptr_t khronos_uintptr_t; +#elif defined(_WIN64) +typedef signed long long int khronos_intptr_t; +typedef unsigned long long int khronos_uintptr_t; +#else +typedef signed long int khronos_intptr_t; +typedef unsigned long int khronos_uintptr_t; +#endif + +#if defined(_WIN64) +typedef signed long long int khronos_ssize_t; +typedef unsigned long long int khronos_usize_t; +#else +typedef signed long int khronos_ssize_t; +typedef unsigned long int khronos_usize_t; +#endif + +#if KHRONOS_SUPPORT_FLOAT +/* + * Float type + */ +typedef float khronos_float_t; +#endif + +#if KHRONOS_SUPPORT_INT64 +/* Time types + * + * These types can be used to represent a time interval in nanoseconds or + * an absolute Unadjusted System Time. Unadjusted System Time is the number + * of nanoseconds since some arbitrary system event (e.g. since the last + * time the system booted). The Unadjusted System Time is an unsigned + * 64 bit value that wraps back to 0 every 584 years. Time intervals + * may be either signed or unsigned. + */ +typedef khronos_uint64_t khronos_utime_nanoseconds_t; +typedef khronos_int64_t khronos_stime_nanoseconds_t; +#endif + +/* + * Dummy value used to pad enum types to 32 bits. + */ +#ifndef KHRONOS_MAX_ENUM +#define KHRONOS_MAX_ENUM 0x7FFFFFFF +#endif + +/* + * Enumerated boolean type + * + * Values other than zero should be considered to be true. Therefore + * comparisons should not be made against KHRONOS_TRUE. + */ +typedef enum { + KHRONOS_FALSE = 0, + KHRONOS_TRUE = 1, + KHRONOS_BOOLEAN_ENUM_FORCE_SIZE = KHRONOS_MAX_ENUM +} khronos_boolean_enum_t; + +#endif /* __khrplatform_h_ */ diff --git a/Extra2D/include/extra2d/action/action.h b/Extra2D/include/extra2d/action/action.h new file mode 100644 index 0000000..5c4428f --- /dev/null +++ b/Extra2D/include/extra2d/action/action.h @@ -0,0 +1,76 @@ +#pragma once + +#include +#include + +namespace extra2d { +class Node; + +enum class ActionState { Idle, Running, Paused, Completed }; + +class Action { +public: + using ProgressCallback = std::function; + using CompletionCallback = std::function; + + Action(); + virtual ~Action() = default; + + Action(const Action &) = delete; + Action &operator=(const Action &) = delete; + Action(Action &&) = default; + Action &operator=(Action &&) = default; + + virtual void start(Node *target); + virtual void stop(); + virtual void update(float dt); + virtual void step(float dt); + + virtual bool isDone() const = 0; + virtual Action *clone() const = 0; + virtual Action *reverse() const = 0; + + void pause(); + void resume(); + void restart(); + + ActionState getState() const { return state_; } + float getElapsed() const { return elapsed_; } + float getDuration() const { return duration_; } + Node *getTarget() const { return target_; } + Node *getOriginalTarget() const { return originalTarget_; } + + void setDuration(float duration) { duration_ = duration; } + void setSpeed(float speed) { speed_ = speed; } + float getSpeed() const { return speed_; } + + void setProgressCallback(ProgressCallback callback) { + progressCallback_ = std::move(callback); + } + void setCompletionCallback(CompletionCallback callback) { + completionCallback_ = std::move(callback); + } + + void setTag(int tag) { tag_ = tag; } + int getTag() const { return tag_; } + +protected: + virtual void onStart() {} + virtual void onUpdate(float progress) = 0; + virtual void onComplete() {} + + void setDone() { state_ = ActionState::Completed; } + + Node *target_ = nullptr; + Node *originalTarget_ = nullptr; + ActionState state_ = ActionState::Idle; + float elapsed_ = 0.0f; + float duration_ = 0.0f; + float speed_ = 1.0f; + int tag_ = -1; + ProgressCallback progressCallback_; + CompletionCallback completionCallback_; +}; + +using ActionPtr = std::unique_ptr; +} // namespace extra2d diff --git a/Extra2D/include/extra2d/action/actions.h b/Extra2D/include/extra2d/action/actions.h new file mode 100644 index 0000000..9dd956c --- /dev/null +++ b/Extra2D/include/extra2d/action/actions.h @@ -0,0 +1,278 @@ +#pragma once + +#include "extra2d/action/action.h" +#include "extra2d/core/math_types.h" +#include +#include + +namespace extra2d { +// Interval Action Base +class IntervalAction : public Action { +public: + explicit IntervalAction(float duration); + bool isDone() const override; +}; + +// Instant Action Base +class InstantAction : public Action { +public: + InstantAction(); + bool isDone() const override; +}; + +// Move Actions +class MoveBy : public IntervalAction { +public: + MoveBy(float duration, const Vec2 &delta); + + Action *clone() const override; + Action *reverse() const override; + +protected: + void onStart() override; + void onUpdate(float progress) override; + +private: + Vec2 delta_; + Vec2 startPosition_; +}; + +class MoveTo : public IntervalAction { +public: + MoveTo(float duration, const Vec2 &position); + + Action *clone() const override; + Action *reverse() const override; + +protected: + void onStart() override; + void onUpdate(float progress) override; + +private: + Vec2 endPosition_; + Vec2 startPosition_; + Vec2 delta_; +}; + +// Scale Actions +class ScaleBy : public IntervalAction { +public: + ScaleBy(float duration, float scale); + ScaleBy(float duration, float scaleX, float scaleY); + ScaleBy(float duration, const Vec2 &scale); + + Action *clone() const override; + Action *reverse() const override; + +protected: + void onStart() override; + void onUpdate(float progress) override; + +private: + Vec2 deltaScale_; + Vec2 startScale_; +}; + +class ScaleTo : public IntervalAction { +public: + ScaleTo(float duration, float scale); + ScaleTo(float duration, float scaleX, float scaleY); + ScaleTo(float duration, const Vec2 &scale); + + Action *clone() const override; + Action *reverse() const override; + +protected: + void onStart() override; + void onUpdate(float progress) override; + +private: + Vec2 endScale_; + Vec2 startScale_; + Vec2 delta_; +}; + +// Rotate Actions +class RotateBy : public IntervalAction { +public: + RotateBy(float duration, float deltaAngle); + RotateBy(float duration, float deltaAngleX, float deltaAngleY); + + Action *clone() const override; + Action *reverse() const override; + +protected: + void onStart() override; + void onUpdate(float progress) override; + +private: + float deltaAngle_ = 0.0f; + float startAngle_ = 0.0f; +}; + +class RotateTo : public IntervalAction { +public: + RotateTo(float duration, float angle); + RotateTo(float duration, float angleX, float angleY); + + Action *clone() const override; + Action *reverse() const override; + +protected: + void onStart() override; + void onUpdate(float progress) override; + +private: + float endAngle_ = 0.0f; + float startAngle_ = 0.0f; + float deltaAngle_ = 0.0f; +}; + +// Fade Actions +class FadeIn : public IntervalAction { +public: + explicit FadeIn(float duration); + + Action *clone() const override; + Action *reverse() const override; + +protected: + void onStart() override; + void onUpdate(float progress) override; + +private: + float startOpacity_ = 0.0f; +}; + +class FadeOut : public IntervalAction { +public: + explicit FadeOut(float duration); + + Action *clone() const override; + Action *reverse() const override; + +protected: + void onStart() override; + void onUpdate(float progress) override; + +private: + float startOpacity_ = 0.0f; +}; + +class FadeTo : public IntervalAction { +public: + FadeTo(float duration, float opacity); + + Action *clone() const override; + Action *reverse() const override; + +protected: + void onStart() override; + void onUpdate(float progress) override; + +private: + float endOpacity_ = 0.0f; + float startOpacity_ = 0.0f; + float deltaOpacity_ = 0.0f; +}; + +// Composite Actions +class Sequence : public IntervalAction { +public: + Sequence(const std::vector &actions); + ~Sequence(); + + Action *clone() const override; + Action *reverse() const override; + +protected: + void onStart() override; + void onUpdate(float progress) override; + +private: + std::vector actions_; + int currentIndex_ = 0; + float split_ = 0.0f; + float last_ = 0.0f; +}; + +class Spawn : public IntervalAction { +public: + Spawn(const std::vector &actions); + ~Spawn(); + + Action *clone() const override; + Action *reverse() const override; + +protected: + void onStart() override; + void onUpdate(float progress) override; + +private: + std::vector actions_; +}; + +// Loop Action +class Loop : public Action { +public: + Loop(Action *action, int times = -1); + ~Loop(); + + bool isDone() const override; + Action *clone() const override; + Action *reverse() const override; + +protected: + void onStart() override; + void onUpdate(float progress) override; + +private: + Action *action_ = nullptr; + int times_ = 1; + int currentTimes_ = 0; +}; + +// Delay Action +class Delay : public IntervalAction { +public: + explicit Delay(float duration); + + Action *clone() const override; + Action *reverse() const override; + +protected: + void onUpdate(float progress) override; +}; + +// CallFunc Action +class CallFunc : public InstantAction { +public: + using Callback = std::function; + + explicit CallFunc(Callback callback); + + Action *clone() const override; + Action *reverse() const override; + +protected: + void onUpdate(float progress) override; + +private: + Callback callback_; +}; + +// Helper functions +inline Sequence *sequence(const std::vector &actions) { + return new Sequence(actions); +} +inline Spawn *spawn(const std::vector &actions) { + return new Spawn(actions); +} +inline Loop *loop(Action *action, int times = -1) { + return new Loop(action, times); +} +inline Delay *delay(float duration) { return new Delay(duration); } +inline CallFunc *callFunc(CallFunc::Callback callback) { + return new CallFunc(std::move(callback)); +} +} // namespace extra2d diff --git a/Extra2D/include/extra2d/action/ease.h b/Extra2D/include/extra2d/action/ease.h new file mode 100644 index 0000000..d91b48f --- /dev/null +++ b/Extra2D/include/extra2d/action/ease.h @@ -0,0 +1,67 @@ +#pragma once + +namespace extra2d { +// Easing function type +using EaseFunction = float (*)(float); + +// Linear (no easing) +float easeLinear(float t); + +// Quadratic +float easeInQuad(float t); +float easeOutQuad(float t); +float easeInOutQuad(float t); + +// Cubic +float easeInCubic(float t); +float easeOutCubic(float t); +float easeInOutCubic(float t); + +// Quartic +float easeInQuart(float t); +float easeOutQuart(float t); +float easeInOutQuart(float t); + +// Quintic +float easeInQuint(float t); +float easeOutQuint(float t); +float easeInOutQuint(float t); + +// Sine +float easeInSine(float t); +float easeOutSine(float t); +float easeInOutSine(float t); + +// Exponential +float easeInExpo(float t); +float easeOutExpo(float t); +float easeInOutExpo(float t); + +// Circular +float easeInCirc(float t); +float easeOutCirc(float t); +float easeInOutCirc(float t); + +// Back +float easeInBack(float t); +float easeOutBack(float t); +float easeInOutBack(float t); + +// Elastic +float easeInElastic(float t); +float easeOutElastic(float t); +float easeInOutElastic(float t); + +// Bounce +float easeInBounce(float t); +float easeOutBounce(float t); +float easeInOutBounce(float t); + +// Ease Action wrapper +class Action; + +class EaseAction { +public: + static Action *create(Action *action, EaseFunction easeFunc); +}; +} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/als_parser.h b/Extra2D/include/extra2d/animation/als_parser.h new file mode 100644 index 0000000..5215091 --- /dev/null +++ b/Extra2D/include/extra2d/animation/als_parser.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// ALS 图层信息 +// ============================================================================ +struct AlsLayerInfo { + std::string aniPath; // 子动画的 ANI 文件路径 + int zOrder = 0; // 层级顺序 + Vec2 offset; // 层偏移 +}; + +// ============================================================================ +// ALS 解析结果 +// ============================================================================ +struct AlsParseResult { + bool success = false; + std::string errorMessage; + std::vector layers; +}; + +// ============================================================================ +// AlsParser - ALS 复合动画文件解析器 +// 解析 .als 文件获取多层动画的图层信息 +// ============================================================================ +class AlsParser { +public: + AlsParser() = default; + + /// 从文件解析 + AlsParseResult parse(const std::string &filePath); + + /// 从内存内容解析 + AlsParseResult parseFromMemory(const std::string &content, + const std::string &basePath = ""); + + /// 设置基础路径 + void setBasePath(const std::string &basePath) { basePath_ = basePath; } + +private: + std::string basePath_; + + std::string resolvePath(const std::string &relativePath) const; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/ani_binary_parser.h b/Extra2D/include/extra2d/animation/ani_binary_parser.h new file mode 100644 index 0000000..08627a8 --- /dev/null +++ b/Extra2D/include/extra2d/animation/ani_binary_parser.h @@ -0,0 +1,68 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// DNF ANI 二进制格式中的节点类型枚举 +// ============================================================================ +enum class AniNodeType : uint16_t { + Loop = 0, + Shadow = 1, + Coord = 3, + ImageRate = 7, + ImageRotate = 8, + RGBA = 9, + Interpolation = 10, + GraphicEffect = 11, + Delay = 12, + DamageType = 13, + DamageBox = 14, + AttackBox = 15, + PlaySound = 16, + Preload = 17, + Spectrum = 18, + SetFlag = 23, + FlipType = 24, + LoopStart = 25, + LoopEnd = 26, + Clip = 27, + Operation = 28, +}; + +// ============================================================================ +// AniBinaryParser - ANI 二进制格式解析器 +// 参考 DNF-Porting 的 PvfAnimation 实现 +// ============================================================================ +class AniBinaryParser { +public: + AniBinaryParser() = default; + + /// 从二进制数据解析 + AniParseResult parse(const uint8_t *data, size_t length); + + /// 从文件解析 + AniParseResult parseFromFile(const std::string &filePath); + + /// 设置路径替换回调 + void setPathResolver(PathResolveCallback callback) { + pathResolver_ = std::move(callback); + } + + /// 设置基础路径 + void setBasePath(const std::string &basePath) { basePath_ = basePath; } + +private: + PathResolveCallback pathResolver_; + std::string basePath_; + + std::string resolvePath(const std::string &relativePath) const; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/ani_parser.h b/Extra2D/include/extra2d/animation/ani_parser.h new file mode 100644 index 0000000..d704faa --- /dev/null +++ b/Extra2D/include/extra2d/animation/ani_parser.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// ANI 文件解析结果 +// ============================================================================ +struct AniParseResult { + bool success = false; + std::string errorMessage; + Ptr clip; +}; + +// ============================================================================ +// AniParser - ANI 脚本文件解析器 +// 将原始 ANI 文件格式解析为 AnimationClip 数据 +// ============================================================================ +class AniParser { +public: + AniParser() = default; + + /// 从文件解析 + AniParseResult parse(const std::string &filePath); + + /// 从内存内容解析 + AniParseResult parseFromMemory(const std::string &content, + const std::string &basePath = ""); + + /// 设置路径替换回调(对应原始 AdditionalOptions) + void setPathResolver(PathResolveCallback callback) { + pathResolver_ = std::move(callback); + } + + /// 设置基础路径(用于解析相对路径) + void setBasePath(const std::string &basePath) { basePath_ = basePath; } + +private: + PathResolveCallback pathResolver_; + std::string basePath_; + + std::string resolvePath(const std::string &relativePath) const; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/animated_sprite.h b/Extra2D/include/extra2d/animation/animated_sprite.h new file mode 100644 index 0000000..66fc72f --- /dev/null +++ b/Extra2D/include/extra2d/animation/animated_sprite.h @@ -0,0 +1,128 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// AnimatedSprite - 动画精灵节点 +// 将 AnimationController 与 Sprite 渲染桥接,接入场景图 +// ============================================================================ +class AnimatedSprite : public Sprite { +public: + AnimatedSprite(); + ~AnimatedSprite() override = default; + + // ------ 静态工厂 ------ + static Ptr create(); + static Ptr create(Ptr clip); + static Ptr create(const std::string &aniFilePath); + + // ------ 动画绑定 ------ + void setAnimationClip(Ptr clip); + void loadAnimation(const std::string &aniFilePath); + Ptr getAnimationClip() const; + + // ------ 动画字典 ------ + void addAnimation(const std::string &name, Ptr clip); + void play(const std::string &name, bool loop = true); + bool hasAnimation(const std::string &name) const; + Ptr getAnimation(const std::string &name) const; + const std::string &getCurrentAnimationName() const; + + // ------ 播放控制(委托 controller_)------ + void play(); + void pause(); + void resume(); + void stop(); + void reset(); + bool isPlaying() const; + bool isPaused() const; + bool isStopped() const; + + // ------ 属性控制 ------ + void setLooping(bool loop); + bool isLooping() const; + void setPlaybackSpeed(float speed); + float getPlaybackSpeed() const; + + // ------ 帧控制 ------ + void setFrameIndex(size_t index); + size_t getCurrentFrameIndex() const; + size_t getTotalFrames() const; + void nextFrame(); + void prevFrame(); + + // ------ 帧范围限制 ------ + /// 设置帧播放范围(用于精灵图动画,限制在指定范围内循环) + /// @param start 起始帧索引(包含) + /// @param end 结束帧索引(包含),-1表示不限制 + void setFrameRange(int start, int end = -1); + + /// 获取当前帧范围 + /// @return pair<起始帧, 结束帧>,结束帧为-1表示不限制 + std::pair getFrameRange() const; + + /// 清除帧范围限制(恢复播放所有帧) + void clearFrameRange(); + + /// 检查是否设置了帧范围限制 + bool hasFrameRange() const; + + // ------ 回调 ------ + void setCompletionCallback(AnimationController::CompletionCallback cb); + void setKeyframeCallback(AnimationController::KeyframeCallback cb); + void setSoundTriggerCallback(AnimationController::SoundTriggerCallback cb); + + // ------ 碰撞盒访问(当前帧)------ + const std::vector> &getCurrentDamageBoxes() const; + const std::vector> &getCurrentAttackBoxes() const; + + // ------ 帧变换控制 ------ + /// 设置是否由动画帧数据覆盖节点的 position/scale/rotation + /// ANI 动画需要开启(默认),精灵图动画应关闭 + void setApplyFrameTransform(bool apply) { applyFrameTransform_ = apply; } + bool isApplyFrameTransform() const { return applyFrameTransform_; } + + // ------ 自动播放 ------ + void setAutoPlay(bool autoPlay) { autoPlay_ = autoPlay; } + bool isAutoPlay() const { return autoPlay_; } + + // ------ 直接控制器访问 ------ + AnimationController &getController() { return controller_; } + const AnimationController &getController() const { return controller_; } + +protected: + void onUpdate(float dt) override; + void onEnter() override; + +private: + AnimationController controller_; + bool autoPlay_ = false; + bool applyFrameTransform_ = true; + + // 动画字典 + std::unordered_map> animations_; + std::string currentAnimationName_; + static const std::string emptyString_; + + // 帧范围限制(用于精灵图动画) + int frameRangeStart_ = 0; // 起始帧索引 + int frameRangeEnd_ = -1; // 结束帧索引,-1表示不限制 + + // 空碰撞盒列表(用于无帧时返回引用) + static const std::vector> emptyBoxes_; + + void applyFrame(const AnimationFrame &frame); + void onFrameChanged(size_t oldIdx, size_t newIdx, + const AnimationFrame &frame); +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/animation_cache.h b/Extra2D/include/extra2d/animation/animation_cache.h new file mode 100644 index 0000000..e894fd0 --- /dev/null +++ b/Extra2D/include/extra2d/animation/animation_cache.h @@ -0,0 +1,102 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace extra2d { + +// 路径替换回调(对应原始 AdditionalOptions) +using PathResolveCallback = std::function; + +// ============================================================================ +// AnimationCache - 动画片段全局缓存(借鉴 Cocos AnimationCache) +// 同一 ANI 文件只解析一次,后续直接复用数据 +// ============================================================================ +class AnimationCache { +public: + static AnimationCache &getInstance() { + static AnimationCache instance; + return instance; + } + + // ------ 加载与获取 ------ + + /// 从文件加载(自动缓存),已缓存则直接返回 + /// 注意:实际的 ANI 解析逻辑在 AniParser 中实现 + /// 此方法在 animation_cache.cpp 中实现,依赖 AniParser + Ptr loadClip(const std::string &aniFilePath); + + /// 从缓存获取(不触发加载) + Ptr getClip(const std::string &name) const { + std::lock_guard lock(mutex_); + auto it = clips_.find(name); + if (it != clips_.end()) + return it->second; + return nullptr; + } + + /// 手动添加到缓存 + void addClip(Ptr clip, const std::string &name) { + std::lock_guard lock(mutex_); + clips_[name] = std::move(clip); + } + + // ------ 缓存管理 ------ + + bool has(const std::string &name) const { + std::lock_guard lock(mutex_); + return clips_.find(name) != clips_.end(); + } + + void removeClip(const std::string &name) { + std::lock_guard lock(mutex_); + clips_.erase(name); + } + + /// 移除未被外部引用的动画片段 + void removeUnusedClips() { + std::lock_guard lock(mutex_); + for (auto it = clips_.begin(); it != clips_.end();) { + if (it->second.use_count() == 1) { + it = clips_.erase(it); + } else { + ++it; + } + } + } + + void clear() { + std::lock_guard lock(mutex_); + clips_.clear(); + } + + size_t count() const { + std::lock_guard lock(mutex_); + return clips_.size(); + } + + // ------ 路径配置 ------ + void setPathResolver(PathResolveCallback resolver) { + pathResolver_ = std::move(resolver); + } + + PathResolveCallback getPathResolver() const { return pathResolver_; } + +private: + AnimationCache() = default; + ~AnimationCache() = default; + AnimationCache(const AnimationCache &) = delete; + AnimationCache &operator=(const AnimationCache &) = delete; + + mutable std::mutex mutex_; + std::unordered_map> clips_; + PathResolveCallback pathResolver_; +}; + +// 便捷宏 +#define E2D_ANIMATION_CACHE() ::extra2d::AnimationCache::getInstance() + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/animation_clip.h b/Extra2D/include/extra2d/animation/animation_clip.h new file mode 100644 index 0000000..16bd5c2 --- /dev/null +++ b/Extra2D/include/extra2d/animation/animation_clip.h @@ -0,0 +1,184 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// AnimationClip - 动画片段(纯数据,可复用) +// 借鉴 Cocos:一份 AnimationClip 可被多个 AnimationNode 同时使用 +// ============================================================================ +class AnimationClip { +public: + AnimationClip() = default; + + explicit AnimationClip(const std::string &name) : name_(name) {} + + // ------ 帧管理 ------ + void addFrame(const AnimationFrame &frame) { frames_.push_back(frame); } + + void addFrame(AnimationFrame &&frame) { frames_.push_back(std::move(frame)); } + + void insertFrame(size_t index, const AnimationFrame &frame) { + assert(index <= frames_.size()); + frames_.insert(frames_.begin() + static_cast(index), frame); + } + + void removeFrame(size_t index) { + assert(index < frames_.size()); + frames_.erase(frames_.begin() + static_cast(index)); + } + + void clearFrames() { frames_.clear(); } + + const AnimationFrame &getFrame(size_t index) const { + assert(index < frames_.size()); + return frames_[index]; + } + + AnimationFrame &getFrame(size_t index) { + assert(index < frames_.size()); + return frames_[index]; + } + + size_t getFrameCount() const { return frames_.size(); } + bool empty() const { return frames_.empty(); } + + // ------ 全局属性(对应原始 AnimationFlag)------ + FramePropertySet &globalProperties() { return globalProperties_; } + const FramePropertySet &globalProperties() const { return globalProperties_; } + + bool isLooping() const { + return globalProperties_.getOr(FramePropertyKey::Loop, false); + } + + void setLooping(bool loop) { globalProperties_.withLoop(loop); } + + // ------ 时间信息 ------ + float getTotalDuration() const { + float total = 0.0f; + for (const auto &frame : frames_) { + total += frame.delay; + } + return total; + } + + // ------ 预计算最大帧尺寸 ------ + Size getMaxFrameSize() const { + Size maxSize; + for (const auto &frame : frames_) { + if (frame.spriteFrame && frame.spriteFrame->isValid()) { + const auto &rect = frame.spriteFrame->getRect(); + if (rect.size.width > maxSize.width) + maxSize.width = rect.size.width; + if (rect.size.height > maxSize.height) + maxSize.height = rect.size.height; + } + } + return maxSize; + } + + // ------ 元数据 ------ + void setName(const std::string &name) { name_ = name; } + const std::string &getName() const { return name_; } + + void setSourcePath(const std::string &path) { sourcePath_ = path; } + const std::string &getSourcePath() const { return sourcePath_; } + + // ------ 静态工厂 ------ + static Ptr create(const std::string &name = "") { + return makePtr(name); + } + + /// 从精灵图网格创建(所有帧按顺序) + static Ptr createFromGrid(Ptr texture, int frameWidth, + int frameHeight, + float frameDurationMs = 100.0f, + int frameCount = -1, int spacing = 0, + int margin = 0) { + if (!texture) + return nullptr; + + int texW = texture->getWidth(); + int texH = texture->getHeight(); + int usableW = texW - 2 * margin; + int usableH = texH - 2 * margin; + int cols = (usableW + spacing) / (frameWidth + spacing); + int rows = (usableH + spacing) / (frameHeight + spacing); + int total = (frameCount > 0) ? frameCount : cols * rows; + + auto clip = makePtr(); + for (int i = 0; i < total; ++i) { + int col = i % cols; + int row = i / cols; + if (row >= rows) + break; + + // 翻转行顺序:精灵图第0行在顶部,但OpenGL纹理V坐标从底部开始 + // 所以将行索引翻转,使第0行对应纹理底部(V=1.0),第3行对应纹理顶部(V=0.0) + int flippedRow = (rows - 1) - row; + + Rect rect( + static_cast(margin + col * (frameWidth + spacing)), + static_cast(margin + flippedRow * (frameHeight + spacing)), + static_cast(frameWidth), static_cast(frameHeight)); + + auto sf = SpriteFrame::create(texture, rect); + AnimationFrame frame; + frame.spriteFrame = std::move(sf); + frame.delay = frameDurationMs; + clip->addFrame(std::move(frame)); + } + return clip; + } + + /// 从精灵图网格创建(指定帧索引列表) + static Ptr + createFromGridIndices(Ptr texture, int frameWidth, int frameHeight, + const std::vector &frameIndices, + float frameDurationMs = 100.0f, int spacing = 0, + int margin = 0) { + if (!texture) + return nullptr; + + int texW = texture->getWidth(); + int texH = texture->getHeight(); + int usableW = texW - 2 * margin; + int usableH = texH - 2 * margin; + int cols = (usableW + spacing) / (frameWidth + spacing); + int rows = (usableH + spacing) / (frameHeight + spacing); + + auto clip = makePtr(); + for (int idx : frameIndices) { + int col = idx % cols; + int row = idx / cols; + + // 翻转行顺序:精灵图第0行在顶部,但OpenGL纹理V坐标从底部开始 + int flippedRow = (rows - 1) - row; + + Rect rect( + static_cast(margin + col * (frameWidth + spacing)), + static_cast(margin + flippedRow * (frameHeight + spacing)), + static_cast(frameWidth), static_cast(frameHeight)); + + auto sf = SpriteFrame::create(texture, rect); + AnimationFrame frame; + frame.spriteFrame = std::move(sf); + frame.delay = frameDurationMs; + clip->addFrame(std::move(frame)); + } + return clip; + } + +private: + std::string name_; + std::string sourcePath_; + std::vector frames_; + FramePropertySet globalProperties_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/animation_controller.h b/Extra2D/include/extra2d/animation/animation_controller.h new file mode 100644 index 0000000..0bf18f4 --- /dev/null +++ b/Extra2D/include/extra2d/animation/animation_controller.h @@ -0,0 +1,108 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// 动画播放状态 +// ============================================================================ +enum class AnimPlayState : uint8 { Stopped, Playing, Paused }; + +// ============================================================================ +// AnimationController - 动画播放控制器 +// 借鉴 Cocos Creator 的 AnimationState:纯播放逻辑,不持有渲染资源 +// ============================================================================ +class AnimationController { +public: + // 回调类型定义 + using FrameChangeCallback = std::function; + using KeyframeCallback = std::function; + using SoundTriggerCallback = std::function; + using CompletionCallback = std::function; + + AnimationController() = default; + + // ------ 绑定动画数据 ------ + void setClip(Ptr clip); + Ptr getClip() const { return clip_; } + + // ------ 播放控制 ------ + void play(); + void pause(); + void resume(); + void stop(); + void reset(); + + // ------ 帧控制 ------ + void setFrameIndex(size_t index); + void nextFrame(); + void prevFrame(); + + // ------ 核心更新(每帧调用)------ + void update(float dt); + + // ------ 状态查询 ------ + AnimPlayState getState() const { return state_; } + bool isPlaying() const { return state_ == AnimPlayState::Playing; } + bool isPaused() const { return state_ == AnimPlayState::Paused; } + bool isStopped() const { return state_ == AnimPlayState::Stopped; } + + size_t getCurrentFrameIndex() const { return currentFrameIndex_; } + size_t getTotalFrames() const; + const AnimationFrame &getCurrentFrame() const; + + float getPlaybackSpeed() const { return playbackSpeed_; } + void setPlaybackSpeed(float speed) { playbackSpeed_ = speed; } + + bool isLooping() const; + void setLooping(bool loop); + + // ------ 插值状态 ------ + float getInterpolationFactor() const { return interpolationFactor_; } + bool isInterpolating() const { return interpolating_; } + + // ------ 回调注册 ------ + void setFrameChangeCallback(FrameChangeCallback cb) { + onFrameChange_ = std::move(cb); + } + void setKeyframeCallback(KeyframeCallback cb) { onKeyframe_ = std::move(cb); } + void setSoundTriggerCallback(SoundTriggerCallback cb) { + onSoundTrigger_ = std::move(cb); + } + void setCompletionCallback(CompletionCallback cb) { + onComplete_ = std::move(cb); + } + +private: + Ptr clip_; + AnimPlayState state_ = AnimPlayState::Stopped; + + size_t currentFrameIndex_ = 0; + float accumulatedTime_ = 0.0f; // 当前帧已累积时间 (ms) + float playbackSpeed_ = 1.0f; + bool loopOverride_ = false; // 外部循环覆盖值 + bool hasLoopOverride_ = false; // 是否使用外部循环覆盖 + + // 插值状态 + bool interpolating_ = false; + float interpolationFactor_ = 0.0f; + + // 回调 + FrameChangeCallback onFrameChange_; + KeyframeCallback onKeyframe_; + SoundTriggerCallback onSoundTrigger_; + CompletionCallback onComplete_; + + // 内部方法 + void advanceFrame(size_t newIndex); + void processFrameProperties(const AnimationFrame &frame); + void updateInterpolation(); +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/animation_event.h b/Extra2D/include/extra2d/animation/animation_event.h new file mode 100644 index 0000000..f94af58 --- /dev/null +++ b/Extra2D/include/extra2d/animation/animation_event.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include + +namespace extra2d { + +// 前向声明 +class Node; + +// ============================================================================ +// 动画事件类型 +// ============================================================================ +enum class AnimationEventType : uint32_t { + FrameChanged = 0x2001, // 帧切换 + KeyframeHit = 0x2002, // 关键帧触发 + SoundTrigger = 0x2003, // 音效触发 + AnimationStart = 0x2004, // 动画开始播放 + AnimationEnd = 0x2005, // 动画播放结束 + AnimationLoop = 0x2006, // 动画循环一轮 +}; + +// ============================================================================ +// 动画事件数据 +// ============================================================================ +struct AnimationEvent { + AnimationEventType type; + size_t frameIndex = 0; + size_t previousFrameIndex = 0; + int keyframeFlag = -1; + std::string soundPath; + Node *source = nullptr; +}; + +// ============================================================================ +// 动画事件回调类型 +// ============================================================================ +using AnimationEventCallback = std::function; +using KeyframeHitCallback = std::function; +using AnimationCompleteCallback = std::function; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/animation_frame.h b/Extra2D/include/extra2d/animation/animation_frame.h new file mode 100644 index 0000000..71536a2 --- /dev/null +++ b/Extra2D/include/extra2d/animation/animation_frame.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// AnimationFrame - 单帧数据 +// 引用 SpriteFrame 而非直接持有纹理(借鉴 Cocos 模式) +// 通过 FramePropertySet 支持不固定数据(ANI Flag 系统增强版) +// ============================================================================ +struct AnimationFrame { + // ------ 核心数据(固定部分)------ + Ptr spriteFrame; // 精灵帧引用(Cocos 模式) + std::string texturePath; // 原始图片路径(用于解析时定位资源) + int textureIndex = 0; // 精灵图集索引 + Vec2 offset; // 位置偏移 + float delay = 100.0f; // 帧延迟(毫秒) + + // ------ 碰撞盒数据(DNF ANI 格式)------ + std::vector> damageBoxes; // 伤害碰撞盒 + std::vector> attackBoxes; // 攻击碰撞盒 + + // ------ 不固定数据(属性集合)------ + FramePropertySet properties; // 类型安全的 Flag 系统 + + // ------ 便捷方法 ------ + bool hasTexture() const { + return spriteFrame != nullptr && spriteFrame->isValid(); + } + + bool hasInterpolation() const { + return properties.getOr(FramePropertyKey::Interpolation, false); + } + + bool hasKeyframeCallback() const { + return properties.has(FramePropertyKey::SetFlag); + } + + int getKeyframeIndex() const { + return properties.getOr(FramePropertyKey::SetFlag, -1); + } + + Vec2 getEffectiveScale() const { + return properties.getOr(FramePropertyKey::ImageRate, Vec2::One()); + } + + float getEffectiveRotation() const { + return properties.getOr(FramePropertyKey::ImageRotate, 0.0f); + } + + Color getEffectiveColor() const { + return properties.getOr(FramePropertyKey::ColorTint, Colors::White); + } +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/animation_node.h b/Extra2D/include/extra2d/animation/animation_node.h new file mode 100644 index 0000000..84d5f10 --- /dev/null +++ b/Extra2D/include/extra2d/animation/animation_node.h @@ -0,0 +1,108 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// AnimationNode - 动画节点(继承 Node) +// 使用 FrameRenderer 单渲染器策略,不依赖 Sprite 基类 +// 适用于需要独立渲染控制的动画(如特效、复合动画图层) +// ============================================================================ +class AnimationNode : public Node { +public: + AnimationNode(); + ~AnimationNode() override = default; + + // ------ 静态工厂(Cocos 风格)------ + static Ptr create(); + static Ptr create(Ptr clip); + static Ptr create(const std::string &aniFilePath); + + // ------ 动画数据 ------ + void setClip(Ptr clip); + Ptr getClip() const; + bool loadFromFile(const std::string &aniFilePath); + + // ------ 播放控制 ------ + void play(); + void pause(); + void resume(); + void stop(); + void reset(); + + bool isPlaying() const; + bool isPaused() const; + bool isStopped() const; + + void setPlaybackSpeed(float speed); + float getPlaybackSpeed() const; + void setLooping(bool loop); + bool isLooping() const; + + // ------ 帧控制 ------ + void setFrameIndex(size_t index); + size_t getCurrentFrameIndex() const; + size_t getTotalFrames() const; + + // ------ 事件回调 ------ + void setKeyframeCallback(KeyframeHitCallback callback); + void setCompletionCallback(AnimationCompleteCallback callback); + void + setFrameChangeCallback(AnimationController::FrameChangeCallback callback); + void addEventListener(AnimationEventCallback callback); + + // ------ 视觉属性 ------ + void setTintColor(const Color &color); + Color getTintColor() const { return tintColor_; } + void setFlipX(bool flip) { flipX_ = flip; } + void setFlipY(bool flip) { flipY_ = flip; } + bool isFlipX() const { return flipX_; } + bool isFlipY() const { return flipY_; } + + // ------ 自动播放 ------ + void setAutoPlay(bool autoPlay) { autoPlay_ = autoPlay; } + bool isAutoPlay() const { return autoPlay_; } + + // ------ 碰撞盒访问 ------ + const std::vector> &getCurrentDamageBoxes() const; + const std::vector> &getCurrentAttackBoxes() const; + + // ------ 查询 ------ + Size getMaxFrameSize() const; + Rect getBoundingBox() const override; + + // ------ 直接访问 ------ + AnimationController &getController() { return controller_; } + const AnimationController &getController() const { return controller_; } + FrameRenderer &getFrameRenderer() { return frameRenderer_; } + const FrameRenderer &getFrameRenderer() const { return frameRenderer_; } + +protected: + void onUpdate(float dt) override; + void onDraw(RenderBackend &renderer) override; + void onEnter() override; + void onExit() override; + +private: + AnimationController controller_; + FrameRenderer frameRenderer_; + Color tintColor_ = Colors::White; + bool flipX_ = false; + bool flipY_ = false; + bool autoPlay_ = false; + std::vector eventListeners_; + + static const std::vector> emptyBoxes_; + + void setupControllerCallbacks(); + void dispatchEvent(const AnimationEvent &event); +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/composite_animation.h b/Extra2D/include/extra2d/animation/composite_animation.h new file mode 100644 index 0000000..2439749 --- /dev/null +++ b/Extra2D/include/extra2d/animation/composite_animation.h @@ -0,0 +1,65 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// CompositeAnimation - ALS 多层复合动画节点 +// 管理多个 AnimationNode 图层,统一控制播放 +// 对应 DNF 的 ALS 格式(多层动画叠加) +// ============================================================================ +class CompositeAnimation : public Node { +public: + CompositeAnimation() = default; + ~CompositeAnimation() override = default; + + // ------ 静态工厂 ------ + static Ptr create(); + static Ptr create(const std::string &alsFilePath); + + // ------ 加载 ------ + bool loadFromFile(const std::string &alsFilePath); + + // ------ 图层管理 ------ + void addLayer(Ptr node, int zOrder = 0); + void removeLayer(size_t index); + Ptr getLayer(size_t index) const; + Ptr getMainLayer() const; + size_t getLayerCount() const; + + // ------ 统一播放控制 ------ + void play(); + void pause(); + void resume(); + void stop(); + void reset(); + void setPlaybackSpeed(float speed); + void setLooping(bool loop); + + bool isPlaying() const; + bool isStopped() const; + + // ------ 事件回调(绑定到主图层)------ + void setKeyframeCallback(KeyframeHitCallback callback); + void setCompletionCallback(AnimationCompleteCallback callback); + void addEventListener(AnimationEventCallback callback); + + // ------ 视觉属性(应用到所有图层)------ + void setTintColor(const Color &color); + void setFlipX(bool flip); + void setFlipY(bool flip); + +private: + struct LayerEntry { + Ptr node; + int zOrder = 0; + }; + std::vector layers_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/frame_property.h b/Extra2D/include/extra2d/animation/frame_property.h new file mode 100644 index 0000000..418e7bf --- /dev/null +++ b/Extra2D/include/extra2d/animation/frame_property.h @@ -0,0 +1,210 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// 帧属性键 - 强类型枚举替代原始 ANI 的字符串键 +// ============================================================================ +enum class FramePropertyKey : uint32 { + // 事件触发 + SetFlag = 0x0001, // int: 关键帧回调索引 + PlaySound = 0x0002, // string: 音效路径 + + // 变换属性 + ImageRate = 0x0010, // Vec2: 缩放比例 + ImageRotate = 0x0011, // float: 旋转角度(度) + ImageOffset = 0x0012, // Vec2: 额外位置偏移 + + // 视觉效果 + BlendLinearDodge = 0x0020, // bool: 线性减淡 + BlendAdditive = 0x0021, // bool: 加法混合 + ColorTint = 0x0022, // Color: RGBA 颜色 + + // 控制标记 + Interpolation = 0x0030, // bool: 启用到下一帧的插值 + Loop = 0x0031, // bool: 全局循环标记 + + // DNF ANI 扩展属性 + DamageType = 0x0040, // int: 伤害类型 (0=Normal, 1=SuperArmor, 2=Unbreakable) + Shadow = 0x0041, // bool: 阴影 + FlipType = 0x0042, // int: 翻转类型 (1=Horizon, 2=Vertical, 3=All) + Coord = 0x0043, // int: 坐标系 + LoopStart = 0x0044, // bool: 循环起始标记 + LoopEnd = 0x0045, // int: 循环结束帧数 + GraphicEffect = 0x0046, // int: 图形特效类型 + ClipRegion = 0x0047, // vector: 裁剪区域 [4个int16] + + // 用户自定义扩展区间 (0x1000+) + UserDefined = 0x1000, +}; + +// ============================================================================ +// 帧属性值 - variant 多态值(优化版本) +// 使用紧凑存储,常用小类型直接内联,大类型使用索引引用 +// ============================================================================ + +// 前向声明 +struct FramePropertyValue; + +// 属性存储类型枚举 +enum class PropertyValueType : uint8_t { + Empty = 0, + Bool = 1, + Int = 2, + Float = 3, + Vec2 = 4, + Color = 5, + String = 6, // 字符串使用索引引用 + IntVector = 7, // vector 使用索引引用 +}; + +// 紧凑的属性值结构(16字节) +struct FramePropertyValue { + PropertyValueType type = PropertyValueType::Empty; + uint8_t padding[3] = {0}; + + // 使用结构体包装非平凡类型,使其可以在union中使用 + struct Vec2Storage { + float x, y; + Vec2Storage() = default; + Vec2Storage(const Vec2& v) : x(v.x), y(v.y) {} + operator Vec2() const { return Vec2(x, y); } + }; + + struct ColorStorage { + float r, g, b, a; + ColorStorage() = default; + ColorStorage(const Color& c) : r(c.r), g(c.g), b(c.b), a(c.a) {} + operator Color() const { return Color(r, g, b, a); } + }; + + union Data { + bool boolValue; + int intValue; + float floatValue; + Vec2Storage vec2Value; + ColorStorage colorValue; + uint32_t stringIndex; // 字符串池索引 + uint32_t vectorIndex; // vector池索引 + + Data() : intValue(0) {} // 默认构造函数 + ~Data() {} // 析构函数 + } data; + + FramePropertyValue() : type(PropertyValueType::Empty) {} + explicit FramePropertyValue(bool v) : type(PropertyValueType::Bool) { data.boolValue = v; } + explicit FramePropertyValue(int v) : type(PropertyValueType::Int) { data.intValue = v; } + explicit FramePropertyValue(float v) : type(PropertyValueType::Float) { data.floatValue = v; } + explicit FramePropertyValue(const Vec2& v) : type(PropertyValueType::Vec2) { data.vec2Value = v; } + explicit FramePropertyValue(const Color& v) : type(PropertyValueType::Color) { data.colorValue = v; } + + bool isInline() const { + return type <= PropertyValueType::Color; + } + + bool isString() const { return type == PropertyValueType::String; } + bool isIntVector() const { return type == PropertyValueType::IntVector; } +}; + +// ============================================================================ +// FramePropertyKey 的 hash 支持 +// ============================================================================ +struct FramePropertyKeyHash { + size_t operator()(FramePropertyKey key) const noexcept { + return std::hash{}(static_cast(key)); + } +}; + +// ============================================================================ +// FramePropertySet - 单帧属性集合(优化版本) +// 使用紧凑存储和线性探测哈希表,提高缓存命中率 +// ============================================================================ +class FramePropertySet { +public: + FramePropertySet() = default; + + // ------ 设置属性 ------ + void set(FramePropertyKey key, FramePropertyValue value); + void set(FramePropertyKey key, bool value) { set(key, FramePropertyValue(value)); } + void set(FramePropertyKey key, int value) { set(key, FramePropertyValue(value)); } + void set(FramePropertyKey key, float value) { set(key, FramePropertyValue(value)); } + void set(FramePropertyKey key, const Vec2& value) { set(key, FramePropertyValue(value)); } + void set(FramePropertyKey key, const Color& value) { set(key, FramePropertyValue(value)); } + void set(FramePropertyKey key, const std::string& value); + void set(FramePropertyKey key, const std::vector& value); + + void setCustom(const std::string &key, std::any value); + + // ------ 类型安全获取 ------ + template std::optional get(FramePropertyKey key) const; + + template + T getOr(FramePropertyKey key, const T &defaultValue) const { + auto result = get(key); + return result.value_or(defaultValue); + } + + std::optional getCustom(const std::string &key) const; + + // ------ 查询 ------ + bool has(FramePropertyKey key) const; + bool hasCustom(const std::string &key) const; + bool empty() const { return properties_.empty() && customProperties_.empty(); } + size_t count() const { return properties_.size() + customProperties_.size(); } + + // ------ 移除 ------ + void remove(FramePropertyKey key); + void removeCustom(const std::string &key); + void clear(); + + // ------ 迭代 ------ + using PropertyMap = std::unordered_map; + const PropertyMap &properties() const { return properties_; } + + // ------ 链式 API ------ + FramePropertySet &withSetFlag(int index); + FramePropertySet &withPlaySound(const std::string &path); + FramePropertySet &withImageRate(const Vec2 &scale); + FramePropertySet &withImageRotate(float degrees); + FramePropertySet &withColorTint(const Color &color); + FramePropertySet &withInterpolation(bool enabled = true); + FramePropertySet &withBlendLinearDodge(bool enabled = true); + FramePropertySet &withLoop(bool enabled = true); + +private: + PropertyMap properties_; + std::unordered_map customProperties_; + + // 字符串池和vector池,用于存储大对象 + mutable std::vector stringPool_; + mutable std::vector> vectorPool_; + mutable uint32_t nextStringIndex_ = 0; + mutable uint32_t nextVectorIndex_ = 0; + + uint32_t allocateString(const std::string& str); + uint32_t allocateVector(const std::vector& vec); + const std::string* getString(uint32_t index) const; + const std::vector* getVector(uint32_t index) const; +}; + +// 模板特化声明 +template <> std::optional FramePropertySet::get(FramePropertyKey key) const; +template <> std::optional FramePropertySet::get(FramePropertyKey key) const; +template <> std::optional FramePropertySet::get(FramePropertyKey key) const; +template <> std::optional FramePropertySet::get(FramePropertyKey key) const; +template <> std::optional FramePropertySet::get(FramePropertyKey key) const; +template <> std::optional FramePropertySet::get(FramePropertyKey key) const; +template <> std::optional> FramePropertySet::get>(FramePropertyKey key) const; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/frame_renderer.h b/Extra2D/include/extra2d/animation/frame_renderer.h new file mode 100644 index 0000000..e9be061 --- /dev/null +++ b/Extra2D/include/extra2d/animation/frame_renderer.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// FrameRenderer - 帧渲染器 +// 单渲染器 + SpriteFrame 引用策略,替代 N帧=N个Sprite 的旧设计 +// 负责预加载帧的 SpriteFrame、渲染当前帧、处理混合模式 +// ============================================================================ +class FrameRenderer { +public: + FrameRenderer() = default; + + // ------ 预加载 ------ + // 解析所有帧的 SpriteFrame(通过 SpriteFrameCache) + bool preloadFrames(const std::vector &frames); + void releaseFrames(); + + // ------ 渲染当前帧 ------ + void renderFrame(RenderBackend &renderer, const AnimationFrame &frame, + size_t frameIndex, const Vec2 &position, float nodeOpacity, + const Color &tintColor, bool flipX, bool flipY); + + // ------ 渲染插值帧 ------ + void renderInterpolated(RenderBackend &renderer, + const AnimationFrame &fromFrame, size_t fromIndex, + const InterpolatedProperties &props, + const Vec2 &position, float nodeOpacity, + const Color &tintColor, bool flipX, bool flipY); + + // ------ 混合模式映射 ------ + static BlendMode mapBlendMode(const FramePropertySet &props); + + // ------ 查询 ------ + Ptr getSpriteFrame(size_t frameIndex) const; + Size getMaxFrameSize() const { return maxFrameSize_; } + bool isLoaded() const { return !spriteFrames_.empty(); } + +private: + std::vector> spriteFrames_; + Size maxFrameSize_; + + void drawSpriteFrame(RenderBackend &renderer, Ptr sf, + const Vec2 &position, const Vec2 &offset, + const Vec2 &scale, float rotation, float opacity, + const Color &tint, bool flipX, bool flipY, + BlendMode blend); +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/interpolation_engine.h b/Extra2D/include/extra2d/animation/interpolation_engine.h new file mode 100644 index 0000000..8978ab5 --- /dev/null +++ b/Extra2D/include/extra2d/animation/interpolation_engine.h @@ -0,0 +1,106 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// 插值结果 - 两帧之间的插值后属性 +// ============================================================================ +struct InterpolatedProperties { + Vec2 position; + Vec2 scale = Vec2::One(); + float rotation = 0.0f; + Color color = Colors::White; +}; + +// ============================================================================ +// 插值曲线类型 +// ============================================================================ +enum class InterpolationCurve : uint8 { + Linear, // 线性(原始系统的 uniform velocity) + EaseIn, // 缓入 + EaseOut, // 缓出 + EaseInOut, // 缓入缓出 +}; + +// ============================================================================ +// InterpolationEngine - 帧间属性插值计算(静态方法,无状态) +// 独立于 AnimationController,可复用于其他系统 +// ============================================================================ +class InterpolationEngine { +public: + /// 核心插值计算:根据 t 因子 (0~1) 计算两帧之间的插值属性 + static InterpolatedProperties + interpolate(const AnimationFrame &from, const AnimationFrame &to, float t, + InterpolationCurve curve = InterpolationCurve::Linear) { + float curvedT = applyCurve(t, curve); + + InterpolatedProperties result; + result.position = lerpPosition(from, to, curvedT); + result.scale = lerpScale(from, to, curvedT); + result.rotation = lerpRotation(from, to, curvedT); + result.color = lerpColor(from, to, curvedT); + return result; + } + + /// 位置插值 + static Vec2 lerpPosition(const AnimationFrame &from, const AnimationFrame &to, + float t) { + return Vec2::lerp(from.offset, to.offset, t); + } + + /// 缩放插值 + static Vec2 lerpScale(const AnimationFrame &from, const AnimationFrame &to, + float t) { + Vec2 fromScale = from.getEffectiveScale(); + Vec2 toScale = to.getEffectiveScale(); + return Vec2::lerp(fromScale, toScale, t); + } + + /// 旋转插值 + static float lerpRotation(const AnimationFrame &from, + const AnimationFrame &to, float t) { + float fromRot = from.getEffectiveRotation(); + float toRot = to.getEffectiveRotation(); + return math::lerp(fromRot, toRot, t); + } + + /// 颜色插值 + static Color lerpColor(const AnimationFrame &from, const AnimationFrame &to, + float t) { + Color fromColor = from.getEffectiveColor(); + Color toColor = to.getEffectiveColor(); + return Color::lerp(fromColor, toColor, t); + } + + /// 应用曲线函数 + static float applyCurve(float t, InterpolationCurve curve) { + t = math::clamp(t, 0.0f, 1.0f); + + switch (curve) { + case InterpolationCurve::Linear: + return t; + + case InterpolationCurve::EaseIn: + return t * t; + + case InterpolationCurve::EaseOut: + return t * (2.0f - t); + + case InterpolationCurve::EaseInOut: + if (t < 0.5f) + return 2.0f * t * t; + else + return -1.0f + (4.0f - 2.0f * t) * t; + } + + return t; + } +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/sprite_frame.h b/Extra2D/include/extra2d/animation/sprite_frame.h new file mode 100644 index 0000000..45a843d --- /dev/null +++ b/Extra2D/include/extra2d/animation/sprite_frame.h @@ -0,0 +1,73 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// SpriteFrame - 精灵帧(纹理 + 区域 + 偏移的中间抽象) +// 借鉴 Cocos2d-x SpriteFrame:解耦纹理物理存储与逻辑帧 +// 一个纹理图集可包含多个 SpriteFrame,减少纹理切换提升渲染性能 +// ============================================================================ +class SpriteFrame { +public: + SpriteFrame() = default; + + SpriteFrame(Ptr texture, const Rect &rect) + : texture_(std::move(texture)), rect_(rect), originalSize_(rect.size) {} + + SpriteFrame(Ptr texture, const Rect &rect, const Vec2 &offset, + const Size &originalSize) + : texture_(std::move(texture)), rect_(rect), offset_(offset), + originalSize_(originalSize) {} + + // ------ 静态创建 ------ + static Ptr create(Ptr texture, const Rect &rect) { + return makePtr(std::move(texture), rect); + } + + static Ptr create(Ptr texture, const Rect &rect, + const Vec2 &offset, const Size &originalSize) { + return makePtr(std::move(texture), rect, offset, originalSize); + } + + // ------ 纹理信息 ------ + void setTexture(Ptr texture) { texture_ = std::move(texture); } + Ptr getTexture() const { return texture_; } + + // ------ 矩形区域(在纹理图集中的位置)------ + void setRect(const Rect &rect) { rect_ = rect; } + const Rect &getRect() const { return rect_; } + + // ------ 偏移(图集打包时的裁剪偏移)------ + void setOffset(const Vec2 &offset) { offset_ = offset; } + const Vec2 &getOffset() const { return offset_; } + + // ------ 原始尺寸(裁剪前的完整尺寸)------ + void setOriginalSize(const Size &size) { originalSize_ = size; } + const Size &getOriginalSize() const { return originalSize_; } + + // ------ 旋转标志(图集工具可能旋转90度)------ + void setRotated(bool rotated) { rotated_ = rotated; } + bool isRotated() const { return rotated_; } + + // ------ 名称(用于缓存索引)------ + void setName(const std::string &name) { name_ = name; } + const std::string &getName() const { return name_; } + + // ------ 有效性检查 ------ + bool isValid() const { return texture_ != nullptr; } + +private: + Ptr texture_; + Rect rect_; + Vec2 offset_; + Size originalSize_; + bool rotated_ = false; + std::string name_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/animation/sprite_frame_cache.h b/Extra2D/include/extra2d/animation/sprite_frame_cache.h new file mode 100644 index 0000000..aa6a03c --- /dev/null +++ b/Extra2D/include/extra2d/animation/sprite_frame_cache.h @@ -0,0 +1,171 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// SpriteFrameCache - 精灵帧全局缓存(借鉴 Cocos SpriteFrameCache) +// 全局单例管理所有精灵帧,避免重复创建,支持图集自动切割 +// ============================================================================ +class SpriteFrameCache { +public: + static SpriteFrameCache &getInstance() { + static SpriteFrameCache instance; + return instance; + } + + // ------ 添加帧 ------ + + /// 添加单个精灵帧 + void addSpriteFrame(Ptr frame, const std::string &name) { + std::lock_guard lock(mutex_); + frames_[name] = std::move(frame); + } + + /// 从纹理和矩形区域创建并添加帧 + void addSpriteFrameFromTexture(Ptr texture, const Rect &rect, + const std::string &name) { + auto frame = SpriteFrame::create(std::move(texture), rect); + frame->setName(name); + addSpriteFrame(std::move(frame), name); + } + + /// 从纹理图集批量切割添加(等宽等高网格) + void addSpriteFramesFromGrid(const std::string &texturePath, int frameWidth, + int frameHeight, int frameCount = -1, + int spacing = 0, int margin = 0) { + auto texture = TexturePool::getInstance().get(texturePath); + if (!texture) + return; + addSpriteFramesFromGrid(texture, texturePath, frameWidth, frameHeight, + frameCount, spacing, margin); + } + + /// 从纹理对象批量切割添加(等宽等高网格,无需走 TexturePool) + void addSpriteFramesFromGrid(Ptr texture, + const std::string &keyPrefix, int frameWidth, + int frameHeight, int frameCount = -1, + int spacing = 0, int margin = 0) { + if (!texture) + return; + + int texW = texture->getWidth(); + int texH = texture->getHeight(); + int usableW = texW - 2 * margin; + int usableH = texH - 2 * margin; + int cols = (usableW + spacing) / (frameWidth + spacing); + int rows = (usableH + spacing) / (frameHeight + spacing); + int total = (frameCount > 0) ? frameCount : cols * rows; + + std::lock_guard lock(mutex_); + for (int i = 0; i < total; ++i) { + int col = i % cols; + int row = i / cols; + if (row >= rows) + break; + + Rect rect(static_cast(margin + col * (frameWidth + spacing)), + static_cast(margin + row * (frameHeight + spacing)), + static_cast(frameWidth), + static_cast(frameHeight)); + + std::string name = keyPrefix + "#" + std::to_string(i); + auto frame = SpriteFrame::create(texture, rect); + frame->setName(name); + frames_[name] = std::move(frame); + } + } + + // ------ 获取帧 ------ + + /// 按名称获取 + Ptr getSpriteFrame(const std::string &name) const { + std::lock_guard lock(mutex_); + auto it = frames_.find(name); + if (it != frames_.end()) + return it->second; + return nullptr; + } + + /// 通过路径+索引获取或创建(ANI 格式的定位方式) + Ptr getOrCreateFromFile(const std::string &texturePath, + int index = 0) { + std::string key = texturePath + "#" + std::to_string(index); + + { + std::lock_guard lock(mutex_); + auto it = frames_.find(key); + if (it != frames_.end()) + return it->second; + } + + // 缓存未命中,从 TexturePool 加载纹理并创建 SpriteFrame + auto texture = TexturePool::getInstance().get(texturePath); + if (!texture) + return nullptr; + + // 默认整张纹理作为一帧(index=0),或用整张纹理 + Rect rect(0.0f, 0.0f, static_cast(texture->getWidth()), + static_cast(texture->getHeight())); + + auto frame = SpriteFrame::create(texture, rect); + frame->setName(key); + + std::lock_guard lock(mutex_); + frames_[key] = frame; + return frame; + } + + // ------ 缓存管理 ------ + + bool has(const std::string &name) const { + std::lock_guard lock(mutex_); + return frames_.find(name) != frames_.end(); + } + + void removeSpriteFrame(const std::string &name) { + std::lock_guard lock(mutex_); + frames_.erase(name); + } + + /// 移除未被外部引用的精灵帧(use_count == 1 表示仅缓存自身持有) + void removeUnusedSpriteFrames() { + std::lock_guard lock(mutex_); + for (auto it = frames_.begin(); it != frames_.end();) { + if (it->second.use_count() == 1) { + it = frames_.erase(it); + } else { + ++it; + } + } + } + + void clear() { + std::lock_guard lock(mutex_); + frames_.clear(); + } + + size_t count() const { + std::lock_guard lock(mutex_); + return frames_.size(); + } + +private: + SpriteFrameCache() = default; + ~SpriteFrameCache() = default; + SpriteFrameCache(const SpriteFrameCache &) = delete; + SpriteFrameCache &operator=(const SpriteFrameCache &) = delete; + + mutable std::mutex mutex_; + std::unordered_map> frames_; +}; + +// 便捷宏 +#define E2D_SPRITE_FRAME_CACHE() ::extra2d::SpriteFrameCache::getInstance() + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/app/application.h b/Extra2D/include/extra2d/app/application.h new file mode 100644 index 0000000..f5138e1 --- /dev/null +++ b/Extra2D/include/extra2d/app/application.h @@ -0,0 +1,135 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace extra2d { + +// 前向声明 +class Input; +class AudioEngine; +class SceneManager; +class ResourceManager; +class TimerManager; +class EventQueue; +class EventDispatcher; +class Camera; + +// ============================================================================ +// Application 配置 +// ============================================================================ + +enum class PlatformType { + Auto = 0, + PC, + Switch +}; + +struct AppConfig { + std::string title = "Easy2D Application"; + int width = 800; + int height = 600; + bool fullscreen = false; + bool resizable = true; + bool vsync = true; + int fpsLimit = 0; + BackendType renderBackend = BackendType::OpenGL; + int msaaSamples = 0; + PlatformType platform = PlatformType::Auto; +}; + +// ============================================================================ +// Application 单例 - 应用主控 +// ============================================================================ +class Application { +public: + // Meyer's 单例 + static Application &instance(); + + // 禁止拷贝 + Application(const Application &) = delete; + Application &operator=(const Application &) = delete; + + // ------------------------------------------------------------------------ + // 生命周期 + // ------------------------------------------------------------------------ + bool init(const AppConfig &config); + void shutdown(); + void run(); + void quit(); + + // ------------------------------------------------------------------------ + // 状态控制 + // ------------------------------------------------------------------------ + void pause(); + void resume(); + bool isPaused() const { return paused_; } + bool isRunning() const { return running_; } + + // ------------------------------------------------------------------------ + // 子系统访问 + // ------------------------------------------------------------------------ + Window &window() { return *window_; } + RenderBackend &renderer() { return *renderer_; } + Input &input(); + AudioEngine &audio(); + SceneManager &scenes(); + ResourceManager &resources(); + TimerManager &timers(); + EventQueue &eventQueue(); + EventDispatcher &eventDispatcher(); + Camera &camera(); + + // ------------------------------------------------------------------------ + // 便捷方法 + // ------------------------------------------------------------------------ + void enterScene(Ptr scene); + void enterScene(Ptr scene, Ptr transition); + + float deltaTime() const { return deltaTime_; } + float totalTime() const { return totalTime_; } + int fps() const { return currentFps_; } + + // 获取配置 + const AppConfig &getConfig() const { return config_; } + +private: + Application() = default; + ~Application(); + + void mainLoop(); + void update(); + void render(); + + // 配置 + AppConfig config_; + + // 子系统 + UniquePtr window_; + UniquePtr renderer_; + UniquePtr sceneManager_; + UniquePtr resourceManager_; + UniquePtr timerManager_; + UniquePtr eventQueue_; + UniquePtr eventDispatcher_; + UniquePtr camera_; + + // 状态 + bool initialized_ = false; + bool running_ = false; + bool paused_ = false; + bool shouldQuit_ = false; + + // 时间 + float deltaTime_ = 0.0f; + float totalTime_ = 0.0f; + double lastFrameTime_ = 0.0; + int frameCount_ = 0; + float fpsTimer_ = 0.0f; + int currentFps_ = 0; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/audio/audio_engine.h b/Extra2D/include/extra2d/audio/audio_engine.h new file mode 100644 index 0000000..c388d7e --- /dev/null +++ b/Extra2D/include/extra2d/audio/audio_engine.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { + +class Sound; + +class AudioEngine { +public: + static AudioEngine& getInstance(); + + AudioEngine(const AudioEngine&) = delete; + AudioEngine& operator=(const AudioEngine&) = delete; + AudioEngine(AudioEngine&&) = delete; + AudioEngine& operator=(AudioEngine&&) = delete; + + bool initialize(); + void shutdown(); + + std::shared_ptr loadSound(const std::string& filePath); + std::shared_ptr loadSound(const std::string& name, const std::string& filePath); + + std::shared_ptr getSound(const std::string& name); + void unloadSound(const std::string& name); + void unloadAllSounds(); + + void setMasterVolume(float volume); + float getMasterVolume() const; + + void pauseAll(); + void resumeAll(); + void stopAll(); + +private: + AudioEngine() = default; + ~AudioEngine(); + + std::unordered_map> sounds_; + float masterVolume_ = 1.0f; + bool initialized_ = false; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/audio/sound.h b/Extra2D/include/extra2d/audio/sound.h new file mode 100644 index 0000000..399cc00 --- /dev/null +++ b/Extra2D/include/extra2d/audio/sound.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include + +struct Mix_Chunk; + +namespace extra2d { + +class AudioEngine; + +class Sound { +public: + ~Sound(); + + Sound(const Sound&) = delete; + Sound& operator=(const Sound&) = delete; + + bool play(); + void pause(); + void resume(); + void stop(); + + bool isPlaying() const; + bool isPaused() const; + + void setVolume(float volume); + float getVolume() const { return volume_; } + + void setLooping(bool looping); + bool isLooping() const { return looping_; } + + void setPitch(float pitch); + float getPitch() const { return pitch_; } + + float getDuration() const; + float getCursor() const; + void setCursor(float seconds); + + const std::string& getFilePath() const { return filePath_; } + const std::string& getName() const { return name_; } + +private: + friend class AudioEngine; + + Sound(const std::string& name, const std::string& filePath, Mix_Chunk* chunk); + + std::string name_; + std::string filePath_; + Mix_Chunk* chunk_ = nullptr; + int channel_ = -1; // SDL_mixer 分配的通道,-1 表示未播放 + float volume_ = 1.0f; + float pitch_ = 1.0f; + bool looping_ = false; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/core/color.h b/Extra2D/include/extra2d/core/color.h new file mode 100644 index 0000000..9f853b1 --- /dev/null +++ b/Extra2D/include/extra2d/core/color.h @@ -0,0 +1,137 @@ +#pragma once + +#include +#include +#include + +namespace extra2d { + +/// 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((rgb >> 16) & 0xFF) / 255.0f), + g(static_cast((rgb >> 8) & 0xFF) / 255.0f), + b(static_cast((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 + +// 为了向后兼容,在 Color 结构体内提供静态引用 +struct ColorConstants { + 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; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/core/math_types.h b/Extra2D/include/extra2d/core/math_types.h new file mode 100644 index 0000000..2c4d68e --- /dev/null +++ b/Extra2D/include/extra2d/core/math_types.h @@ -0,0 +1,335 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +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) {} + explicit Vec2(const glm::vec2 &v) : x(v.x), y(v.y) {} + + glm::vec2 toGlm() const { return {x, y}; } + static Vec2 fromGlm(const glm::vec2 &v) { return {v.x, v.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; + +// --------------------------------------------------------------------------- +// 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) {} + explicit Vec3(const glm::vec3 &v) : x(v.x), y(v.y), z(v.z) {} + + glm::vec3 toGlm() const { return {x, y, z}; } + static Vec3 fromGlm(const glm::vec3 &v) { return {v.x, v.y, v.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; } + +// --------------------------------------------------------------------------- +// 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}; } +}; + +// --------------------------------------------------------------------------- +// 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}; } +}; + +// --------------------------------------------------------------------------- +// 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 std::clamp(value, minVal, maxVal); +} + +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 diff --git a/Extra2D/include/extra2d/core/string.h b/Extra2D/include/extra2d/core/string.h new file mode 100644 index 0000000..ddd51f5 --- /dev/null +++ b/Extra2D/include/extra2d/core/string.h @@ -0,0 +1,209 @@ +#pragma once + +#include + +namespace extra2d { + +// ============================================================================ +// 字符串编码转换工具函数 +// 统一使用 std::string (UTF-8) 作为项目标准字符串类型 +// ============================================================================ + +// UTF-8 ↔ UTF-16 转换 +std::u16string utf8ToUtf16(const std::string& utf8); +std::string utf16ToUtf8(const std::u16string& utf16); + +// UTF-8 ↔ UTF-32 转换 +std::u32string utf8ToUtf32(const std::string& utf8); +std::string utf32ToUtf8(const std::u32string& utf32); + +// UTF-8 ↔ Wide String 转换 +std::wstring utf8ToWide(const std::string& utf8); +std::string wideToUtf8(const std::wstring& wide); + +// UTF-8 ↔ GBK/GB2312 转换(Windows 中文系统常用) +std::string utf8ToGbk(const std::string& utf8); +std::string gbkToUtf8(const std::string& gbk); + +// ============================================================================ +// 内联实现 +// ============================================================================ + +inline std::u16string utf8ToUtf16(const std::string& utf8) { + if (utf8.empty()) return std::u16string(); + + // UTF-8 → UTF-32 → UTF-16 (with surrogate pairs) + std::u32string u32 = utf8ToUtf32(utf8); + std::u16string result; + result.reserve(u32.size()); + + for (char32_t ch : u32) { + if (ch <= 0xFFFF) { + result.push_back(static_cast(ch)); + } else if (ch <= 0x10FFFF) { + // Surrogate pair + ch -= 0x10000; + result.push_back(static_cast(0xD800 | (ch >> 10))); + result.push_back(static_cast(0xDC00 | (ch & 0x3FF))); + } + } + + return result; +} + +inline std::string utf16ToUtf8(const std::u16string& utf16) { + if (utf16.empty()) return std::string(); + + // UTF-16 → UTF-32 → UTF-8 + std::u32string u32; + u32.reserve(utf16.size()); + + for (size_t i = 0; i < utf16.size(); ++i) { + char16_t cu = utf16[i]; + char32_t ch; + if (cu >= 0xD800 && cu <= 0xDBFF && i + 1 < utf16.size()) { + // High surrogate + char16_t cl = utf16[i + 1]; + if (cl >= 0xDC00 && cl <= 0xDFFF) { + ch = 0x10000 + ((static_cast(cu - 0xD800) << 10) | + (cl - 0xDC00)); + ++i; + } else { + ch = cu; // Invalid, pass through + } + } else { + ch = cu; + } + u32.push_back(ch); + } + + return utf32ToUtf8(u32); +} + +inline std::u32string utf8ToUtf32(const std::string& utf8) { + std::u32string result; + result.reserve(utf8.size()); + + const char* ptr = utf8.c_str(); + const char* end = ptr + utf8.size(); + + while (ptr < end) { + char32_t ch = 0; + unsigned char byte = static_cast(*ptr); + + if ((byte & 0x80) == 0) { + // 1-byte sequence + ch = byte; + ptr += 1; + } else if ((byte & 0xE0) == 0xC0) { + // 2-byte sequence + ch = (byte & 0x1F) << 6; + ch |= (static_cast(ptr[1]) & 0x3F); + ptr += 2; + } else if ((byte & 0xF0) == 0xE0) { + // 3-byte sequence + ch = (byte & 0x0F) << 12; + ch |= (static_cast(ptr[1]) & 0x3F) << 6; + ch |= (static_cast(ptr[2]) & 0x3F); + ptr += 3; + } else if ((byte & 0xF8) == 0xF0) { + // 4-byte sequence + ch = (byte & 0x07) << 18; + ch |= (static_cast(ptr[1]) & 0x3F) << 12; + ch |= (static_cast(ptr[2]) & 0x3F) << 6; + ch |= (static_cast(ptr[3]) & 0x3F); + ptr += 4; + } else { + // Invalid UTF-8, skip + ptr += 1; + continue; + } + + result.push_back(ch); + } + + return result; +} + +inline std::string utf32ToUtf8(const std::u32string& utf32) { + std::string result; + + for (char32_t ch : utf32) { + if (ch <= 0x7F) { + // 1-byte + result.push_back(static_cast(ch)); + } else if (ch <= 0x7FF) { + // 2-byte + result.push_back(static_cast(0xC0 | ((ch >> 6) & 0x1F))); + result.push_back(static_cast(0x80 | (ch & 0x3F))); + } else if (ch <= 0xFFFF) { + // 3-byte + result.push_back(static_cast(0xE0 | ((ch >> 12) & 0x0F))); + result.push_back(static_cast(0x80 | ((ch >> 6) & 0x3F))); + result.push_back(static_cast(0x80 | (ch & 0x3F))); + } else if (ch <= 0x10FFFF) { + // 4-byte + result.push_back(static_cast(0xF0 | ((ch >> 18) & 0x07))); + result.push_back(static_cast(0x80 | ((ch >> 12) & 0x3F))); + result.push_back(static_cast(0x80 | ((ch >> 6) & 0x3F))); + result.push_back(static_cast(0x80 | (ch & 0x3F))); + } + } + + return result; +} + +inline std::wstring utf8ToWide(const std::string& utf8) { + if (utf8.empty()) return std::wstring(); + + if constexpr (sizeof(wchar_t) == 4) { + // wchar_t is 32-bit (Linux/Switch): same as UTF-32 + std::u32string u32 = utf8ToUtf32(utf8); + return std::wstring(u32.begin(), u32.end()); + } else { + // wchar_t is 16-bit (Windows): same as UTF-16 + std::u16string u16 = utf8ToUtf16(utf8); + return std::wstring(u16.begin(), u16.end()); + } +} + +inline std::string wideToUtf8(const std::wstring& wide) { + if (wide.empty()) return std::string(); + + if constexpr (sizeof(wchar_t) == 4) { + std::u32string u32(wide.begin(), wide.end()); + return utf32ToUtf8(u32); + } else { + std::u16string u16(wide.begin(), wide.end()); + return utf16ToUtf8(u16); + } +} + +// GBK/GB2312 转换(Windows 平台实现) +// 注意:Windows 实现在 .cpp 文件中,避免头文件包含 windows.h 导致冲突 +#ifdef _WIN32 +// 前向声明,实现在 .cpp 文件中 +std::string utf8ToGbkImpl(const std::string& utf8); +std::string gbkToUtf8Impl(const std::string& gbk); + +inline std::string utf8ToGbk(const std::string& utf8) { + return utf8ToGbkImpl(utf8); +} + +inline std::string gbkToUtf8(const std::string& gbk) { + return gbkToUtf8Impl(gbk); +} +#else +// 非 Windows 平台,GBK 转换使用 iconv 或返回原字符串 +inline std::string utf8ToGbk(const std::string& utf8) { + // TODO: 使用 iconv 实现 + return utf8; +} + +inline std::string gbkToUtf8(const std::string& gbk) { + // TODO: 使用 iconv 实现 + return gbk; +} +#endif + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/core/types.h b/Extra2D/include/extra2d/core/types.h new file mode 100644 index 0000000..fb75de7 --- /dev/null +++ b/Extra2D/include/extra2d/core/types.h @@ -0,0 +1,47 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { + +// --------------------------------------------------------------------------- +// 智能指针别名 +// --------------------------------------------------------------------------- +template using Ptr = std::shared_ptr; + +template using UniquePtr = std::unique_ptr; + +template using WeakPtr = std::weak_ptr; + +/// 创建 shared_ptr 的便捷函数 +template inline Ptr makePtr(Args &&...args) { + return std::make_shared(std::forward(args)...); +} + +/// 创建 unique_ptr 的便捷函数 +template +inline UniquePtr makeUnique(Args &&...args) { + return std::make_unique(std::forward(args)...); +} + +// --------------------------------------------------------------------------- +// 函数别名 +// --------------------------------------------------------------------------- +template using Function = std::function; + +// --------------------------------------------------------------------------- +// 基础类型别名 +// --------------------------------------------------------------------------- +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 diff --git a/Extra2D/include/extra2d/effects/custom_effect_manager.h b/Extra2D/include/extra2d/effects/custom_effect_manager.h new file mode 100644 index 0000000..9cc7000 --- /dev/null +++ b/Extra2D/include/extra2d/effects/custom_effect_manager.h @@ -0,0 +1,324 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// 自定义特效类型 +// ============================================================================ +enum class CustomEffectType { + Particle, // 粒子特效 + PostProcess, // 后处理特效 + Shader, // Shader特效 + Combined // 组合特效 +}; + +// ============================================================================ +// 自定义特效配置 +// ============================================================================ +struct CustomEffectConfig { + std::string name; // 特效名称 + CustomEffectType type; // 特效类型 + std::string description; // 描述 + + // 粒子特效配置 + EmitterConfig emitterConfig; + + // 后处理特效配置 + std::string shaderVertPath; // 顶点着色器路径 + std::string shaderFragPath; // 片段着色器路径 + std::unordered_map shaderParams; // Shader参数 + + // 通用配置 + float duration; // 持续时间(-1表示无限) + bool loop; // 是否循环 + float delay; // 延迟启动时间 +}; + +// ============================================================================ +// 自定义特效基类 +// ============================================================================ +class CustomEffect { +public: + explicit CustomEffect(const CustomEffectConfig &config); + virtual ~CustomEffect() = default; + + // ------------------------------------------------------------------------ + // 生命周期 + // ------------------------------------------------------------------------ + virtual bool init(); + virtual void update(float dt); + virtual void render(RenderBackend &renderer); + virtual void shutdown(); + + // ------------------------------------------------------------------------ + // 控制 + // ------------------------------------------------------------------------ + void play(); + void pause(); + void stop(); + void reset(); + + bool isPlaying() const { return playing_; } + bool isFinished() const { return finished_; } + float getElapsedTime() const { return elapsedTime_; } + + // ------------------------------------------------------------------------ + // 配置 + // ------------------------------------------------------------------------ + const std::string &getName() const { return config_.name; } + const CustomEffectConfig &getConfig() const { return config_; } + + void setPosition(const Vec2 &pos) { position_ = pos; } + void setRotation(float rot) { rotation_ = rot; } + void setScale(float scale) { scale_ = scale; } + + Vec2 getPosition() const { return position_; } + float getRotation() const { return rotation_; } + float getScale() const { return scale_; } + +protected: + CustomEffectConfig config_; + Vec2 position_ = Vec2::Zero(); + float rotation_ = 0.0f; + float scale_ = 1.0f; + + bool playing_ = false; + bool paused_ = false; + bool finished_ = false; + float elapsedTime_ = 0.0f; + float delayTimer_ = 0.0f; +}; + +// ============================================================================ +// 自定义粒子特效 +// ============================================================================ +class CustomParticleEffect : public CustomEffect { +public: + explicit CustomParticleEffect(const CustomEffectConfig &config); + + bool init() override; + void update(float dt) override; + void render(RenderBackend &renderer) override; + void shutdown() override; + + void play(); + void stop(); + + Ptr getEmitter() { return emitter_; } + +private: + Ptr particleSystem_; + Ptr emitter_; +}; + +// ============================================================================ +// 自定义后处理特效 +// ============================================================================ +class CustomPostProcessEffect : public CustomEffect, public PostProcessEffect { +public: + explicit CustomPostProcessEffect(const CustomEffectConfig &config); + + bool init() override; + void update(float dt) override; + void shutdown() override; + + void onShaderBind(GLShader &shader) override; + + void setParam(const std::string &name, float value); + float getParam(const std::string &name) const; + +private: + std::unordered_map runtimeParams_; +}; + +// ============================================================================ +// 自定义特效工厂 +// ============================================================================ +class CustomEffectFactory { +public: + using EffectCreator = + std::function(const CustomEffectConfig &)>; + + static CustomEffectFactory &getInstance(); + + // 注册自定义特效创建器 + void registerEffect(const std::string &typeName, EffectCreator creator); + + // 创建特效 + Ptr create(const std::string &typeName, + const CustomEffectConfig &config); + + // 检查是否已注册 + bool isRegistered(const std::string &typeName) const; + + // 获取所有已注册的类型 + std::vector getRegisteredTypes() const; + +private: + CustomEffectFactory() = default; + ~CustomEffectFactory() = default; + CustomEffectFactory(const CustomEffectFactory &) = delete; + CustomEffectFactory &operator=(const CustomEffectFactory &) = delete; + + std::unordered_map creators_; +}; + +// ============================================================================ +// 自定义特效管理器 +// ============================================================================ +class CustomEffectManager { +public: + static CustomEffectManager &getInstance(); + + // ------------------------------------------------------------------------ + // 初始化和关闭 + // ------------------------------------------------------------------------ + bool init(); + void shutdown(); + + // ------------------------------------------------------------------------ + // 特效管理 + // ------------------------------------------------------------------------ + + /** + * @brief 从文件加载特效配置(支持JSON和文本格式) + * 自动检测格式:JSON格式优先,失败则回退到文本格式 + */ + bool loadFromFile(const std::string &filepath); + + /** + * @brief 从文本文件加载特效配置(简化格式) + * 格式:每行一个参数,如 EMISSION 100 + */ + bool loadFromTextFile(const std::string &filepath); + + /** + * @brief 保存特效配置到文件 + * @param useJson true=JSON格式, false=文本格式 + */ + bool saveToFile(const std::string &name, const std::string &filepath, + bool useJson = true); + + /** + * @brief 保存所有特效配置到一个JSON文件 + */ + bool saveAllToFile(const std::string &filepath); + + /** + * @brief 注册特效配置 + */ + void registerConfig(const std::string &name, + const CustomEffectConfig &config); + + /** + * @brief 获取特效配置 + */ + CustomEffectConfig *getConfig(const std::string &name); + + /** + * @brief 移除特效配置 + */ + void removeConfig(const std::string &name); + + /** + * @brief 获取所有配置名称 + */ + std::vector getConfigNames() const; + + // ------------------------------------------------------------------------ + // 特效实例管理 + // ------------------------------------------------------------------------ + + /** + * @brief 创建特效实例 + */ + Ptr createEffect(const std::string &name); + + /** + * @brief 从配置直接创建特效 + */ + Ptr createEffectFromConfig(const CustomEffectConfig &config); + + /** + * @brief 销毁特效实例 + */ + void destroyEffect(Ptr effect); + + /** + * @brief 更新所有特效 + */ + void update(float dt); + + /** + * @brief 渲染所有特效 + */ + void render(RenderBackend &renderer); + + /** + * @brief 停止所有特效 + */ + void stopAll(); + + // ------------------------------------------------------------------------ + // 便捷方法 + // ------------------------------------------------------------------------ + + /** + * @brief 播放特效(简写) + */ + Ptr play(const std::string &name, const Vec2 &position); + + /** + * @brief 播放特效并自动销毁 + */ + void playOneShot(const std::string &name, const Vec2 &position); + +private: + CustomEffectManager() = default; + ~CustomEffectManager() = default; + CustomEffectManager(const CustomEffectManager &) = delete; + CustomEffectManager &operator=(const CustomEffectManager &) = delete; + + std::unordered_map configs_; + std::vector> activeEffects_; +}; + +// ============================================================================ +// 便捷宏 +// ============================================================================ +#define E2D_CUSTOM_EFFECT_MANAGER() \ + ::extra2d::CustomEffectManager::getInstance() +#define E2D_CUSTOM_EFFECT_FACTORY() \ + ::extra2d::CustomEffectFactory::getInstance() + +// ============================================================================ +// 预设特效快速创建 +// ============================================================================ +class EffectBuilder { +public: + // 粒子特效 + static CustomEffectConfig Particle(const std::string &name); + static CustomEffectConfig Fire(const std::string &name); + static CustomEffectConfig Smoke(const std::string &name); + static CustomEffectConfig Explosion(const std::string &name); + static CustomEffectConfig Magic(const std::string &name); + static CustomEffectConfig Sparkle(const std::string &name); + + // 后处理特效 + static CustomEffectConfig Bloom(const std::string &name); + static CustomEffectConfig Blur(const std::string &name); + static CustomEffectConfig Vignette(const std::string &name); + static CustomEffectConfig ColorGrading(const std::string &name); +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/effects/particle_system.h b/Extra2D/include/extra2d/effects/particle_system.h new file mode 100644 index 0000000..8c56158 --- /dev/null +++ b/Extra2D/include/extra2d/effects/particle_system.h @@ -0,0 +1,266 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// 粒子数据 +// ============================================================================ +struct Particle { + Vec2 position; + Vec2 velocity; + Vec2 acceleration; + float rotation; + float angularVelocity; + float size; + float sizeDelta; + Color color; + Color colorDelta; + float life; + float maxLife; + bool active; + + Particle() + : position(Vec2::Zero()), velocity(Vec2::Zero()), + acceleration(Vec2::Zero()), rotation(0.0f), angularVelocity(0.0f), + size(1.0f), sizeDelta(0.0f), color(Colors::White), + colorDelta(Colors::Transparent), life(0.0f), maxLife(1.0f), + active(false) {} +}; + +// ============================================================================ +// 发射器配置 +// ============================================================================ +struct EmitterConfig { + // 发射速率 + float emissionRate = 100.0f; // 每秒发射粒子数 + float emissionDuration = -1.0f; // 发射持续时间(-1表示无限) + + // 粒子生命周期 + float minLife = 1.0f; + float maxLife = 2.0f; + + // 粒子大小 + float minStartSize = 10.0f; + float maxStartSize = 20.0f; + float minEndSize = 0.0f; + float maxEndSize = 5.0f; + + // 粒子速度 + Vec2 minVelocity = Vec2(-50.0f, -50.0f); + Vec2 maxVelocity = Vec2(50.0f, 50.0f); + + // 粒子加速度 + Vec2 acceleration = Vec2(0.0f, -100.0f); // 重力 + + // 粒子旋转 + float minRotation = 0.0f; + float maxRotation = 360.0f; + float minAngularVelocity = -90.0f; + float maxAngularVelocity = 90.0f; + + // 颜色 + Color startColor = Colors::White; + Color endColor = Colors::Transparent; + + // 发射形状 + enum class Shape { + Point, // 点发射 + Circle, // 圆形区域 + Rectangle, // 矩形区域 + Cone // 锥形 + }; + Shape shape = Shape::Point; + float shapeRadius = 50.0f; // 圆形/锥形半径 + Vec2 shapeSize = Vec2(100.0f, 100.0f); // 矩形大小 + float coneAngle = 45.0f; // 锥形角度 + + // 纹理 + Ptr texture = nullptr; + + // 混合模式 + BlendMode blendMode = BlendMode::Additive; +}; + +// ============================================================================ +// 粒子发射器 +// ============================================================================ +class ParticleEmitter { +public: + ParticleEmitter(); + ~ParticleEmitter() = default; + + // ------------------------------------------------------------------------ + // 初始化和关闭 + // ------------------------------------------------------------------------ + bool init(size_t maxParticles); + void shutdown(); + + // ------------------------------------------------------------------------ + // 配置 + // ------------------------------------------------------------------------ + void setConfig(const EmitterConfig &config) { config_ = config; } + const EmitterConfig &getConfig() const { return config_; } + + // 链式配置API + ParticleEmitter &withEmissionRate(float rate) { + config_.emissionRate = rate; + return *this; + } + ParticleEmitter &withLife(float minLife, float maxLife) { + config_.minLife = minLife; + config_.maxLife = maxLife; + return *this; + } + ParticleEmitter &withSize(float minStart, float maxStart, float minEnd = 0.0f, + float maxEnd = 0.0f) { + config_.minStartSize = minStart; + config_.maxStartSize = maxStart; + config_.minEndSize = minEnd; + config_.maxEndSize = maxEnd; + return *this; + } + ParticleEmitter &withVelocity(const Vec2 &minVel, const Vec2 &maxVel) { + config_.minVelocity = minVel; + config_.maxVelocity = maxVel; + return *this; + } + ParticleEmitter &withAcceleration(const Vec2 &accel) { + config_.acceleration = accel; + return *this; + } + ParticleEmitter &withColor(const Color &start, const Color &end) { + config_.startColor = start; + config_.endColor = end; + return *this; + } + ParticleEmitter &withTexture(Ptr texture) { + config_.texture = texture; + return *this; + } + ParticleEmitter &withBlendMode(BlendMode mode) { + config_.blendMode = mode; + return *this; + } + + // ------------------------------------------------------------------------ + // 发射控制 + // ------------------------------------------------------------------------ + void start(); + void stop(); + void burst(int count); // 爆发发射 + void reset(); + bool isEmitting() const { return emitting_; } + + // ------------------------------------------------------------------------ + // 更新和渲染 + // ------------------------------------------------------------------------ + void update(float dt); + void render(RenderBackend &renderer); + + // ------------------------------------------------------------------------ + // 状态查询 + // ------------------------------------------------------------------------ + size_t getActiveParticleCount() const { return activeCount_; } + size_t getMaxParticles() const { return particles_.size(); } + bool isActive() const { return activeCount_ > 0 || emitting_; } + + // ------------------------------------------------------------------------ + // 变换 + // ------------------------------------------------------------------------ + void setPosition(const Vec2 &pos) { position_ = pos; } + void setRotation(float rot) { rotation_ = rot; } + Vec2 getPosition() const { return position_; } + float getRotation() const { return rotation_; } + +private: + EmitterConfig config_; + std::vector particles_; + size_t activeCount_ = 0; + + Vec2 position_ = Vec2::Zero(); + float rotation_ = 0.0f; + + bool emitting_ = false; + float emissionTimer_ = 0.0f; + float emissionTime_ = 0.0f; + + std::mt19937 rng_; + + void emitParticle(); + float randomFloat(float min, float max); + Vec2 randomPointInShape(); + Vec2 randomVelocity(); +}; + +// ============================================================================ +// 粒子系统 - 管理多个发射器 +// ============================================================================ +class ParticleSystem : public Node { +public: + ParticleSystem(); + ~ParticleSystem() override = default; + + // ------------------------------------------------------------------------ + // 静态创建方法 + // ------------------------------------------------------------------------ + static Ptr create(); + + // ------------------------------------------------------------------------ + // 发射器管理 + // ------------------------------------------------------------------------ + Ptr addEmitter(const EmitterConfig &config = {}); + void removeEmitter(Ptr emitter); + void removeAllEmitters(); + size_t getEmitterCount() const { return emitters_.size(); } + + // ------------------------------------------------------------------------ + // 全局控制 + // ------------------------------------------------------------------------ + void startAll(); + void stopAll(); + void resetAll(); + + // ------------------------------------------------------------------------ + // 预设 + // ------------------------------------------------------------------------ + static EmitterConfig PresetFire(); + static EmitterConfig PresetSmoke(); + static EmitterConfig PresetExplosion(); + static EmitterConfig PresetSparkle(); + static EmitterConfig PresetRain(); + static EmitterConfig PresetSnow(); + + // ------------------------------------------------------------------------ + // 重写Node方法 + // ------------------------------------------------------------------------ + void onUpdate(float dt) override; + void onDraw(RenderBackend &renderer) override; + +private: + std::vector> emitters_; +}; + +// ============================================================================ +// 粒子预设(便捷类) +// ============================================================================ +class ParticlePreset { +public: + static EmitterConfig Fire(); + static EmitterConfig Smoke(); + static EmitterConfig Explosion(); + static EmitterConfig Sparkle(); + static EmitterConfig Rain(); + static EmitterConfig Snow(); + static EmitterConfig Magic(); + static EmitterConfig Bubbles(); +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/effects/post_process.h b/Extra2D/include/extra2d/effects/post_process.h new file mode 100644 index 0000000..638bc68 --- /dev/null +++ b/Extra2D/include/extra2d/effects/post_process.h @@ -0,0 +1,228 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// 前向声明 +// ============================================================================ +class RenderTarget; +class RenderBackend; + +// ============================================================================ +// 后处理效果基类 +// ============================================================================ +class PostProcessEffect { +public: + PostProcessEffect(const std::string &name); + virtual ~PostProcessEffect() = default; + + // ------------------------------------------------------------------------ + // 生命周期 + // ------------------------------------------------------------------------ + + /** + * @brief 初始化效果 + */ + virtual bool init(); + + /** + * @brief 关闭效果 + */ + virtual void shutdown(); + + // ------------------------------------------------------------------------ + // 渲染 + // ------------------------------------------------------------------------ + + /** + * @brief 应用效果 + * @param source 输入纹理 + * @param target 输出渲染目标 + * @param renderer 渲染后端 + */ + virtual void apply(const Texture &source, RenderTarget &target, + RenderBackend &renderer); + + /** + * @brief 设置Shader参数(子类重写) + */ + virtual void onShaderBind(GLShader &shader) {} + + // ------------------------------------------------------------------------ + // 状态 + // ------------------------------------------------------------------------ + const std::string &getName() const { return name_; } + bool isEnabled() const { return enabled_; } + void setEnabled(bool enabled) { enabled_ = enabled; } + bool isValid() const { return valid_; } + + // ------------------------------------------------------------------------ + // 链式API + // ------------------------------------------------------------------------ + PostProcessEffect &withEnabled(bool enabled) { + enabled_ = enabled; + return *this; + } + +protected: + std::string name_; + bool enabled_ = true; + bool valid_ = false; + Ptr shader_; + + /** + * @brief 加载自定义Shader + */ + bool loadShader(const std::string &vertSource, const std::string &fragSource); + bool loadShaderFromFile(const std::string &vertPath, + const std::string &fragPath); + + /** + * @brief 渲染全屏四边形 + */ + void renderFullscreenQuad(); + +private: + static GLuint quadVao_; + static GLuint quadVbo_; + static bool quadInitialized_; + + void initQuad(); + void destroyQuad(); +}; + +// ============================================================================ +// 后处理栈 - 管理多个后处理效果 +// ============================================================================ +class PostProcessStack { +public: + PostProcessStack(); + ~PostProcessStack(); + + // ------------------------------------------------------------------------ + // 初始化和关闭 + // ------------------------------------------------------------------------ + bool init(int width, int height); + void shutdown(); + + // ------------------------------------------------------------------------ + // 效果管理 + // ------------------------------------------------------------------------ + + /** + * @brief 添加效果到栈 + */ + void addEffect(Ptr effect); + + /** + * @brief 插入效果到指定位置 + */ + void insertEffect(size_t index, Ptr effect); + + /** + * @brief 移除效果 + */ + void removeEffect(const std::string &name); + void removeEffect(size_t index); + + /** + * @brief 获取效果 + */ + Ptr getEffect(const std::string &name); + Ptr getEffect(size_t index); + + /** + * @brief 清空所有效果 + */ + void clearEffects(); + + /** + * @brief 获取效果数量 + */ + size_t getEffectCount() const { return effects_.size(); } + + // ------------------------------------------------------------------------ + // 渲染 + // ------------------------------------------------------------------------ + + /** + * @brief 开始捕获场景 + */ + void beginCapture(); + + /** + * @brief 结束捕获并应用所有效果 + */ + void endCapture(RenderBackend &renderer); + + /** + * @brief 直接应用效果到纹理 + */ + void process(const Texture &source, RenderTarget &target, + RenderBackend &renderer); + + // ------------------------------------------------------------------------ + // 配置 + // ------------------------------------------------------------------------ + void resize(int width, int height); + bool isValid() const { return valid_; } + + // ------------------------------------------------------------------------ + // 便捷方法 - 添加内置效果 + // ------------------------------------------------------------------------ + PostProcessStack &addBloom(float intensity = 1.0f, float threshold = 0.8f); + PostProcessStack &addBlur(float radius = 2.0f); + PostProcessStack &addColorGrading(const Color &tint); + PostProcessStack &addVignette(float intensity = 0.5f); + PostProcessStack &addChromaticAberration(float amount = 1.0f); + +private: + std::vector> effects_; + Ptr renderTargetA_; + Ptr renderTargetB_; + int width_ = 0; + int height_ = 0; + bool valid_ = false; + bool capturing_ = false; +}; + +// ============================================================================ +// 全局后处理管理 +// ============================================================================ +class PostProcessManager { +public: + static PostProcessManager &getInstance(); + + void init(int width, int height); + void shutdown(); + + PostProcessStack &getMainStack() { return mainStack_; } + + void resize(int width, int height); + void beginFrame(); + void endFrame(RenderBackend &renderer); + +private: + PostProcessManager() = default; + ~PostProcessManager() = default; + PostProcessManager(const PostProcessManager &) = delete; + PostProcessManager &operator=(const PostProcessManager &) = delete; + + PostProcessStack mainStack_; + bool initialized_ = false; +}; + +// ============================================================================ +// 便捷宏 +// ============================================================================ +#define E2D_POST_PROCESS() ::extra2d::PostProcessManager::getInstance() + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/event/event.h b/Extra2D/include/extra2d/event/event.h new file mode 100644 index 0000000..45eb403 --- /dev/null +++ b/Extra2D/include/extra2d/event/event.h @@ -0,0 +1,172 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// 事件类型枚举 +// ============================================================================ +enum class EventType { + None = 0, + + // 窗口事件 + WindowClose, + WindowResize, + WindowFocus, + WindowLostFocus, + WindowMoved, + + // 键盘事件 + KeyPressed, + KeyReleased, + KeyRepeat, + + // 鼠标事件 + MouseButtonPressed, + MouseButtonReleased, + MouseMoved, + MouseScrolled, + + // UI 事件 + UIHoverEnter, + UIHoverExit, + UIPressed, + UIReleased, + UIClicked, + + // 游戏手柄事件 + GamepadConnected, + GamepadDisconnected, + GamepadButtonPressed, + GamepadButtonReleased, + GamepadAxisMoved, + + // 触摸事件 (移动端) + TouchBegan, + TouchMoved, + TouchEnded, + TouchCancelled, + + // 自定义事件 + Custom +}; + +// ============================================================================ +// 键盘事件数据 +// ============================================================================ +struct KeyEvent { + int keyCode; + int scancode; + int mods; // 修饰键 (Shift, Ctrl, Alt, etc.) +}; + +// ============================================================================ +// 鼠标事件数据 +// ============================================================================ +struct MouseButtonEvent { + int button; + int mods; + Vec2 position; +}; + +struct MouseMoveEvent { + Vec2 position; + Vec2 delta; +}; + +struct MouseScrollEvent { + Vec2 offset; + Vec2 position; +}; + +// ============================================================================ +// 窗口事件数据 +// ============================================================================ +struct WindowResizeEvent { + int width; + int height; +}; + +struct WindowMoveEvent { + int x; + int y; +}; + +// ============================================================================ +// 游戏手柄事件数据 +// ============================================================================ +struct GamepadButtonEvent { + int gamepadId; + int button; +}; + +struct GamepadAxisEvent { + int gamepadId; + int axis; + float value; +}; + +// ============================================================================ +// 触摸事件数据 +// ============================================================================ +struct TouchEvent { + int touchId; + Vec2 position; +}; + +// ============================================================================ +// 自定义事件数据 +// ============================================================================ +struct CustomEvent { + uint32_t id; + void *data; +}; + +// ============================================================================ +// 事件结构 +// ============================================================================ +struct Event { + EventType type = EventType::None; + double timestamp = 0.0; + bool handled = false; + + // 事件数据联合体 + std::variant + data; + + // 便捷访问方法 + bool isWindowEvent() const { + return type == EventType::WindowClose || type == EventType::WindowResize || + type == EventType::WindowFocus || + type == EventType::WindowLostFocus || type == EventType::WindowMoved; + } + + bool isKeyboardEvent() const { + return type == EventType::KeyPressed || type == EventType::KeyReleased || + type == EventType::KeyRepeat; + } + + bool isMouseEvent() const { + return type == EventType::MouseButtonPressed || + type == EventType::MouseButtonReleased || + type == EventType::MouseMoved || type == EventType::MouseScrolled; + } + + // 静态工厂方法 + static Event createWindowResize(int width, int height); + static Event createWindowClose(); + static Event createKeyPress(int keyCode, int scancode, int mods); + static Event createKeyRelease(int keyCode, int scancode, int mods); + static Event createMouseButtonPress(int button, int mods, const Vec2 &pos); + static Event createMouseButtonRelease(int button, int mods, const Vec2 &pos); + static Event createMouseMove(const Vec2 &pos, const Vec2 &delta); + static Event createMouseScroll(const Vec2 &offset, const Vec2 &pos); +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/event/event_dispatcher.h b/Extra2D/include/extra2d/event/event_dispatcher.h new file mode 100644 index 0000000..1ddff8a --- /dev/null +++ b/Extra2D/include/extra2d/event/event_dispatcher.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// 事件监听器 ID +// ============================================================================ +using ListenerId = uint64_t; + +// ============================================================================ +// 事件分发器 +// ============================================================================ +class EventDispatcher { +public: + using EventCallback = std::function; + + EventDispatcher(); + ~EventDispatcher() = default; + + // 添加监听器 + ListenerId addListener(EventType type, EventCallback callback); + + // 移除监听器 + void removeListener(ListenerId id); + void removeAllListeners(EventType type); + void removeAllListeners(); + + // 分发事件 + void dispatch(Event &event); + void dispatch(const Event &event); + + // 处理事件队列 + void processQueue(class EventQueue &queue); + + // 统计 + size_t getListenerCount(EventType type) const; + size_t getTotalListenerCount() const; + +private: + struct Listener { + ListenerId id; + EventType type; + EventCallback callback; + }; + + std::unordered_map> listeners_; + ListenerId nextId_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/event/event_queue.h b/Extra2D/include/extra2d/event/event_queue.h new file mode 100644 index 0000000..5ef03bd --- /dev/null +++ b/Extra2D/include/extra2d/event/event_queue.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// 事件队列 - 线程安全的事件队列 +// ============================================================================ +class EventQueue { +public: + EventQueue(); + ~EventQueue() = default; + + // 添加事件到队列 + void push(const Event &event); + void push(Event &&event); + + // 从队列取出事件 + bool poll(Event &event); + + // 查看队列头部事件(不移除) + bool peek(Event &event) const; + + // 清空队列 + void clear(); + + // 队列状态 + bool empty() const; + size_t size() const; + +private: + std::queue queue_; + mutable std::mutex mutex_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/event/input_codes.h b/Extra2D/include/extra2d/event/input_codes.h new file mode 100644 index 0000000..eeb4f43 --- /dev/null +++ b/Extra2D/include/extra2d/event/input_codes.h @@ -0,0 +1,212 @@ +#pragma once + +// SDL2 键码定义 +#include + +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 diff --git a/Extra2D/include/extra2d/extra2d.h b/Extra2D/include/extra2d/extra2d.h new file mode 100644 index 0000000..6ec0de3 --- /dev/null +++ b/Extra2D/include/extra2d/extra2d.h @@ -0,0 +1,102 @@ +#pragma once + +// Easy2D v3.0 - 统一入口头文件 +// 包含所有公共 API + +// Core +#include +#include +#include +#include + +// Platform +#include +#include + +// Graphics +#include +#include +#include +#include +#include +#include +#include +#include + +// Scene +#include +#include +#include +#include +#include +#include + +// Animation +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// UI +#include +#include +#include +#include +#include +#include +#include +#include + +// Action +#include +#include +#include + +// Event +#include +#include +#include +#include + +// Audio +#include +#include + +// Resource +#include + +// Utils +#include +#include +#include +#include + +// Spatial +#include +#include +#include +#include + +// Effects +#include +#include +#include + +// Application +#include + +#ifdef __SWITCH__ +#include +#endif \ No newline at end of file diff --git a/Extra2D/include/extra2d/graphics/alpha_mask.h b/Extra2D/include/extra2d/graphics/alpha_mask.h new file mode 100644 index 0000000..98cdd6b --- /dev/null +++ b/Extra2D/include/extra2d/graphics/alpha_mask.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// Alpha 遮罩 - 存储图片的非透明区域信息 +// ============================================================================ +class AlphaMask { +public: + AlphaMask() = default; + AlphaMask(int width, int height); + + /// 从像素数据创建遮罩 + static AlphaMask createFromPixels(const uint8_t *pixels, int width, + int height, int channels); + + /// 获取指定位置的透明度(0-255) + uint8_t getAlpha(int x, int y) const; + + /// 检查指定位置是否不透明 + bool isOpaque(int x, int y, uint8_t threshold = 128) const; + + /// 检查指定位置是否在遮罩范围内 + bool isValid(int x, int y) const; + + /// 获取遮罩尺寸 + int getWidth() const { return width_; } + int getHeight() const { return height_; } + Size getSize() const { + return Size(static_cast(width_), static_cast(height_)); + } + + /// 获取原始数据 + const std::vector &getData() const { return data_; } + + /// 检查遮罩是否有效 + bool isValid() const { return !data_.empty() && width_ > 0 && height_ > 0; } + +private: + int width_ = 0; + int height_ = 0; + std::vector data_; // Alpha值数组 +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/camera.h b/Extra2D/include/extra2d/graphics/camera.h new file mode 100644 index 0000000..2b56423 --- /dev/null +++ b/Extra2D/include/extra2d/graphics/camera.h @@ -0,0 +1,93 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// 2D 正交相机 +// ============================================================================ +class Camera { +public: + Camera(); + Camera(float left, float right, float bottom, float top); + Camera(const Size &viewport); + ~Camera() = default; + + // ------------------------------------------------------------------------ + // 位置和变换 + // ------------------------------------------------------------------------ + void setPosition(const Vec2 &position); + void setPosition(float x, float y); + Vec2 getPosition() const { return position_; } + + void setRotation(float degrees); + float getRotation() const { return rotation_; } + + void setZoom(float zoom); + float getZoom() const { return zoom_; } + + // ------------------------------------------------------------------------ + // 视口设置 + // ------------------------------------------------------------------------ + void setViewport(float left, float right, float bottom, float top); + void setViewport(const Rect &rect); + Rect getViewport() const; + + // ------------------------------------------------------------------------ + // 矩阵获取 + // ------------------------------------------------------------------------ + glm::mat4 getViewMatrix() const; + glm::mat4 getProjectionMatrix() const; + glm::mat4 getViewProjectionMatrix() const; + + // ------------------------------------------------------------------------ + // 坐标转换 + // ------------------------------------------------------------------------ + Vec2 screenToWorld(const Vec2 &screenPos) const; + Vec2 worldToScreen(const Vec2 &worldPos) const; + Vec2 screenToWorld(float x, float y) const; + Vec2 worldToScreen(float x, float y) const; + + // ------------------------------------------------------------------------ + // 移动相机 + // ------------------------------------------------------------------------ + void move(const Vec2 &offset); + void move(float x, float y); + + // ------------------------------------------------------------------------ + // 边界限制 + // ------------------------------------------------------------------------ + void setBounds(const Rect &bounds); + void clearBounds(); + void clampToBounds(); + + // ------------------------------------------------------------------------ + // 快捷方法:看向某点 + // ------------------------------------------------------------------------ + void lookAt(const Vec2 &target); + +private: + Vec2 position_ = Vec2::Zero(); + float rotation_ = 0.0f; + float zoom_ = 1.0f; + + float left_ = -1.0f; + float right_ = 1.0f; + float bottom_ = -1.0f; + float top_ = 1.0f; + + Rect bounds_; + bool hasBounds_ = false; + + mutable glm::mat4 viewMatrix_; + mutable glm::mat4 projMatrix_; + mutable glm::mat4 vpMatrix_; + mutable bool viewDirty_ = true; + mutable bool projDirty_ = true; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/font.h b/Extra2D/include/extra2d/graphics/font.h new file mode 100644 index 0000000..c3f06d8 --- /dev/null +++ b/Extra2D/include/extra2d/graphics/font.h @@ -0,0 +1,50 @@ +#pragma once + +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// 字形信息 +// ============================================================================ +struct Glyph { + float u0, v0; // 纹理坐标左下角 + float u1, v1; // 纹理坐标右上角 + float width; // 字形宽度(像素) + float height; // 字形高度(像素) + float bearingX; // 水平偏移 + float bearingY; // 垂直偏移 + float advance; // 前进距离 +}; + +// ============================================================================ +// 字体图集接口 +// ============================================================================ +class FontAtlas { +public: + virtual ~FontAtlas() = default; + + // 获取字形信息 + virtual const Glyph *getGlyph(char32_t codepoint) const = 0; + + // 获取纹理 + virtual class Texture *getTexture() const = 0; + + // 获取字体大小 + virtual int getFontSize() const = 0; + + virtual float getAscent() const = 0; + virtual float getDescent() const = 0; + virtual float getLineGap() const = 0; + virtual float getLineHeight() const = 0; + + // 计算文字尺寸 + virtual Vec2 measureText(const std::string &text) = 0; + + // 是否支持 SDF 渲染 + virtual bool isSDF() const = 0; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/opengl/gl_font_atlas.h b/Extra2D/include/extra2d/graphics/opengl/gl_font_atlas.h new file mode 100644 index 0000000..eaa7dd9 --- /dev/null +++ b/Extra2D/include/extra2d/graphics/opengl/gl_font_atlas.h @@ -0,0 +1,63 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// OpenGL 字体图集实现 - 使用 stb_rect_pack 进行矩形打包 +// ============================================================================ +class GLFontAtlas : public FontAtlas { +public: + GLFontAtlas(const std::string &filepath, int fontSize, bool useSDF = false); + ~GLFontAtlas(); + + // FontAtlas 接口实现 + const Glyph *getGlyph(char32_t codepoint) const override; + Texture *getTexture() const override { return texture_.get(); } + int getFontSize() const override { return fontSize_; } + float getAscent() const override { return ascent_; } + float getDescent() const override { return descent_; } + float getLineGap() const override { return lineGap_; } + float getLineHeight() const override { return ascent_ - descent_ + lineGap_; } + Vec2 measureText(const std::string &text) override; + bool isSDF() const override { return useSDF_; } + +private: + // 图集配置 + static constexpr int ATLAS_WIDTH = 512; + static constexpr int ATLAS_HEIGHT = 512; + static constexpr int PADDING = 2; // 字形之间的间距 + + int fontSize_; + bool useSDF_; + mutable std::unique_ptr texture_; + mutable std::unordered_map glyphs_; + + // stb_rect_pack 上下文 + mutable stbrp_context packContext_; + mutable std::vector packNodes_; + mutable int currentY_; + + std::vector fontData_; + stbtt_fontinfo fontInfo_; + float scale_; + float ascent_; + float descent_; + float lineGap_; + + void createAtlas(); + void cacheGlyph(char32_t codepoint) const; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/opengl/gl_renderer.h b/Extra2D/include/extra2d/graphics/opengl/gl_renderer.h new file mode 100644 index 0000000..7152d27 --- /dev/null +++ b/Extra2D/include/extra2d/graphics/opengl/gl_renderer.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include + +#include + +namespace extra2d { + +class Window; + +// ============================================================================ +// OpenGL 渲染器实现 +// ============================================================================ +class GLRenderer : public RenderBackend { +public: + GLRenderer(); + ~GLRenderer() override; + + // RenderBackend 接口实现 + bool init(Window* window) override; + void shutdown() override; + + void beginFrame(const Color& clearColor) override; + void endFrame() override; + void setViewport(int x, int y, int width, int height) override; + void setVSync(bool enabled) override; + + void setBlendMode(BlendMode mode) override; + void setViewProjection(const glm::mat4& matrix) override; + + Ptr createTexture(int width, int height, const uint8_t* pixels, int channels) override; + Ptr loadTexture(const std::string& filepath) override; + + void beginSpriteBatch() override; + void drawSprite(const Texture& texture, const Rect& destRect, const Rect& srcRect, + const Color& tint, float rotation, const Vec2& anchor) override; + void drawSprite(const Texture& texture, const Vec2& position, const Color& tint) override; + void endSpriteBatch() override; + + void drawLine(const Vec2& start, const Vec2& end, const Color& color, float width) override; + void drawRect(const Rect& rect, const Color& color, float width) override; + void fillRect(const Rect& rect, const Color& color) override; + void drawCircle(const Vec2& center, float radius, const Color& color, int segments, float width) override; + void fillCircle(const Vec2& center, float radius, const Color& color, int segments) override; + void drawTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3, const Color& color, float width) override; + void fillTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3, const Color& color) override; + void drawPolygon(const std::vector& points, const Color& color, float width) override; + void fillPolygon(const std::vector& points, const Color& color) override; + + Ptr createFontAtlas(const std::string& filepath, int fontSize, bool useSDF = false) override; + void drawText(const FontAtlas& font, const std::string& text, const Vec2& position, const Color& color) override; + void drawText(const FontAtlas& font, const std::string& text, float x, float y, const Color& color) override; + + Stats getStats() const override { return stats_; } + void resetStats() override; + +private: + Window* window_; + GLSpriteBatch spriteBatch_; + GLShader shapeShader_; + + GLuint shapeVao_; + GLuint shapeVbo_; + + glm::mat4 viewProjection_; + Stats stats_; + bool vsync_; + + void initShapeRendering(); + void setupBlendMode(BlendMode mode); +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/opengl/gl_shader.h b/Extra2D/include/extra2d/graphics/opengl/gl_shader.h new file mode 100644 index 0000000..2f63818 --- /dev/null +++ b/Extra2D/include/extra2d/graphics/opengl/gl_shader.h @@ -0,0 +1,55 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// OpenGL Shader 程序 +// ============================================================================ +class GLShader { +public: + GLShader(); + ~GLShader(); + + // 从源码编译 + bool compileFromSource(const char* vertexSource, const char* fragmentSource); + + // 从文件加载并编译 + bool compileFromFile(const std::string& vertexPath, const std::string& fragmentPath); + + // 使用/激活 + void bind() const; + void unbind() const; + + // Uniform 设置 + void setBool(const std::string& name, bool value); + void setInt(const std::string& name, int value); + void setFloat(const std::string& name, float value); + void setVec2(const std::string& name, const glm::vec2& value); + void setVec3(const std::string& name, const glm::vec3& value); + void setVec4(const std::string& name, const glm::vec4& value); + void setMat4(const std::string& name, const glm::mat4& value); + + // 获取程序 ID + GLuint getProgramID() const { return programID_; } + + // 检查是否有效 + bool isValid() const { return programID_ != 0; } + +private: + GLuint programID_; + std::unordered_map uniformCache_; + + GLuint compileShader(GLenum type, const char* source); + GLint getUniformLocation(const std::string& name); +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/opengl/gl_sprite_batch.h b/Extra2D/include/extra2d/graphics/opengl/gl_sprite_batch.h new file mode 100644 index 0000000..8a8ed90 --- /dev/null +++ b/Extra2D/include/extra2d/graphics/opengl/gl_sprite_batch.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace extra2d { + +// ============================================================================ +// OpenGL 精灵批渲染器 +// ============================================================================ +class GLSpriteBatch { +public: + static constexpr size_t MAX_SPRITES = 10000; + static constexpr size_t VERTICES_PER_SPRITE = 4; + static constexpr size_t INDICES_PER_SPRITE = 6; + + struct Vertex { + glm::vec2 position; + glm::vec2 texCoord; + glm::vec4 color; + }; + + struct SpriteData { + glm::vec2 position; + glm::vec2 size; + glm::vec2 texCoordMin; + glm::vec2 texCoordMax; + glm::vec4 color; + float rotation; + glm::vec2 anchor; + bool isSDF = false; + }; + + GLSpriteBatch(); + ~GLSpriteBatch(); + + bool init(); + void shutdown(); + + void begin(const glm::mat4 &viewProjection); + void draw(const Texture &texture, const SpriteData &data); + void end(); + + // 统计 + uint32_t getDrawCallCount() const { return drawCallCount_; } + uint32_t getSpriteCount() const { return spriteCount_; } + +private: + GLuint vao_; + GLuint vbo_; + GLuint ibo_; + GLShader shader_; + + std::vector vertices_; + std::vector indices_; + + const Texture *currentTexture_; + bool currentIsSDF_; + glm::mat4 viewProjection_; + + uint32_t drawCallCount_; + uint32_t spriteCount_; + + void flush(); + void setupShader(); +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/opengl/gl_texture.h b/Extra2D/include/extra2d/graphics/opengl/gl_texture.h new file mode 100644 index 0000000..8d559a7 --- /dev/null +++ b/Extra2D/include/extra2d/graphics/opengl/gl_texture.h @@ -0,0 +1,72 @@ +#pragma once + +#include +#include + +#include + +#include +#include + +namespace extra2d { + +// ============================================================================ +// OpenGL 纹理实现 +// ============================================================================ +class GLTexture : public Texture { +public: + GLTexture(int width, int height, const uint8_t* pixels, int channels); + GLTexture(const std::string& filepath); + ~GLTexture(); + + // Texture 接口实现 + int getWidth() const override { return width_; } + int getHeight() const override { return height_; } + Size getSize() const override { return Size(static_cast(width_), static_cast(height_)); } + int getChannels() const override { return channels_; } + PixelFormat getFormat() const override; + void* getNativeHandle() const override { return reinterpret_cast(static_cast(textureID_)); } + bool isValid() const override { return textureID_ != 0; } + void setFilter(bool linear) override; + void setWrap(bool repeat) override; + + // 从参数创建纹理的工厂方法 + static Ptr create(int width, int height, PixelFormat format); + + // 加载压缩纹理(KTX/DDS 格式) + bool loadCompressed(const std::string& filepath); + + // OpenGL 特定 + GLuint getTextureID() const { return textureID_; } + void bind(unsigned int slot = 0) const; + void unbind() const; + + // 获取纹理数据大小(字节),用于 VRAM 跟踪 + size_t getDataSize() const { return dataSize_; } + + // Alpha 遮罩 + bool hasAlphaMask() const { return alphaMask_ != nullptr && alphaMask_->isValid(); } + const AlphaMask* getAlphaMask() const { return alphaMask_.get(); } + void generateAlphaMask(); // 从当前纹理数据生成遮罩 + +private: + GLuint textureID_; + int width_; + int height_; + int channels_; + PixelFormat format_; + size_t dataSize_; + + // 原始像素数据(用于生成遮罩) + std::vector pixelData_; + std::unique_ptr alphaMask_; + + void createTexture(const uint8_t* pixels); + + // KTX 文件加载 + bool loadKTX(const std::string& filepath); + // DDS 文件加载 + bool loadDDS(const std::string& filepath); +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/render_backend.h b/Extra2D/include/extra2d/graphics/render_backend.h new file mode 100644 index 0000000..fb75f2c --- /dev/null +++ b/Extra2D/include/extra2d/graphics/render_backend.h @@ -0,0 +1,131 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { + +// 前向声明 +class Window; +class Texture; +class FontAtlas; +class Shader; + +// ============================================================================ +// 渲染后端类型 +// ============================================================================ +enum class BackendType { + OpenGL, + // Vulkan, + // Metal, + // D3D11, + // D3D12 +}; + +// ============================================================================ +// 混合模式 +// ============================================================================ +enum class BlendMode { + None, // 不混合 + Alpha, // 标准 Alpha 混合 + Additive, // 加法混合 + Multiply // 乘法混合 +}; + +// ============================================================================ +// 渲染后端抽象接口 +// ============================================================================ +class RenderBackend { +public: + virtual ~RenderBackend() = default; + + // ------------------------------------------------------------------------ + // 生命周期 + // ------------------------------------------------------------------------ + virtual bool init(Window *window) = 0; + virtual void shutdown() = 0; + + // ------------------------------------------------------------------------ + // 帧管理 + // ------------------------------------------------------------------------ + virtual void beginFrame(const Color &clearColor) = 0; + virtual void endFrame() = 0; + virtual void setViewport(int x, int y, int width, int height) = 0; + virtual void setVSync(bool enabled) = 0; + + // ------------------------------------------------------------------------ + // 状态设置 + // ------------------------------------------------------------------------ + virtual void setBlendMode(BlendMode mode) = 0; + virtual void setViewProjection(const glm::mat4 &matrix) = 0; + + // ------------------------------------------------------------------------ + // 纹理 + // ------------------------------------------------------------------------ + virtual Ptr createTexture(int width, int height, + const uint8_t *pixels, int channels) = 0; + virtual Ptr loadTexture(const std::string &filepath) = 0; + + // ------------------------------------------------------------------------ + // 精灵批渲染 + // ------------------------------------------------------------------------ + virtual void beginSpriteBatch() = 0; + virtual void drawSprite(const Texture &texture, const Rect &destRect, + const Rect &srcRect, const Color &tint, + float rotation, const Vec2 &anchor) = 0; + virtual void drawSprite(const Texture &texture, const Vec2 &position, + const Color &tint) = 0; + virtual void endSpriteBatch() = 0; + + // ------------------------------------------------------------------------ + // 形状渲染 + // ------------------------------------------------------------------------ + virtual void drawLine(const Vec2 &start, const Vec2 &end, const Color &color, + float width = 1.0f) = 0; + virtual void drawRect(const Rect &rect, const Color &color, + float width = 1.0f) = 0; + virtual void fillRect(const Rect &rect, const Color &color) = 0; + virtual void drawCircle(const Vec2 ¢er, float radius, const Color &color, + int segments = 32, float width = 1.0f) = 0; + virtual void fillCircle(const Vec2 ¢er, float radius, const Color &color, + int segments = 32) = 0; + virtual void drawTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, + const Color &color, float width = 1.0f) = 0; + virtual void fillTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3, + const Color &color) = 0; + virtual void drawPolygon(const std::vector &points, const Color &color, + float width = 1.0f) = 0; + virtual void fillPolygon(const std::vector &points, + const Color &color) = 0; + + // ------------------------------------------------------------------------ + // 文字渲染 + // ------------------------------------------------------------------------ + virtual Ptr createFontAtlas(const std::string &filepath, + int fontSize, bool useSDF = false) = 0; + virtual void drawText(const FontAtlas &font, const std::string &text, + const Vec2 &position, const Color &color) = 0; + virtual void drawText(const FontAtlas &font, const std::string &text, float x, + float y, const Color &color) = 0; + + // ------------------------------------------------------------------------ + // 统计信息 + // ------------------------------------------------------------------------ + struct Stats { + uint32_t drawCalls = 0; + uint32_t triangleCount = 0; + uint32_t textureBinds = 0; + uint32_t shaderBinds = 0; + }; + virtual Stats getStats() const = 0; + virtual void resetStats() = 0; + + // ------------------------------------------------------------------------ + // 工厂方法 + // ------------------------------------------------------------------------ + static UniquePtr create(BackendType type); +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/render_command.h b/Extra2D/include/extra2d/graphics/render_command.h new file mode 100644 index 0000000..283fa60 --- /dev/null +++ b/Extra2D/include/extra2d/graphics/render_command.h @@ -0,0 +1,122 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +// 前向声明 +class Texture; +class FontAtlas; + +// ============================================================================ +// 渲染命令类型 +// ============================================================================ +enum class RenderCommandType { + Sprite, + Line, + Rect, + FilledRect, + Circle, + FilledCircle, + Triangle, + FilledTriangle, + Polygon, + FilledPolygon, + Text +}; + +// ============================================================================ +// 精灵数据 +// ============================================================================ +struct SpriteData { + Ptr texture; + Rect destRect; + Rect srcRect; + Color tint; + float rotation; + Vec2 anchor; +}; + +// ============================================================================ +// 直线数据 +// ============================================================================ +struct LineData { + Vec2 start; + Vec2 end; + Color color; + float width; +}; + +// ============================================================================ +// 矩形数据 +// ============================================================================ +struct RectData { + Rect rect; + Color color; + float width; +}; + +// ============================================================================ +// 圆形数据 +// ============================================================================ +struct CircleData { + Vec2 center; + float radius; + Color color; + int segments; + float width; +}; + +// ============================================================================ +// 三角形数据 +// ============================================================================ +struct TriangleData { + Vec2 p1; + Vec2 p2; + Vec2 p3; + Color color; + float width; +}; + +// ============================================================================ +// 多边形数据 +// ============================================================================ +struct PolygonData { + std::vector points; + Color color; + float width; +}; + +// ============================================================================ +// 文字数据 +// ============================================================================ +struct TextData { + Ptr font; + std::string text; + Vec2 position; + Color color; +}; + +// ============================================================================ +// 渲染命令 +// ============================================================================ +struct RenderCommand { + RenderCommandType type; + int zOrder; + + std::variant + data; + + // 用于排序 + bool operator<(const RenderCommand &other) const { + return zOrder < other.zOrder; + } +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/render_target.h b/Extra2D/include/extra2d/graphics/render_target.h new file mode 100644 index 0000000..e214c7d --- /dev/null +++ b/Extra2D/include/extra2d/graphics/render_target.h @@ -0,0 +1,333 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// 渲染目标配置 +// ============================================================================ +struct RenderTargetConfig { + int width = 800; // 宽度 + int height = 600; // 高度 + PixelFormat colorFormat = PixelFormat::RGBA8; // 颜色格式 + bool hasDepth = true; // 是否包含深度缓冲 + bool hasDepthBuffer = true; // 兼容旧API的别名 (同hasDepth) + bool hasStencil = false; // 是否包含模板缓冲 + int samples = 1; // 多重采样数 (1 = 无MSAA) + bool autoResize = true; // 是否自动调整大小 +}; + +// ============================================================================ +// 渲染目标 - 基于FBO的离屏渲染 +// ============================================================================ +class RenderTarget { +public: + RenderTarget(); + ~RenderTarget(); + + // 禁止拷贝 + RenderTarget(const RenderTarget &) = delete; + RenderTarget &operator=(const RenderTarget &) = delete; + + // 允许移动 + RenderTarget(RenderTarget &&other) noexcept; + RenderTarget &operator=(RenderTarget &&other) noexcept; + + // ------------------------------------------------------------------------ + // 创建和销毁 + // ------------------------------------------------------------------------ + + /** + * @brief 创建渲染目标 + */ + bool create(const RenderTargetConfig &config); + + /** + * @brief 初始化渲染目标(create的别名,兼容旧API) + */ + bool init(const RenderTargetConfig &config) { return create(config); } + + /** + * @brief 从现有纹理创建渲染目标 + */ + bool createFromTexture(Ptr texture, bool hasDepth = false); + + /** + * @brief 销毁渲染目标 + */ + void destroy(); + + /** + * @brief 关闭渲染目标(destroy的别名,兼容旧API) + */ + void shutdown() { destroy(); } + + /** + * @brief 检查是否有效 + */ + bool isValid() const { return fbo_ != 0; } + + // ------------------------------------------------------------------------ + // 尺寸和格式 + // ------------------------------------------------------------------------ + + int getWidth() const { return width_; } + int getHeight() const { return height_; } + Vec2 getSize() const { + return Vec2(static_cast(width_), static_cast(height_)); + } + PixelFormat getColorFormat() const { return colorFormat_; } + + // ------------------------------------------------------------------------ + // 绑定和解绑 + // ------------------------------------------------------------------------ + + /** + * @brief 绑定为当前渲染目标 + */ + void bind(); + + /** + * @brief 解绑(恢复默认渲染目标) + */ + void unbind(); + + /** + * @brief 清除渲染目标 + */ + void clear(const Color &color = Colors::Transparent); + + // ------------------------------------------------------------------------ + // 纹理访问 + // ------------------------------------------------------------------------ + + /** + * @brief 获取颜色纹理 + */ + Ptr getColorTexture() const { return colorTexture_; } + + /** + * @brief 获取深度纹理(如果有) + */ + Ptr getDepthTexture() const { return depthTexture_; } + + // ------------------------------------------------------------------------ + // 视口和裁剪 + // ------------------------------------------------------------------------ + + /** + * @brief 设置视口(相对于渲染目标) + */ + void setViewport(int x, int y, int width, int height); + + /** + * @brief 获取完整视口 + */ + void getFullViewport(int &x, int &y, int &width, int &height) const; + + // ------------------------------------------------------------------------ + // 工具方法 + // ------------------------------------------------------------------------ + + /** + * @brief 调整大小(会销毁并重新创建) + */ + bool resize(int width, int height); + + /** + * @brief 复制到另一个渲染目标 + */ + void copyTo(RenderTarget &target); + + /** + * @brief 复制到另一个渲染目标(blitTo的别名,兼容旧API) + * @param target 目标渲染目标 + * @param color 是否复制颜色缓冲 + * @param depth 是否复制深度缓冲 + */ + void blitTo(RenderTarget &target, bool color = true, bool depth = false); + + /** + * @brief 复制到屏幕 + */ + void copyToScreen(int screenWidth, int screenHeight); + + /** + * @brief 保存为图像文件 + */ + bool saveToFile(const std::string &filepath); + + // ------------------------------------------------------------------------ + // 静态方法 + // ------------------------------------------------------------------------ + + /** + * @brief 创建渲染目标的静态工厂方法 + */ + static Ptr createFromConfig(const RenderTargetConfig &config); + + /** + * @brief 获取当前绑定的渲染目标ID + */ + static GLuint getCurrentFBO(); + + /** + * @brief 绑定默认渲染目标(屏幕) + */ + static void bindDefault(); + + /** + * @brief 获取FBO ID(供内部使用) + */ + GLuint getFBO() const { return fbo_; } + +protected: + GLuint fbo_ = 0; // 帧缓冲对象 + GLuint rbo_ = 0; // 渲染缓冲对象(深度/模板) + + Ptr colorTexture_; // 颜色纹理 + Ptr depthTexture_; // 深度纹理(可选) + + int width_ = 0; + int height_ = 0; + PixelFormat colorFormat_ = PixelFormat::RGBA8; + bool hasDepth_ = false; + bool hasStencil_ = false; + int samples_ = 1; + + bool createFBO(); + void deleteFBO(); +}; + +// ============================================================================ +// 多重采样渲染目标(用于MSAA) +// ============================================================================ +class MultisampleRenderTarget : public RenderTarget { +public: + /** + * @brief 创建多重采样渲染目标 + */ + bool create(int width, int height, int samples = 4); + + /** + * @brief 解析到普通渲染目标(用于显示) + */ + void resolveTo(RenderTarget &target); + + /** + * @brief 销毁渲染目标 + */ + void destroy(); + +private: + GLuint colorRBO_ = 0; // 多重采样颜色渲染缓冲 +}; + +// ============================================================================ +// 渲染目标栈(用于嵌套渲染) +// ============================================================================ +class RenderTargetStack { +public: + static RenderTargetStack &getInstance(); + + /** + * @brief 压入渲染目标 + */ + void push(RenderTarget *target); + + /** + * @brief 弹出渲染目标 + */ + void pop(); + + /** + * @brief 获取当前渲染目标 + */ + RenderTarget *getCurrent() const; + + /** + * @brief 获取栈大小 + */ + size_t size() const; + + /** + * @brief 清空栈 + */ + void clear(); + +private: + RenderTargetStack() = default; + ~RenderTargetStack() = default; + + std::vector stack_; + mutable std::mutex mutex_; +}; + +// ============================================================================ +// 渲染目标管理器 - 全局渲染目标管理 +// ============================================================================ +class RenderTargetManager { +public: + /** + * @brief 获取单例实例 + */ + static RenderTargetManager &getInstance(); + + /** + * @brief 初始化渲染目标管理器 + * @param width 默认宽度 + * @param height 默认高度 + */ + bool init(int width, int height); + + /** + * @brief 关闭渲染目标管理器 + */ + void shutdown(); + + /** + * @brief 创建新的渲染目标 + */ + Ptr createRenderTarget(const RenderTargetConfig &config); + + /** + * @brief 获取默认渲染目标 + */ + RenderTarget *getDefaultRenderTarget() const { + return defaultRenderTarget_.get(); + } + + /** + * @brief 调整所有受管渲染目标的大小 + */ + void resize(int width, int height); + + /** + * @brief 检查是否已初始化 + */ + bool isInitialized() const { return initialized_; } + +private: + RenderTargetManager() = default; + ~RenderTargetManager() = default; + RenderTargetManager(const RenderTargetManager &) = delete; + RenderTargetManager &operator=(const RenderTargetManager &) = delete; + + Ptr defaultRenderTarget_; + std::vector> renderTargets_; + bool initialized_ = false; +}; + +// ============================================================================ +// 便捷宏 +// ============================================================================ +#define E2D_RENDER_TARGET_STACK() ::extra2d::RenderTargetStack::getInstance() +#define E2D_RENDER_TARGET_MANAGER() \ + ::extra2d::RenderTargetManager::getInstance() + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/shader_preset.h b/Extra2D/include/extra2d/graphics/shader_preset.h new file mode 100644 index 0000000..801f88b --- /dev/null +++ b/Extra2D/include/extra2d/graphics/shader_preset.h @@ -0,0 +1,319 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { + +struct WaterParams { + float waveSpeed = 1.0f; + float waveAmplitude = 0.02f; + float waveFrequency = 4.0f; +}; + +struct OutlineParams { + Color color = Colors::Black; + float thickness = 2.0f; +}; + +struct DistortionParams { + float distortionAmount = 0.02f; + float timeScale = 1.0f; +}; + +struct PixelateParams { + float pixelSize = 8.0f; +}; + +struct InvertParams { + float strength = 1.0f; +}; + +struct GrayscaleParams { + float intensity = 1.0f; +}; + +struct BlurParams { + float radius = 5.0f; +}; + +namespace ShaderSource { + +static const char* StandardVert = R"( +#version 300 es +precision highp float; +layout(location = 0) in vec2 a_position; +layout(location = 1) in vec2 a_texCoord; +layout(location = 2) in vec4 a_color; + +uniform mat4 u_viewProjection; +uniform mat4 u_model; + +out vec2 v_texCoord; +out vec4 v_color; + +void main() { + gl_Position = u_viewProjection * u_model * vec4(a_position, 0.0, 1.0); + v_texCoord = a_texCoord; + v_color = a_color; +} +)"; + +static const char* StandardFrag = R"( +#version 300 es +precision highp float; +in vec2 v_texCoord; +in vec4 v_color; + +uniform sampler2D u_texture; +uniform float u_opacity; + +out vec4 fragColor; + +void main() { + vec4 texColor = texture(u_texture, v_texCoord); + fragColor = texColor * v_color; + fragColor.a *= u_opacity; + + if (fragColor.a < 0.01) { + discard; + } +} +)"; + +static const char* WaterFrag = R"( +#version 300 es +precision highp float; +in vec2 v_texCoord; +in vec4 v_color; + +uniform sampler2D u_texture; +uniform float u_waveSpeed; +uniform float u_waveAmplitude; +uniform float u_waveFrequency; +uniform float u_time; + +out vec4 fragColor; + +void main() { + vec2 uv = v_texCoord; + + // 水波纹效果 + float wave = sin(uv.y * u_waveFrequency + u_time * u_waveSpeed) * u_waveAmplitude; + uv.x += wave; + + vec4 texColor = texture(u_texture, uv); + fragColor = texColor * v_color; + + if (fragColor.a < 0.01) { + discard; + } +} +)"; + +static const char* OutlineFrag = R"( +#version 300 es +precision highp float; +in vec2 v_texCoord; +in vec4 v_color; + +uniform sampler2D u_texture; +uniform vec4 u_outlineColor; +uniform float u_thickness; +uniform vec2 u_textureSize; + +out vec4 fragColor; + +void main() { + vec4 color = texture(u_texture, v_texCoord); + + // 简单的描边检测 + float alpha = 0.0; + vec2 offset = u_thickness / u_textureSize; + + alpha += texture(u_texture, v_texCoord + vec2(-offset.x, 0.0)).a; + alpha += texture(u_texture, v_texCoord + vec2(offset.x, 0.0)).a; + alpha += texture(u_texture, v_texCoord + vec2(0.0, -offset.y)).a; + alpha += texture(u_texture, v_texCoord + vec2(0.0, offset.y)).a; + + if (color.a < 0.1 && alpha > 0.0) { + fragColor = u_outlineColor; + } else { + fragColor = color; + } + + if (fragColor.a < 0.01) { + discard; + } +} +)"; + +static const char* DistortionFrag = R"( +#version 300 es +precision highp float; +in vec2 v_texCoord; +in vec4 v_color; + +uniform sampler2D u_texture; +uniform float u_distortionAmount; +uniform float u_time; +uniform float u_timeScale; + +out vec4 fragColor; + +void main() { + vec2 uv = v_texCoord; + + // 扭曲效果 + float t = u_time * u_timeScale; + float dx = sin(uv.y * 10.0 + t) * u_distortionAmount; + float dy = cos(uv.x * 10.0 + t) * u_distortionAmount; + uv += vec2(dx, dy); + + vec4 texColor = texture(u_texture, uv); + fragColor = texColor * v_color; + + if (fragColor.a < 0.01) { + discard; + } +} +)"; + +static const char* PixelateFrag = R"( +#version 300 es +precision highp float; +in vec2 v_texCoord; +in vec4 v_color; + +uniform sampler2D u_texture; +uniform float u_pixelSize; +uniform vec2 u_textureSize; +uniform float u_opacity; + +out vec4 fragColor; + +void main() { + vec2 pixel = u_pixelSize / u_textureSize; + vec2 uv = floor(v_texCoord / pixel) * pixel + pixel * 0.5; + + vec4 texColor = texture(u_texture, uv); + fragColor = texColor * v_color; + fragColor.a *= u_opacity; + + if (fragColor.a < 0.01) { + discard; + } +} +)"; + +static const char* InvertFrag = R"( +#version 300 es +precision highp float; +in vec2 v_texCoord; +in vec4 v_color; + +uniform sampler2D u_texture; +uniform float u_strength; +uniform float u_opacity; + +out vec4 fragColor; + +void main() { + vec4 texColor = texture(u_texture, v_texCoord) * v_color; + vec3 inverted = vec3(1.0) - texColor.rgb; + texColor.rgb = mix(texColor.rgb, inverted, u_strength); + + fragColor = texColor; + fragColor.a *= u_opacity; + + if (fragColor.a < 0.01) { + discard; + } +} +)"; + +static const char* GrayscaleFrag = R"( +#version 300 es +precision highp float; +in vec2 v_texCoord; +in vec4 v_color; + +uniform sampler2D u_texture; +uniform float u_intensity; +uniform float u_opacity; + +out vec4 fragColor; + +void main() { + vec4 texColor = texture(u_texture, v_texCoord) * v_color; + + float gray = dot(texColor.rgb, vec3(0.299, 0.587, 0.114)); + texColor.rgb = mix(texColor.rgb, vec3(gray), u_intensity); + + fragColor = texColor; + fragColor.a *= u_opacity; + + if (fragColor.a < 0.01) { + discard; + } +} +)"; + +static const char* BlurFrag = R"( +#version 300 es +precision highp float; +in vec2 v_texCoord; +in vec4 v_color; + +uniform sampler2D u_texture; +uniform float u_radius; +uniform vec2 u_textureSize; +uniform float u_opacity; + +out vec4 fragColor; + +void main() { + vec2 texel = u_radius / u_textureSize; + + vec4 sum = vec4(0.0); + sum += texture(u_texture, v_texCoord + texel * vec2(-1.0, -1.0)); + sum += texture(u_texture, v_texCoord + texel * vec2( 0.0, -1.0)); + sum += texture(u_texture, v_texCoord + texel * vec2( 1.0, -1.0)); + sum += texture(u_texture, v_texCoord + texel * vec2(-1.0, 0.0)); + sum += texture(u_texture, v_texCoord + texel * vec2( 0.0, 0.0)); + sum += texture(u_texture, v_texCoord + texel * vec2( 1.0, 0.0)); + sum += texture(u_texture, v_texCoord + texel * vec2(-1.0, 1.0)); + sum += texture(u_texture, v_texCoord + texel * vec2( 0.0, 1.0)); + sum += texture(u_texture, v_texCoord + texel * vec2( 1.0, 1.0)); + + vec4 texColor = sum / 9.0; + fragColor = texColor * v_color; + fragColor.a *= u_opacity; + + if (fragColor.a < 0.01) { + discard; + } +} +)"; + +} // namespace ShaderSource + +class ShaderPreset { +public: + static Ptr Water(const WaterParams& params); + static Ptr Outline(const OutlineParams& params); + static Ptr Distortion(const DistortionParams& params); + static Ptr Pixelate(const PixelateParams& params); + static Ptr Invert(const InvertParams& params); + static Ptr Grayscale(const GrayscaleParams& params); + static Ptr Blur(const BlurParams& params); + + static Ptr GrayscaleOutline(const GrayscaleParams& grayParams, + const OutlineParams& outlineParams); + static Ptr PixelateInvert(const PixelateParams& pixParams, + const InvertParams& invParams); +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/shader_system.h b/Extra2D/include/extra2d/graphics/shader_system.h new file mode 100644 index 0000000..a3af2fe --- /dev/null +++ b/Extra2D/include/extra2d/graphics/shader_system.h @@ -0,0 +1,179 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// Shader参数绑定回调 +// ============================================================================ +using ShaderBindCallback = std::function; + +// ============================================================================ +// Shader系统 - 管理所有Shader的加载、缓存和热重载 +// ============================================================================ +class ShaderSystem { +public: + // ------------------------------------------------------------------------ + // 单例访问 + // ------------------------------------------------------------------------ + static ShaderSystem &getInstance(); + + // ------------------------------------------------------------------------ + // 初始化和关闭 + // ------------------------------------------------------------------------ + bool init(); + void shutdown(); + + // ------------------------------------------------------------------------ + // Shader加载 + // ------------------------------------------------------------------------ + + /** + * @brief 从文件加载Shader + * @param name Shader名称(用于缓存) + * @param vertPath 顶点着色器文件路径 + * @param fragPath 片段着色器文件路径 + * @return 加载的Shader,失败返回nullptr + */ + Ptr loadFromFile(const std::string &name, + const std::string &vertPath, + const std::string &fragPath); + + /** + * @brief 从源码字符串加载Shader + * @param name Shader名称(用于缓存) + * @param vertSource 顶点着色器源码 + * @param fragSource 片段着色器源码 + * @return 加载的Shader,失败返回nullptr + */ + Ptr loadFromSource(const std::string &name, + const std::string &vertSource, + const std::string &fragSource); + + /** + * @brief 从已编译的程序获取Shader + * @param name Shader名称 + * @return 缓存的Shader,不存在返回nullptr + */ + Ptr get(const std::string &name); + + /** + * @brief 检查Shader是否存在 + */ + bool has(const std::string &name) const; + + // ------------------------------------------------------------------------ + // Shader移除 + // ------------------------------------------------------------------------ + void remove(const std::string &name); + void clear(); + + // ------------------------------------------------------------------------ + // 热重载支持 + // ------------------------------------------------------------------------ + + /** + * @brief 启用/禁用文件监视 + */ + void setFileWatching(bool enable); + bool isFileWatching() const { return fileWatching_; } + + /** + * @brief 更新文件监视(在主循环中调用) + */ + void updateFileWatching(); + + /** + * @brief 重载指定Shader + */ + bool reload(const std::string &name); + + /** + * @brief 重载所有Shader + */ + void reloadAll(); + + // ------------------------------------------------------------------------ + // 内置Shader获取 + // ------------------------------------------------------------------------ + Ptr getBuiltinSpriteShader(); + Ptr getBuiltinParticleShader(); + Ptr getBuiltinPostProcessShader(); + Ptr getBuiltinShapeShader(); + + // ------------------------------------------------------------------------ + // 工具方法 + // ------------------------------------------------------------------------ + + /** + * @brief 从文件读取文本内容 + */ + static std::string readFile(const std::string &filepath); + + /** + * @brief 获取Shader文件的最后修改时间 + */ + static uint64_t getFileModifiedTime(const std::string &filepath); + +private: + ShaderSystem() = default; + ~ShaderSystem() = default; + ShaderSystem(const ShaderSystem &) = delete; + ShaderSystem &operator=(const ShaderSystem &) = delete; + + struct ShaderInfo { + Ptr shader; + std::string vertPath; + std::string fragPath; + uint64_t vertModifiedTime; + uint64_t fragModifiedTime; + bool isBuiltin; + }; + + std::unordered_map shaders_; + bool fileWatching_ = false; + float watchTimer_ = 0.0f; + static constexpr float WATCH_INTERVAL = 1.0f; // 检查间隔(秒) + + // 内置Shader缓存 + Ptr builtinSpriteShader_; + Ptr builtinParticleShader_; + Ptr builtinPostProcessShader_; + Ptr builtinShapeShader_; + + bool loadBuiltinShaders(); + void checkAndReload(); +}; + +// ============================================================================ +// Shader参数包装器 - 简化Uniform设置 +// ============================================================================ +class ShaderParams { +public: + explicit ShaderParams(GLShader &shader); + + ShaderParams &setBool(const std::string &name, bool value); + ShaderParams &setInt(const std::string &name, int value); + ShaderParams &setFloat(const std::string &name, float value); + ShaderParams &setVec2(const std::string &name, const glm::vec2 &value); + ShaderParams &setVec3(const std::string &name, const glm::vec3 &value); + ShaderParams &setVec4(const std::string &name, const glm::vec4 &value); + ShaderParams &setMat4(const std::string &name, const glm::mat4 &value); + ShaderParams &setColor(const std::string &name, const Color &color); + +private: + GLShader &shader_; +}; + +// ============================================================================ +// 便捷宏 +// ============================================================================ +#define E2D_SHADER_SYSTEM() ::extra2d::ShaderSystem::getInstance() + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/texture.h b/Extra2D/include/extra2d/graphics/texture.h new file mode 100644 index 0000000..712915e --- /dev/null +++ b/Extra2D/include/extra2d/graphics/texture.h @@ -0,0 +1,64 @@ +#pragma once + +#include +#include + +namespace extra2d { + +// ============================================================================ +// 像素格式枚举 +// ============================================================================ +enum class PixelFormat { + R8, // 单通道灰度 + RG8, // 双通道 + RGB8, // RGB 24位 + RGBA8, // RGBA 32位(默认) + RGB16F, // RGB 半精度浮点 + RGBA16F, // RGBA 半精度浮点 + RGB32F, // RGB 全精度浮点 + RGBA32F, // RGBA 全精度浮点 + Depth16, // 16位深度 + Depth24, // 24位深度 + Depth32F, // 32位浮点深度 + Depth24Stencil8, // 24位深度 + 8位模板 + + // 压缩纹理格式 + ETC2_RGB8, // ETC2 RGB 压缩 + ETC2_RGBA8, // ETC2 RGBA 压缩 + ASTC_4x4, // ASTC 4x4 压缩 + ASTC_6x6, // ASTC 6x6 压缩 + ASTC_8x8 // ASTC 8x8 压缩 +}; + +// ============================================================================ +// 纹理接口 +// ============================================================================ +class Texture { +public: + virtual ~Texture() = default; + + // 获取尺寸 + virtual int getWidth() const = 0; + virtual int getHeight() const = 0; + virtual Size getSize() const = 0; + + // 获取通道数 + virtual int getChannels() const = 0; + + // 获取像素格式 + virtual PixelFormat getFormat() const = 0; + + // 获取原始句柄(用于底层渲染) + virtual void* getNativeHandle() const = 0; + + // 是否有效 + virtual bool isValid() const = 0; + + // 设置过滤模式 + virtual void setFilter(bool linear) = 0; + + // 设置环绕模式 + virtual void setWrap(bool repeat) = 0; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/texture_pool.h b/Extra2D/include/extra2d/graphics/texture_pool.h new file mode 100644 index 0000000..0971714 --- /dev/null +++ b/Extra2D/include/extra2d/graphics/texture_pool.h @@ -0,0 +1,208 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// 纹理池配置 +// ============================================================================ +struct TexturePoolConfig { + size_t maxCacheSize = 64 * 1024 * 1024; // 最大缓存大小 (64MB) + size_t maxTextureCount = 256; // 最大纹理数量 + float unloadInterval = 30.0f; // 自动清理间隔 (秒) + bool enableAsyncLoad = true; // 启用异步加载 +}; + +// ============================================================================ +// 纹理池 - 使用LRU缓存策略管理纹理复用 +// ============================================================================ +class TexturePool { +public: + // ------------------------------------------------------------------------ + // 单例访问 + // ------------------------------------------------------------------------ + static TexturePool &getInstance(); + + // ------------------------------------------------------------------------ + // 初始化和关闭 + // ------------------------------------------------------------------------ + bool init(const TexturePoolConfig &config = TexturePoolConfig{}); + void shutdown(); + + // ------------------------------------------------------------------------ + // 纹理获取 + // ------------------------------------------------------------------------ + + /** + * @brief 从文件获取纹理(带缓存) + * @param filepath 纹理文件路径 + * @return 纹理对象,失败返回nullptr + */ + Ptr get(const std::string &filepath); + + /** + * @brief 异步加载纹理 + * @param filepath 纹理文件路径 + * @param callback 加载完成回调 + */ + void getAsync(const std::string &filepath, + std::function)> callback); + + /** + * @brief 从内存数据创建纹理(自动缓存) + * @param name 纹理名称(用于缓存键) + * @param data 图像数据 + * @param width 宽度 + * @param height 高度 + * @param format 像素格式 + * @return 纹理对象 + */ + Ptr createFromData(const std::string &name, const uint8_t *data, + int width, int height, + PixelFormat format = PixelFormat::RGBA8); + + // ------------------------------------------------------------------------ + // 缓存管理 + // ------------------------------------------------------------------------ + + /** + * @brief 手动添加纹理到缓存 + */ + void add(const std::string &key, Ptr texture); + + /** + * @brief 从缓存移除纹理 + */ + void remove(const std::string &key); + + /** + * @brief 检查纹理是否在缓存中 + */ + bool has(const std::string &key) const; + + /** + * @brief 清空所有缓存 + */ + void clear(); + + /** + * @brief 清理未使用的纹理(LRU策略) + * @param targetSize 目标缓存大小 + */ + void trim(size_t targetSize); + + // ------------------------------------------------------------------------ + // 统计信息 + // ------------------------------------------------------------------------ + + /** + * @brief 获取当前缓存的纹理数量 + */ + size_t getTextureCount() const; + + /** + * @brief 获取当前缓存的总大小(字节) + */ + size_t getCacheSize() const; + + /** + * @brief 获取缓存命中率 + */ + float getHitRate() const; + + /** + * @brief 打印统计信息 + */ + void printStats() const; + + // ------------------------------------------------------------------------ + // 自动清理 + // ------------------------------------------------------------------------ + + /** + * @brief 更新(在主循环中调用,用于自动清理) + */ + void update(float dt); + + /** + * @brief 设置自动清理间隔 + */ + void setAutoUnloadInterval(float interval); + +private: + TexturePool() = default; + ~TexturePool() = default; + TexturePool(const TexturePool &) = delete; + TexturePool &operator=(const TexturePool &) = delete; + + // 侵入式LRU节点 - 减少内存分配和指针跳转 + struct LRUNode { + std::string key; + uint32_t prev; // 数组索引,0表示无效 + uint32_t next; // 数组索引,0表示无效 + bool valid = false; + }; + + // 缓存项 - 紧凑存储 + struct CacheEntry { + Ptr texture; + size_t size; // 纹理大小(字节) + float lastAccessTime; // 最后访问时间 + uint32_t accessCount; // 访问次数 + uint32_t lruIndex; // LRU节点索引 + }; + + mutable std::mutex mutex_; + TexturePoolConfig config_; + + // 缓存存储 + std::unordered_map cache_; + + // 侵入式LRU链表 - 使用数组索引代替指针,提高缓存局部性 + std::vector lruNodes_; + uint32_t lruHead_ = 0; // 最近使用 + uint32_t lruTail_ = 0; // 最久未使用 + uint32_t freeList_ = 0; // 空闲节点链表 + + // 统计 + size_t totalSize_ = 0; + uint64_t hitCount_ = 0; + uint64_t missCount_ = 0; + float autoUnloadTimer_ = 0.0f; + bool initialized_ = false; + + // 异步加载队列 + struct AsyncLoadTask { + std::string filepath; + std::function)> callback; + }; + std::vector asyncTasks_; + + // LRU操作 + uint32_t allocateLRUNode(const std::string& key); + void freeLRUNode(uint32_t index); + void moveToFront(uint32_t index); + void removeFromList(uint32_t index); + std::string evictLRU(); + + // 内部方法 + void touch(const std::string &key); + void evict(); + Ptr loadTexture(const std::string &filepath); + size_t calculateTextureSize(int width, int height, PixelFormat format); + void processAsyncTasks(); +}; + +// ============================================================================ +// 便捷宏 +// ============================================================================ +#define E2D_TEXTURE_POOL() ::extra2d::TexturePool::getInstance() + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/graphics/vram_manager.h b/Extra2D/include/extra2d/graphics/vram_manager.h new file mode 100644 index 0000000..1375413 --- /dev/null +++ b/Extra2D/include/extra2d/graphics/vram_manager.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// VRAM 管理器 - 跟踪显存使用情况 +// ============================================================================ +class VRAMManager { +public: + static VRAMManager& getInstance(); + + // 纹理显存跟踪 + void allocTexture(size_t size); + void freeTexture(size_t size); + + // VBO/FBO 显存跟踪 + void allocBuffer(size_t size); + void freeBuffer(size_t size); + + // 查询显存使用情况 + size_t getUsedVRAM() const; + size_t getTextureVRAM() const; + size_t getBufferVRAM() const; + size_t getAvailableVRAM() const; + + // 显存预算管理 + void setVRAMBudget(size_t budget); + size_t getVRAMBudget() const; + bool isOverBudget() const; + + // 统计信息 + void printStats() const; + + // 重置计数器 + void reset(); + +private: + VRAMManager(); + ~VRAMManager() = default; + VRAMManager(const VRAMManager&) = delete; + VRAMManager& operator=(const VRAMManager&) = delete; + + mutable std::mutex mutex_; + + size_t textureVRAM_; + size_t bufferVRAM_; + size_t vramBudget_; + + // 统计 + uint32_t textureAllocCount_; + uint32_t textureFreeCount_; + uint32_t bufferAllocCount_; + uint32_t bufferFreeCount_; + size_t peakTextureVRAM_; + size_t peakBufferVRAM_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/platform/input.h b/Extra2D/include/extra2d/platform/input.h new file mode 100644 index 0000000..ff16b13 --- /dev/null +++ b/Extra2D/include/extra2d/platform/input.h @@ -0,0 +1,141 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace extra2d { + +// ============================================================================ +// 鼠标按钮枚举 +// ============================================================================ +enum class MouseButton { + Left = 0, + Right = 1, + Middle = 2, + Button4 = 3, + Button5 = 4, + Button6 = 5, + Button7 = 6, + Button8 = 7, + Count = 8 +}; + +// ============================================================================ +// Input 类 - 跨平台输入管理 +// 支持: 键盘、鼠标、手柄、触摸屏 +// ============================================================================ +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); + + // ------------------------------------------------------------------------ + // 触摸屏 (Switch 原生支持,PC 端模拟或禁用) + // ------------------------------------------------------------------------ + bool isTouching() const { return touching_; } + Vec2 getTouchPosition() const { return touchPosition_; } + int getTouchCount() const { return touchCount_; } + + // ------------------------------------------------------------------------ + // 便捷方法 + // ------------------------------------------------------------------------ + bool isAnyKeyDown() const; + bool isAnyMouseDown() const; + +private: + static constexpr int MAX_BUTTONS = SDL_CONTROLLER_BUTTON_MAX; + static constexpr int MAX_KEYS = SDL_NUM_SCANCODES; + + SDL_GameController *controller_; + + // 键盘状态 (PC 端使用) + std::array keysDown_; + std::array prevKeysDown_; + + // 手柄按钮状态 + std::array buttonsDown_; + std::array prevButtonsDown_; + + // 摇杆状态 + float leftStickX_; + float leftStickY_; + float rightStickX_; + float rightStickY_; + + // 鼠标状态 (PC 端使用) + Vec2 mousePosition_; + Vec2 prevMousePosition_; + float mouseScroll_; + float prevMouseScroll_; + std::array mouseButtonsDown_; + std::array prevMouseButtonsDown_; + + // 触摸屏状态 (Switch 原生) + bool touching_; + bool prevTouching_; + Vec2 touchPosition_; + Vec2 prevTouchPosition_; + int touchCount_; + + // 映射键盘 keyCode 到 SDL GameController 按钮 (Switch 兼容模式) + SDL_GameControllerButton mapKeyToButton(int keyCode) const; + + // 更新键盘状态 + void updateKeyboard(); + + // 更新鼠标状态 + void updateMouse(); + + // 更新手柄状态 + void updateGamepad(); + + // 更新触摸屏状态 + void updateTouch(); +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/platform/window.h b/Extra2D/include/extra2d/platform/window.h new file mode 100644 index 0000000..234d5e7 --- /dev/null +++ b/Extra2D/include/extra2d/platform/window.h @@ -0,0 +1,153 @@ +#pragma once + +#include +#include +#include +#include + +#include + +namespace extra2d { + +// 前向声明 +class EventQueue; +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 getWidth() const { return width_; } + int getHeight() const { return height_; } + Size getSize() const { return Size(static_cast(width_), static_cast(height_)); } + Vec2 getPosition() 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_; } + + // 事件队列 + void setEventQueue(EventQueue* queue) { eventQueue_ = queue; } + EventQueue* getEventQueue() const { return eventQueue_; } + + // 获取输入管理器 + Input* getInput() const { return input_.get(); } + + // 光标操作 (PC 端有效,Switch 上为空操作) + void setCursor(CursorShape shape); + void resetCursor(); + void setMouseVisible(bool visible); + + // 窗口回调 + using ResizeCallback = std::function; + using FocusCallback = std::function; + using CloseCallback = std::function; + + 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_; + EventQueue* eventQueue_; + UniquePtr input_; + + ResizeCallback resizeCallback_; + FocusCallback focusCallback_; + CloseCallback closeCallback_; + + bool initSDL(const WindowConfig& config); + void deinitSDL(); + void initCursors(); + void deinitCursors(); + void updateContentScale(); +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/resource/resource_manager.h b/Extra2D/include/extra2d/resource/resource_manager.h new file mode 100644 index 0000000..11cf8f9 --- /dev/null +++ b/Extra2D/include/extra2d/resource/resource_manager.h @@ -0,0 +1,130 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// 资源管理器 - 统一管理纹理、字体、音效等资源 +// ============================================================================ +class ResourceManager { +public: + // ------------------------------------------------------------------------ + // 单例访问 + // ------------------------------------------------------------------------ + static ResourceManager &getInstance(); + + // ------------------------------------------------------------------------ + // 纹理资源 + // ------------------------------------------------------------------------ + + /// 加载纹理(带缓存) + Ptr loadTexture(const std::string &filepath); + + /// 加载纹理并生成Alpha遮罩(用于不规则形状图片) + Ptr loadTextureWithAlphaMask(const std::string &filepath); + + /// 通过key获取已缓存的纹理 + Ptr getTexture(const std::string &key) const; + + /// 检查纹理是否已缓存 + bool hasTexture(const std::string &key) const; + + /// 卸载指定纹理 + void unloadTexture(const std::string &key); + + // ------------------------------------------------------------------------ + // Alpha遮罩资源 + // ------------------------------------------------------------------------ + + /// 获取纹理的Alpha遮罩(如果已生成) + const AlphaMask *getAlphaMask(const std::string &textureKey) const; + + /// 为已加载的纹理生成Alpha遮罩 + bool generateAlphaMask(const std::string &textureKey); + + /// 检查纹理是否有Alpha遮罩 + bool hasAlphaMask(const std::string &textureKey) const; + + // ------------------------------------------------------------------------ + // 字体图集资源 + // ------------------------------------------------------------------------ + + /// 加载字体图集(带缓存) + Ptr loadFont(const std::string &filepath, int fontSize, + bool useSDF = false); + + /// 通过key获取已缓存的字体图集 + Ptr getFont(const std::string &key) const; + + /// 检查字体是否已缓存 + bool hasFont(const std::string &key) const; + + /// 卸载指定字体 + void unloadFont(const std::string &key); + + // ------------------------------------------------------------------------ + // 音效资源 + // ------------------------------------------------------------------------ + + /// 加载音效(带缓存) + Ptr loadSound(const std::string &filepath); + Ptr loadSound(const std::string &name, const std::string &filepath); + + /// 通过key获取已缓存的音效 + Ptr getSound(const std::string &key) const; + + /// 检查音效是否已缓存 + bool hasSound(const std::string &key) const; + + /// 卸载指定音效 + void unloadSound(const std::string &key); + + // ------------------------------------------------------------------------ + // 缓存清理 + // ------------------------------------------------------------------------ + + /// 清理所有失效的弱引用(自动清理已释放的资源) + void purgeUnused(); + + /// 清理指定类型的所有缓存 + void clearTextureCache(); + void clearFontCache(); + void clearSoundCache(); + + /// 清理所有资源缓存 + void clearAllCaches(); + + /// 获取各类资源的缓存数量 + size_t getTextureCacheSize() const; + size_t getFontCacheSize() const; + size_t getSoundCacheSize() const; + + ResourceManager(); + ~ResourceManager(); + ResourceManager(const ResourceManager &) = delete; + ResourceManager &operator=(const ResourceManager &) = delete; + + // 生成字体缓存key + std::string makeFontKey(const std::string &filepath, int fontSize, + bool useSDF) const; + + // 互斥锁保护缓存 + mutable std::mutex textureMutex_; + mutable std::mutex fontMutex_; + mutable std::mutex soundMutex_; + + // 资源缓存 - 使用弱指针实现自动清理 + std::unordered_map> textureCache_; + std::unordered_map> fontCache_; + std::unordered_map> soundCache_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/scene/node.h b/Extra2D/include/extra2d/scene/node.h new file mode 100644 index 0000000..b09cf79 --- /dev/null +++ b/Extra2D/include/extra2d/scene/node.h @@ -0,0 +1,229 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +// 前向声明 +class Scene; +class Action; +class RenderBackend; +struct RenderCommand; + +// ============================================================================ +// 节点基类 - 场景图的基础 +// ============================================================================ +class Node : public std::enable_shared_from_this { +public: + Node(); + virtual ~Node(); + + // ------------------------------------------------------------------------ + // 层级管理 + // ------------------------------------------------------------------------ + void addChild(Ptr child); + + /** + * @brief 批量添加子节点 + * @param children 子节点列表 + */ + void addChildren(std::vector> &&children); + + void removeChild(Ptr child); + void removeChildByName(const std::string &name); + void removeFromParent(); + void removeAllChildren(); + + Ptr getParent() const { return parent_.lock(); } + const std::vector> &getChildren() const { return children_; } + Ptr getChildByName(const std::string &name) const; + Ptr getChildByTag(int tag) const; + + // ------------------------------------------------------------------------ + // 变换属性 + // ------------------------------------------------------------------------ + void setPosition(const Vec2 &pos); + void setPosition(float x, float y); + Vec2 getPosition() const { return position_; } + + void setRotation(float degrees); + float getRotation() const { return rotation_; } + + void setScale(const Vec2 &scale); + void setScale(float scale); + void setScale(float x, float y); + Vec2 getScale() const { return scale_; } + + void setAnchor(const Vec2 &anchor); + void setAnchor(float x, float y); + Vec2 getAnchor() const { return anchor_; } + + void setSkew(const Vec2 &skew); + void setSkew(float x, float y); + Vec2 getSkew() const { return skew_; } + + void setOpacity(float opacity); + float getOpacity() const { return opacity_; } + + void setVisible(bool visible); + bool isVisible() const { return visible_; } + + void setZOrder(int zOrder); + int getZOrder() const { return zOrder_; } + + // ------------------------------------------------------------------------ + // 世界变换 + // ------------------------------------------------------------------------ + Vec2 convertToWorldSpace(const Vec2 &localPos) const; + Vec2 convertToNodeSpace(const Vec2 &worldPos) const; + + glm::mat4 getLocalTransform() const; + glm::mat4 getWorldTransform() const; + + /** + * @brief 标记变换矩阵为脏状态,并传播到所有子节点 + */ + void markTransformDirty(); + + // ------------------------------------------------------------------------ + // 名称和标签 + // ------------------------------------------------------------------------ + void setName(const std::string &name) { name_ = name; } + const std::string &getName() const { return name_; } + + void setTag(int tag) { tag_ = tag; } + int getTag() const { return tag_; } + + // ------------------------------------------------------------------------ + // 生命周期回调 + // ------------------------------------------------------------------------ + virtual void onEnter(); + virtual void onExit(); + virtual void onUpdate(float dt); + virtual void onRender(RenderBackend &renderer); + virtual void onAttachToScene(Scene *scene); + virtual void onDetachFromScene(); + + // ------------------------------------------------------------------------ + // 边界框(用于空间索引) + // ------------------------------------------------------------------------ + virtual Rect getBoundingBox() const; + + // 是否需要参与空间索引(默认 true) + void setSpatialIndexed(bool indexed) { spatialIndexed_ = indexed; } + bool isSpatialIndexed() const { return spatialIndexed_; } + + // 更新空间索引(手动调用,通常在边界框变化后) + void updateSpatialIndex(); + + // ------------------------------------------------------------------------ + // 动作系统 + // ------------------------------------------------------------------------ + void runAction(Ptr action); + void stopAllActions(); + void stopAction(Ptr action); + void stopActionByTag(int tag); + Ptr getActionByTag(int tag) const; + size_t getActionCount() const { return actions_.size(); } + + // ------------------------------------------------------------------------ + // 事件系统 + // ------------------------------------------------------------------------ + EventDispatcher &getEventDispatcher() { return eventDispatcher_; } + + // ------------------------------------------------------------------------ + // 内部方法 + // ------------------------------------------------------------------------ + void update(float dt); + void render(RenderBackend &renderer); + void sortChildren(); + + bool isRunning() const { return running_; } + Scene *getScene() const { return scene_; } + + // 多线程渲染命令收集 + virtual void collectRenderCommands(std::vector &commands, + int parentZOrder = 0); + +protected: + // 子类重写 + virtual void onDraw(RenderBackend &renderer) {} + virtual void onUpdateNode(float dt) {} + virtual void generateRenderCommand(std::vector &commands, + int zOrder) {}; + + // 供子类访问的内部状态 + Vec2 &getPositionRef() { return position_; } + Vec2 &getScaleRef() { return scale_; } + Vec2 &getAnchorRef() { return anchor_; } + float getRotationRef() { return rotation_; } + float getOpacityRef() { return opacity_; } + +private: + // ========================================================================== + // 成员变量按类型大小降序排列,减少内存对齐填充 + // 64位系统对齐:std::string(32) > glm::mat4(64) > std::vector(24) > + // double(8) > float(4) > int(4) > bool(1) + // ========================================================================== + + // 1. 大块内存(64字节) + mutable glm::mat4 localTransform_; // 64 bytes + mutable glm::mat4 worldTransform_; // 64 bytes + + // 2. 字符串和容器(24-32字节) + std::string name_; // 32 bytes + std::vector> children_; // 24 bytes + + // 3. 子节点索引(加速查找) + std::unordered_map> nameIndex_; // 56 bytes + std::unordered_map> tagIndex_; // 56 bytes + + // 4. 动作系统(使用 unordered_map 加速 tag 查找) + std::unordered_map> actionByTag_; // 56 bytes + std::vector> actions_; // 24 bytes(无 tag 的 Action) + + // 5. 事件分发器 + EventDispatcher eventDispatcher_; // 大小取决于实现 + + // 6. 父节点引用 + WeakPtr parent_; // 16 bytes + + // 7. 变换属性(按访问频率分组) + Vec2 position_ = Vec2::Zero(); // 8 bytes + Vec2 scale_ = Vec2(1.0f, 1.0f); // 8 bytes + Vec2 anchor_ = Vec2(0.5f, 0.5f); // 8 bytes + Vec2 skew_ = Vec2::Zero(); // 8 bytes + + // 8. 边界框(用于空间索引) + Rect lastSpatialBounds_; // 16 bytes + + // 9. 浮点属性 + float rotation_ = 0.0f; // 4 bytes + float opacity_ = 1.0f; // 4 bytes + + // 10. 整数属性 + int zOrder_ = 0; // 4 bytes + int tag_ = -1; // 4 bytes + + // 11. 场景指针 + Scene *scene_ = nullptr; // 8 bytes + + // 12. 布尔标志(打包在一起) + mutable bool transformDirty_ = true; // 1 byte + mutable bool worldTransformDirty_ = true; // 1 byte + bool childrenOrderDirty_ = false; // 1 byte + bool visible_ = true; // 1 byte + bool running_ = false; // 1 byte + bool spatialIndexed_ = true; // 1 byte + // 填充 2 bytes 到 8 字节对齐 +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/scene/scene.h b/Extra2D/include/extra2d/scene/scene.h new file mode 100644 index 0000000..053ce36 --- /dev/null +++ b/Extra2D/include/extra2d/scene/scene.h @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace extra2d { + +// 前向声明 +struct RenderCommand; + +// ============================================================================ +// 场景类 - 节点容器,管理整个场景图 +// ============================================================================ +class Scene : public Node { +public: + Scene(); + ~Scene() override = default; + + // ------------------------------------------------------------------------ + // 场景属性 + // ------------------------------------------------------------------------ + void setBackgroundColor(const Color &color) { backgroundColor_ = color; } + Color getBackgroundColor() const { return backgroundColor_; } + + // ------------------------------------------------------------------------ + // 摄像机 + // ------------------------------------------------------------------------ + void setCamera(Ptr camera); + Ptr getCamera() const { return camera_; } + + Camera *getActiveCamera() const { + return camera_ ? camera_.get() : defaultCamera_.get(); + } + + // ------------------------------------------------------------------------ + // 视口和尺寸 + // ------------------------------------------------------------------------ + void setViewportSize(float width, float height); + void setViewportSize(const Size &size); + Size getViewportSize() const { return viewportSize_; } + + float getWidth() const { return viewportSize_.width; } + float getHeight() const { return viewportSize_.height; } + + // ------------------------------------------------------------------------ + // 场景状态 + // ------------------------------------------------------------------------ + bool isPaused() const { return paused_; } + void pause() { paused_ = true; } + void resume() { paused_ = false; } + + // ------------------------------------------------------------------------ + // 渲染和更新 + // ------------------------------------------------------------------------ + void renderScene(RenderBackend &renderer); + void renderContent(RenderBackend &renderer); + void updateScene(float dt); + void collectRenderCommands(std::vector &commands, + int parentZOrder = 0) override; + + // ------------------------------------------------------------------------ + // 空间索引系统 + // ------------------------------------------------------------------------ + SpatialManager &getSpatialManager() { return spatialManager_; } + const SpatialManager &getSpatialManager() const { return spatialManager_; } + + // 启用/禁用空间索引 + void setSpatialIndexingEnabled(bool enabled) { + spatialIndexingEnabled_ = enabled; + } + bool isSpatialIndexingEnabled() const { return spatialIndexingEnabled_; } + + // 节点空间索引管理(内部使用) + void updateNodeInSpatialIndex(Node *node, const Rect &oldBounds, + const Rect &newBounds); + void removeNodeFromSpatialIndex(Node *node); + + // 碰撞检测查询 + std::vector queryNodesInArea(const Rect &area) const; + std::vector queryNodesAtPoint(const Vec2 &point) const; + std::vector> queryCollisions() const; + + // ------------------------------------------------------------------------ + // 静态创建方法 + // ------------------------------------------------------------------------ + static Ptr create(); + +protected: + void onEnter() override; + void onExit() override; + + friend class SceneManager; + +private: + Color backgroundColor_ = Colors::Black; + Size viewportSize_ = Size::Zero(); + + Ptr camera_; + Ptr defaultCamera_; + + bool paused_ = false; + + // 空间索引系统 + SpatialManager spatialManager_; + bool spatialIndexingEnabled_ = true; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/scene/scene_manager.h b/Extra2D/include/extra2d/scene/scene_manager.h new file mode 100644 index 0000000..bbe19d8 --- /dev/null +++ b/Extra2D/include/extra2d/scene/scene_manager.h @@ -0,0 +1,153 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace extra2d { + +// 前向声明 +struct RenderCommand; +class Transition; + +// ============================================================================ +// 场景切换特效类型 +// ============================================================================ +enum class TransitionType { + None, + Fade, + SlideLeft, + SlideRight, + SlideUp, + SlideDown, + Scale, + Flip +}; + +// ============================================================================ +// 场景管理器 - 管理场景的生命周期和切换 +// ============================================================================ +class SceneManager { +public: + using TransitionCallback = std::function; + + // ------------------------------------------------------------------------ + // 单例访问 + // ------------------------------------------------------------------------ + static SceneManager &getInstance(); + + // ------------------------------------------------------------------------ + // 场景栈操作 + // ------------------------------------------------------------------------ + + // 运行第一个场景 + void runWithScene(Ptr scene); + + // 替换当前场景 + void replaceScene(Ptr scene); + void replaceScene(Ptr scene, TransitionType transition, + float duration = 0.5f); + + // 压入新场景(当前场景暂停) + void pushScene(Ptr scene); + void pushScene(Ptr scene, TransitionType transition, + float duration = 0.5f); + + // 弹出当前场景(恢复上一个场景) + void popScene(); + void popScene(TransitionType transition, float duration = 0.5f); + + // 弹出到根场景 + void popToRootScene(); + void popToRootScene(TransitionType transition, float duration = 0.5f); + + // 弹出到指定场景 + void popToScene(const std::string &name); + void popToScene(const std::string &name, TransitionType transition, + float duration = 0.5f); + + // ------------------------------------------------------------------------ + // 获取场景 + // ------------------------------------------------------------------------ + Ptr getCurrentScene() const; + Ptr getPreviousScene() const; + Ptr getRootScene() const; + + // 通过名称获取场景 + Ptr getSceneByName(const std::string &name) const; + + // ------------------------------------------------------------------------ + // 查询 + // ------------------------------------------------------------------------ + size_t getSceneCount() const { return sceneStack_.size(); } + bool isEmpty() const { return sceneStack_.empty(); } + bool hasScene(const std::string &name) const; + + // ------------------------------------------------------------------------ + // 更新和渲染 + // ------------------------------------------------------------------------ + void update(float dt); + void render(RenderBackend &renderer); + void collectRenderCommands(std::vector &commands); + + // ------------------------------------------------------------------------ + // 过渡控制 + // ------------------------------------------------------------------------ + bool isTransitioning() const { return isTransitioning_; } + void setTransitionCallback(TransitionCallback callback) { + transitionCallback_ = callback; + } + + // ------------------------------------------------------------------------ + // 清理 + // ------------------------------------------------------------------------ + void end(); + void purgeCachedScenes(); + +public: + SceneManager() = default; + ~SceneManager() = default; + SceneManager(const SceneManager &) = delete; + SceneManager &operator=(const SceneManager &) = delete; + + // 场景切换(供 Application 使用) + void enterScene(Ptr scene); + void enterScene(Ptr scene, Ptr transition); + +private: + void doSceneSwitch(); + void startTransition(Ptr from, Ptr to, TransitionType type, + float duration, Function stackAction); + void updateTransition(float dt); + void finishTransition(); + void dispatchPointerEvents(Scene &scene); + + std::stack> sceneStack_; + std::unordered_map> namedScenes_; + + // Transition state + bool isTransitioning_ = false; + TransitionType currentTransition_ = TransitionType::None; + float transitionDuration_ = 0.0f; + float transitionElapsed_ = 0.0f; + Ptr outgoingScene_; + Ptr incomingScene_; + Ptr activeTransition_; + Function transitionStackAction_; + TransitionCallback transitionCallback_; + + // Next scene to switch to (queued during transition) + Ptr nextScene_; + bool sendCleanupToScene_ = false; + + Node *hoverTarget_ = nullptr; + Node *captureTarget_ = nullptr; + Vec2 lastPointerWorld_ = Vec2::Zero(); + bool hasLastPointerWorld_ = false; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/scene/shape_node.h b/Extra2D/include/extra2d/scene/shape_node.h new file mode 100644 index 0000000..64550e6 --- /dev/null +++ b/Extra2D/include/extra2d/scene/shape_node.h @@ -0,0 +1,104 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// 形状类型 +// ============================================================================ +enum class ShapeType { Point, Line, Rect, Circle, Triangle, Polygon }; + +// ============================================================================ +// 形状节点 - 用于绘制几何形状 +// ============================================================================ +class ShapeNode : public Node { +public: + ShapeNode(); + ~ShapeNode() override = default; + + // ------------------------------------------------------------------------ + // 静态创建方法 + // ------------------------------------------------------------------------ + static Ptr create(); + + // 点 + static Ptr createPoint(const Vec2 &pos, const Color &color); + + // 线 + static Ptr createLine(const Vec2 &start, const Vec2 &end, + const Color &color, float width = 1.0f); + + // 矩形 + static Ptr createRect(const Rect &rect, const Color &color, + float width = 1.0f); + static Ptr createFilledRect(const Rect &rect, const Color &color); + + // 圆形 + static Ptr createCircle(const Vec2 ¢er, float radius, + const Color &color, int segments = 32, + float width = 1.0f); + static Ptr createFilledCircle(const Vec2 ¢er, float radius, + const Color &color, + int segments = 32); + + // 三角形 + static Ptr createTriangle(const Vec2 &p1, const Vec2 &p2, + const Vec2 &p3, const Color &color, + float width = 1.0f); + static Ptr createFilledTriangle(const Vec2 &p1, const Vec2 &p2, + const Vec2 &p3, + const Color &color); + + // 多边形 + static Ptr createPolygon(const std::vector &points, + const Color &color, float width = 1.0f); + static Ptr createFilledPolygon(const std::vector &points, + const Color &color); + + // ------------------------------------------------------------------------ + // 属性设置 + // ------------------------------------------------------------------------ + void setShapeType(ShapeType type) { shapeType_ = type; } + ShapeType getShapeType() const { return shapeType_; } + + void setColor(const Color &color) { color_ = color; } + Color getColor() const { return color_; } + + void setFilled(bool filled) { filled_ = filled; } + bool isFilled() const { return filled_; } + + void setLineWidth(float width) { lineWidth_ = width; } + float getLineWidth() const { return lineWidth_; } + + void setSegments(int segments) { segments_ = segments; } + int getSegments() const { return segments_; } + + // ------------------------------------------------------------------------ + // 点设置 + // ------------------------------------------------------------------------ + void setPoints(const std::vector &points); + const std::vector &getPoints() const { return points_; } + void addPoint(const Vec2 &point); + void clearPoints(); + + Rect getBoundingBox() const override; + +protected: + void onDraw(RenderBackend &renderer) override; + void generateRenderCommand(std::vector &commands, + int zOrder) override; + +private: + ShapeType shapeType_ = ShapeType::Rect; + Color color_ = Colors::White; + bool filled_ = false; + float lineWidth_ = 1.0f; + int segments_ = 32; + std::vector points_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/scene/sprite.h b/Extra2D/include/extra2d/scene/sprite.h new file mode 100644 index 0000000..0968ec4 --- /dev/null +++ b/Extra2D/include/extra2d/scene/sprite.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include + +namespace extra2d { + +// ============================================================================ +// 精灵节点 +// ============================================================================ +class Sprite : public Node { +public: + Sprite(); + explicit Sprite(Ptr texture); + ~Sprite() override = default; + + // 纹理 + void setTexture(Ptr texture); + Ptr getTexture() const { return texture_; } + + // 纹理矩形 (用于图集) + void setTextureRect(const Rect &rect); + Rect getTextureRect() const { return textureRect_; } + + // 颜色混合 + void setColor(const Color &color); + Color getColor() const { return color_; } + + // 翻转 + void setFlipX(bool flip); + void setFlipY(bool flip); + bool isFlipX() const { return flipX_; } + bool isFlipY() const { return flipY_; } + + // 静态创建方法 + static Ptr create(); + static Ptr create(Ptr texture); + static Ptr create(Ptr texture, const Rect &rect); + + Rect getBoundingBox() const override; + +protected: + void onDraw(RenderBackend &renderer) override; + void generateRenderCommand(std::vector &commands, + int zOrder) override; + +private: + Ptr texture_; + Rect textureRect_; + Color color_ = Colors::White; + bool flipX_ = false; + bool flipY_ = false; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/scene/transition.h b/Extra2D/include/extra2d/scene/transition.h new file mode 100644 index 0000000..0c2353f --- /dev/null +++ b/Extra2D/include/extra2d/scene/transition.h @@ -0,0 +1,137 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { + +// ============================================================================ +// 过渡方向 +// ============================================================================ +enum class TransitionDirection { Left, Right, Up, Down }; + +// ============================================================================ +// 过渡效果基类 +// ============================================================================ +class Transition : public std::enable_shared_from_this { +public: + using FinishCallback = std::function; + + Transition(float duration); + virtual ~Transition() = default; + + // 开始过渡 + void start(Ptr from, Ptr to); + + // 更新过渡进度 + void update(float dt); + + // 渲染过渡效果 + virtual void render(RenderBackend &renderer); + + // 是否完成 + bool isFinished() const { return isFinished_; } + + // 获取进度 [0, 1] + float getProgress() const { return progress_; } + + // 获取淡入淡出进度 (0->1 for fade in, 1->0 for fade out) + float getFadeInAlpha() const; + float getFadeOutAlpha() const; + + // 设置完成回调 + void setFinishCallback(FinishCallback callback) { + finishCallback_ = callback; + } + + // 获取源场景和目标场景 + Ptr getOutgoingScene() const { return outgoingScene_; } + Ptr getIncomingScene() const { return incomingScene_; } + +protected: + // 子类实现具体的渲染效果 + virtual void onRenderTransition(RenderBackend &renderer, float progress) = 0; + + // 过渡完成时调用 + virtual void onFinish(); + + float duration_; + float elapsed_; + float progress_; + bool isFinished_; + bool isStarted_; + + Ptr outgoingScene_; + Ptr incomingScene_; + FinishCallback finishCallback_; +}; + +// ============================================================================ +// 淡入淡出过渡 +// ============================================================================ +class FadeTransition : public Transition { +public: + FadeTransition(float duration); + +protected: + void onRenderTransition(RenderBackend &renderer, float progress) override; +}; + +// ============================================================================ +// 滑动过渡 +// ============================================================================ +class SlideTransition : public Transition { +public: + SlideTransition(float duration, TransitionDirection direction); + +protected: + void onRenderTransition(RenderBackend &renderer, float progress) override; + +private: + TransitionDirection direction_; +}; + +// ============================================================================ +// 缩放过渡 +// ============================================================================ +class ScaleTransition : public Transition { +public: + ScaleTransition(float duration); + +protected: + void onRenderTransition(RenderBackend &renderer, float progress) override; +}; + +// ============================================================================ +// 翻页过渡 +// ============================================================================ +class FlipTransition : public Transition { +public: + enum class Axis { Horizontal, Vertical }; + + FlipTransition(float duration, Axis axis = Axis::Horizontal); + +protected: + void onRenderTransition(RenderBackend &renderer, float progress) override; + +private: + Axis axis_; +}; + +// ============================================================================ +// 马赛克/方块过渡 +// ============================================================================ +class BoxTransition : public Transition { +public: + BoxTransition(float duration, int divisions = 8); + +protected: + void onRenderTransition(RenderBackend &renderer, float progress) override; + +private: + int divisions_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/spatial/quadtree.h b/Extra2D/include/extra2d/spatial/quadtree.h new file mode 100644 index 0000000..d48337e --- /dev/null +++ b/Extra2D/include/extra2d/spatial/quadtree.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include + +namespace extra2d { + +class QuadTree : public ISpatialIndex { +public: + static constexpr int MAX_OBJECTS = 10; + static constexpr int MAX_LEVELS = 5; + + struct QuadTreeNode { + Rect bounds; + int level; + std::vector> objects; + std::array, 4> children; + + QuadTreeNode(const Rect &bounds, int level); + bool contains(const Rect &rect) const; + bool intersects(const Rect &rect) const; + }; + + explicit QuadTree(const Rect &worldBounds); + ~QuadTree() override = default; + + void insert(Node *node, const Rect &bounds) override; + void remove(Node *node) override; + void update(Node *node, const Rect &newBounds) override; + + std::vector query(const Rect &area) const override; + std::vector query(const Vec2 &point) const override; + std::vector> queryCollisions() const override; + + void clear() override; + size_t size() const override; + bool empty() const override; + + void rebuild() override; + +private: + void split(QuadTreeNode *node); + void insertIntoNode(QuadTreeNode *node, Node *object, const Rect &bounds); + void queryNode(const QuadTreeNode *node, const Rect &area, + std::vector &results) const; + void queryNode(const QuadTreeNode *node, const Vec2 &point, + std::vector &results) const; + void + collectCollisions(const QuadTreeNode *node, + std::vector> &collisions) const; + bool removeFromNode(QuadTreeNode *node, Node *object); + + /** + * @brief 使用扫描线算法检测节点内对象的碰撞 + * @param objects 对象列表 + * @param collisions 输出碰撞对 + */ + void detectCollisionsInNode( + const std::vector> &objects, + std::vector> &collisions) const; + + std::unique_ptr root_; + Rect worldBounds_; + size_t objectCount_ = 0; + + // 碰撞检测用的临时缓冲区,避免重复分配 + mutable std::vector> collisionBuffer_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/spatial/spatial_hash.h b/Extra2D/include/extra2d/spatial/spatial_hash.h new file mode 100644 index 0000000..57d42bf --- /dev/null +++ b/Extra2D/include/extra2d/spatial/spatial_hash.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include + +namespace extra2d { + +/** + * @brief 空间哈希实现 - 优化内存布局版本 + * 使用连续内存存储单元格内容,减少内存碎片 + */ +class SpatialHash : public ISpatialIndex { +public: + using CellKey = std::pair; + + struct CellKeyHash { + size_t operator()(const CellKey &key) const { + return std::hash()(key.first) ^ + (std::hash()(key.second) << 1); + } + }; + + explicit SpatialHash(float cellSize = 64.0f); + ~SpatialHash() override = default; + + void insert(Node *node, const Rect &bounds) override; + void remove(Node *node) override; + void update(Node *node, const Rect &newBounds) override; + + std::vector query(const Rect &area) const override; + std::vector query(const Vec2 &point) const override; + std::vector> queryCollisions() const override; + + void clear() override; + size_t size() const override; + bool empty() const override; + + void rebuild() override; + + void setCellSize(float cellSize); + float getCellSize() const { return cellSize_; } + +private: + /** + * @brief 单元格数据 - 使用vector代替unordered_set减少内存开销 + */ + struct Cell { + std::vector objects; + + void insert(Node *node); + void remove(Node *node); + bool contains(Node *node) const; + void clear() { objects.clear(); } + size_t size() const { return objects.size(); } + bool empty() const { return objects.empty(); } + }; + + CellKey getCellKey(float x, float y) const; + void getCellsForRect(const Rect &rect, std::vector &cells) const; + void insertIntoCells(Node *node, const Rect &bounds); + void removeFromCells(Node *node, const Rect &bounds); + + float cellSize_; + // 使用vector存储对象列表代替unordered_set,内存更紧凑 + std::unordered_map grid_; + std::unordered_map objectBounds_; + size_t objectCount_ = 0; + + // 查询用的临时缓冲区,避免重复分配 + mutable std::vector queryBuffer_; + mutable std::vector> collisionBuffer_; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/spatial/spatial_index.h b/Extra2D/include/extra2d/spatial/spatial_index.h new file mode 100644 index 0000000..a8e33dd --- /dev/null +++ b/Extra2D/include/extra2d/spatial/spatial_index.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include +#include + +namespace extra2d { + +class Node; + +enum class SpatialStrategy { Auto, QuadTree, SpatialHash }; + +struct SpatialQueryResult { + Node *node; + Rect bounds; +}; + +class ISpatialIndex { +public: + virtual ~ISpatialIndex() = default; + + virtual void insert(Node *node, const Rect &bounds) = 0; + virtual void remove(Node *node) = 0; + virtual void update(Node *node, const Rect &newBounds) = 0; + + virtual std::vector query(const Rect &area) const = 0; + virtual std::vector query(const Vec2 &point) const = 0; + virtual std::vector> queryCollisions() const = 0; + + virtual void clear() = 0; + virtual size_t size() const = 0; + virtual bool empty() const = 0; + + virtual void rebuild() = 0; +}; + +using SpatialIndexPtr = std::unique_ptr; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/spatial/spatial_manager.h b/Extra2D/include/extra2d/spatial/spatial_manager.h new file mode 100644 index 0000000..ad6ddad --- /dev/null +++ b/Extra2D/include/extra2d/spatial/spatial_manager.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include +#include + +namespace extra2d { + +class SpatialManager { +public: + using QueryCallback = std::function; + + SpatialManager(); + explicit SpatialManager(const Rect &worldBounds); + ~SpatialManager() = default; + + void setStrategy(SpatialStrategy strategy); + void setAutoThresholds(size_t quadTreeThreshold, size_t hashThreshold); + + void setWorldBounds(const Rect &bounds); + Rect getWorldBounds() const { return worldBounds_; } + + void insert(Node *node, const Rect &bounds); + void remove(Node *node); + void update(Node *node, const Rect &newBounds); + + std::vector query(const Rect &area) const; + std::vector query(const Vec2 &point) const; + std::vector> queryCollisions() const; + + void query(const Rect &area, const QueryCallback &callback) const; + void query(const Vec2 &point, const QueryCallback &callback) const; + + void clear(); + size_t size() const; + bool empty() const; + + void rebuild(); + void optimize(); + + SpatialStrategy getCurrentStrategy() const; + const char *getStrategyName() const; + + static std::unique_ptr createIndex(SpatialStrategy strategy, + const Rect &bounds); + +private: + void selectOptimalStrategy(); + + SpatialStrategy currentStrategy_ = SpatialStrategy::Auto; + SpatialStrategy activeStrategy_ = SpatialStrategy::QuadTree; + std::unique_ptr index_; + Rect worldBounds_; + + size_t quadTreeThreshold_ = 1000; + size_t hashThreshold_ = 5000; + + mutable size_t queryCount_ = 0; + mutable size_t totalQueryTime_ = 0; +}; + +} // namespace extra2d diff --git a/Extra2D/include/extra2d/ui/button.h b/Extra2D/include/extra2d/ui/button.h new file mode 100644 index 0000000..a9d804e --- /dev/null +++ b/Extra2D/include/extra2d/ui/button.h @@ -0,0 +1,242 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace extra2d { + +// 图片缩放模式 +enum class ImageScaleMode { + Original, // 使用原图大小 + Stretch, // 拉伸填充 + ScaleFit, // 等比缩放,保持完整显示 + ScaleFill // 等比缩放,填充整个区域(可能裁剪) +}; + +// ============================================================================ +// 基础按钮类 +// ============================================================================ +class Button : public Widget { +public: + Button(); + explicit Button(const std::string &text); + ~Button() override = default; + + // ------------------------------------------------------------------------ + // 静态创建方法 + // ------------------------------------------------------------------------ + static Ptr