refactor: 移除图形渲染相关代码并简化核心结构

移除图形渲染模块、着色器、纹理、字体等组件
删除过渡场景和精灵节点等场景管理功能
简化应用程序类,移除渲染和场景相关接口
更新GLFW窗口模块,移除OpenGL相关初始化
清理不再使用的头文件和实现文件
This commit is contained in:
ChestnutYueyue 2026-02-20 16:36:31 +08:00
parent 9e911db53c
commit f9e244299f
120 changed files with 32 additions and 67610 deletions

View File

@ -1,311 +0,0 @@
#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 <KHR/khrplatform.h>
* 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 <stdint.h>
*/
#include <stdint.h>
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 <inttypes.h>
*/
#include <inttypes.h>
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 <stdint.h>
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_ */

View File

@ -9,9 +9,7 @@
namespace extra2d { namespace extra2d {
class GLFWWindow; class GLFWWindow;
class Renderer;
class WindowModule; class WindowModule;
class RenderModule;
/** /**
* @brief * @brief
@ -87,18 +85,6 @@ public:
*/ */
GLFWWindow *window(); GLFWWindow *window();
/**
* @brief
* @return
*/
Renderer *renderer();
/**
* @brief
* @param scene
*/
void enterScene(Ptr<class Scene> scene);
float deltaTime() const { return deltaTime_; } float deltaTime() const { return deltaTime_; }
float totalTime() const { return totalTime_; } float totalTime() const { return totalTime_; }
int fps() const { return currentFps_; } int fps() const { return currentFps_; }
@ -110,7 +96,6 @@ private:
void mainLoop(); void mainLoop();
void update(); void update();
void render(); void render();
void configureCameraService();
bool initialized_ = false; bool initialized_ = false;
bool running_ = false; bool running_ = false;

View File

@ -10,32 +10,11 @@
#include <extra2d/core/registry.h> #include <extra2d/core/registry.h>
#include <extra2d/core/types.h> #include <extra2d/core/types.h>
// Config removed - app info now in Application class
// Platform // Platform
#include <extra2d/platform/glfw/glfw_window.h> #include <extra2d/platform/glfw/glfw_window.h>
#include <extra2d/platform/keys.h> #include <extra2d/platform/keys.h>
#include <extra2d/platform/window_module.h> #include <extra2d/platform/window_module.h>
// Graphics
#include <extra2d/graphics/camera/camera.h>
#include <extra2d/graphics/camera/viewport_adapter.h>
#include <extra2d/graphics/core/render_module.h>
#include <extra2d/graphics/core/render_target.h>
#include <extra2d/graphics/memory/vram_manager.h>
#include <extra2d/graphics/opengl/gl_renderer.h>
#include <extra2d/graphics/shader/shader_manager.h>
#include <extra2d/graphics/texture/font.h>
#include <extra2d/graphics/texture/texture.h>
#include <extra2d/graphics/texture/texture_pool.h>
// Scene
#include <extra2d/scene/node.h>
#include <extra2d/scene/scene.h>
#include <extra2d/scene/scene_manager.h>
#include <extra2d/scene/shape_node.h>
#include <extra2d/scene/sprite.h>
// Event // Event
#include <extra2d/event/event.h> #include <extra2d/event/event.h>
#include <extra2d/event/event_dispatcher.h> #include <extra2d/event/event_dispatcher.h>
@ -46,10 +25,8 @@
#include <extra2d/utils/timer.h> #include <extra2d/utils/timer.h>
// Services // Services
#include <extra2d/services/camera_service.h>
#include <extra2d/services/event_service.h> #include <extra2d/services/event_service.h>
#include <extra2d/services/logger_service.h> #include <extra2d/services/logger_service.h>
#include <extra2d/services/scene_service.h>
#include <extra2d/services/timer_service.h> #include <extra2d/services/timer_service.h>
// Application // Application

View File

@ -1,157 +0,0 @@
#pragma once
#include <extra2d/core/color.h>
#include <extra2d/core/math_types.h>
#include <extra2d/core/types.h>
#include <glm/mat4x4.hpp>
#include <cstdint>
#include <vector>
namespace extra2d {
// ============================================================================
// 形状顶点结构
// ============================================================================
struct ShapeVertex {
float x, y; // 位置
float r, g, b, a; // 颜色
ShapeVertex() = default;
ShapeVertex(float px, float py, const Color& c)
: x(px), y(py), r(c.r), g(c.g), b(c.b), a(c.a) {}
};
// ============================================================================
// 形状批处理抽象接口 - 后端无关
// ============================================================================
class ShapeBatch {
public:
virtual ~ShapeBatch() = default;
/**
* @brief
* @return true
*/
virtual bool init() = 0;
/**
* @brief
*/
virtual void shutdown() = 0;
/**
* @brief
* @param viewProjection
*/
virtual void begin(const glm::mat4& viewProjection) = 0;
/**
* @brief
*/
virtual void end() = 0;
/**
* @brief 线
* @param start
* @param end
* @param color
* @param width 线
*/
virtual void drawLine(const Vec2& start, const Vec2& end,
const Color& color, float width = 1.0f) = 0;
/**
* @brief
* @param rect
* @param color
* @param width
*/
virtual void drawRect(const Rect& rect, const Color& color,
float width = 1.0f) = 0;
/**
* @brief
* @param rect
* @param color
*/
virtual void fillRect(const Rect& rect, const Color& color) = 0;
/**
* @brief
* @param center
* @param radius
* @param color
* @param segments
* @param width
*/
virtual void drawCircle(const Vec2& center, float radius,
const Color& color, int segments = 32,
float width = 1.0f) = 0;
/**
* @brief
* @param center
* @param radius
* @param color
* @param segments
*/
virtual void fillCircle(const Vec2& center, float radius,
const Color& color, int segments = 32) = 0;
/**
* @brief
* @param p1 1
* @param p2 2
* @param p3 3
* @param color
* @param width
*/
virtual void drawTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3,
const Color& color, float width = 1.0f) = 0;
/**
* @brief
* @param p1 1
* @param p2 2
* @param p3 3
* @param color
*/
virtual void fillTriangle(const Vec2& p1, const Vec2& p2, const Vec2& p3,
const Color& color) = 0;
/**
* @brief
* @param points
* @param color
* @param width
*/
virtual void drawPolygon(const std::vector<Vec2>& points,
const Color& color, float width = 1.0f) = 0;
/**
* @brief
* @param points
* @param color
*/
virtual void fillPolygon(const std::vector<Vec2>& points,
const Color& color) = 0;
/**
* @brief
* @return
*/
virtual uint32_t getDrawCallCount() const = 0;
/**
* @brief
*/
virtual void resetDrawCallCount() = 0;
/**
* @brief
* @return true
*/
virtual bool isValid() const = 0;
};
} // namespace extra2d

View File

@ -1,121 +0,0 @@
#pragma once
#include <extra2d/core/types.h>
#include <extra2d/core/math_types.h>
#include <extra2d/core/color.h>
#include <extra2d/graphics/texture/texture.h>
#include <glm/mat4x4.hpp>
#include <array>
#include <vector>
#include <cstdint>
namespace extra2d {
// ============================================================================
// 三角函数查表 - 避免每帧计算 sin/cos
// ============================================================================
class TrigLookup {
public:
TrigLookup();
// 通过角度(0-360)获取 sin/cos
float sin(int angle) const;
float cos(int angle) const;
// 通过弧度获取 sin/cos
float sinRad(float rad) const;
float cosRad(float rad) const;
private:
static constexpr int TABLE_SIZE = 360 * 4; // 0.25度精度
std::array<float, TABLE_SIZE> sinTable_;
std::array<float, TABLE_SIZE> cosTable_;
};
// ============================================================================
// 精灵批次数据 - 后端无关
// ============================================================================
struct SpriteVertex {
Vec2 position;
Vec2 texCoord;
Color color;
};
struct SpriteData {
Vec2 position;
Vec2 size;
float rotation;
Vec2 pivot;
Color color;
const Texture* texture;
Rect uvRect;
};
// ============================================================================
// 通用精灵批处理 - 后端无关
// 负责:顶点生成、批次管理、三角函数查表
// ============================================================================
class SpriteBatch {
public:
static constexpr size_t MAX_SPRITES = 10000;
static constexpr size_t VERTICES_PER_SPRITE = 4;
static constexpr size_t INDICES_PER_SPRITE = 6;
static constexpr size_t MAX_VERTICES = MAX_SPRITES * VERTICES_PER_SPRITE;
static constexpr size_t MAX_INDICES = MAX_SPRITES * INDICES_PER_SPRITE;
SpriteBatch();
~SpriteBatch() = default;
// 开始批次
void begin(const glm::mat4& viewProjection);
// 结束批次 - 返回需要绘制的批次列表
void end();
// 绘制单个精灵
void draw(const SpriteData& sprite);
// 批量绘制 - 一次性处理多个精灵
void drawBatch(const std::vector<SpriteData>& sprites);
// 立即绘制 - 不缓存,直接提交
void drawImmediate(const SpriteData& sprite);
// 获取当前批次数据
const std::vector<SpriteVertex>& getVertices() const { return vertices_; }
const std::vector<uint16_t>& getIndices() const { return indices_; }
size_t getSpriteCount() const { return spriteCount_; }
// 检查是否需要刷新
bool needsFlush() const { return spriteCount_ >= MAX_SPRITES; }
// 清空批次
void clear();
private:
// 三角函数查表
TrigLookup trigLookup_;
// 顶点数据 - 使用固定大小数组避免动态分配
std::vector<SpriteVertex> vertices_;
std::vector<uint16_t> indices_;
size_t spriteCount_;
// 变换矩阵
glm::mat4 viewProjection_;
glm::mat4 cachedVP_;
bool vpDirty_;
// 生成索引
void generateIndices();
// 生成顶点
void generateVertices(const SpriteData& sprite, size_t vertexOffset);
// 刷新批次
void flush();
};
} // namespace extra2d

View File

@ -1,111 +0,0 @@
#pragma once
#include <extra2d/core/color.h>
#include <extra2d/core/math_types.h>
#include <extra2d/core/types.h>
#include <glm/mat4x4.hpp>
namespace extra2d {
class ViewportAdapter;
// ============================================================================
// 2D 正交相机
// ============================================================================
class Camera {
public:
Camera();
Camera(float left, float right, float bottom, float top);
Camera(const Size &viewport);
~Camera() = default;
// ------------------------------------------------------------------------
// 位置和变换
// ------------------------------------------------------------------------
void setPos(const Vec2 &position);
void setPos(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();
// ------------------------------------------------------------------------
// 视口适配器
// ------------------------------------------------------------------------
/**
* @brief
* @param adapter
*/
void setViewportAdapter(ViewportAdapter *adapter);
/**
* @brief
*/
void applyViewportAdapter();
// ------------------------------------------------------------------------
// 快捷方法:看向某点
// ------------------------------------------------------------------------
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;
ViewportAdapter *viewportAdapter_ = nullptr;
mutable glm::mat4 viewMatrix_;
mutable glm::mat4 projMatrix_;
mutable glm::mat4 vpMatrix_;
mutable bool viewDirty_ = true;
mutable bool projDirty_ = true;
};
} // namespace extra2d

View File

@ -1,332 +0,0 @@
#pragma once
#include <extra2d/core/color.h>
#include <extra2d/core/math_types.h>
#include <extra2d/core/types.h>
#include <glm/mat4x4.hpp>
namespace extra2d {
// ============================================================================
// 视口适配模式枚举
// ============================================================================
enum class ViewportMode {
AspectRatio,
Stretch,
Center,
Custom
};
// ============================================================================
// 黑边位置枚举
// ============================================================================
enum class LetterboxPosition {
Center,
LeftTop,
RightTop,
LeftBottom,
RightBottom
};
// ============================================================================
// 视口配置结构体
// ============================================================================
struct ViewportConfig {
float logicWidth = 1920.0f;
float logicHeight = 1080.0f;
ViewportMode mode = ViewportMode::AspectRatio;
LetterboxPosition letterboxPosition = LetterboxPosition::Center;
Color letterboxColor = Colors::Black;
bool autoScaleInCenterMode = true;
float customScale = 1.0f;
Vec2 customOffset = Vec2::Zero();
Rect customViewport = Rect::Zero();
};
// ============================================================================
// 视口计算结果结构体
// ============================================================================
struct ViewportResult {
Rect viewport;
float scaleX = 1.0f;
float scaleY = 1.0f;
float uniformScale = 1.0f;
Vec2 offset;
bool hasLetterbox = false;
struct Letterbox {
Rect top;
Rect bottom;
Rect left;
Rect right;
} letterbox;
};
// ============================================================================
// 视口适配器类
// ============================================================================
class ViewportAdapter {
public:
ViewportAdapter();
ViewportAdapter(float logicWidth, float logicHeight);
~ViewportAdapter() = default;
// ------------------------------------------------------------------------
// 配置设置
// ------------------------------------------------------------------------
/**
* @brief
* @param config
*/
void setConfig(const ViewportConfig &config);
/**
* @brief
* @return
*/
const ViewportConfig &getConfig() const { return config_; }
/**
* @brief
* @param width
* @param height
*/
void setLogicSize(float width, float height);
/**
* @brief
* @param mode
*/
void setMode(ViewportMode mode);
/**
* @brief
* @param position
*/
void setLetterboxPosition(LetterboxPosition position);
/**
* @brief
* @param color
*/
void setLetterboxColor(const Color &color);
// ------------------------------------------------------------------------
// 更新和计算
// ------------------------------------------------------------------------
/**
* @brief
* @param screenWidth
* @param screenHeight
*/
void update(int screenWidth, int screenHeight);
/**
* @brief
* @return
*/
const ViewportResult &getResult() const { return result_; }
// ------------------------------------------------------------------------
// 坐标转换
// ------------------------------------------------------------------------
/**
* @brief
* @param screenPos
* @return
*/
Vec2 screenToLogic(const Vec2 &screenPos) const;
/**
* @brief
* @param logicPos
* @return
*/
Vec2 logicToScreen(const Vec2 &logicPos) const;
/**
* @brief
* @param x X坐标
* @param y Y坐标
* @return
*/
Vec2 screenToLogic(float x, float y) const;
/**
* @brief
* @param x X坐标
* @param y Y坐标
* @return
*/
Vec2 logicToScreen(float x, float y) const;
// ------------------------------------------------------------------------
// 矩阵获取
// ------------------------------------------------------------------------
/**
* @brief
* @return
*/
glm::mat4 getMatrix() const;
/**
* @brief
* @return
*/
glm::mat4 getInvMatrix() const;
// ------------------------------------------------------------------------
// 区域检测
// ------------------------------------------------------------------------
/**
* @brief
* @param screenPos
* @return true
*/
bool isInViewport(const Vec2 &screenPos) const;
/**
* @brief
* @param screenPos
* @return true
*/
bool isInLetterbox(const Vec2 &screenPos) const;
// ------------------------------------------------------------------------
// Getter 方法
// ------------------------------------------------------------------------
/**
* @brief
* @return
*/
float getLogicWidth() const { return config_.logicWidth; }
/**
* @brief
* @return
*/
float getLogicHeight() const { return config_.logicHeight; }
/**
* @brief
* @return
*/
Size getLogicSize() const {
return Size(config_.logicWidth, config_.logicHeight);
}
/**
* @brief
* @return
*/
int getScreenWidth() const { return screenWidth_; }
/**
* @brief
* @return
*/
int getScreenHeight() const { return screenHeight_; }
/**
* @brief
* @return
*/
Size getScreenSize() const {
return Size(static_cast<float>(screenWidth_),
static_cast<float>(screenHeight_));
}
/**
* @brief X方向缩放比例
* @return X方向缩放比例
*/
float getScaleX() const { return result_.scaleX; }
/**
* @brief Y方向缩放比例
* @return Y方向缩放比例
*/
float getScaleY() const { return result_.scaleY; }
/**
* @brief
* @return
*/
float getUniformScale() const { return result_.uniformScale; }
/**
* @brief
* @return
*/
Vec2 getOffset() const { return result_.offset; }
/**
* @brief
* @return
*/
Rect getViewport() const { return result_.viewport; }
/**
* @brief
* @return true
*/
bool hasLetterbox() const { return result_.hasLetterbox; }
/**
* @brief
* @return
*/
const ViewportResult::Letterbox &getLetterbox() const {
return result_.letterbox;
}
private:
/**
* @brief
*/
void calculateAspectRatio();
/**
* @brief
*/
void calculateStretch();
/**
* @brief
*/
void calculateCenter();
/**
* @brief
*/
void calculateCustom();
/**
* @brief
*/
void calculateLetterbox();
/**
* @brief
* @param extraWidth
* @param extraHeight
*/
void applyLetterboxPosition(float extraWidth, float extraHeight);
ViewportConfig config_;
ViewportResult result_;
int screenWidth_ = 0;
int screenHeight_ = 0;
mutable glm::mat4 viewportMatrix_;
mutable glm::mat4 inverseViewportMatrix_;
mutable bool matrixDirty_ = true;
};
} // namespace extra2d

View File

@ -1,223 +0,0 @@
#pragma once
#include <extra2d/core/color.h>
#include <extra2d/core/math_types.h>
#include <extra2d/core/types.h>
#include <extra2d/graphics/texture/texture.h>
#include <glm/mat4x4.hpp>
#include <cstdint>
#include <variant>
namespace extra2d {
// 前向声明
class Texture;
class FontAtlas;
/**
* @brief
*/
enum class RenderCommandType : uint8_t {
None = 0,
Sprite, // 精灵绘制
Line, // 线条绘制
Rect, // 矩形绘制
FilledRect, // 填充矩形
Circle, // 圆形绘制
FilledCircle, // 填充圆形
Triangle, // 三角形绘制
FilledTriangle, // 填充三角形
Polygon, // 多边形绘制
FilledPolygon, // 填充多边形
Text, // 文本绘制
Custom // 自定义绘制
};
/**
* @brief
*/
struct SpriteCommandData {
const Texture* texture;
Rect destRect;
Rect srcRect;
Color tint;
float rotation;
Vec2 anchor;
uint32_t sortKey; // 用于自动排序的键值
SpriteCommandData()
: texture(nullptr), destRect(), srcRect(), tint(Colors::White),
rotation(0.0f), anchor(0.0f, 0.0f), sortKey(0) {}
SpriteCommandData(const Texture* tex, const Rect& dest, const Rect& src,
const Color& t, float rot, const Vec2& anc, uint32_t key)
: texture(tex), destRect(dest), srcRect(src), tint(t),
rotation(rot), anchor(anc), sortKey(key) {}
};
/**
* @brief 线
*/
struct LineCommandData {
Vec2 start;
Vec2 end;
Color color;
float width;
LineCommandData() : start(), end(), color(Colors::White), width(1.0f) {}
LineCommandData(const Vec2& s, const Vec2& e, const Color& c, float w)
: start(s), end(e), color(c), width(w) {}
};
/**
* @brief
*/
struct RectCommandData {
Rect rect;
Color color;
float width;
bool filled;
RectCommandData() : rect(), color(Colors::White), width(1.0f), filled(false) {}
RectCommandData(const Rect& r, const Color& c, float w, bool f)
: rect(r), color(c), width(w), filled(f) {}
};
/**
* @brief
*/
struct CircleCommandData {
Vec2 center;
float radius;
Color color;
int segments;
float width;
bool filled;
CircleCommandData() : center(), radius(0.0f), color(Colors::White),
segments(32), width(1.0f), filled(false) {}
CircleCommandData(const Vec2& c, float r, const Color& col, int seg, float w, bool f)
: center(c), radius(r), color(col), segments(seg), width(w), filled(f) {}
};
/**
* @brief
*/
struct TriangleCommandData {
Vec2 p1, p2, p3;
Color color;
float width;
bool filled;
TriangleCommandData() : p1(), p2(), p3(), color(Colors::White),
width(1.0f), filled(false) {}
TriangleCommandData(const Vec2& a, const Vec2& b, const Vec2& c, const Color& col, float w, bool f)
: p1(a), p2(b), p3(c), color(col), width(w), filled(f) {}
};
/**
* @brief
*/
struct PolygonCommandData {
std::vector<Vec2> points;
Color color;
float width;
bool filled;
PolygonCommandData() : color(Colors::White), width(1.0f), filled(false) {}
PolygonCommandData(std::vector<Vec2> pts, const Color& col, float w, bool f)
: points(std::move(pts)), color(col), width(w), filled(f) {}
};
/**
* @brief
*/
struct TextCommandData {
const FontAtlas* font;
std::string text;
Vec2 position;
Color color;
TextCommandData() : font(nullptr), text(), position(), color(Colors::White) {}
};
/**
* @brief
* 使 variant
*/
struct RenderCommand {
RenderCommandType type;
uint32_t layer; // 渲染层级,用于排序
uint32_t order; // 提交顺序,保证同层级内稳定排序
glm::mat4 transform; // 变换矩阵
// 使用 variant 存储具体数据
std::variant<
SpriteCommandData,
LineCommandData,
RectCommandData,
CircleCommandData,
TriangleCommandData,
PolygonCommandData,
TextCommandData
> data;
RenderCommand() : type(RenderCommandType::None), layer(0), order(0),
transform(1.0f) {}
// 便捷构造函数
static RenderCommand makeSprite(const Texture* tex, const Rect& dest,
const Rect& src, const Color& tint,
float rot = 0.0f, const Vec2& anc = Vec2(0, 0),
uint32_t lyr = 0);
static RenderCommand makeLine(const Vec2& s, const Vec2& e, const Color& c,
float w = 1.0f, uint32_t lyr = 0);
static RenderCommand makeRect(const Rect& r, const Color& c,
float w = 1.0f, bool fill = false, uint32_t lyr = 0);
};
/**
* @brief
*
*/
class RenderCommandBuffer {
public:
static constexpr size_t INITIAL_CAPACITY = 1024;
static constexpr size_t MAX_CAPACITY = 65536;
RenderCommandBuffer();
~RenderCommandBuffer();
// 添加渲染命令
void addCommand(const RenderCommand& cmd);
void addCommand(RenderCommand&& cmd);
// 批量添加(预留空间后使用)
RenderCommand& emplaceCommand();
// 排序命令(按纹理、层级等)
void sortCommands();
// 清空缓冲区
void clear();
// 获取命令列表
const std::vector<RenderCommand>& getCommands() const { return commands_; }
std::vector<RenderCommand>& getCommands() { return commands_; }
// 统计
size_t size() const { return commands_.size(); }
bool empty() const { return commands_.empty(); }
size_t capacity() const { return commands_.capacity(); }
// 预分配空间
void reserve(size_t capacity);
private:
std::vector<RenderCommand> commands_;
uint32_t nextOrder_;
// 排序比较函数
static bool compareCommands(const RenderCommand& a, const RenderCommand& b);
};
} // namespace extra2d

View File

@ -1,73 +0,0 @@
#pragma once
#include <extra2d/core/module.h>
#include <extra2d/graphics/opengl/gl_renderer.h>
#include <extra2d/platform/window_module.h>
#include <functional>
#include <typeindex>
namespace extra2d {
/**
* @brief
*/
struct RenderCfg {
int targetFPS;
bool vsync;
int multisamples;
int priority;
RenderCfg() : targetFPS(60), vsync(true), multisamples(0), priority(10) {}
};
/**
* @brief
* OpenGL
*/
class RenderModule : public Module {
public:
/**
* @brief Lambda
* @param configFn
*/
explicit RenderModule(std::function<void(RenderCfg &)> configFn);
/**
* @brief
*/
~RenderModule() override;
bool init() override;
void shutdown() override;
bool ok() const override { return initialized_; }
const char *name() const override { return "render"; }
int priority() const override { return cfg_.priority; }
/**
* @brief
* @return
*/
std::vector<std::type_index> deps() const override {
return {std::type_index(typeid(WindowModule))};
}
/**
* @brief
* RenderModule OpenGL 线
* @return false
*/
bool allowParallelInit() const override { return false; }
/**
* @brief
* @return
*/
Renderer *renderer() const { return renderer_.get(); }
private:
RenderCfg cfg_;
UniquePtr<Renderer> renderer_;
bool initialized_ = false;
};
} // namespace extra2d

View File

@ -1,313 +0,0 @@
#pragma once
#include <extra2d/core/color.h>
#include <extra2d/core/types.h>
#include <extra2d/graphics/opengl/gl_texture.h>
#include <extra2d/graphics/texture/texture.h>
#include <mutex>
namespace extra2d {
// ============================================================================
// 渲染目标配置
// ============================================================================
struct RenderTargetConfig {
int width = 800; // 宽度
int height = 600; // 高度
PixelFormat colorFormat = PixelFormat::RGBA8; // 颜色格式
bool hasDepth = true; // 是否包含深度缓冲
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
*/
bool createFromTexture(Ptr<Texture> texture, bool hasDepth = false);
/**
* @brief
*/
void 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<float>(width_), static_cast<float>(height_));
}
PixelFormat getColorFormat() const { return colorFormat_; }
// ------------------------------------------------------------------------
// 绑定和解绑
// ------------------------------------------------------------------------
/**
* @brief
*/
void bind();
/**
* @brief
*/
void unbind();
/**
* @brief
*/
void clear(const Color &color = Colors::Transparent);
// ------------------------------------------------------------------------
// 纹理访问
// ------------------------------------------------------------------------
/**
* @brief
*/
Ptr<Texture> getColorTexture() const { return colorTexture_; }
/**
* @brief
*/
Ptr<Texture> 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
*/
void copyToScreen(int screenWidth, int screenHeight);
/**
* @brief
*/
bool saveToFile(const std::string &filepath);
// ------------------------------------------------------------------------
// 静态方法
// ------------------------------------------------------------------------
/**
* @brief
*/
static Ptr<RenderTarget> 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<Texture> colorTexture_; // 颜色纹理
Ptr<Texture> 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 &get();
/**
* @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<RenderTarget *> stack_;
mutable std::mutex mutex_;
};
// ============================================================================
// 渲染目标管理器 - 全局渲染目标管理
// ============================================================================
class RenderTargetMgr {
public:
/**
* @brief
*/
static RenderTargetMgr &get();
/**
* @brief
* @param width
* @param height
*/
bool init(int width, int height);
/**
* @brief
*/
void shutdown();
/**
* @brief
*/
Ptr<RenderTarget> 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:
RenderTargetMgr() = default;
~RenderTargetMgr() = default;
RenderTargetMgr(const RenderTargetMgr &) = delete;
RenderTargetMgr &operator=(const RenderTargetMgr &) = delete;
Ptr<RenderTarget> defaultRenderTarget_;
std::vector<Ptr<RenderTarget>> renderTargets_;
bool initialized_ = false;
};
// ============================================================================
// 便捷宏
// ============================================================================
#define E2D_RENDER_TARGET_STACK() ::extra2d::RenderTargetStack::get()
#define E2D_RENDER_TARGET_MGR() ::extra2d::RenderTargetMgr::get()
} // namespace extra2d

View File

@ -1,36 +0,0 @@
#pragma once
#include <atomic>
namespace extra2d {
// ============================================================================
// GPU 上下文状态管理器
// 用于跟踪 OpenGL/Vulkan 等 GPU 上下文的生命周期状态
// 确保在 GPU 资源析构时能安全地检查上下文是否有效
// ============================================================================
class GPUContext {
public:
/// 获取单例实例
static GPUContext& get();
/// 标记 GPU 上下文为有效(在初始化完成后调用)
void markValid();
/// 标记 GPU 上下文为无效(在销毁前调用)
void markInvalid();
/// 检查 GPU 上下文是否有效
bool isValid() const;
private:
GPUContext() = default;
~GPUContext() = default;
GPUContext(const GPUContext&) = delete;
GPUContext& operator=(const GPUContext&) = delete;
std::atomic<bool> valid_{false};
};
} // namespace extra2d

View File

@ -1,62 +0,0 @@
#pragma once
#include <cstddef>
#include <cstdint>
#include <mutex>
namespace extra2d {
// ============================================================================
// VRAM 管理器 - 跟踪显存使用情况
// ============================================================================
class VRAMMgr {
public:
static VRAMMgr& get();
// 纹理显存跟踪
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:
VRAMMgr();
~VRAMMgr() = default;
VRAMMgr(const VRAMMgr&) = delete;
VRAMMgr& operator=(const VRAMMgr&) = 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

View File

@ -1,83 +0,0 @@
#pragma once
#include <extra2d/graphics/resources/buffer.h>
#include <glad/glad.h>
#include <cstddef>
#include <cstdint>
namespace extra2d {
// ============================================================================
// OpenGL 缓冲区实现
// ============================================================================
class GLBuffer : public Buffer {
public:
/**
* @brief
*/
GLBuffer();
/**
* @brief
*/
~GLBuffer() override;
/**
* @brief
* @param desc
* @return true
*/
bool init(const BufferDesc& desc);
/**
* @brief
*/
void shutdown();
// Buffer 接口实现
void bind() override;
void unbind() override;
void setData(const void* data, size_t size) override;
void updateData(const void* data, size_t offset, size_t size) override;
void* map() override;
void unmap() override;
size_t getSize() const override { return size_; }
BufferType getType() const override { return type_; }
BufferUsage getUsage() const override { return usage_; }
bool isValid() const override { return bufferID_ != 0; }
uintptr_t getNativeHandle() const override { return static_cast<uintptr_t>(bufferID_); }
/**
* @brief OpenGL ID
* @return ID
*/
GLuint getBufferID() const { return bufferID_; }
/**
* @brief OpenGL
* @return
*/
GLenum getTarget() const { return target_; }
private:
GLuint bufferID_ = 0;
GLenum target_ = GL_ARRAY_BUFFER;
size_t size_ = 0;
BufferType type_ = BufferType::Vertex;
BufferUsage usage_ = BufferUsage::Static;
GLenum glUsage_ = GL_STATIC_DRAW;
bool mapped_ = false;
void* mappedPtr_ = nullptr;
/**
* @brief 使 OpenGL
*/
static GLenum convertUsage(BufferUsage usage);
/**
* @brief OpenGL
*/
static GLenum convertType(BufferType type);
};
} // namespace extra2d

View File

@ -1,138 +0,0 @@
#pragma once
#include <glad/glad.h>
#include <string>
namespace extra2d {
// ============================================================================
// OpenGL 版本信息
// ============================================================================
struct GLVersion {
int major = 0;
int minor = 0;
bool es = false; // 是否为 ES 版本
};
// ============================================================================
// OpenGL 上下文管理类
// ============================================================================
class GLContext {
public:
/**
* @brief GLContext
*/
static GLContext &get();
/**
* @brief OpenGL
* @return true
*/
bool init();
/**
* @brief OpenGL
*/
void shutdown();
/**
* @brief
* @return true
*/
bool isValid() const { return initialized_; }
/**
* @brief OpenGL
*/
const GLVersion &getVersion() const { return version_; }
/**
* @brief OpenGL
*/
std::string getVersionString() const;
/**
* @brief GPU
*/
std::string getVendor() const;
/**
* @brief GPU
*/
std::string getRenderer() const;
/**
* @brief
* @param extension
* @return true
*/
bool hasExtension(const std::string &extension) const;
/**
* @brief
*/
int getMaxTextureSize() const;
/**
* @brief
*/
int getMaxTextureUnits() const;
/**
* @brief
*/
int getMaxVertexAttribs() const;
/**
* @brief uniform
*/
int getMaxUniformBufferBindings() const;
/**
* @brief OpenGL ES
*/
bool isGLES() const { return version_.es; }
/**
* @brief VAO
*/
bool hasVAO() const;
/**
* @brief FBO
*/
bool hasFBO() const;
/**
* @brief Shader
*/
bool hasShader() const;
private:
GLContext() = default;
~GLContext() = default;
GLContext(const GLContext &) = delete;
GLContext &operator=(const GLContext &) = delete;
bool initialized_ = false;
GLVersion version_;
// 缓存的限制值
mutable int maxTextureSize_ = -1;
mutable int maxTextureUnits_ = -1;
mutable int maxVertexAttribs_ = -1;
mutable int maxUniformBufferBindings_ = -1;
/**
* @brief OpenGL
*/
void parseVersion();
/**
* @brief OpenGL
*/
bool loadExtensions();
};
} // namespace extra2d

View File

@ -1,85 +0,0 @@
#pragma once
#include <extra2d/graphics/opengl/gl_texture.h>
#include <extra2d/graphics/texture/font.h>
#include <stb/stb_rect_pack.h>
#include <stb/stb_truetype.h>
#include <unordered_map>
#include <vector>
namespace extra2d {
// ============================================================================
// OpenGL 字体图集实现 (使用 STB 库)
// 使用 stb_rect_pack 进行动态矩形打包,支持动态缓存字形
// ============================================================================
class GLFontAtlas : public FontAtlas {
public:
GLFontAtlas(const std::string &filepath, int fontSize, bool useSDF = false);
~GLFontAtlas() override;
// 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 lineHeight_; }
bool isSDF() const override { return useSDF_; }
Vec2 measureText(const std::string &text) override;
private:
// 字形数据内部结构
struct GlyphData {
float width;
float height;
float bearingX;
float bearingY;
float advance;
float u0, v0, u1, v1;
};
// 图集配置 - 增大尺寸以支持更多字符
static constexpr int ATLAS_WIDTH = 1024;
static constexpr int ATLAS_HEIGHT = 1024;
static constexpr int PADDING = 2; // 字形之间的间距
bool useSDF_;
int fontSize_;
Ptr<GLTexture> texture_;
std::unordered_map<char32_t, GlyphData> glyphs_;
float lineHeight_;
float ascent_;
float descent_;
float lineGap_;
// 字体数据
std::vector<unsigned char> fontData_;
stbtt_fontinfo fontInfo_;
float scale_;
// stb_rect_pack 上下文 - 持久化以支持增量打包
mutable stbrp_context packContext_;
mutable std::vector<stbrp_node> packNodes_;
// 预分配缓冲区,避免每次动态分配
mutable std::vector<uint8_t> glyphBitmapCache_;
mutable std::vector<uint8_t> glyphRgbaCache_;
// 初始化字体
bool initFont(const std::string &filepath);
// 创建空白图集纹理
void createAtlas();
// 缓存字形到图集
void cacheGlyph(char32_t codepoint);
// 更新图集纹理区域
void updateAtlas(int x, int y, int width, int height,
const std::vector<uint8_t> &data);
};
} // namespace extra2d

View File

@ -1,105 +0,0 @@
#pragma once
#include <extra2d/graphics/resources/framebuffer.h>
#include <glad/glad.h>
#include <array>
#include <cstdint>
namespace extra2d {
// ============================================================================
// OpenGL 帧缓冲实现
// ============================================================================
class GLFramebuffer : public Framebuffer {
public:
// 最大颜色附件数
static constexpr int MAX_COLOR_ATTACHMENTS = 8;
/**
* @brief
*/
GLFramebuffer();
/**
* @brief
*/
~GLFramebuffer() override;
/**
* @brief
* @param desc
* @return true
*/
bool init(const FramebufferDesc& desc);
/**
* @brief
*/
void shutdown();
// Framebuffer 接口实现
void bind() override;
void unbind() override;
void attachColorTexture(Ptr<Texture> texture, int attachment = 0) override;
void attachDepthTexture(Ptr<Texture> texture) override;
void attachDepthStencilTexture(Ptr<Texture> texture) override;
bool isComplete() override;
Ptr<Texture> getColorTexture(int attachment = 0) const override;
Ptr<Texture> getDepthTexture() const override;
int getWidth() const override { return width_; }
int getHeight() const override { return height_; }
Size getSize() const override { return Size(static_cast<float>(width_), static_cast<float>(height_)); }
bool isValid() const override { return fboID_ != 0; }
uintptr_t getNativeHandle() const override { return static_cast<uintptr_t>(fboID_); }
void clear(const Color& color, bool clearColor = true,
bool clearDepth = true, bool clearStencil = false) override;
void setViewport(int x, int y, int width, int height) override;
bool readPixels(int x, int y, int width, int height,
std::vector<uint8_t>& outData) override;
/**
* @brief OpenGL FBO ID
* @return FBO ID
*/
GLuint getFboID() const { return fboID_; }
/**
* @brief 便
* @param width
* @param height
* @param colorFormat
* @param depthFormat
* @return true
*/
bool createWithTextures(int width, int height,
PixelFormat colorFormat = PixelFormat::RGBA8,
PixelFormat depthFormat = PixelFormat::Depth24);
private:
GLuint fboID_ = 0;
int width_ = 0;
int height_ = 0;
int numColorAttachments_ = 1;
bool hasDepth_ = false;
bool hasStencil_ = false;
// 附件纹理
std::array<Ptr<Texture>, MAX_COLOR_ATTACHMENTS> colorTextures_;
Ptr<Texture> depthTexture_;
Ptr<Texture> depthStencilTexture_;
// 是否为内置纹理(需要自动清理)
bool hasInternalTextures_ = false;
/**
* @brief
*/
bool checkStatus();
/**
* @brief OpenGL
*/
static GLenum getColorAttachment(int index);
};
} // namespace extra2d

View File

@ -1,131 +0,0 @@
#pragma once
#include <extra2d/graphics/resources/pipeline.h>
#include <glad/glad.h>
#include <cstdint>
namespace extra2d {
// ============================================================================
// OpenGL 管线状态实现
// ============================================================================
class GLPipeline : public Pipeline {
public:
/**
* @brief
*/
GLPipeline();
/**
* @brief
*/
~GLPipeline() override;
/**
* @brief 线
* @param desc 线
* @return true
*/
bool init(const PipelineDesc& desc);
/**
* @brief 线
*/
void shutdown();
// Pipeline 接口实现
void bind() override;
void unbind() override;
void setBlendMode(BlendMode mode) override;
BlendMode getBlendMode() const override { return blendMode_; }
void setDepthTest(bool enabled) override;
void setDepthWrite(bool enabled) override;
void setDepthFunc(DepthFunc func) override;
void setCullMode(CullMode mode) override;
bool isValid() const override { return initialized_; }
uintptr_t getNativeHandle() const override { return 0; } // OpenGL 管线没有单一句柄
/**
* @brief
* @param x X坐标
* @param y Y坐标
* @param width
* @param height
*/
void setViewport(int x, int y, int width, int height);
/**
* @brief
* @param x X坐标
* @param y Y坐标
* @param width
* @param height
*/
void getViewport(int& x, int& y, int& width, int& height) const;
/**
* @brief
*/
void applyAllStates();
private:
bool initialized_ = false;
// 当前状态
BlendMode blendMode_ = BlendMode::Alpha;
bool blendEnabled_ = true;
bool depthTest_ = false;
bool depthWrite_ = false;
DepthFunc depthFunc_ = DepthFunc::Less;
CullMode cullMode_ = CullMode::None;
// 视口
int viewportX_ = 0;
int viewportY_ = 0;
int viewportWidth_ = 0;
int viewportHeight_ = 0;
// 状态缓存(避免冗余 GL 调用)
BlendMode cachedBlendMode_ = BlendMode::None;
bool cachedBlendEnabled_ = false;
bool cachedDepthTest_ = false;
bool cachedDepthWrite_ = false;
DepthFunc cachedDepthFunc_ = DepthFunc::Less;
CullMode cachedCullMode_ = CullMode::None;
int cachedViewportX_ = -1;
int cachedViewportY_ = -1;
int cachedViewportWidth_ = -1;
int cachedViewportHeight_ = -1;
/**
* @brief
*/
void applyBlendState();
/**
* @brief
*/
void applyDepthState();
/**
* @brief
*/
void applyCullState();
/**
* @brief OpenGL
*/
static void getBlendFactors(BlendMode mode, GLenum& srcFactor, GLenum& dstFactor);
/**
* @brief OpenGL
*/
static GLenum convertDepthFunc(DepthFunc func);
/**
* @brief OpenGL
*/
static GLenum convertCullMode(CullMode mode);
};
} // namespace extra2d

View File

@ -1,158 +0,0 @@
#pragma once
#include <extra2d/core/color.h>
#include <extra2d/core/math_types.h>
#include <extra2d/core/types.h>
#include <extra2d/graphics/opengl/gl_buffer.h>
#include <extra2d/graphics/opengl/gl_framebuffer.h>
#include <extra2d/graphics/opengl/gl_pipeline.h>
#include <extra2d/graphics/opengl/gl_sprite_batch.h>
#include <extra2d/graphics/resources/pipeline.h>
#include <extra2d/graphics/shader/shader_interface.h>
#include <array>
#include <glad/glad.h>
#include <glm/mat4x4.hpp>
#include <vector>
namespace extra2d {
class GLFWWindow;
class GLContext;
class GLFramebuffer;
class FontAtlas;
/**
* @brief
*/
struct RenderStats {
uint32_t drawCalls = 0;
uint32_t triangleCount = 0;
uint32_t textureBinds = 0;
uint32_t shaderBinds = 0;
};
/**
* @brief OpenGL
*/
class Renderer {
public:
Renderer();
~Renderer();
bool init(GLFWWindow *window);
void shutdown();
void beginFrame(const Color &clearColor);
void endFrame();
void setViewport(int x, int y, int width, int height);
void setVSync(bool enabled);
void setBlendMode(BlendMode mode);
void setViewProjection(const glm::mat4 &matrix);
void pushTransform(const glm::mat4 &transform);
void popTransform();
glm::mat4 getCurrentTransform() const;
Ptr<Texture> createTexture(int width, int height, const uint8_t *pixels,
int channels);
Ptr<Texture> loadTexture(const std::string &filepath);
void beginSpriteBatch();
void drawSprite(const Texture &texture, const Rect &destRect,
const Rect &srcRect, const Color &tint, float rotation,
const Vec2 &anchor);
void drawSprite(const Texture &texture, const Vec2 &position,
const Color &tint);
void endSpriteBatch();
void flush();
void drawLine(const Vec2 &start, const Vec2 &end, const Color &color,
float width = 1.0f);
void drawRect(const Rect &rect, const Color &color, float width = 1.0f);
void fillRect(const Rect &rect, const Color &color);
void drawCircle(const Vec2 &center, float radius, const Color &color,
int segments = 32, float width = 1.0f);
void fillCircle(const Vec2 &center, float radius, const Color &color,
int segments = 32);
void drawTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
const Color &color, float width = 1.0f);
void fillTriangle(const Vec2 &p1, const Vec2 &p2, const Vec2 &p3,
const Color &color);
void drawPolygon(const std::vector<Vec2> &points, const Color &color,
float width = 1.0f);
void fillPolygon(const std::vector<Vec2> &points, const Color &color);
Ptr<FontAtlas> createFontAtlas(const std::string &filepath, int fontSize,
bool useSDF = false);
void drawText(const FontAtlas &font, const std::string &text,
const Vec2 &position, const Color &color);
void drawText(const FontAtlas &font, const std::string &text, float x,
float y, const Color &color);
RenderStats getStats() const { return stats_; }
void resetStats();
Ptr<GLFramebuffer> createFramebuffer(const FramebufferDesc &desc);
void bindFramebuffer(GLFramebuffer *framebuffer);
void unbindFramebuffer();
Ptr<GLFramebuffer> getDefaultFramebuffer() const;
void clearFramebuffer(const Color &color, bool clearColor = true,
bool clearDepth = true, bool clearStencil = false);
private:
static constexpr size_t MAX_CIRCLE_SEGMENTS = 128;
static constexpr size_t MAX_SHAPE_VERTICES = 8192;
static constexpr size_t MAX_LINE_VERTICES = 16384;
struct ShapeVertex {
float x, y;
float r, g, b, a;
};
GLFWWindow *window_;
GLSpriteBatch spriteBatch_;
Ptr<IShader> shapeShader_;
Ptr<IShader> sdfFontShader_;
GLuint shapeVao_;
GLBuffer shapeBuffer_;
GLuint lineVao_;
GLBuffer lineBuffer_;
glm::mat4 viewProjection_;
std::vector<glm::mat4> transformStack_;
RenderStats stats_;
bool vsync_;
std::array<ShapeVertex, MAX_SHAPE_VERTICES> shapeVertexCache_;
size_t shapeVertexCount_ = 0;
GLenum currentShapeMode_ = GL_TRIANGLES;
std::array<ShapeVertex, MAX_LINE_VERTICES> lineVertexCache_;
size_t lineVertexCount_ = 0;
float currentLineWidth_ = 1.0f;
GLPipeline pipeline_;
bool batchActive_ = false;
bool autoBatchEnabled_ = true;
const Texture *currentBatchTexture_ = nullptr;
std::vector<SpriteData> pendingSprites_;
static constexpr size_t MAX_BATCH_SPRITES = 1000;
mutable Ptr<GLFramebuffer> defaultFramebuffer_;
GLFramebuffer *currentFramebuffer_ = nullptr;
void initShapeRendering();
void ensureBatchActive();
void submitPendingSprites();
void flushShapeBatch();
void flushLineBatch();
void addShapeVertex(float x, float y, const Color &color);
void addLineVertex(float x, float y, const Color &color);
void submitShapeBatch(GLenum mode);
};
} // namespace extra2d

View File

@ -1,196 +0,0 @@
#pragma once
#include <extra2d/core/color.h>
#include <extra2d/graphics/shader/shader_interface.h>
#include <glad/glad.h>
#include <unordered_map>
#include <vector>
namespace extra2d {
class GLShader : public IShader {
public:
/**
* @brief
*/
GLShader();
/**
* @brief
*/
~GLShader() override;
/**
* @brief Shader程序
*/
void bind() const override;
/**
* @brief Shader程序
*/
void unbind() const override;
/**
* @brief uniform变量
* @param name uniform变量名
* @param value
*/
void setBool(const std::string& name, bool value) override;
/**
* @brief uniform变量
* @param name uniform变量名
* @param value
*/
void setInt(const std::string& name, int value) override;
/**
* @brief uniform变量
* @param name uniform变量名
* @param value
*/
void setFloat(const std::string& name, float value) override;
/**
* @brief uniform变量
* @param name uniform变量名
* @param value
*/
void setVec2(const std::string& name, const glm::vec2& value) override;
/**
* @brief uniform变量
* @param name uniform变量名
* @param value
*/
void setVec3(const std::string& name, const glm::vec3& value) override;
/**
* @brief uniform变量
* @param name uniform变量名
* @param value
*/
void setVec4(const std::string& name, const glm::vec4& value) override;
/**
* @brief 4x4矩阵类型uniform变量
* @param name uniform变量名
* @param value 4x4矩阵值
*/
void setMat4(const std::string& name, const glm::mat4& value) override;
/**
* @brief uniform变量
* @param name uniform变量名
* @param color
*/
void setColor(const std::string& name, const Color& color) override;
/**
* @brief Shader是否有效
* @return truefalse
*/
bool isValid() const override { return programID_ != 0; }
/**
* @brief OpenGL程序ID
* @return OpenGL程序ID
*/
uint32_t getNativeHandle() const override { return programID_; }
/**
* @brief Shader名称
* @return Shader名称
*/
const std::string& getName() const override { return name_; }
/**
* @brief Shader名称
* @param name Shader名称
*/
void setName(const std::string& name) override { name_ = name; }
/**
* @brief Shader
* @param vertexSource
* @param fragmentSource
* @return truefalse
*/
bool compileFromSource(const char* vertexSource, const char* fragmentSource);
/**
* @brief Shader
* @param binary
* @return truefalse
*/
bool compileFromBinary(const std::vector<uint8_t>& binary);
/**
* @brief Shader二进制数据
* @param outBinary
* @return truefalse
*/
bool getBinary(std::vector<uint8_t>& outBinary);
/**
* @brief OpenGL程序ID
* @return OpenGL程序ID
*/
GLuint getProgramID() const { return programID_; }
private:
GLuint programID_ = 0;
std::string name_;
std::unordered_map<std::string, GLint> uniformCache_;
/**
* @brief
* @param type
* @param source
* @return ID0
*/
GLuint compileShader(GLenum type, const char* source);
/**
* @brief uniform位置
* @param name uniform变量名
* @return uniform位置
*/
GLint getUniformLocation(const std::string& name);
};
class GLShaderFactory : public IShaderFactory {
public:
/**
* @brief Shader
* @param name Shader名称
* @param vertSource
* @param fragSource
* @return Shader实例
*/
Ptr<IShader> createFromSource(
const std::string& name,
const std::string& vertSource,
const std::string& fragSource) override;
/**
* @brief Shader
* @param name Shader名称
* @param binary
* @return Shader实例
*/
Ptr<IShader> createFromBinary(
const std::string& name,
const std::vector<uint8_t>& binary) override;
/**
* @brief Shader的二进制数据
* @param shader Shader实例
* @param outBinary
* @return truefalse
*/
bool getShaderBinary(const IShader& shader,
std::vector<uint8_t>& outBinary) override;
};
} // namespace extra2d

View File

@ -1,88 +0,0 @@
#pragma once
#include <extra2d/graphics/batch/sprite_batch.h>
#include <extra2d/graphics/opengl/gl_buffer.h>
#include <extra2d/graphics/opengl/gl_texture.h>
#include <extra2d/graphics/shader/shader_interface.h>
#include <extra2d/graphics/shader/shader_manager.h>
#include <glad/glad.h>
#include <vector>
namespace extra2d {
// ============================================================================
// OpenGL 精灵批处理渲染器
// 使用 batch/sprite_batch 作为后端无关的批处理层
// ============================================================================
class GLSpriteBatch {
public:
GLSpriteBatch();
~GLSpriteBatch();
// 初始化/关闭
bool init();
void shutdown();
// 批处理生命周期
void begin(const glm::mat4 &viewProjection);
void end();
// 使用指定着色器开始批处理
void begin(const glm::mat4 &viewProjection, Ptr<IShader> shader);
// 绘制单个精灵
void draw(const Texture &texture, const SpriteData &data);
// 批量绘制(用于文本渲染优化)
void drawBatch(const Texture &texture,
const std::vector<SpriteData> &sprites);
// 获取绘制调用次数
uint32_t getDrawCallCount() const { return drawCallCount_; }
// 设置自定义着色器用于SDF字体等特殊渲染
void setShader(Ptr<IShader> shader);
// 获取当前着色器
Ptr<IShader> getShader() const { return shader_; }
// 设置额外的uniform值用于SDF字体等特殊渲染
void setExtraUniforms(const UniformValueMap &uniforms) {
extraUniforms_ = uniforms;
}
void clearExtraUniforms() { extraUniforms_.clear(); }
private:
// OpenGL 对象
GLuint vao_;
GLBuffer vbo_; // 顶点缓冲区(动态)
GLBuffer ebo_; // 索引缓冲区(静态)
// 后端无关的批处理层
SpriteBatch batch_;
// 批次管理
struct Batch {
const GLTexture *texture;
size_t startVertex;
size_t vertexCount;
};
std::vector<Batch> batches_;
const GLTexture *currentTexture_;
// 着色器和矩阵
Ptr<IShader> shader_;
uint32_t drawCallCount_;
glm::mat4 viewProjection_;
// 额外的uniform值用于SDF字体等特殊渲染
UniformValueMap extraUniforms_;
// 内部方法
void flush();
void submitBatch();
};
} // namespace extra2d

View File

@ -1,72 +0,0 @@
#pragma once
#include <extra2d/graphics/texture/texture.h>
#include <extra2d/graphics/texture/alpha_mask.h>
#include <glad/glad.h>
#include <memory>
#include <string>
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<float>(width_), static_cast<float>(height_)); }
int getChannels() const override { return channels_; }
PixelFormat getFormat() const override;
void* getNativeHandle() const override { return reinterpret_cast<void*>(static_cast<uintptr_t>(textureID_)); }
bool isValid() const override { return textureID_ != 0; }
void setFilter(bool linear) override;
void setWrap(bool repeat) override;
// 从参数创建纹理的工厂方法
static Ptr<Texture> 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<uint8_t> pixelData_;
std::unique_ptr<AlphaMask> alphaMask_;
void createTexture(const uint8_t* pixels);
// KTX 文件加载
bool loadKTX(const std::string& filepath);
// DDS 文件加载
bool loadDDS(const std::string& filepath);
};
} // namespace extra2d

View File

@ -1,111 +0,0 @@
#pragma once
#include <extra2d/core/types.h>
#include <cstddef>
#include <cstdint>
namespace extra2d {
// ============================================================================
// 缓冲区类型枚举
// ============================================================================
enum class BufferType {
Vertex, // 顶点缓冲
Index, // 索引缓冲
Uniform // 统一缓冲
};
// ============================================================================
// 缓冲区使用模式枚举
// ============================================================================
enum class BufferUsage {
Static, // 静态数据,很少更新
Dynamic, // 动态数据,频繁更新
Stream // 流式数据,每帧更新
};
// ============================================================================
// 缓冲区描述结构
// ============================================================================
struct BufferDesc {
BufferType type = BufferType::Vertex;
BufferUsage usage = BufferUsage::Static;
size_t size = 0; // 缓冲区大小(字节)
const void* initialData = nullptr; // 初始数据
};
// ============================================================================
// 缓冲区抽象接口 - 渲染后端无关
// ============================================================================
class Buffer {
public:
virtual ~Buffer() = default;
/**
* @brief
*/
virtual void bind() = 0;
/**
* @brief
*/
virtual void unbind() = 0;
/**
* @brief
* @param data
* @param size
*/
virtual void setData(const void* data, size_t size) = 0;
/**
* @brief
* @param data
* @param offset
* @param size
*/
virtual void updateData(const void* data, size_t offset, size_t size) = 0;
/**
* @brief
* @return nullptr
*/
virtual void* map() = 0;
/**
* @brief
*/
virtual void unmap() = 0;
/**
* @brief
* @return
*/
virtual size_t getSize() const = 0;
/**
* @brief
* @return
*/
virtual BufferType getType() const = 0;
/**
* @brief 使
* @return 使
*/
virtual BufferUsage getUsage() const = 0;
/**
* @brief
* @return true
*/
virtual bool isValid() const = 0;
/**
* @brief
* @return
*/
virtual uintptr_t getNativeHandle() const = 0;
};
} // namespace extra2d

View File

@ -1,131 +0,0 @@
#pragma once
#include <extra2d/core/color.h>
#include <extra2d/core/math_types.h>
#include <extra2d/core/types.h>
#include <extra2d/graphics/texture/texture.h>
#include <string>
#include <unordered_map>
namespace extra2d {
// ============================================================================
// 字形信息结构
// ============================================================================
struct Glyph {
float width = 0; // 字形宽度
float height = 0; // 字形高度
float bearingX = 0; // 水平偏移
float bearingY = 0; // 垂直偏移(从基线到字形顶部)
float advance = 0; // 水平步进
float u0 = 0, v0 = 0; // 纹理坐标左下角
float u1 = 0, v1 = 0; // 纹理坐标右上角
};
// ============================================================================
// 字体图集描述结构
// ============================================================================
struct FontAtlasDesc {
std::string filepath; // 字体文件路径
int fontSize = 16; // 字体大小
bool useSDF = false; // 是否使用SDF渲染
int atlasSize = 512; // 图集大小
int padding = 2; // 字形间距
};
// ============================================================================
// 字体图集抽象接口 - 渲染后端无关
// ============================================================================
class FontAtlas {
public:
virtual ~FontAtlas() = default;
/**
* @brief
* @param desc
* @return true
*/
virtual bool init(const FontAtlasDesc& desc) = 0;
/**
* @brief
*/
virtual void shutdown() = 0;
/**
* @brief
* @param codepoint Unicode
* @return nullptr
*/
virtual const Glyph* getGlyph(char32_t codepoint) const = 0;
/**
* @brief
* @return
*/
virtual Ptr<Texture> getTexture() const = 0;
/**
* @brief
* @return
*/
virtual int getFontSize() const = 0;
/**
* @brief
* @return
*/
virtual float getLineHeight() const = 0;
/**
* @brief 线
* @return
*/
virtual float getAscent() const = 0;
/**
* @brief 线
* @return
*/
virtual float getDescent() const = 0;
/**
* @brief
* @param text
* @return
*/
virtual float measureText(const std::string& text) const = 0;
/**
* @brief
* @param text
* @return
*/
virtual Size measureTextSize(const std::string& text) const = 0;
/**
* @brief 使SDF渲染
* @return 使SDF返回 true
*/
virtual bool isSDF() const = 0;
/**
* @brief
* @return true
*/
virtual bool isValid() const = 0;
/**
* @brief
* @param text
* @return
*/
virtual int preloadGlyphs(const std::string& text) = 0;
/**
* @brief
*/
virtual void clearCache() = 0;
};
} // namespace extra2d

View File

@ -1,140 +0,0 @@
#pragma once
#include <extra2d/core/color.h>
#include <extra2d/core/types.h>
#include <extra2d/graphics/texture/texture.h>
#include <cstdint>
namespace extra2d {
// ============================================================================
// 帧缓冲描述结构
// ============================================================================
struct FramebufferDesc {
int width = 0; // 帧缓冲宽度
int height = 0; // 帧缓冲高度
int colorAttachments = 1; // 颜色附件数量
bool hasDepth = false; // 是否有深度附件
bool hasStencil = false; // 是否有模板附件
bool multisample = false; // 是否多重采样
int samples = 4; // 采样数(多重采样时有效)
};
// ============================================================================
// 帧缓冲抽象接口 - 渲染后端无关
// ============================================================================
class Framebuffer {
public:
virtual ~Framebuffer() = default;
/**
* @brief
*/
virtual void bind() = 0;
/**
* @brief
*/
virtual void unbind() = 0;
/**
* @brief
* @param texture
* @param attachment 0-7
*/
virtual void attachColorTexture(Ptr<Texture> texture, int attachment = 0) = 0;
/**
* @brief
* @param texture
*/
virtual void attachDepthTexture(Ptr<Texture> texture) = 0;
/**
* @brief
* @param texture
*/
virtual void attachDepthStencilTexture(Ptr<Texture> texture) = 0;
/**
* @brief
* @return true
*/
virtual bool isComplete() = 0;
/**
* @brief
* @param attachment
* @return
*/
virtual Ptr<Texture> getColorTexture(int attachment = 0) const = 0;
/**
* @brief
* @return
*/
virtual Ptr<Texture> getDepthTexture() const = 0;
/**
* @brief
* @return
*/
virtual int getWidth() const = 0;
/**
* @brief
* @return
*/
virtual int getHeight() const = 0;
/**
* @brief
* @return
*/
virtual Size getSize() const = 0;
/**
* @brief
* @return true
*/
virtual bool isValid() const = 0;
/**
* @brief
* @return
*/
virtual uintptr_t getNativeHandle() const = 0;
/**
* @brief
* @param color
* @param clearColor
* @param clearDepth
* @param clearStencil
*/
virtual void clear(const Color& color, bool clearColor = true,
bool clearDepth = true, bool clearStencil = false) = 0;
/**
* @brief
* @param x X坐标
* @param y Y坐标
* @param width
* @param height
*/
virtual void setViewport(int x, int y, int width, int height) = 0;
/**
* @brief
* @param x X坐标
* @param y Y坐标
* @param width
* @param height
* @param outData
* @return true
*/
virtual bool readPixels(int x, int y, int width, int height,
std::vector<uint8_t>& outData) = 0;
};
} // namespace extra2d

View File

@ -1,162 +0,0 @@
#pragma once
#include <extra2d/core/color.h>
#include <extra2d/core/types.h>
#include <cstdint>
namespace extra2d {
// ============================================================================
// 混合模式枚举
// ============================================================================
enum class BlendMode {
None, // 不混合
Alpha, // 标准 Alpha 混合
Additive, // 加法混合
Multiply // 乘法混合
};
// ============================================================================
// 深度测试函数枚举
// ============================================================================
enum class DepthFunc {
Never, // 永不通过
Less, // 小于
Equal, // 等于
LessEqual, // 小于等于
Greater, // 大于
NotEqual, // 不等于
GreaterEqual,// 大于等于
Always // 总是通过
};
// ============================================================================
// 裁剪模式枚举
// ============================================================================
enum class CullMode {
None, // 不裁剪
Front, // 裁剪正面
Back, // 裁剪背面
Both // 裁剪双面
};
// ============================================================================
// 顶点属性格式枚举
// ============================================================================
enum class VertexFormat {
Float1, // 1个float
Float2, // 2个float
Float3, // 3个float
Float4, // 4个float
Byte4, // 4个byte
UByte4, // 4个ubyte
Short2, // 2个short
Short4 // 4个short
};
// ============================================================================
// 顶点属性描述
// ============================================================================
struct VertexAttribute {
uint32_t location = 0; // 属性位置
VertexFormat format = VertexFormat::Float3; // 数据格式
uint32_t offset = 0; // 在顶点结构中的偏移
uint32_t stride = 0; // 顶点结构大小
bool normalized = false; // 是否归一化
VertexAttribute() = default;
VertexAttribute(uint32_t loc, VertexFormat fmt, uint32_t off, uint32_t str, bool norm = false)
: location(loc), format(fmt), offset(off), stride(str), normalized(norm) {}
};
// ============================================================================
// 管线描述结构
// ============================================================================
struct PipelineDesc {
// 混合状态
BlendMode blendMode = BlendMode::Alpha;
bool blendEnabled = true;
// 深度状态
bool depthTest = false;
bool depthWrite = false;
DepthFunc depthFunc = DepthFunc::Less;
// 裁剪状态
CullMode cullMode = CullMode::None;
// 顶点布局
std::vector<VertexAttribute> vertexAttributes;
// 着色器(由后端特定实现设置)
void* vertexShader = nullptr;
void* fragmentShader = nullptr;
};
// ============================================================================
// 渲染管线抽象接口 - 渲染后端无关
// ============================================================================
class Pipeline {
public:
virtual ~Pipeline() = default;
/**
* @brief 线
*/
virtual void bind() = 0;
/**
* @brief 线
*/
virtual void unbind() = 0;
/**
* @brief
* @param mode
*/
virtual void setBlendMode(BlendMode mode) = 0;
/**
* @brief
* @return
*/
virtual BlendMode getBlendMode() const = 0;
/**
* @brief
* @param enabled
*/
virtual void setDepthTest(bool enabled) = 0;
/**
* @brief
* @param enabled
*/
virtual void setDepthWrite(bool enabled) = 0;
/**
* @brief
* @param func
*/
virtual void setDepthFunc(DepthFunc func) = 0;
/**
* @brief
* @param mode
*/
virtual void setCullMode(CullMode mode) = 0;
/**
* @brief 线
* @return true
*/
virtual bool isValid() const = 0;
/**
* @brief
* @return
*/
virtual uintptr_t getNativeHandle() const = 0;
};
} // namespace extra2d

View File

@ -1,134 +0,0 @@
#pragma once
#include <extra2d/core/color.h>
#include <extra2d/core/types.h>
#include <glm/mat4x4.hpp>
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <string>
#include <vector>
namespace extra2d {
// ============================================================================
// 着色器类型枚举
// ============================================================================
enum class ShaderType {
Vertex, // 顶点着色器
Fragment, // 片段着色器
Geometry, // 几何着色器
Compute // 计算着色器
};
// ============================================================================
// 着色器描述结构
// ============================================================================
struct ShaderDesc {
std::string name; // 着色器名称
std::string vertexSource; // 顶点着色器源码
std::string fragmentSource; // 片段着色器源码
std::string geometrySource; // 几何着色器源码(可选)
std::vector<uint8_t> binaryData; // 预编译二进制数据(可选)
};
// ============================================================================
// 着色器抽象接口 - 渲染后端无关
// ============================================================================
class Shader {
public:
virtual ~Shader() = default;
/**
* @brief
*/
virtual void bind() = 0;
/**
* @brief
*/
virtual void unbind() = 0;
/**
* @brief uniform
* @param name uniform
* @param value
*/
virtual void setBool(const std::string& name, bool value) = 0;
/**
* @brief uniform
* @param name uniform
* @param value
*/
virtual void setInt(const std::string& name, int value) = 0;
/**
* @brief uniform
* @param name uniform
* @param value
*/
virtual void setFloat(const std::string& name, float value) = 0;
/**
* @brief uniform
* @param name uniform
* @param value
*/
virtual void setVec2(const std::string& name, const glm::vec2& value) = 0;
/**
* @brief uniform
* @param name uniform
* @param value
*/
virtual void setVec3(const std::string& name, const glm::vec3& value) = 0;
/**
* @brief uniform
* @param name uniform
* @param value
*/
virtual void setVec4(const std::string& name, const glm::vec4& value) = 0;
/**
* @brief 4x4 uniform
* @param name uniform
* @param value 4x4
*/
virtual void setMat4(const std::string& name, const glm::mat4& value) = 0;
/**
* @brief uniform
* @param name uniform
* @param color
*/
virtual void setColor(const std::string& name, const Color& color) = 0;
/**
* @brief
* @param name uniform
* @param slot
*/
virtual void setTexture(const std::string& name, int slot) = 0;
/**
* @brief
* @return
*/
virtual const std::string& getName() const = 0;
/**
* @brief
* @return true
*/
virtual bool isValid() const = 0;
/**
* @brief
* @return
*/
virtual uintptr_t getNativeHandle() const = 0;
};
} // namespace extra2d

View File

@ -1,131 +0,0 @@
#pragma once
#include <extra2d/core/types.h>
#include <string>
#include <unordered_map>
#include <vector>
namespace extra2d {
// ============================================================================
// Shader缓存条目
// ============================================================================
struct ShaderCacheEntry {
std::string name;
std::string sourceHash;
uint64_t compileTime = 0;
std::vector<uint8_t> binary;
std::vector<std::string> dependencies;
};
// ============================================================================
// Shader缓存管理器
// ============================================================================
class ShaderCache {
public:
/**
* @brief
* @return
*/
static ShaderCache& getInstance();
/**
* @brief
* @param cacheDir
* @return truefalse
*/
bool init(const std::string& cacheDir);
/**
* @brief
*/
void shutdown();
/**
* @brief
* @param name Shader名称
* @param sourceHash
* @return truefalse
*/
bool hasValidCache(const std::string& name, const std::string& sourceHash);
/**
* @brief
* @param name Shader名称
* @return nullptr
*/
Ptr<ShaderCacheEntry> loadCache(const std::string& name);
/**
* @brief
* @param entry
* @return truefalse
*/
bool saveCache(const ShaderCacheEntry& entry);
/**
* @brief 使
* @param name Shader名称
*/
void invalidate(const std::string& name);
/**
* @brief
*/
void clearAll();
/**
* @brief
* @param vertSource
* @param fragSource
* @return
*/
static std::string computeHash(const std::string& vertSource,
const std::string& fragSource);
/**
* @brief
* @return truefalse
*/
bool isInitialized() const { return initialized_; }
private:
ShaderCache() = default;
~ShaderCache() = default;
ShaderCache(const ShaderCache&) = delete;
ShaderCache& operator=(const ShaderCache&) = delete;
std::string cacheDir_;
std::unordered_map<std::string, ShaderCacheEntry> cacheMap_;
bool initialized_ = false;
/**
* @brief
* @return truefalse
*/
bool loadCacheIndex();
/**
* @brief
* @return truefalse
*/
bool saveCacheIndex();
/**
* @brief
* @param name Shader名称
* @return
*/
std::string getCachePath(const std::string& name) const;
/**
* @brief
* @return truefalse
*/
bool ensureCacheDirectory();
};
// 便捷宏
#define E2D_SHADER_CACHE() ::extra2d::ShaderCache::getInstance()
} // namespace extra2d

View File

@ -1,131 +0,0 @@
#pragma once
#include <extra2d/core/types.h>
#include <functional>
#include <string>
#include <unordered_map>
#include <vector>
#ifdef _WIN32
#include <windows.h>
#endif
namespace extra2d {
// ============================================================================
// 文件变化事件
// ============================================================================
struct FileChangeEvent {
std::string filepath;
enum class Type { Created, Modified, Deleted, Renamed } type;
uint64_t timestamp = 0;
};
// ============================================================================
// 文件变化回调
// ============================================================================
using FileChangeCallback = std::function<void(const FileChangeEvent &)>;
// ============================================================================
// Shader热重载管理器
// ============================================================================
class ShaderHotReloader {
public:
/**
* @brief
* @return
*/
static ShaderHotReloader &getInstance();
/**
* @brief
* @return truefalse
*/
bool init();
/**
* @brief
*/
void shutdown();
/**
* @brief Shader文件监视
* @param shaderName Shader名称
* @param filePaths
* @param callback
*/
void watch(const std::string &shaderName,
const std::vector<std::string> &filePaths,
FileChangeCallback callback);
/**
* @brief
* @param shaderName Shader名称
*/
void unwatch(const std::string &shaderName);
/**
* @brief
*/
void update();
/**
* @brief /
* @param enabled
*/
void setEnabled(bool enabled);
/**
* @brief
* @return truefalse
*/
bool isEnabled() const { return enabled_; }
/**
* @brief
* @return truefalse
*/
bool isInitialized() const { return initialized_; }
private:
ShaderHotReloader() = default;
~ShaderHotReloader() = default;
ShaderHotReloader(const ShaderHotReloader &) = delete;
ShaderHotReloader &operator=(const ShaderHotReloader &) = delete;
bool enabled_ = false;
bool initialized_ = false;
struct WatchInfo {
std::vector<std::string> filePaths;
FileChangeCallback callback;
std::unordered_map<std::string, uint64_t> modifiedTimes;
};
std::unordered_map<std::string, WatchInfo> watchMap_;
#ifdef _WIN32
HANDLE watchHandle_ = nullptr;
std::vector<uint8_t> buffer_;
std::string watchDir_;
bool watching_ = false;
#endif
/**
* @brief
*/
void pollChanges();
/**
* @brief
* @param filepath
* @return
*/
static uint64_t getFileModifiedTime(const std::string &filepath);
};
// 便捷宏
#define E2D_SHADER_HOT_RELOADER() ::extra2d::ShaderHotReloader::getInstance()
} // namespace extra2d

View File

@ -1,152 +0,0 @@
#pragma once
#include <extra2d/core/types.h>
#include <glm/mat4x4.hpp>
#include <glm/vec2.hpp>
#include <glm/vec3.hpp>
#include <glm/vec4.hpp>
#include <string>
#include <vector>
namespace extra2d {
class Color;
// ============================================================================
// Shader抽象接口 - 渲染后端无关
// ============================================================================
class IShader {
public:
virtual ~IShader() = default;
/**
* @brief Shader程序
*/
virtual void bind() const = 0;
/**
* @brief Shader程序
*/
virtual void unbind() const = 0;
/**
* @brief uniform变量
* @param name uniform变量名
* @param value
*/
virtual void setBool(const std::string& name, bool value) = 0;
/**
* @brief uniform变量
* @param name uniform变量名
* @param value
*/
virtual void setInt(const std::string& name, int value) = 0;
/**
* @brief uniform变量
* @param name uniform变量名
* @param value
*/
virtual void setFloat(const std::string& name, float value) = 0;
/**
* @brief uniform变量
* @param name uniform变量名
* @param value
*/
virtual void setVec2(const std::string& name, const glm::vec2& value) = 0;
/**
* @brief uniform变量
* @param name uniform变量名
* @param value
*/
virtual void setVec3(const std::string& name, const glm::vec3& value) = 0;
/**
* @brief uniform变量
* @param name uniform变量名
* @param value
*/
virtual void setVec4(const std::string& name, const glm::vec4& value) = 0;
/**
* @brief 4x4矩阵类型uniform变量
* @param name uniform变量名
* @param value 4x4矩阵值
*/
virtual void setMat4(const std::string& name, const glm::mat4& value) = 0;
/**
* @brief uniform变量
* @param name uniform变量名
* @param color
*/
virtual void setColor(const std::string& name, const Color& color) = 0;
/**
* @brief Shader是否有效
* @return truefalse
*/
virtual bool isValid() const = 0;
/**
* @brief OpenGL程序ID
* @return
*/
virtual uint32_t getNativeHandle() const = 0;
/**
* @brief Shader名称
* @return Shader名称
*/
virtual const std::string& getName() const = 0;
/**
* @brief Shader名称
* @param name Shader名称
*/
virtual void setName(const std::string& name) = 0;
};
// ============================================================================
// Shader工厂接口 - 用于创建渲染后端特定的Shader实例
// ============================================================================
class IShaderFactory {
public:
virtual ~IShaderFactory() = default;
/**
* @brief Shader
* @param name Shader名称
* @param vertSource
* @param fragSource
* @return Shader实例
*/
virtual Ptr<IShader> createFromSource(
const std::string& name,
const std::string& vertSource,
const std::string& fragSource) = 0;
/**
* @brief Shader
* @param name Shader名称
* @param binary
* @return Shader实例
*/
virtual Ptr<IShader> createFromBinary(
const std::string& name,
const std::vector<uint8_t>& binary) = 0;
/**
* @brief Shader的二进制数据
* @param shader Shader实例
* @param outBinary
* @return truefalse
*/
virtual bool getShaderBinary(const IShader& shader,
std::vector<uint8_t>& outBinary) = 0;
};
} // namespace extra2d

View File

@ -1,294 +0,0 @@
#pragma once
#include <extra2d/core/types.h>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <string>
#include <unordered_map>
#include <vector>
namespace extra2d {
// ============================================================================
// Shader加载结果
// ============================================================================
struct ShaderLoadResult {
bool success = false;
std::string errorMessage;
std::string vertSource;
std::string fragSource;
std::vector<std::string> dependencies;
};
// ============================================================================
// Shader Uniform 定义
// ============================================================================
struct ShaderUniformDef {
std::string type;
std::string description;
float defaultValue = 0.0f; // 默认值用于float类型
float defaultVec2[2] = {0, 0}; // 默认值用于vec2类型
float defaultVec3[3] = {0, 0, 0}; // 默认值用于vec3类型
float defaultVec4[4] = {0, 0, 0, 0}; // 默认值用于vec4类型
float defaultMat4[16] = {
1, 0, 0, 0, 0, 1, 0, 0,
0, 0, 1, 0, 0, 0, 0, 1}; // 默认值用于mat4类型默认单位矩阵
int defaultInt = 0; // 默认值用于int类型
bool defaultBool = false; // 默认值用于bool类型
bool hasDefault = false; // 是否有默认值
};
// ============================================================================
// Shader Uniform 值类型
// ============================================================================
struct ShaderUniformValue {
enum class Type { Float, Int, Bool, Vec2, Vec3, Vec4, Mat4 } type;
union {
float f[16]; // 足够存储 mat4
int i;
bool b;
} data;
// 构造函数
ShaderUniformValue() : type(Type::Float) { data.f[0] = 0; }
ShaderUniformValue(float v) : type(Type::Float) { data.f[0] = v; }
ShaderUniformValue(int v) : type(Type::Int) { data.i = v; }
ShaderUniformValue(bool v) : type(Type::Bool) { data.b = v; }
ShaderUniformValue(const glm::vec2 &v) : type(Type::Vec2) {
data.f[0] = v.x;
data.f[1] = v.y;
}
ShaderUniformValue(const glm::vec3 &v) : type(Type::Vec3) {
data.f[0] = v.x;
data.f[1] = v.y;
data.f[2] = v.z;
}
ShaderUniformValue(const glm::vec4 &v) : type(Type::Vec4) {
data.f[0] = v.x;
data.f[1] = v.y;
data.f[2] = v.z;
data.f[3] = v.w;
}
ShaderUniformValue(const glm::mat4 &m) : type(Type::Mat4) {
const float *ptr = glm::value_ptr(m);
for (int i = 0; i < 16; ++i)
data.f[i] = ptr[i];
}
};
// Uniform 值映射表
using UniformValueMap = std::unordered_map<std::string, ShaderUniformValue>;
// ============================================================================
// Shader Sampler 定义
// ============================================================================
struct ShaderSamplerDef {
std::string type;
std::string description;
};
// ============================================================================
// Shader元数据
// ============================================================================
struct ShaderMetadata {
std::string name;
std::string category;
std::string version;
std::string description;
std::string vertPath;
std::string fragPath;
std::string combinedPath;
uint64_t lastModified = 0;
std::vector<std::string> defines;
std::unordered_map<std::string, std::string> uniforms;
std::unordered_map<std::string, ShaderUniformDef> uniformDefs;
std::unordered_map<std::string, ShaderSamplerDef> samplerDefs;
};
// ============================================================================
// ShaderLoader接口 - 支持多种文件格式加载
// ============================================================================
class IShaderLoader {
public:
virtual ~IShaderLoader() = default;
/**
* @brief Shader (.vert + .frag)
* @param name Shader名称
* @param vertPath
* @param fragPath
* @return
*/
virtual ShaderLoadResult
loadFromSeparateFiles(const std::string &name, const std::string &vertPath,
const std::string &fragPath) = 0;
/**
* @brief Shader (.shader)
* @param path Shader文件路径
* @return
*/
virtual ShaderLoadResult loadFromCombinedFile(const std::string &path) = 0;
/**
* @brief Shader
* @param vertSource
* @param fragSource
* @return
*/
virtual ShaderLoadResult loadFromSource(const std::string &vertSource,
const std::string &fragSource) = 0;
/**
* @brief Shader源码中的#include指令
* @param source
* @param baseDir
* @param outDependencies
* @return
*/
virtual std::string
processIncludes(const std::string &source, const std::string &baseDir,
std::vector<std::string> &outDependencies) = 0;
/**
* @brief
* @param source
* @param defines
* @return
*/
virtual std::string applyDefines(const std::string &source,
const std::vector<std::string> &defines) = 0;
/**
* @brief Shader元数据
* @param path Shader文件路径
* @return
*/
virtual ShaderMetadata getMetadata(const std::string &path) = 0;
};
// ============================================================================
// 默认ShaderLoader实现
// ============================================================================
class ShaderLoader : public IShaderLoader {
public:
ShaderLoader();
~ShaderLoader() override = default;
/**
* @brief Shader (.vert + .frag)
* @param name Shader名称
* @param vertPath
* @param fragPath
* @return
*/
ShaderLoadResult loadFromSeparateFiles(const std::string &name,
const std::string &vertPath,
const std::string &fragPath) override;
/**
* @brief Shader (.shader)
* @param path Shader文件路径
* @return
*/
ShaderLoadResult loadFromCombinedFile(const std::string &path) override;
/**
* @brief Shader
* @param vertSource
* @param fragSource
* @return
*/
ShaderLoadResult loadFromSource(const std::string &vertSource,
const std::string &fragSource) override;
/**
* @brief Shader源码中的#include指令
* @param source
* @param baseDir
* @param outDependencies
* @return
*/
std::string
processIncludes(const std::string &source, const std::string &baseDir,
std::vector<std::string> &outDependencies) override;
/**
* @brief
* @param source
* @param defines
* @return
*/
std::string applyDefines(const std::string &source,
const std::vector<std::string> &defines) override;
/**
* @brief Shader元数据
* @param path Shader文件路径
* @return
*/
ShaderMetadata getMetadata(const std::string &path) override;
/**
* @brief include搜索路径
* @param path
*/
void addIncludePath(const std::string &path);
/**
* @brief
* @param filepath
* @return
*/
static std::string readFile(const std::string &filepath);
/**
* @brief
* @param filepath
* @return
*/
static uint64_t getFileModifiedTime(const std::string &filepath);
/**
* @brief
* @param filepath
* @return truefalse
*/
static bool fileExists(const std::string &filepath);
private:
std::vector<std::string> includePaths_;
std::unordered_map<std::string, std::string> includeCache_;
/**
* @brief Shader文件
* @param content
* @param outVert
* @param outFrag
* @param outMetadata
* @return truefalse
*/
bool parseCombinedFile(const std::string &content, std::string &outVert,
std::string &outFrag, ShaderMetadata &outMetadata);
/**
* @brief JSON块
* @param jsonContent JSON内容
* @param outMetadata
* @return truefalse
*/
bool parseMetadata(const std::string &jsonContent,
ShaderMetadata &outMetadata);
/**
* @brief include文件路径
* @param includeName include文件名
* @param baseDir
* @return
*/
std::string findIncludeFile(const std::string &includeName,
const std::string &baseDir);
};
} // namespace extra2d

View File

@ -1,291 +0,0 @@
#pragma once
#include <extra2d/graphics/shader/shader_cache.h>
#include <extra2d/graphics/shader/shader_hot_reloader.h>
#include <extra2d/graphics/shader/shader_interface.h>
#include <extra2d/graphics/shader/shader_loader.h>
#include <functional>
#include <unordered_map>
namespace extra2d {
// ============================================================================
// Shader重载回调
// ============================================================================
using ShaderReloadCallback = std::function<void(Ptr<IShader> newShader)>;
// ============================================================================
// Shader管理器 - 统一入口
// ============================================================================
class ShaderManager {
public:
/**
* @brief
* @return Shader管理器实例引用
*/
static ShaderManager& getInstance();
// ------------------------------------------------------------------------
// 初始化和关闭
// ------------------------------------------------------------------------
/**
* @brief 使Shader系统
* 使romfs/sdmc/
* @param factory Shader工厂
* @param appName
* @return truefalse
*/
bool init(Ptr<IShaderFactory> factory, const std::string& appName = "extra2d");
/**
* @brief Shader系统
* @param shaderDir Shader文件目录
* @param cacheDir
* @param factory Shader工厂
* @return truefalse
*/
bool init(const std::string& shaderDir,
const std::string& cacheDir,
Ptr<IShaderFactory> factory);
/**
* @brief Shader系统
*/
void shutdown();
/**
* @brief
* @return truefalse
*/
bool isInitialized() const { return initialized_; }
/**
* @brief
* Switch平台使用romfs
* @return true
*/
bool isHotReloadSupported() const { return hotReloadSupported_; }
// ------------------------------------------------------------------------
// Shader加载
// ------------------------------------------------------------------------
/**
* @brief Shader
* @param name Shader名称
* @param vertPath
* @param fragPath
* @return Shader实例
*/
Ptr<IShader> loadFromFiles(const std::string& name,
const std::string& vertPath,
const std::string& fragPath);
/**
* @brief Shader
* @param path Shader文件路径
* @return Shader实例
*/
Ptr<IShader> loadFromCombinedFile(const std::string& path);
/**
* @brief Shader
* @param name Shader名称
* @param vertSource
* @param fragSource
* @return Shader实例
*/
Ptr<IShader> loadFromSource(const std::string& name,
const std::string& vertSource,
const std::string& fragSource);
/**
* @brief Shader
* @param name Shader名称
* @return Shader实例nullptr
*/
Ptr<IShader> get(const std::string& name) const;
/**
* @brief Shader是否存在
* @param name Shader名称
* @return truefalse
*/
bool has(const std::string& name) const;
/**
* @brief Shader
* @param name Shader名称
*/
void remove(const std::string& name);
/**
* @brief Shader
*/
void clear();
// ------------------------------------------------------------------------
// 热重载
// ------------------------------------------------------------------------
/**
* @brief
* @param name Shader名称
* @param callback
*/
void setReloadCallback(const std::string& name, ShaderReloadCallback callback);
/**
* @brief /
* @param enabled
*/
void setHotReloadEnabled(bool enabled);
/**
* @brief
* @return truefalse
*/
bool isHotReloadEnabled() const;
/**
* @brief
*/
void update();
/**
* @brief Shader
* @param name Shader名称
* @return truefalse
*/
bool reload(const std::string& name);
// ------------------------------------------------------------------------
// 内置Shader
// ------------------------------------------------------------------------
/**
* @brief Shader
* @param name Shader名称
* @return Shader实例
*/
Ptr<IShader> getBuiltin(const std::string& name);
/**
* @brief Shader
* @return truefalse
*/
bool loadBuiltinShaders();
/**
* @brief JSON元数据文件加载Shader
* @param jsonPath JSON元数据文件路径
* @param name Shader名称
* @return Shader实例
*/
Ptr<IShader> loadFromMetadata(const std::string& jsonPath, const std::string& name);
// ------------------------------------------------------------------------
// 工具方法
// ------------------------------------------------------------------------
/**
* @brief Shader目录
* @return Shader目录路径
*/
const std::string& getShaderDir() const { return shaderDir_; }
/**
* @brief ShaderLoader
* @return ShaderLoader引用
*/
ShaderLoader& getLoader() { return loader_; }
/**
* @brief Shader元数据
* @param name Shader名称
* @return Shader元数据
*/
ShaderMetadata getMetadata(const std::string& name) const;
/**
* @brief Shader的uniform定义
* @param name Shader名称
* @return uniform定义映射
*/
std::unordered_map<std::string, ShaderUniformDef> getUniformDefs(const std::string& name) const;
/**
* @brief Shader的sampler定义
* @param name Shader名称
* @return sampler定义映射
*/
std::unordered_map<std::string, ShaderSamplerDef> getSamplerDefs(const std::string& name) const;
/**
* @brief uniform值到着色器
* JSON元数据中的uniform定义uniform值
* @param shader
* @param shaderName Shader名称
* @param values uniform值映射表
*/
void applyUniforms(Ptr<IShader> shader, const std::string& shaderName, const UniformValueMap& values);
/**
* @brief sampler绑定到着色器
* JSON元数据中的sampler定义
* @param shader
* @param shaderName Shader名称
*/
void applySamplers(Ptr<IShader> shader, const std::string& shaderName);
private:
ShaderManager() = default;
~ShaderManager() = default;
ShaderManager(const ShaderManager&) = delete;
ShaderManager& operator=(const ShaderManager&) = delete;
std::string shaderDir_;
std::string cacheDir_;
Ptr<IShaderFactory> factory_;
ShaderLoader loader_;
struct ShaderInfo {
Ptr<IShader> shader;
ShaderMetadata metadata;
ShaderReloadCallback reloadCallback;
std::string vertSource;
std::string fragSource;
std::vector<std::string> filePaths;
};
std::unordered_map<std::string, ShaderInfo> shaders_;
bool initialized_ = false;
bool hotReloadEnabled_ = false;
bool hotReloadSupported_ = true;
/**
* @brief Shader
* @param name Shader名称
* @param sourceHash
* @param vertSource
* @param fragSource
* @return Shader实例
*/
Ptr<IShader> loadFromCache(const std::string& name,
const std::string& sourceHash,
const std::string& vertSource,
const std::string& fragSource);
/**
* @brief
* @param shaderName Shader名称
* @param event
*/
void handleFileChange(const std::string& shaderName, const FileChangeEvent& event);
};
// 便捷宏
#define E2D_SHADER_MANAGER() ::extra2d::ShaderManager::getInstance()
} // namespace extra2d

View File

@ -1,112 +0,0 @@
#pragma once
#include <extra2d/core/color.h>
#include <extra2d/core/types.h>
#include <extra2d/graphics/shader/shader_interface.h>
#include <glm/vec4.hpp>
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;
};
class ShaderPreset {
public:
/**
* @brief
* @param params
* @return
*/
static Ptr<IShader> Water(const WaterParams& params = {});
/**
* @brief
* @param params
* @return
*/
static Ptr<IShader> Outline(const OutlineParams& params = {});
/**
* @brief
* @param params
* @return
*/
static Ptr<IShader> Distortion(const DistortionParams& params = {});
/**
* @brief
* @param params
* @return
*/
static Ptr<IShader> Pixelate(const PixelateParams& params = {});
/**
* @brief
* @param params
* @return
*/
static Ptr<IShader> Invert(const InvertParams& params = {});
/**
* @brief
* @param params
* @return
*/
static Ptr<IShader> Grayscale(const GrayscaleParams& params = {});
/**
* @brief
* @param params
* @return
*/
static Ptr<IShader> Blur(const BlurParams& params = {});
/**
* @brief +
* @param grayParams
* @param outlineParams
* @return
*/
static Ptr<IShader> GrayscaleOutline(const GrayscaleParams& grayParams,
const OutlineParams& outlineParams);
/**
* @brief +
* @param pixParams
* @param invParams
* @return
*/
static Ptr<IShader> PixelateInvert(const PixelateParams& pixParams,
const InvertParams& invParams);
};
} // namespace extra2d

View File

@ -1,49 +0,0 @@
#pragma once
#include <extra2d/core/math_types.h>
#include <extra2d/core/types.h>
#include <vector>
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<float>(width_), static_cast<float>(height_));
}
/// 获取原始数据
const std::vector<uint8_t> &getData() const { return data_; }
/// 检查遮罩是否有效
bool isValid() const { return !data_.empty() && width_ > 0 && height_ > 0; }
private:
int width_ = 0;
int height_ = 0;
std::vector<uint8_t> data_; // Alpha值数组
};
} // namespace extra2d

View File

@ -1,50 +0,0 @@
#pragma once
#include <extra2d/core/color.h>
#include <extra2d/core/math_types.h>
#include <extra2d/core/types.h>
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

View File

@ -1,64 +0,0 @@
#pragma once
#include <extra2d/core/types.h>
#include <extra2d/core/math_types.h>
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

View File

@ -1,185 +0,0 @@
#pragma once
#include <extra2d/core/color.h>
#include <extra2d/core/math_types.h>
#include <extra2d/core/types.h>
#include <extra2d/graphics/opengl/gl_texture.h>
#include <extra2d/graphics/texture/texture.h>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
namespace extra2d {
// ============================================================================
// 纹理图集 - 自动将小纹理合并到大图集以减少 DrawCall
// ============================================================================
/**
* @brief
*/
struct AtlasEntry {
std::string name; // 原始纹理名称/路径
Rect uvRect; // 在图集中的 UV 坐标范围
Vec2 originalSize; // 原始纹理尺寸
uint32_t padding; // 边距(用于避免纹理 bleeding
AtlasEntry() : uvRect(), originalSize(), padding(2) {}
};
/**
* @brief
*
*/
class TextureAtlasPage {
public:
static constexpr int DEFAULT_SIZE = 2048;
static constexpr int MAX_SIZE = 4096;
static constexpr int MIN_TEXTURE_SIZE = 32; // 小于此大小的纹理才考虑合并
static constexpr int PADDING = 2; // 纹理间边距
TextureAtlasPage(int width = DEFAULT_SIZE, int height = DEFAULT_SIZE);
~TextureAtlasPage();
// 尝试添加纹理到图集
// 返回是否成功,如果成功则输出 uvRect
bool tryAddTexture(const std::string &name, int texWidth, int texHeight,
const uint8_t *pixels, Rect &outUvRect);
// 获取图集纹理
Ptr<Texture> getTexture() const { return texture_; }
// 获取条目
const AtlasEntry *getEntry(const std::string &name) const;
// 获取使用率
float getUsageRatio() const;
// 获取尺寸
int getWidth() const { return width_; }
int getHeight() const { return height_; }
// 是否已满
bool isFull() const { return isFull_; }
private:
int width_, height_;
Ptr<Texture> texture_;
std::unordered_map<std::string, AtlasEntry> entries_;
// 矩形打包数据
struct PackNode {
int x, y, width, height;
bool used;
std::unique_ptr<PackNode> left;
std::unique_ptr<PackNode> right;
PackNode(int x_, int y_, int w, int h)
: x(x_), y(y_), width(w), height(h), used(false) {}
};
std::unique_ptr<PackNode> root_;
bool isFull_;
int usedArea_;
// 递归插入
PackNode *insert(PackNode *node, int width, int height);
void writePixels(int x, int y, int w, int h, const uint8_t *pixels);
};
/**
* @brief
*
*/
class TextureAtlas {
public:
TextureAtlas();
~TextureAtlas();
// 初始化
void init(int pageSize = TextureAtlasPage::DEFAULT_SIZE);
// 添加纹理到图集
// 如果纹理太大,返回 false应该作为独立纹理加载
bool addTexture(const std::string &name, int width, int height,
const uint8_t *pixels);
// 查询纹理是否在图集中
bool contains(const std::string &name) const;
// 获取纹理在图集中的信息
// 返回图集纹理和 UV 坐标
const Texture *getAtlasTexture(const std::string &name) const;
Rect getUVRect(const std::string &name) const;
// 获取原始纹理尺寸
Vec2 getOriginalSize(const std::string &name) const;
// 获取所有图集页面
const std::vector<std::unique_ptr<TextureAtlasPage>> &getPages() const {
return pages_;
}
// 获取总使用率
float getTotalUsageRatio() const;
// 清空所有图集
void clear();
// 设置是否启用自动图集
void setEnabled(bool enabled) { enabled_ = enabled; }
bool isEnabled() const { return enabled_; }
// 设置纹理大小阈值(小于此大小的纹理才进入图集)
void setSizeThreshold(int threshold) { sizeThreshold_ = threshold; }
int getSizeThreshold() const { return sizeThreshold_; }
private:
std::vector<std::unique_ptr<TextureAtlasPage>> pages_;
std::unordered_map<std::string, TextureAtlasPage *> entryToPage_;
int pageSize_;
int sizeThreshold_;
bool enabled_;
bool initialized_;
};
/**
* @brief
*/
class TextureAtlasMgr {
public:
static TextureAtlasMgr &get();
// 获取主图集
TextureAtlas &getAtlas() { return atlas_; }
// 快捷方法
bool addTexture(const std::string &name, int width, int height,
const uint8_t *pixels) {
return atlas_.addTexture(name, width, height, pixels);
}
bool contains(const std::string &name) const { return atlas_.contains(name); }
const Texture *getAtlasTexture(const std::string &name) const {
return atlas_.getAtlasTexture(name);
}
Rect getUVRect(const std::string &name) const {
return atlas_.getUVRect(name);
}
private:
TextureAtlasMgr() = default;
~TextureAtlasMgr() = default;
TextureAtlasMgr(const TextureAtlasMgr &) = delete;
TextureAtlasMgr &operator=(const TextureAtlasMgr &) = delete;
TextureAtlas atlas_;
};
} // namespace extra2d

View File

@ -1,560 +0,0 @@
#pragma once
#include <atomic>
#include <chrono>
#include <cstdint>
#include <extra2d/core/math_types.h>
#include <extra2d/core/service_locator.h>
#include <extra2d/core/types.h>
#include <extra2d/graphics/texture/texture.h>
#include <extra2d/services/logger_service.h>
#include <functional>
#include <mutex>
#include <string>
#include <unordered_map>
namespace extra2d {
class Scene;
class Renderer;
// ============================================================================
// 纹理加载选项
// ============================================================================
struct TextureLoadOptions {
bool generateMipmaps = true; // 是否生成 mipmaps
bool sRGB = true; // 是否使用 sRGB 色彩空间
bool premultiplyAlpha = false; // 是否预乘 Alpha
PixelFormat preferredFormat = PixelFormat::RGBA8; // 首选像素格式
};
// ============================================================================
// 纹理键 - 用于唯一标识纹理缓存条目
// ============================================================================
struct TextureKey {
std::string path; // 纹理文件路径
Rect region; // 纹理区域(用于纹理图集)
/**
* @brief
*/
TextureKey() = default;
/**
* @brief
* @param p
*/
explicit TextureKey(const std::string &p) : path(p), region(Rect::Zero()) {}
/**
* @brief +
* @param p
* @param r
*/
TextureKey(const std::string &p, const Rect &r) : path(p), region(r) {}
/**
* @brief
* @param other TextureKey
* @return
*/
bool operator==(const TextureKey &other) const {
return path == other.path && region == other.region;
}
/**
* @brief
* @param other TextureKey
* @return
*/
bool operator!=(const TextureKey &other) const { return !(*this == other); }
};
// ============================================================================
// TextureKey 哈希函子
// ============================================================================
struct TextureKeyHash {
/**
* @brief TextureKey
* @param key
* @return
*/
size_t operator()(const TextureKey &key) const {
size_t h1 = std::hash<std::string>{}(key.path);
size_t h2 = std::hash<float>{}(key.region.origin.x);
size_t h3 = std::hash<float>{}(key.region.origin.y);
size_t h4 = std::hash<float>{}(key.region.size.width);
size_t h5 = std::hash<float>{}(key.region.size.height);
// 组合哈希值
size_t result = h1;
result ^= h2 + 0x9e3779b9 + (result << 6) + (result >> 2);
result ^= h3 + 0x9e3779b9 + (result << 6) + (result >> 2);
result ^= h4 + 0x9e3779b9 + (result << 6) + (result >> 2);
result ^= h5 + 0x9e3779b9 + (result << 6) + (result >> 2);
return result;
}
};
// ============================================================================
// 纹理池条目
// ============================================================================
struct TexturePoolEntry {
Ptr<Texture> texture; // 纹理对象
mutable std::atomic<uint32_t> refCount; // 引用计数
TextureKey key; // 纹理键
size_t memorySize; // 内存占用(字节)
mutable uint64_t lastAccessTime; // 最后访问时间戳
/**
* @brief
*/
TexturePoolEntry()
: texture(nullptr), refCount(0), key(), memorySize(0), lastAccessTime(0) {
}
/**
* @brief
* @param tex
* @param k
* @param memSize
*/
TexturePoolEntry(Ptr<Texture> tex, const TextureKey &k, size_t memSize)
: texture(tex), refCount(1), key(k), memorySize(memSize),
lastAccessTime(getCurrentTime()) {}
/**
* @brief
* @param other
*/
TexturePoolEntry(TexturePoolEntry &&other) noexcept
: texture(std::move(other.texture)),
refCount(other.refCount.load(std::memory_order_relaxed)),
key(std::move(other.key)), memorySize(other.memorySize),
lastAccessTime(other.lastAccessTime) {}
/**
* @brief
* @param other
* @return
*/
TexturePoolEntry &operator=(TexturePoolEntry &&other) noexcept {
if (this != &other) {
texture = std::move(other.texture);
refCount.store(other.refCount.load(std::memory_order_relaxed),
std::memory_order_relaxed);
key = std::move(other.key);
memorySize = other.memorySize;
lastAccessTime = other.lastAccessTime;
}
return *this;
}
// 禁止拷贝
TexturePoolEntry(const TexturePoolEntry &) = delete;
TexturePoolEntry &operator=(const TexturePoolEntry &) = delete;
/**
* @brief 访
*/
void touch() const { lastAccessTime = getCurrentTime(); }
/**
* @brief
* @return
*/
static uint64_t getCurrentTime() {
auto now = std::chrono::steady_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
now.time_since_epoch());
return static_cast<uint64_t>(duration.count());
}
};
// ============================================================================
// 纹理引用智能指针 - 自动管理纹理池引用计数
// ============================================================================
class TextureRef {
public:
/**
* @brief
*/
TextureRef() : texture_(nullptr), entry_(nullptr), mutex_(nullptr) {}
/**
* @brief
* @param texture
* @param entry
* @param mutex
*/
TextureRef(Ptr<Texture> texture, TexturePoolEntry *entry, std::mutex *mutex)
: texture_(texture), entry_(entry), mutex_(mutex) {}
/**
* @brief
* @param texture
* @return
*/
static TextureRef fromTexture(Ptr<Texture> texture) {
return TextureRef(texture, nullptr, nullptr);
}
/**
* @brief
* @param other TextureRef
*/
TextureRef(const TextureRef &other)
: texture_(other.texture_), entry_(other.entry_), mutex_(other.mutex_) {
if (entry_ && entry_->refCount.load(std::memory_order_relaxed) > 0) {
entry_->refCount.fetch_add(1, std::memory_order_relaxed);
}
}
/**
* @brief
* @param other TextureRef
*/
TextureRef(TextureRef &&other) noexcept
: texture_(std::move(other.texture_)), entry_(other.entry_),
mutex_(other.mutex_) {
other.entry_ = nullptr;
other.mutex_ = nullptr;
}
/**
* @brief
*/
~TextureRef() { reset(); }
/**
* @brief
* @param other TextureRef
* @return
*/
TextureRef &operator=(const TextureRef &other) {
if (this != &other) {
reset();
texture_ = other.texture_;
entry_ = other.entry_;
mutex_ = other.mutex_;
if (entry_ && entry_->refCount.load(std::memory_order_relaxed) > 0) {
entry_->refCount.fetch_add(1, std::memory_order_relaxed);
}
}
return *this;
}
/**
* @brief
* @param other TextureRef
* @return
*/
TextureRef &operator=(TextureRef &&other) noexcept {
if (this != &other) {
reset();
texture_ = std::move(other.texture_);
entry_ = other.entry_;
mutex_ = other.mutex_;
other.entry_ = nullptr;
other.mutex_ = nullptr;
}
return *this;
}
/**
* @brief
*/
void reset() {
if (entry_ && mutex_) {
std::lock_guard<std::mutex> lock(*mutex_);
if (entry_->refCount.load(std::memory_order_relaxed) > 0) {
entry_->refCount.fetch_sub(1, std::memory_order_relaxed);
}
}
texture_.reset();
entry_ = nullptr;
mutex_ = nullptr;
}
/**
* @brief
* @return
*/
Texture *get() const { return texture_.get(); }
/**
* @brief
* @return
*/
Ptr<Texture> getPtr() const { return texture_; }
/**
* @brief
* @return
*/
bool valid() const { return texture_ != nullptr; }
/**
* @brief
*/
explicit operator bool() const { return valid(); }
/**
* @brief
*/
Texture *operator->() const { return texture_.get(); }
/**
* @brief
*/
Texture &operator*() const { return *texture_; }
private:
Ptr<Texture> texture_;
TexturePoolEntry *entry_;
std::mutex *mutex_;
};
// ============================================================================
// 纹理池 - 纹理缓存和内存管理系统
// 特性:
// - 纹理缓存和复用
// - 引用计数管理
// - 内存使用限制
// - LRU 淘汰策略
// - 线程安全
// ============================================================================
class TexturePool {
public:
// ========================================================================
// 统计信息
// ========================================================================
struct Stats {
size_t textureCount = 0; // 纹理数量
size_t memoryUsage = 0; // 内存使用量(字节)
size_t maxMemoryUsage = 0; // 最大内存使用量
size_t cacheHits = 0; // 缓存命中次数
size_t cacheMisses = 0; // 缓存未命中次数
size_t evictionCount = 0; // 淘汰次数
};
// ========================================================================
// 构造和析构
// ========================================================================
/**
* @brief
*/
TexturePool();
/**
* @brief
* @param scene
* @param maxMemoryUsage 使0
*/
explicit TexturePool(Scene *scene, size_t maxMemoryUsage = 0);
/**
* @brief
*/
~TexturePool();
// 禁止拷贝
TexturePool(const TexturePool &) = delete;
TexturePool &operator=(const TexturePool &) = delete;
/**
* @brief
* @param scene
* @param maxMemoryUsage 使0
*/
void init(Scene *scene, size_t maxMemoryUsage = 0);
// ========================================================================
// 纹理加载
// ========================================================================
/**
* @brief
* @param path
* @param options
* @return
*/
TextureRef load(const std::string &path,
const TextureLoadOptions &options = TextureLoadOptions());
/**
* @brief
* @param path
* @param region
* @param options
* @return
*/
TextureRef load(const std::string &path, const Rect &region,
const TextureLoadOptions &options = TextureLoadOptions());
/**
* @brief
* @param data
* @param width
* @param height
* @param channels
* @param key
* @return
*/
TextureRef loadFromMemory(const uint8_t *data, int width, int height,
int channels, const std::string &key);
/**
* @brief
* @param path
* @param options
* @return
*/
TextureRef
getOrLoad(const std::string &path,
const TextureLoadOptions &options = TextureLoadOptions());
/**
* @brief
* @param path
* @param region
* @param options
* @return
*/
TextureRef
getOrLoad(const std::string &path, const Rect &region,
const TextureLoadOptions &options = TextureLoadOptions());
// ========================================================================
// 引用计数管理
// ========================================================================
/**
* @brief
* @param key
* @return
*/
bool addRef(const TextureKey &key);
/**
* @brief
* @param key
* @return
*/
uint32_t release(const TextureKey &key);
/**
* @brief
* @param key
* @return
*/
uint32_t getRefCount(const TextureKey &key) const;
// ========================================================================
// 缓存管理
// ========================================================================
/**
* @brief
* @param key
* @return
*/
bool isCached(const TextureKey &key) const;
/**
* @brief
* @param key
* @return
*/
bool removeFromCache(const TextureKey &key);
/**
* @brief 0
* @return
*/
size_t collectGarbage();
/**
* @brief
*/
void clear();
// ========================================================================
// 内存管理
// ========================================================================
/**
* @brief 使
* @return 使
*/
size_t getMemoryUsage() const;
/**
* @brief 使
* @param maxMemory 使0
*/
void setMaxMemoryUsage(size_t maxMemory);
/**
* @brief 使
* @return 使
*/
size_t getMaxMemoryUsage() const { return maxMemoryUsage_; }
/**
* @brief LRU
* @param targetMemory 使
* @return
*/
size_t evictLRU(size_t targetMemory = 0);
// ========================================================================
// 统计信息
// ========================================================================
/**
* @brief
* @return
*/
Stats getStats() const;
/**
* @brief
*/
void resetStats();
private:
/**
* @brief
* @param texture
* @return
*/
static size_t calculateTextureMemory(const Texture *texture);
/**
* @brief
* @return
*/
bool needsEviction() const;
/**
* @brief
*/
void tryAutoEvict();
Scene *scene_; // 场景指针
mutable std::mutex mutex_; // 互斥锁
std::unordered_map<TextureKey, TexturePoolEntry, TextureKeyHash>
cache_; // 纹理缓存
size_t maxMemoryUsage_; // 最大内存使用量
size_t currentMemoryUsage_; // 当前内存使用量
// 统计信息
mutable std::atomic<size_t> cacheHits_;
mutable std::atomic<size_t> cacheMisses_;
mutable std::atomic<size_t> evictionCount_;
};
} // namespace extra2d

View File

@ -1,238 +0,0 @@
#pragma once
#include <extra2d/core/color.h>
#include <extra2d/core/math_types.h>
#include <extra2d/core/types.h>
#include <extra2d/event/event_dispatcher.h>
#include <extra2d/graphics/opengl/gl_renderer.h>
#include <memory>
#include <string>
#include <vector>
namespace extra2d {
class Scene;
class Renderer;
struct RenderCommand;
// ============================================================================
// 节点基类 - 场景图的基础
// ============================================================================
class Node : public std::enable_shared_from_this<Node> {
public:
Node();
virtual ~Node();
// ------------------------------------------------------------------------
// 层级管理
// ------------------------------------------------------------------------
void addChild(Ptr<Node> child);
/**
* @brief
* @param children
*/
void addChildren(std::vector<Ptr<Node>> &&children);
void removeChild(Ptr<Node> child);
void removeChildByName(const std::string &name);
void detach();
void clearChildren();
Ptr<Node> getParent() const { return parent_.lock(); }
const std::vector<Ptr<Node>> &getChildren() const { return children_; }
Ptr<Node> findChild(const std::string &name) const;
Ptr<Node> findChildByTag(int tag) const;
// ------------------------------------------------------------------------
// 变换属性
// ------------------------------------------------------------------------
void setPos(const Vec2 &pos);
void setPos(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_; }
/**
* @brief
* @param color RGB颜色
*/
void setColor(const Color3B &color);
Color3B getColor() const { return color_; }
/**
* @brief X轴翻转
*/
void setFlipX(bool flipX);
bool isFlipX() const { return flipX_; }
/**
* @brief Y轴翻转
*/
void setFlipY(bool flipY);
bool isFlipY() const { return flipY_; }
void setZOrder(int zOrder);
int getZOrder() const { return zOrder_; }
// ------------------------------------------------------------------------
// 世界变换
// ------------------------------------------------------------------------
Vec2 toWorld(const Vec2 &localPos) const;
Vec2 toLocal(const Vec2 &worldPos) const;
glm::mat4 getLocalTransform() const;
glm::mat4 getWorldTransform() const;
/**
* @brief
*/
void markTransformDirty();
/**
* @brief
*
*/
void batchTransforms();
/**
* @brief
*/
bool isTransformDirty() const { return transformDirty_; }
bool isWorldTransformDirty() const { return worldTransformDirty_; }
// ------------------------------------------------------------------------
// 名称和标签
// ------------------------------------------------------------------------
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(Renderer &renderer);
virtual void onAttachToScene(Scene *scene);
virtual void onDetachFromScene();
// ------------------------------------------------------------------------
// 边界框
// ------------------------------------------------------------------------
virtual Rect getBounds() const;
// ------------------------------------------------------------------------
// 事件系统
// ------------------------------------------------------------------------
EventDispatcher &getEventDispatcher() { return eventDispatcher_; }
// ------------------------------------------------------------------------
// 内部方法
// ------------------------------------------------------------------------
void update(float dt);
void render(Renderer &renderer);
void sortChildren();
bool isRunning() const { return running_; }
Scene *getScene() const { return scene_; }
// 多线程渲染命令收集
virtual void collectRenderCommands(std::vector<RenderCommand> &commands,
int parentZOrder = 0);
protected:
// 子类重写
virtual void onDraw(Renderer &renderer) {}
virtual void onUpdateNode(float dt) {}
virtual void generateRenderCommand(std::vector<RenderCommand> &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<Ptr<Node>> children_; // 24 bytes
// 3. 子节点索引(加速查找)
std::unordered_map<std::string, WeakPtr<Node>> nameIndex_; // 56 bytes
std::unordered_map<int, WeakPtr<Node>> tagIndex_; // 56 bytes
// 4. 事件分发器
EventDispatcher eventDispatcher_; // 大小取决于实现
// 5. 父节点引用
WeakPtr<Node> 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. 浮点属性
float rotation_ = 0.0f; // 4 bytes
float opacity_ = 1.0f; // 4 bytes
// 10. 颜色属性
Color3B color_ = Color3B(255, 255, 255); // 3 bytes
// 11. 整数属性
int zOrder_ = 0; // 4 bytes
int tag_ = -1; // 4 bytes
// 12. 布尔属性
bool flipX_ = false; // 1 byte
bool flipY_ = false; // 1 byte
// 13. 场景指针
Scene *scene_ = nullptr; // 8 bytes
// 14. 布尔标志(打包在一起)
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
};
} // namespace extra2d

View File

@ -1,89 +0,0 @@
#pragma once
#include <extra2d/core/color.h>
#include <extra2d/graphics/camera/camera.h>
#include <extra2d/scene/node.h>
#include <vector>
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> camera);
Ptr<Camera> 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(Renderer &renderer);
virtual void renderContent(Renderer &renderer);
void updateScene(float dt);
void collectRenderCommands(std::vector<RenderCommand> &commands,
int parentZOrder = 0) override;
// ------------------------------------------------------------------------
// 静态创建方法
// ------------------------------------------------------------------------
static Ptr<Scene> create();
protected:
void onEnter() override;
void onExit() override;
// 过渡场景生命周期回调(供 TransitionScene 使用)
virtual void onExitTransitionDidStart() {}
virtual void onEnterTransitionDidFinish() {}
friend class SceneManager;
friend class TransitionScene;
private:
Color backgroundColor_ = Colors::Black;
Size viewportSize_ = Size::Zero();
Ptr<Camera> camera_;
Ptr<Camera> defaultCamera_;
bool paused_ = false;
};
} // namespace extra2d

View File

@ -1,189 +0,0 @@
#pragma once
#include <extra2d/core/types.h>
#include <extra2d/scene/scene.h>
#include <functional>
#include <stack>
#include <string>
#include <unordered_map>
#include <vector>
namespace extra2d {
struct RenderCommand;
class TransitionScene;
enum class TransitionType;
/**
* @brief -
*/
class SceneManager {
public:
using TransitionCallback = std::function<void()>;
static SceneManager &get();
// ------------------------------------------------------------------------
// 基本场景操作(无过渡效果)
// ------------------------------------------------------------------------
void runWithScene(Ptr<Scene> scene);
void replaceScene(Ptr<Scene> scene);
void pushScene(Ptr<Scene> scene);
void popScene();
void popToRootScene();
void popToScene(const std::string &name);
// ------------------------------------------------------------------------
// 带过渡效果的场景操作
// ------------------------------------------------------------------------
/**
* @brief
* @param scene
* @param transition
* @param duration
*/
void replaceScene(Ptr<Scene> scene, TransitionType transition,
float duration = 0.5f);
/**
* @brief
* @param scene
* @param transition
* @param duration
*/
void pushScene(Ptr<Scene> scene, TransitionType transition,
float duration = 0.5f);
/**
* @brief
* @param transition
* @param duration
*/
void popScene(TransitionType transition, float duration = 0.5f);
/**
* @brief
* @param transition
* @param duration
*/
void popToRootScene(TransitionType transition, float duration = 0.5f);
/**
* @brief
* @param name
* @param transition
* @param duration
*/
void popToScene(const std::string &name, TransitionType transition,
float duration = 0.5f);
/**
* @brief 使
* @param scene
* @param transitionScene
*/
void enterScene(Ptr<Scene> scene, Ptr<TransitionScene> transitionScene);
// ------------------------------------------------------------------------
// 场景查询
// ------------------------------------------------------------------------
Ptr<Scene> getCurrentScene() const;
Ptr<Scene> getPreviousScene() const;
Ptr<Scene> getRootScene() const;
Ptr<Scene> 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(Renderer &renderer);
void collectRenderCommands(std::vector<RenderCommand> &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;
void enterScene(Ptr<Scene> scene);
private:
/**
* @brief
* @param from
* @param to
* @param type
* @param duration
* @param stackAction
*/
void startTransition(Ptr<Scene> from, Ptr<Scene> to, TransitionType type,
float duration, std::function<void()> stackAction);
/**
* @brief
* @param type
* @param duration
* @param inScene
* @return
*/
Ptr<TransitionScene> createTransitionScene(TransitionType type,
float duration,
Ptr<Scene> inScene);
/**
* @brief
*/
void finishTransition();
void doSceneSwitch();
void dispatchPointerEvents(Scene &scene);
void setupEventListeners();
std::stack<Ptr<Scene>> sceneStack_;
std::unordered_map<std::string, Ptr<Scene>> namedScenes_;
bool isTransitioning_ = false;
TransitionCallback transitionCallback_;
Ptr<Scene> nextScene_;
bool sendCleanupToScene_ = false;
Ptr<TransitionScene> activeTransitionScene_;
std::function<void()> transitionStackAction_;
Node *hoverTarget_ = nullptr;
Node *captureTarget_ = nullptr;
Vec2 lastPointerWorld_ = Vec2::Zero();
bool hasLastPointerWorld_ = false;
// 鼠标状态(通过事件更新)
Vec2 mousePos_ = Vec2::Zero();
Vec2 mouseDelta_ = Vec2::Zero();
float scrollDelta_ = 0.0f;
bool mouseLeftPressed_ = false;
bool mouseLeftReleased_ = false;
bool mouseLeftDown_ = false;
ListenerId mouseMoveListener_ = 0;
ListenerId mousePressListener_ = 0;
ListenerId mouseReleaseListener_ = 0;
ListenerId scrollListener_ = 0;
};
} // namespace extra2d

View File

@ -1,104 +0,0 @@
#pragma once
#include <extra2d/core/color.h>
#include <extra2d/core/math_types.h>
#include <extra2d/scene/node.h>
#include <vector>
namespace extra2d {
// ============================================================================
// 形状类型
// ============================================================================
enum class ShapeType { Point, Line, Rect, Circle, Triangle, Polygon };
// ============================================================================
// 形状节点 - 用于绘制几何形状
// ============================================================================
class ShapeNode : public Node {
public:
ShapeNode();
~ShapeNode() override = default;
// ------------------------------------------------------------------------
// 静态创建方法
// ------------------------------------------------------------------------
static Ptr<ShapeNode> create();
// 点
static Ptr<ShapeNode> createPoint(const Vec2 &pos, const Color &color);
// 线
static Ptr<ShapeNode> createLine(const Vec2 &start, const Vec2 &end,
const Color &color, float width = 1.0f);
// 矩形
static Ptr<ShapeNode> createRect(const Rect &rect, const Color &color,
float width = 1.0f);
static Ptr<ShapeNode> createFilledRect(const Rect &rect, const Color &color);
// 圆形
static Ptr<ShapeNode> createCircle(const Vec2 &center, float radius,
const Color &color, int segments = 32,
float width = 1.0f);
static Ptr<ShapeNode> createFilledCircle(const Vec2 &center, float radius,
const Color &color,
int segments = 32);
// 三角形
static Ptr<ShapeNode> createTriangle(const Vec2 &p1, const Vec2 &p2,
const Vec2 &p3, const Color &color,
float width = 1.0f);
static Ptr<ShapeNode> createFilledTriangle(const Vec2 &p1, const Vec2 &p2,
const Vec2 &p3,
const Color &color);
// 多边形
static Ptr<ShapeNode> createPolygon(const std::vector<Vec2> &points,
const Color &color, float width = 1.0f);
static Ptr<ShapeNode> createFilledPolygon(const std::vector<Vec2> &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<Vec2> &points);
const std::vector<Vec2> &getPoints() const { return points_; }
void addPoint(const Vec2 &point);
void clearPoints();
Rect getBounds() const override;
protected:
void onDraw(Renderer &renderer) override;
void generateRenderCommand(std::vector<RenderCommand> &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<Vec2> points_;
};
} // namespace extra2d

View File

@ -1,55 +0,0 @@
#pragma once
#include <extra2d/graphics/texture/texture.h>
#include <extra2d/scene/node.h>
namespace extra2d {
// ============================================================================
// 精灵节点
// ============================================================================
class Sprite : public Node {
public:
Sprite();
explicit Sprite(Ptr<Texture> texture);
~Sprite() override = default;
// 纹理
void setTexture(Ptr<Texture> texture);
Ptr<Texture> 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<Sprite> create();
static Ptr<Sprite> create(Ptr<Texture> texture);
static Ptr<Sprite> create(Ptr<Texture> texture, const Rect &rect);
Rect getBounds() const override;
protected:
void onDraw(Renderer &renderer) override;
void generateRenderCommand(std::vector<RenderCommand> &commands,
int zOrder) override;
private:
Ptr<Texture> texture_;
Rect textureRect_;
Color color_ = Colors::White;
bool flipX_ = false;
bool flipY_ = false;
};
} // namespace extra2d

View File

@ -1,35 +0,0 @@
#pragma once
#include <extra2d/scene/transition_scene.h>
namespace extra2d {
// ============================================================================
// 方块/马赛克过渡场景
// 实现原理:
// 1. 将屏幕分成多个方块
// 2. 方块逐个消失,显示新场景
// ============================================================================
class TransitionBoxScene : public TransitionScene {
public:
/**
* @brief
* @param duration
* @param inScene
* @param divisions 8 8x8
*/
TransitionBoxScene(float duration, Ptr<Scene> inScene, int divisions = 8);
static Ptr<TransitionBoxScene> create(float duration, Ptr<Scene> inScene,
int divisions = 8);
protected:
void onTransitionStart() override;
void renderContent(Renderer &renderer) override;
void updateTransition(float dt) override;
private:
int divisions_;
};
} // namespace extra2d

View File

@ -1,59 +0,0 @@
#pragma once
#include <extra2d/core/color.h>
#include <extra2d/scene/transition_scene.h>
namespace extra2d {
// ============================================================================
// 淡入淡出过渡场景
// 实现原理:
// 1. 创建一个纯色精灵作为遮罩层
// 2. 第一阶段:遮罩从透明淡入到不透明(黑屏),同时显示旧场景
// 3. 切换显示新场景
// 4. 第二阶段:遮罩从不透明淡出到透明,显示新场景
// ============================================================================
class TransitionFadeScene : public TransitionScene {
public:
/**
* @brief
* @param duration
* @param inScene
* @param color
*/
TransitionFadeScene(float duration, Ptr<Scene> inScene,
const Color &color = Colors::Black);
static Ptr<TransitionFadeScene> create(float duration, Ptr<Scene> inScene,
const Color &color = Colors::Black);
protected:
/**
* @brief
*
*/
void onTransitionStart() override;
/**
* @brief
* @param dt
*/
void updateTransition(float dt) override;
/**
* @brief
*
*/
void renderContent(Renderer &renderer) override;
private:
/**
* @brief 退
*/
void hideOutShowIn();
Color maskColor_; // 遮罩颜色
bool hasSwitched_ = false; // 是否已经切换场景
};
} // namespace extra2d

View File

@ -1,38 +0,0 @@
#pragma once
#include <extra2d/scene/transition_scene.h>
namespace extra2d {
// ============================================================================
// 翻页过渡场景
// 实现原理:
// 1. 前半段:旧场景翻转消失
// 2. 后半段:新场景翻转出现
// ============================================================================
class TransitionFlipScene : public TransitionScene {
public:
enum class Axis { Horizontal, Vertical };
/**
* @brief
* @param duration
* @param inScene
* @param axis
*/
TransitionFlipScene(float duration, Ptr<Scene> inScene,
Axis axis = Axis::Horizontal);
static Ptr<TransitionFlipScene> create(float duration, Ptr<Scene> inScene,
Axis axis = Axis::Horizontal);
protected:
void onTransitionStart() override;
void renderContent(Renderer &renderer) override;
void updateTransition(float dt) override;
private:
Axis axis_;
};
} // namespace extra2d

View File

@ -1,30 +0,0 @@
#pragma once
#include <extra2d/scene/transition_scene.h>
namespace extra2d {
// ============================================================================
// 缩放过渡场景
// 实现原理:
// 1. 旧场景缩小消失
// 2. 新场景放大出现
// ============================================================================
class TransitionScaleScene : public TransitionScene {
public:
/**
* @brief
* @param duration
* @param inScene
*/
TransitionScaleScene(float duration, Ptr<Scene> inScene);
static Ptr<TransitionScaleScene> create(float duration, Ptr<Scene> inScene);
protected:
void onTransitionStart() override;
void renderContent(Renderer &renderer) override;
void updateTransition(float dt) override;
};
} // namespace extra2d

View File

@ -1,150 +0,0 @@
#pragma once
#include <extra2d/scene/scene.h>
#include <functional>
namespace extra2d {
// ============================================================================
// 过渡方向
// ============================================================================
enum class TransitionDirection { Left, Right, Up, Down };
// ============================================================================
// 过渡效果类型
// ============================================================================
enum class TransitionType {
None,
Fade,
SlideLeft,
SlideRight,
SlideUp,
SlideDown,
Scale,
Flip,
Box
};
// ============================================================================
// 过渡场景基类 - 继承自 Scene作为中介场景管理过渡效果
// 设计参考 Cocos2d-x 的 TransitionScene
// ============================================================================
class TransitionScene : public Scene {
public:
using FinishCallback = std::function<void()>;
/**
* @brief
* @param duration
* @param inScene
*/
TransitionScene(float duration, Ptr<Scene> inScene);
~TransitionScene() override = default;
// ------------------------------------------------------------------------
// 场景管理
// ------------------------------------------------------------------------
/**
* @brief
*/
Ptr<Scene> getInScene() const { return inScene_; }
/**
* @brief 退
*/
Ptr<Scene> getOutScene() const { return outScene_; }
/**
* @brief 退 SceneManager
*/
void setOutScene(Ptr<Scene> outScene) { outScene_ = outScene; }
/**
* @brief
*/
void setFinishCallback(FinishCallback callback) {
finishCallback_ = callback;
}
/**
* @brief
*/
float getDuration() const { return duration_; }
/**
* @brief [0, 1]
*/
float getProgress() const { return progress_; }
/**
* @brief
*/
bool isFinished() const { return isFinished_; }
/**
* @brief
*/
bool isCancelled() const { return isCancelled_; }
/**
* @brief SceneManager
*/
void finish();
/**
* @brief
* @param immediate false则回滚到原场景
*/
void cancel(bool immediate = false);
// ------------------------------------------------------------------------
// 渲染 - 在 TransitionScene 上渲染新旧两个子场景
// ------------------------------------------------------------------------
void renderContent(Renderer &renderer) override;
// ------------------------------------------------------------------------
// 生命周期
// ------------------------------------------------------------------------
void onEnter() override;
void onExit() override;
protected:
/**
* @brief
* onEnter finish()
*/
virtual void onTransitionStart() = 0;
/**
* @brief
*/
virtual void drawOutScene(Renderer &renderer);
/**
* @brief
*/
virtual void drawInScene(Renderer &renderer);
/**
* @brief
* @param dt
*/
virtual void updateTransition(float dt);
float duration_;
float elapsed_ = 0.0f;
float progress_ = 0.0f;
bool isFinished_ = false;
bool isCancelled_ = false;
Ptr<Scene> inScene_; // 要进入的场景
Ptr<Scene> outScene_; // 要退出的场景
FinishCallback finishCallback_;
FinishCallback cancelCallback_; // 取消回调
friend class SceneManager;
};
} // namespace extra2d

View File

@ -1,36 +0,0 @@
#pragma once
#include <extra2d/scene/transition_scene.h>
namespace extra2d {
// ============================================================================
// 滑动过渡场景
// 实现原理:
// 1. 旧场景向指定方向滑出
// 2. 新场景从相反方向滑入
// ============================================================================
class TransitionSlideScene : public TransitionScene {
public:
/**
* @brief
* @param duration
* @param inScene
* @param direction
*/
TransitionSlideScene(float duration, Ptr<Scene> inScene,
TransitionDirection direction);
static Ptr<TransitionSlideScene> create(float duration, Ptr<Scene> inScene,
TransitionDirection direction);
protected:
void onTransitionStart() override;
void renderContent(Renderer &renderer) override;
void updateTransition(float dt) override;
private:
TransitionDirection direction_;
};
} // namespace extra2d

View File

@ -1,124 +0,0 @@
#pragma once
#include <extra2d/core/service_interface.h>
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/camera/camera.h>
#include <extra2d/graphics/camera/viewport_adapter.h>
#include <functional>
namespace extra2d {
/**
* @brief
*/
class ICameraService : public IService {
public:
virtual ~ICameraService() = default;
virtual void setPosition(const Vec2& position) = 0;
virtual void setPosition(float x, float y) = 0;
virtual Vec2 getPosition() const = 0;
virtual void setRotation(float degrees) = 0;
virtual float getRotation() const = 0;
virtual void setZoom(float zoom) = 0;
virtual float getZoom() const = 0;
virtual void setViewport(float left, float right, float bottom, float top) = 0;
virtual Rect getViewport() const = 0;
virtual glm::mat4 getViewMatrix() const = 0;
virtual glm::mat4 getProjectionMatrix() const = 0;
virtual glm::mat4 getViewProjectionMatrix() const = 0;
virtual Vec2 screenToWorld(const Vec2& screenPos) const = 0;
virtual Vec2 worldToScreen(const Vec2& worldPos) const = 0;
virtual void move(const Vec2& offset) = 0;
virtual void move(float x, float y) = 0;
virtual void setBounds(const Rect& bounds) = 0;
virtual void clearBounds() = 0;
virtual void lookAt(const Vec2& target) = 0;
virtual void setViewportConfig(const ViewportConfig& config) = 0;
/**
* @brief 使 Lambda
* @param configFn ViewportConfig
*/
virtual void setViewportConfig(std::function<void(ViewportConfig&)> configFn) = 0;
virtual const ViewportConfig& getViewportConfig() const = 0;
virtual void updateViewport(int screenWidth, int screenHeight) = 0;
virtual const ViewportResult& getViewportResult() const = 0;
virtual void applyViewportAdapter() = 0;
};
/**
* @brief
*/
class CameraService : public ICameraService {
public:
CameraService();
explicit CameraService(float left, float right, float bottom, float top);
~CameraService() override = default;
ServiceInfo getServiceInfo() const override;
bool initialize() override;
void shutdown() override;
void setPosition(const Vec2& position) override;
void setPosition(float x, float y) override;
Vec2 getPosition() const override;
void setRotation(float degrees) override;
float getRotation() const override;
void setZoom(float zoom) override;
float getZoom() const override;
void setViewport(float left, float right, float bottom, float top) override;
Rect getViewport() const override;
glm::mat4 getViewMatrix() const override;
glm::mat4 getProjectionMatrix() const override;
glm::mat4 getViewProjectionMatrix() const override;
Vec2 screenToWorld(const Vec2& screenPos) const override;
Vec2 worldToScreen(const Vec2& worldPos) const override;
void move(const Vec2& offset) override;
void move(float x, float y) override;
void setBounds(const Rect& bounds) override;
void clearBounds() override;
void lookAt(const Vec2& target) override;
void setViewportConfig(const ViewportConfig& config) override;
void setViewportConfig(std::function<void(ViewportConfig&)> configFn) override;
const ViewportConfig& getViewportConfig() const override;
void updateViewport(int screenWidth, int screenHeight) override;
const ViewportResult& getViewportResult() const override;
void applyViewportAdapter() override;
Camera& getCamera() { return camera_; }
const Camera& getCamera() const { return camera_; }
ViewportAdapter& getViewportAdapter() { return viewportAdapter_; }
const ViewportAdapter& getViewportAdapter() const { return viewportAdapter_; }
private:
Camera camera_;
ViewportAdapter viewportAdapter_;
// 服务注册元数据
E2D_AUTO_REGISTER_SERVICE(ICameraService, CameraService);
};
}

View File

@ -272,21 +272,13 @@ std::string format_str(const char *fmt, Args &&...args) {
} \ } \
} while (0) } while (0)
#define E2D_LOG_TRACE(...) E2D_LOG(::extra2d::LogLevel::Trace, __VA_ARGS__) #define E2D_TRACE(...) E2D_LOG(::extra2d::LogLevel::Trace, __VA_ARGS__)
#define E2D_LOG_DEBUG(...) E2D_LOG(::extra2d::LogLevel::Debug, __VA_ARGS__) #define E2D_DEBUG(...) E2D_LOG(::extra2d::LogLevel::Debug, __VA_ARGS__)
#define E2D_LOG_INFO(...) E2D_LOG(::extra2d::LogLevel::Info, __VA_ARGS__) #define E2D_INFO(...) E2D_LOG(::extra2d::LogLevel::Info, __VA_ARGS__)
#define E2D_LOG_REGISTRY(...) \ #define E2D_REGISTRY(...) E2D_LOG(::extra2d::LogLevel::Registry, __VA_ARGS__)
E2D_LOG(::extra2d::LogLevel::Registry, __VA_ARGS__) #define E2D_WARN(...) E2D_LOG(::extra2d::LogLevel::Warn, __VA_ARGS__)
#define E2D_LOG_WARN(...) E2D_LOG(::extra2d::LogLevel::Warn, __VA_ARGS__) #define E2D_ERROR(...) E2D_LOG(::extra2d::LogLevel::Error, __VA_ARGS__)
#define E2D_LOG_ERROR(...) E2D_LOG(::extra2d::LogLevel::Error, __VA_ARGS__) #define E2D_FATAL(...) E2D_LOG(::extra2d::LogLevel::Fatal, __VA_ARGS__)
#define E2D_LOG_FATAL(...) E2D_LOG(::extra2d::LogLevel::Fatal, __VA_ARGS__)
// 简写宏
#define E2D_INFO(...) E2D_LOG_INFO(__VA_ARGS__)
#define E2D_REGISTRY(...) E2D_LOG_REGISTRY(__VA_ARGS__)
#define E2D_WARN(...) E2D_LOG_WARN(__VA_ARGS__)
#define E2D_ERROR(...) E2D_LOG_ERROR(__VA_ARGS__)
#define E2D_FATAL(...) E2D_LOG_FATAL(__VA_ARGS__)
// 带颜色参数的日志宏 // 带颜色参数的日志宏
#define E2D_LOG_COLOR(level, color, ...) \ #define E2D_LOG_COLOR(level, color, ...) \
@ -302,33 +294,17 @@ std::string format_str(const char *fmt, Args &&...args) {
} \ } \
} while (0) } while (0)
#define E2D_LOG_TRACE_COLOR(color, ...) \ #define E2D_TRACE_COLOR(color, ...) \
E2D_LOG_COLOR(::extra2d::LogLevel::Trace, color, __VA_ARGS__) E2D_LOG_COLOR(::extra2d::LogLevel::Trace, color, __VA_ARGS__)
#define E2D_LOG_DEBUG_COLOR(color, ...) \ #define E2D_DEBUG_COLOR(color, ...) \
E2D_LOG_COLOR(::extra2d::LogLevel::Debug, color, __VA_ARGS__) E2D_LOG_COLOR(::extra2d::LogLevel::Debug, color, __VA_ARGS__)
#define E2D_LOG_INFO_COLOR(color, ...) \ #define E2D_INFO_COLOR(color, ...) \
E2D_LOG_COLOR(::extra2d::LogLevel::Info, color, __VA_ARGS__) E2D_LOG_COLOR(::extra2d::LogLevel::Info, color, __VA_ARGS__)
#define E2D_LOG_REGISTRY_COLOR(color, ...) \
E2D_LOG_COLOR(::extra2d::LogLevel::Registry, color, __VA_ARGS__)
#define E2D_LOG_WARN_COLOR(color, ...) \
E2D_LOG_COLOR(::extra2d::LogLevel::Warn, color, __VA_ARGS__)
#define E2D_LOG_ERROR_COLOR(color, ...) \
E2D_LOG_COLOR(::extra2d::LogLevel::Error, color, __VA_ARGS__)
#define E2D_LOG_FATAL_COLOR(color, ...) \
E2D_LOG_COLOR(::extra2d::LogLevel::Fatal, color, __VA_ARGS__)
// 简写带颜色宏
#define E2D_INFO_COLOR(color, ...) E2D_LOG_INFO_COLOR(color, __VA_ARGS__)
#define E2D_REGISTRY_COLOR(color, ...) \ #define E2D_REGISTRY_COLOR(color, ...) \
E2D_LOG_REGISTRY_COLOR(color, __VA_ARGS__) E2D_LOG_COLOR(::extra2d::LogLevel::Registry, color, __VA_ARGS__)
#define E2D_WARN_COLOR(color, ...) E2D_LOG_WARN_COLOR(color, __VA_ARGS__) #define E2D_WARN_COLOR(color, ...) \
#define E2D_ERROR_COLOR(color, ...) E2D_LOG_ERROR_COLOR(color, __VA_ARGS__) E2D_LOG_COLOR(::extra2d::LogLevel::Warn, color, __VA_ARGS__)
#define E2D_FATAL_COLOR(color, ...) E2D_LOG_FATAL_COLOR(color, __VA_ARGS__) #define E2D_ERROR_COLOR(color, ...) \
E2D_LOG_COLOR(::extra2d::LogLevel::Error, color, __VA_ARGS__)
#ifdef E2D_DEBUG #define E2D_FATAL_COLOR(color, ...) \
#define E2D_DEBUG_LOG(...) E2D_LOG_DEBUG(__VA_ARGS__) E2D_LOG_COLOR(::extra2d::LogLevel::Fatal, color, __VA_ARGS__)
#define E2D_TRACE(...) E2D_LOG_TRACE(__VA_ARGS__)
#else
#define E2D_DEBUG_LOG(...)
#define E2D_TRACE(...)
#endif

View File

@ -1,98 +0,0 @@
#pragma once
#include <extra2d/core/service_interface.h>
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/opengl/gl_renderer.h>
#include <extra2d/scene/scene_manager.h>
namespace extra2d {
/**
* @brief
* 便Mock
*/
class ISceneService : public IService {
public:
virtual ~ISceneService() = default;
virtual void runWithScene(Ptr<Scene> scene) = 0;
virtual void replaceScene(Ptr<Scene> scene) = 0;
virtual void pushScene(Ptr<Scene> scene) = 0;
virtual void popScene() = 0;
virtual void popToRootScene() = 0;
virtual void popToScene(const std::string &name) = 0;
virtual Ptr<Scene> getCurrentScene() const = 0;
virtual Ptr<Scene> getPreviousScene() const = 0;
virtual Ptr<Scene> getRootScene() const = 0;
virtual Ptr<Scene> getSceneByName(const std::string &name) const = 0;
virtual size_t getSceneCount() const = 0;
virtual bool isEmpty() const = 0;
virtual bool hasScene(const std::string &name) const = 0;
virtual void render(Renderer &renderer) = 0;
virtual void collectRenderCommands(std::vector<RenderCommand> &commands) = 0;
virtual bool isTransitioning() const = 0;
virtual void
setTransitionCallback(SceneManager::TransitionCallback callback) = 0;
virtual void end() = 0;
virtual void purgeCachedScenes() = 0;
virtual void enterScene(Ptr<Scene> scene) = 0;
};
/**
* @brief
* SceneManager IService
*/
class SceneService : public ISceneService {
public:
SceneService();
~SceneService() override = default;
ServiceInfo getServiceInfo() const override;
bool initialize() override;
void shutdown() override;
void update(float deltaTime) override;
void runWithScene(Ptr<Scene> scene) override;
void replaceScene(Ptr<Scene> scene) override;
void pushScene(Ptr<Scene> scene) override;
void popScene() override;
void popToRootScene() override;
void popToScene(const std::string &name) override;
Ptr<Scene> getCurrentScene() const override;
Ptr<Scene> getPreviousScene() const override;
Ptr<Scene> getRootScene() const override;
Ptr<Scene> getSceneByName(const std::string &name) const override;
size_t getSceneCount() const override;
bool isEmpty() const override;
bool hasScene(const std::string &name) const override;
void render(Renderer &renderer) override;
void collectRenderCommands(std::vector<RenderCommand> &commands) override;
bool isTransitioning() const override;
void
setTransitionCallback(SceneManager::TransitionCallback callback) override;
void end() override;
void purgeCachedScenes() override;
void enterScene(Ptr<Scene> scene) override;
SceneManager &getManager() { return manager_; }
const SceneManager &getManager() const { return manager_; }
private:
SceneManager manager_;
// 服务注册元数据
E2D_AUTO_REGISTER_SERVICE(ISceneService, SceneService);
};
} // namespace extra2d

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -1,39 +0,0 @@
#version 300 es
precision highp float;
in vec2 v_texCoord;
in vec4 v_color;
uniform sampler2D u_texture;
uniform float u_opacity;
uniform float u_sdfThreshold;
uniform float u_sdfSmoothness;
uniform vec2 u_textureSize;
out vec4 fragColor;
void main() {
// 采样 SDF 纹理SDF 值存储在 alpha 通道,范围 0-255 已映射到 0-1
float sdfValue = texture(u_texture, v_texCoord).a;
// 使用 fwidth 计算屏幕空间的变化率
float fw = fwidth(sdfValue);
// 平衡的抗锯齿:根据屏幕空间变化率调整平滑范围
// 在放大时更平滑,缩小时更锐利
float smoothRange = max(u_sdfSmoothness, fw * 0.5);
// 使用 smoothstep 进行抗锯齿
float alpha = smoothstep(u_sdfThreshold - smoothRange,
u_sdfThreshold + smoothRange,
sdfValue);
// 应用颜色和透明度
fragColor = v_color;
fragColor.a *= alpha * u_opacity;
// 丢弃完全透明的像素
if (fragColor.a < 0.001) {
discard;
}
}

View File

@ -1,10 +0,0 @@
#version 300 es
precision highp float;
in vec4 v_color;
out vec4 fragColor;
void main() {
fragColor = v_color;
}

View File

@ -1,14 +0,0 @@
#version 300 es
precision highp float;
layout(location = 0) in vec2 a_position;
layout(location = 1) in vec4 a_color;
uniform mat4 u_viewProjection;
out vec4 v_color;
void main() {
gl_Position = u_viewProjection * vec4(a_position, 0.0, 1.0);
v_color = a_color;
}

View File

@ -1,20 +0,0 @@
#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;
}
}

View File

@ -1,18 +0,0 @@
#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;
}

View File

@ -1,137 +0,0 @@
// ============================================
// Common Color Functions
// ============================================
#ifndef E2D_COLOR_GLSL
#define E2D_COLOR_GLSL
/**
* @brief RGB转灰度
* @param color RGB颜色
* @return 灰度值
*/
float rgbToGrayscale(vec3 color) {
return dot(color, vec3(0.299, 0.587, 0.114));
}
/**
* @brief RGB转HSV
* @param c RGB颜色
* @return HSV颜色
*/
vec3 rgbToHsv(vec3 c) {
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
/**
* @brief HSV转RGB
* @param c HSV颜色
* @return RGB颜色
*/
vec3 hsvToRgb(vec3 c) {
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
/**
* @brief 调整亮度
* @param color 原始颜色
* @param amount 亮度调整量
* @return 调整后的颜色
*/
vec3 adjustBrightness(vec3 color, float amount) {
return color + amount;
}
/**
* @brief 调整对比度
* @param color 原始颜色
* @param amount 对比度调整量
* @return 调整后的颜色
*/
vec3 adjustContrast(vec3 color, float amount) {
return (color - 0.5) * amount + 0.5;
}
/**
* @brief 调整饱和度
* @param color 原始颜色
* @param amount 饱和度调整量
* @return 调整后的颜色
*/
vec3 adjustSaturation(vec3 color, float amount) {
float gray = rgbToGrayscale(color);
return mix(vec3(gray), color, amount);
}
/**
* @brief 颜色混合(正片叠底)
* @param a 底色
* @param b 混合色
* @return 混合结果
*/
vec3 blendMultiply(vec3 a, vec3 b) {
return a * b;
}
/**
* @brief 颜色混合(滤色)
* @param a 底色
* @param b 混合色
* @return 混合结果
*/
vec3 blendScreen(vec3 a, vec3 b) {
return 1.0 - (1.0 - a) * (1.0 - b);
}
/**
* @brief 颜色混合(叠加)
* @param a 底色
* @param b 混合色
* @return 混合结果
*/
vec3 blendOverlay(vec3 a, vec3 b) {
return mix(
2.0 * a * b,
1.0 - 2.0 * (1.0 - a) * (1.0 - b),
step(0.5, a)
);
}
/**
* @brief 颜色调色
* @param color 原始颜色
* @param tintColor 色调颜色
* @param amount 色调强度
* @return 调色结果
*/
vec3 tint(vec3 color, vec3 tintColor, float amount) {
return mix(color, tintColor, amount);
}
/**
* @brief 预乘Alpha
* @param color RGBA颜色
* @return 预乘后的RGB颜色
*/
vec3 premultiplyAlpha(vec4 color) {
return color.rgb * color.a;
}
/**
* @brief 取消预乘Alpha
* @param color RGB颜色
* @param alpha Alpha值
* @return 未预乘的RGB颜色
*/
vec3 unpremultiplyAlpha(vec3 color, float alpha) {
return alpha > 0.0 ? color / alpha : color;
}
#endif // E2D_COLOR_GLSL

View File

@ -1,96 +0,0 @@
// ============================================
// Common Math Functions
// ============================================
#ifndef E2D_MATH_GLSL
#define E2D_MATH_GLSL
const float PI = 3.14159265359;
const float E = 2.71828182846;
/**
* @brief 角度转弧度
* @param deg 角度值
* @return 弧度值
*/
float degToRad(float deg) {
return deg * PI / 180.0;
}
/**
* @brief 弧度转角度
* @param rad 弧度值
* @return 角度值
*/
float radToDeg(float rad) {
return rad * 180.0 / PI;
}
/**
* @brief 线性插值
* @param a 起始值
* @param b 结束值
* @param t 插值因子 [0, 1]
* @return 插值结果
*/
float lerp(float a, float b, float t) {
return a + (b - a) * t;
}
/**
* @brief 平滑插值
* @param edge0 下边界
* @param edge1 上边界
* @param x 输入值
* @return 平滑插值结果
*/
float smoothStep(float edge0, float edge1, float x) {
float t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
return t * t * (3.0 - 2.0 * t);
}
/**
* @brief 2D向量线性插值
*/
vec2 lerpVec2(vec2 a, vec2 b, float t) {
return a + (b - a) * t;
}
/**
* @brief 计算两点之间的距离
*/
float distance2D(vec2 a, vec2 b) {
return length(b - a);
}
/**
* @brief 计算两点之间的距离平方
*/
float distance2DSquared(vec2 a, vec2 b) {
vec2 diff = b - a;
return dot(diff, diff);
}
/**
* @brief 将值限制在范围内
*/
float clamp01(float x) {
return clamp(x, 0.0, 1.0);
}
/**
* @brief 重复平铺
*/
float repeat(float x, float period) {
return mod(x, period);
}
/**
* @brief 镜像重复
*/
float mirrorRepeat(float x, float period) {
float m = mod(x, period * 2.0);
return m > period ? period * 2.0 - m : m;
}
#endif // E2D_MATH_GLSL

View File

@ -1,60 +0,0 @@
{
"name": "sdf_font",
"category": "builtin",
"version": "1.0",
"description": "SDF字体渲染Shader支持高质量抗锯齿",
"uniforms": {
"u_viewProjection": {
"type": "mat4",
"default": [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
],
"description": "视图投影矩阵"
},
"u_model": {
"type": "mat4",
"default": [
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
],
"description": "模型矩阵"
},
"u_opacity": {
"type": "float",
"default": 1.0,
"description": "透明度"
},
"u_sdfThreshold": {
"type": "float",
"default": 0.5,
"description": "SDF阈值默认0.5范围0-1。值越小字形越粗越大越细"
},
"u_sdfSmoothness": {
"type": "float",
"default": 0.02,
"description": "SDF平滑度控制抗锯齿范围越小越锐利默认0.02"
},
"u_textureSize": {
"type": "vec2",
"default": [512, 512],
"description": "SDF纹理尺寸用于计算像素级平滑"
}
},
"samplers": {
"u_texture": {
"binding": 0,
"description": "SDF纹理采样器"
}
},
"backends": {
"opengl": {
"vertex": "backends/opengl/builtin/sprite.vert",
"fragment": "backends/opengl/builtin/sdf_font.frag"
}
}
}

View File

@ -1,41 +0,0 @@
{
"name": "shape",
"category": "builtin",
"version": "1.0",
"description": "基本形状渲染Shader",
"uniforms": {
"u_viewProjection": {
"type": "mat4",
"default": [
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1
],
"description": "视图投影矩阵"
}
},
"samplers": {},
"backends": {
"opengl": {
"vertex": "backends/opengl/builtin/shape.vert",
"fragment": "backends/opengl/builtin/shape.frag"
},
"vulkan": {
"vertex": "backends/vulkan/builtin/shape.vert.spv",
"fragment": "backends/vulkan/builtin/shape.frag.spv"
}
}
}

View File

@ -1,73 +0,0 @@
{
"name": "sprite",
"category": "builtin",
"version": "1.0",
"description": "标准2D精灵渲染Shader",
"uniforms": {
"u_viewProjection": {
"type": "mat4",
"default": [
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1
],
"description": "视图投影矩阵"
},
"u_model": {
"type": "mat4",
"default": [
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1,
0,
0,
0,
0,
1
],
"description": "模型矩阵"
},
"u_opacity": {
"type": "float",
"default": 1.0,
"description": "透明度"
}
},
"samplers": {
"u_texture": {
"binding": 0,
"description": "纹理采样器"
}
},
"backends": {
"opengl": {
"vertex": "backends/opengl/builtin/sprite.vert",
"fragment": "backends/opengl/builtin/sprite.frag"
},
"vulkan": {
"vertex": "backends/vulkan/builtin/sprite.vert.spv",
"fragment": "backends/vulkan/builtin/sprite.frag.spv"
}
}
}

View File

@ -1,14 +1,9 @@
#include <extra2d/app/application.h> #include <extra2d/app/application.h>
#include <extra2d/core/registry.h> #include <extra2d/core/registry.h>
#include <extra2d/graphics/core/render_module.h>
#include <extra2d/graphics/memory/vram_manager.h>
#include <extra2d/graphics/opengl/gl_renderer.h>
#include <extra2d/platform/glfw/glfw_window.h> #include <extra2d/platform/glfw/glfw_window.h>
#include <extra2d/platform/window_module.h> #include <extra2d/platform/window_module.h>
#include <extra2d/services/camera_service.h>
#include <extra2d/services/event_service.h> #include <extra2d/services/event_service.h>
#include <extra2d/services/logger_service.h> #include <extra2d/services/logger_service.h>
#include <extra2d/services/scene_service.h>
#include <extra2d/services/timer_service.h> #include <extra2d/services/timer_service.h>
namespace extra2d { namespace extra2d {
@ -51,9 +46,6 @@ bool Application::init() {
return false; return false;
} }
// 配置相机服务(需要窗口信息)
configureCameraService();
// 初始化所有服务 // 初始化所有服务
ServiceLocator::instance().initializeAll(); ServiceLocator::instance().initializeAll();
@ -62,40 +54,10 @@ bool Application::init() {
return true; return true;
} }
void Application::configureCameraService() {
auto *winMod = get<WindowModule>();
if (!winMod || !winMod->win()) {
return;
}
auto cameraService = ServiceLocator::instance().getService<ICameraService>();
if (!cameraService) {
return;
}
auto *win = winMod->win();
cameraService->setViewport(0, static_cast<float>(win->width()),
static_cast<float>(win->height()), 0);
ViewportConfig vpConfig;
vpConfig.logicWidth = static_cast<float>(win->width());
vpConfig.logicHeight = static_cast<float>(win->height());
vpConfig.mode = ViewportMode::AspectRatio;
cameraService->setViewportConfig(vpConfig);
cameraService->updateViewport(win->width(), win->height());
win->onResize([cameraService](int width, int height) {
cameraService->updateViewport(width, height);
cameraService->applyViewportAdapter();
});
}
void Application::shutdown() { void Application::shutdown() {
if (!initialized_) if (!initialized_)
return; return;
VRAMMgr::get().printStats();
ServiceLocator::instance().shutdownAll(); ServiceLocator::instance().shutdownAll();
ServiceLocator::instance().clear(); ServiceLocator::instance().clear();
Registry::instance().shutdown(); Registry::instance().shutdown();
@ -170,43 +132,15 @@ void Application::mainLoop() {
} }
render(); render();
// 帧率限制
auto *renderMod = get<RenderModule>();
if (renderMod && renderMod->renderer()) {
// 这里可以添加帧率限制逻辑
}
} }
void Application::update() { ServiceLocator::instance().updateAll(deltaTime_); } void Application::update() { ServiceLocator::instance().updateAll(deltaTime_); }
void Application::render() { void Application::render() {
auto *renderMod = get<RenderModule>();
if (!renderMod || !renderMod->renderer())
return;
auto *renderer = renderMod->renderer();
auto *winMod = get<WindowModule>(); auto *winMod = get<WindowModule>();
if (!winMod || !winMod->win()) if (!winMod || !winMod->win())
return; return;
auto cameraService = ServiceLocator::instance().getService<ICameraService>();
if (cameraService) {
const auto &vp = cameraService->getViewportResult().viewport;
renderer->setViewport(
static_cast<int>(vp.origin.x), static_cast<int>(vp.origin.y),
static_cast<int>(vp.size.width), static_cast<int>(vp.size.height));
renderer->setViewProjection(cameraService->getViewProjectionMatrix());
} else {
renderer->setViewport(0, 0, winMod->win()->width(),
winMod->win()->height());
}
auto sceneService = ServiceLocator::instance().getService<ISceneService>();
if (sceneService) {
sceneService->render(*renderer);
}
winMod->win()->swap(); winMod->win()->swap();
} }
@ -215,19 +149,4 @@ GLFWWindow *Application::window() {
return winMod ? winMod->win() : nullptr; return winMod ? winMod->win() : nullptr;
} }
Renderer *Application::renderer() {
auto *renderMod = get<RenderModule>();
return renderMod ? renderMod->renderer() : nullptr;
}
void Application::enterScene(Ptr<Scene> scene) {
auto sceneService = ServiceLocator::instance().getService<ISceneService>();
auto *winMod = get<WindowModule>();
if (sceneService && scene && winMod && winMod->win()) {
scene->setViewportSize(static_cast<float>(winMod->win()->width()),
static_cast<float>(winMod->win()->height()));
sceneService->enterScene(scene);
}
}
} // namespace extra2d } // namespace extra2d

File diff suppressed because one or more lines are too long

View File

@ -1,72 +0,0 @@
#include <extra2d/graphics/batch/shape_batch.h>
namespace extra2d {
// ============================================================================
// ShapeBatch 基础实现(后端无关部分)
// ============================================================================
// 这里可以添加后端无关的工具函数
// 例如:计算圆形的顶点、三角化多边形等
// 计算圆形顶点
void calculateCircleVertices(std::vector<Vec2>& outVertices,
const Vec2& center, float radius,
int segments, bool fill) {
outVertices.clear();
outVertices.reserve(fill ? segments + 1 : segments);
if (fill) {
// 填充圆形:中心点 + 边缘点
outVertices.push_back(center);
for (int i = 0; i <= segments; ++i) {
float angle = 2.0f * 3.14159265359f * static_cast<float>(i) / static_cast<float>(segments);
outVertices.emplace_back(
center.x + radius * cosf(angle),
center.y + radius * sinf(angle)
);
}
} else {
// 圆形边框:只保留边缘点
for (int i = 0; i < segments; ++i) {
float angle = 2.0f * 3.14159265359f * static_cast<float>(i) / static_cast<float>(segments);
outVertices.emplace_back(
center.x + radius * cosf(angle),
center.y + radius * sinf(angle)
);
}
}
}
// 计算矩形顶点
void calculateRectVertices(std::vector<Vec2>& outVertices, const Rect& rect) {
outVertices.clear();
outVertices.reserve(4);
float x1 = rect.origin.x;
float y1 = rect.origin.y;
float x2 = rect.origin.x + rect.size.width;
float y2 = rect.origin.y + rect.size.height;
outVertices.emplace_back(x1, y1); // 左上
outVertices.emplace_back(x2, y1); // 右上
outVertices.emplace_back(x2, y2); // 右下
outVertices.emplace_back(x1, y2); // 左下
}
// 简单的多边形三角化(扇形三角化,适用于凸多边形)
void triangulatePolygon(std::vector<uint16_t>& outIndices, int vertexCount) {
outIndices.clear();
if (vertexCount < 3) {
return;
}
// 扇形三角化:以第一个顶点为扇形中心
for (int i = 1; i < vertexCount - 1; ++i) {
outIndices.push_back(0);
outIndices.push_back(i);
outIndices.push_back(i + 1);
}
}
} // namespace extra2d

View File

@ -1,206 +0,0 @@
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/batch/sprite_batch.h>
#include <extra2d/services/logger_service.h>
#include <cmath>
#include <cstring>
namespace extra2d {
// ============================================================================
// TrigLookup 实现 - 三角函数查表
// ============================================================================
TrigLookup::TrigLookup() {
constexpr float PI = 3.14159265359f;
constexpr float DEG2RAD = PI / 180.0f;
for (int i = 0; i < TABLE_SIZE; ++i) {
float angle = static_cast<float>(i) * (360.0f / TABLE_SIZE) * DEG2RAD;
sinTable_[i] = std::sin(angle);
cosTable_[i] = std::cos(angle);
}
}
float TrigLookup::sin(int angle) const {
// 规范化角度到 0-360
angle = ((angle % 360) + 360) % 360;
return sinTable_[angle * 4];
}
float TrigLookup::cos(int angle) const {
// 规范化角度到 0-360
angle = ((angle % 360) + 360) % 360;
return cosTable_[angle * 4];
}
float TrigLookup::sinRad(float rad) const {
constexpr float RAD2DEG = 180.0f / 3.14159265359f;
int angle = static_cast<int>(rad * RAD2DEG);
return sin(angle);
}
float TrigLookup::cosRad(float rad) const {
constexpr float RAD2DEG = 180.0f / 3.14159265359f;
int angle = static_cast<int>(rad * RAD2DEG);
return cos(angle);
}
// ============================================================================
// SpriteBatch 实现
// ============================================================================
SpriteBatch::SpriteBatch() : spriteCount_(0), vpDirty_(true) {
// 预分配顶点缓冲区
vertices_.reserve(MAX_VERTICES);
indices_.reserve(MAX_INDICES);
// 生成静态索引缓冲区
generateIndices();
}
void SpriteBatch::generateIndices() {
indices_.clear();
for (size_t i = 0; i < MAX_SPRITES; ++i) {
uint16_t base = static_cast<uint16_t>(i * 4);
// 两个三角形: (0,1,2) 和 (0,2,3)
indices_.push_back(base + 0);
indices_.push_back(base + 1);
indices_.push_back(base + 2);
indices_.push_back(base + 0);
indices_.push_back(base + 2);
indices_.push_back(base + 3);
}
}
void SpriteBatch::begin(const glm::mat4 &viewProjection) {
viewProjection_ = viewProjection;
vpDirty_ = true;
spriteCount_ = 0;
vertices_.clear();
}
void SpriteBatch::end() {
// 批次结束,数据已准备好供后端使用
}
void SpriteBatch::draw(const SpriteData &sprite) {
if (spriteCount_ >= MAX_SPRITES) {
// 缓冲区已满,需要刷新
flush();
}
generateVertices(sprite, spriteCount_ * VERTICES_PER_SPRITE);
spriteCount_++;
}
void SpriteBatch::drawBatch(const std::vector<SpriteData> &sprites) {
size_t index = 0;
while (index < sprites.size()) {
// 计算剩余空间
size_t remainingSpace = MAX_SPRITES - spriteCount_;
size_t batchSize = std::min(remainingSpace, sprites.size() - index);
// 批量生成顶点
for (size_t i = 0; i < batchSize; ++i) {
generateVertices(sprites[index + i], spriteCount_ * VERTICES_PER_SPRITE);
spriteCount_++;
}
index += batchSize;
// 如果缓冲区已满,需要刷新(由后端处理)
if (spriteCount_ >= MAX_SPRITES && index < sprites.size()) {
// 通知后端刷新,然后继续
// 注意:这里只是准备数据,实际 GPU 提交由后端决定
break;
}
}
}
void SpriteBatch::drawImmediate(const SpriteData &sprite) {
// 立即绘制模式:清空当前批次,只绘制这一个精灵
clear();
draw(sprite);
}
void SpriteBatch::generateVertices(const SpriteData &sprite,
size_t vertexOffset) {
// 确保顶点缓冲区足够
if (vertices_.size() < vertexOffset + VERTICES_PER_SPRITE) {
vertices_.resize(vertexOffset + VERTICES_PER_SPRITE);
}
// 计算旋转(使用查表)
float c = 1.0f;
float s = 0.0f;
if (sprite.rotation != 0.0f) {
c = trigLookup_.cosRad(sprite.rotation);
s = trigLookup_.sinRad(sprite.rotation);
}
// 计算精灵的四个角(相对于中心点)
float halfWidth = sprite.size.x * 0.5f;
float halfHeight = sprite.size.y * 0.5f;
// 考虑 pivot 偏移
float pivotOffsetX = (sprite.pivot.x - 0.5f) * sprite.size.x;
float pivotOffsetY = (sprite.pivot.y - 0.5f) * sprite.size.y;
// 四个角的本地坐标
Vec2 localCorners[4] = {
Vec2(-halfWidth - pivotOffsetX, -halfHeight - pivotOffsetY), // 左下
Vec2(halfWidth - pivotOffsetX, -halfHeight - pivotOffsetY), // 右下
Vec2(halfWidth - pivotOffsetX, halfHeight - pivotOffsetY), // 右上
Vec2(-halfWidth - pivotOffsetX, halfHeight - pivotOffsetY) // 左上
};
// 应用旋转和平移
Vec2 worldPos = sprite.position;
for (int i = 0; i < 4; ++i) {
Vec2 rotated;
if (sprite.rotation != 0.0f) {
rotated.x = localCorners[i].x * c - localCorners[i].y * s;
rotated.y = localCorners[i].x * s + localCorners[i].y * c;
} else {
rotated = localCorners[i];
}
vertices_[vertexOffset + i].position = worldPos + rotated;
}
// 设置纹理坐标
// uvRect.origin = (u0, v0) - 左下
// uvRect.size = (width, height) - 从左上到右下的尺寸
float u0 = sprite.uvRect.origin.x;
float v0 = sprite.uvRect.origin.y;
float u1 = u0 + sprite.uvRect.size.width;
float v1 = v0 + sprite.uvRect.size.height;
// 顶点顺序: 左下, 右下, 右上, 左上
// 注意: 在 gl_font_atlas 中 v0 > v1 (因为翻转了V坐标)
// 所以 v0 对应底部v1 对应顶部
vertices_[vertexOffset + 0].texCoord = Vec2(u0, v0); // 左下
vertices_[vertexOffset + 1].texCoord = Vec2(u1, v0); // 右下
vertices_[vertexOffset + 2].texCoord = Vec2(u1, v1); // 右上
vertices_[vertexOffset + 3].texCoord = Vec2(u0, v1); // 左上
// 设置颜色
for (int i = 0; i < 4; ++i) {
vertices_[vertexOffset + i].color = sprite.color;
}
}
void SpriteBatch::flush() {
// 标记需要刷新 - 实际刷新由后端处理
// 这里只是重置计数器,让后端知道需要提交当前批次
spriteCount_ = 0;
vertices_.clear();
}
void SpriteBatch::clear() {
spriteCount_ = 0;
vertices_.clear();
}
} // namespace extra2d

View File

@ -1,351 +0,0 @@
#include <algorithm>
#include <extra2d/graphics/camera/camera.h>
#include <extra2d/graphics/camera/viewport_adapter.h>
#include <glm/gtc/matrix_inverse.hpp>
#include <glm/gtc/matrix_transform.hpp>
namespace extra2d {
/**
* @brief
*
* (-1, -1) (1, 1)
*/
Camera::Camera() : left_(-1.0f), right_(1.0f), bottom_(-1.0f), top_(1.0f) {}
/**
* @brief
* @param left
* @param right
* @param bottom
* @param top
*
*
*/
Camera::Camera(float left, float right, float bottom, float top)
: left_(left), right_(right), bottom_(bottom), top_(top) {}
/**
* @brief
* @param viewport
*
*
*/
Camera::Camera(const Size &viewport)
: left_(0.0f), right_(viewport.width), bottom_(viewport.height),
top_(0.0f) {}
/**
* @brief
* @param position
*
*
*/
void Camera::setPos(const Vec2 &position) {
position_ = position;
viewDirty_ = true;
}
/**
* @brief
* @param x X坐标
* @param y Y坐标
*
*
*/
void Camera::setPos(float x, float y) {
position_.x = x;
position_.y = y;
viewDirty_ = true;
}
/**
* @brief
* @param degrees
*
*
*/
void Camera::setRotation(float degrees) {
rotation_ = degrees;
viewDirty_ = true;
}
/**
* @brief
* @param zoom 1.0
*
*
*/
void Camera::setZoom(float zoom) {
zoom_ = zoom;
viewDirty_ = true;
projDirty_ = true;
}
/**
* @brief
* @param left
* @param right
* @param bottom
* @param top
*
*
*/
void Camera::setViewport(float left, float right, float bottom, float top) {
left_ = left;
right_ = right;
bottom_ = bottom;
top_ = top;
projDirty_ = true;
}
/**
* @brief
* @param rect
*
* 使
*/
void Camera::setViewport(const Rect &rect) {
left_ = rect.left();
right_ = rect.right();
bottom_ = rect.bottom();
top_ = rect.top();
projDirty_ = true;
}
/**
* @brief
* @return
*
*
*/
Rect Camera::getViewport() const {
return Rect(left_, top_, right_ - left_, bottom_ - top_);
}
/**
* @brief
* @return
*
* -> ->
* View = T(-position) × R(-rotation) × S(1/zoom)
*/
glm::mat4 Camera::getViewMatrix() const {
if (viewDirty_) {
viewMatrix_ = glm::mat4(1.0f);
// 1. 平移(最后应用)
viewMatrix_ = glm::translate(viewMatrix_,
glm::vec3(-position_.x, -position_.y, 0.0f));
// 2. 旋转(中间应用)
if (rotation_ != 0.0f) {
viewMatrix_ = glm::rotate(viewMatrix_, -rotation_ * DEG_TO_RAD,
glm::vec3(0.0f, 0.0f, 1.0f));
}
// 3. 缩放(最先应用)
if (zoom_ != 1.0f) {
viewMatrix_ =
glm::scale(viewMatrix_, glm::vec3(1.0f / zoom_, 1.0f / zoom_, 1.0f));
}
viewDirty_ = false;
}
return viewMatrix_;
}
/**
* @brief
* @return
*
* 2D游戏Y轴向下增长
* OpenGL默认Y轴向上Y轴
*/
glm::mat4 Camera::getProjectionMatrix() const {
if (projDirty_) {
// 对于2D游戏Y轴向下增长屏幕坐标系
// OpenGL默认Y轴向上所以需要反转Y轴
// glm::ortho(left, right, bottom, top)
// 为了Y轴向下传入 (bottom=height, top=0)这样Y轴翻转
projMatrix_ = glm::ortho(
left_, right_, // X轴从左到右
bottom_, top_, // Y轴从下到上传入bottom>top实现Y轴向下增长
-1.0f, 1.0f);
projDirty_ = false;
}
return projMatrix_;
}
/**
* @brief -
* @return -
*/
glm::mat4 Camera::getViewProjectionMatrix() const {
return getProjectionMatrix() * getViewMatrix();
}
/**
* @brief
* @param screenPos
* @return
*/
Vec2 Camera::screenToWorld(const Vec2 &screenPos) const {
Vec2 logicPos = screenPos;
// 如果有视口适配器,先转换到逻辑坐标
if (viewportAdapter_) {
logicPos = viewportAdapter_->screenToLogic(screenPos);
}
// 使用逆视图-投影矩阵转换
glm::mat4 invVP = glm::inverse(getViewProjectionMatrix());
glm::vec4 ndc(logicPos.x, logicPos.y, 0.0f, 1.0f);
glm::vec4 world = invVP * ndc;
return Vec2(world.x, world.y);
}
/**
* @brief
* @param worldPos
* @return
*/
Vec2 Camera::worldToScreen(const Vec2 &worldPos) const {
glm::vec4 world(worldPos.x, worldPos.y, 0.0f, 1.0f);
glm::vec4 screen = getViewProjectionMatrix() * world;
Vec2 logicPos(screen.x, screen.y);
// 如果有视口适配器,转换到屏幕坐标
if (viewportAdapter_) {
return viewportAdapter_->logicToScreen(logicPos);
}
return logicPos;
}
/**
* @brief
* @param x X坐标
* @param y Y坐标
* @return
*/
Vec2 Camera::screenToWorld(float x, float y) const {
return screenToWorld(Vec2(x, y));
}
/**
* @brief
* @param x X坐标
* @param y Y坐标
* @return
*/
Vec2 Camera::worldToScreen(float x, float y) const {
return worldToScreen(Vec2(x, y));
}
/**
* @brief
* @param offset
*
*
*/
void Camera::move(const Vec2 &offset) {
position_ += offset;
viewDirty_ = true;
}
/**
* @brief
* @param x X方向偏移量
* @param y Y方向偏移量
*
*
*/
void Camera::move(float x, float y) {
position_.x += x;
position_.y += y;
viewDirty_ = true;
}
/**
* @brief
* @param bounds
*
*
*/
void Camera::setBounds(const Rect &bounds) {
bounds_ = bounds;
hasBounds_ = true;
}
/**
* @brief
*
*
*/
void Camera::clearBounds() { hasBounds_ = false; }
/**
* @brief
*
*
*/
void Camera::clampToBounds() {
if (!hasBounds_)
return;
float viewportWidth = (right_ - left_) / zoom_;
float viewportHeight = (bottom_ - top_) / zoom_;
float minX = bounds_.left() + viewportWidth * 0.5f;
float maxX = bounds_.right() - viewportWidth * 0.5f;
float minY = bounds_.top() + viewportHeight * 0.5f;
float maxY = bounds_.bottom() - viewportHeight * 0.5f;
if (minX > maxX) {
position_.x = bounds_.center().x;
} else {
position_.x = std::clamp(position_.x, minX, maxX);
}
if (minY > maxY) {
position_.y = bounds_.center().y;
} else {
position_.y = std::clamp(position_.y, minY, maxY);
}
viewDirty_ = true;
}
/**
* @brief
* @param target
*
*
*/
void Camera::lookAt(const Vec2 &target) {
position_ = target;
viewDirty_ = true;
}
/**
* @brief
* @param adapter
*/
void Camera::setViewportAdapter(ViewportAdapter *adapter) {
viewportAdapter_ = adapter;
}
/**
* @brief
*
*
*/
void Camera::applyViewportAdapter() {
if (viewportAdapter_) {
const auto &config = viewportAdapter_->getConfig();
setViewport(0.0f, config.logicWidth, config.logicHeight, 0.0f);
}
}
} // namespace extra2d

View File

@ -1,451 +0,0 @@
#include <extra2d/graphics/camera/viewport_adapter.h>
#include <algorithm>
#include <glm/gtc/matrix_transform.hpp>
namespace extra2d {
/**
* @brief
*
*
*/
ViewportAdapter::ViewportAdapter() = default;
/**
* @brief
* @param logicWidth
* @param logicHeight
*
*
*/
ViewportAdapter::ViewportAdapter(float logicWidth, float logicHeight) {
config_.logicWidth = logicWidth;
config_.logicHeight = logicHeight;
}
/**
* @brief
* @param config
*
*
*/
void ViewportAdapter::setConfig(const ViewportConfig &config) {
config_ = config;
matrixDirty_ = true;
}
/**
* @brief
* @param width
* @param height
*
*
*/
void ViewportAdapter::setLogicSize(float width, float height) {
config_.logicWidth = width;
config_.logicHeight = height;
matrixDirty_ = true;
}
/**
* @brief
* @param mode
*
*
*/
void ViewportAdapter::setMode(ViewportMode mode) {
config_.mode = mode;
matrixDirty_ = true;
}
/**
* @brief
* @param position
*
*
*/
void ViewportAdapter::setLetterboxPosition(LetterboxPosition position) {
config_.letterboxPosition = position;
matrixDirty_ = true;
}
/**
* @brief
* @param color
*
*
*/
void ViewportAdapter::setLetterboxColor(const Color &color) {
config_.letterboxColor = color;
}
/**
* @brief
* @param screenWidth
* @param screenHeight
*
*
*/
void ViewportAdapter::update(int screenWidth, int screenHeight) {
if (screenWidth_ == screenWidth && screenHeight_ == screenHeight &&
!matrixDirty_) {
return;
}
screenWidth_ = screenWidth;
screenHeight_ = screenHeight;
matrixDirty_ = true;
result_.hasLetterbox = false;
result_.letterbox.top = Rect::Zero();
result_.letterbox.bottom = Rect::Zero();
result_.letterbox.left = Rect::Zero();
result_.letterbox.right = Rect::Zero();
switch (config_.mode) {
case ViewportMode::AspectRatio:
calculateAspectRatio();
break;
case ViewportMode::Stretch:
calculateStretch();
break;
case ViewportMode::Center:
calculateCenter();
break;
case ViewportMode::Custom:
calculateCustom();
break;
}
}
/**
* @brief
*
*
*/
void ViewportAdapter::calculateAspectRatio() {
if (config_.logicHeight <= 0.0f || screenHeight_ <= 0) {
result_ = ViewportResult();
return;
}
float logicAspect = config_.logicWidth / config_.logicHeight;
float screenAspect = static_cast<float>(screenWidth_) / screenHeight_;
if (screenAspect > logicAspect) {
result_.uniformScale = static_cast<float>(screenHeight_) / config_.logicHeight;
result_.scaleX = result_.uniformScale;
result_.scaleY = result_.uniformScale;
result_.viewport.size.width = config_.logicWidth * result_.uniformScale;
result_.viewport.size.height = static_cast<float>(screenHeight_);
result_.offset.x = (screenWidth_ - result_.viewport.size.width) / 2.0f;
result_.offset.y = 0.0f;
} else {
result_.uniformScale = static_cast<float>(screenWidth_) / config_.logicWidth;
result_.scaleX = result_.uniformScale;
result_.scaleY = result_.uniformScale;
result_.viewport.size.width = static_cast<float>(screenWidth_);
result_.viewport.size.height = config_.logicHeight * result_.uniformScale;
result_.offset.x = 0.0f;
result_.offset.y = (screenHeight_ - result_.viewport.size.height) / 2.0f;
}
result_.viewport.origin = result_.offset;
applyLetterboxPosition(
static_cast<float>(screenWidth_) - result_.viewport.size.width,
static_cast<float>(screenHeight_) - result_.viewport.size.height);
calculateLetterbox();
}
/**
* @brief
*
*
*/
void ViewportAdapter::calculateStretch() {
result_.scaleX = static_cast<float>(screenWidth_) / config_.logicWidth;
result_.scaleY = static_cast<float>(screenHeight_) / config_.logicHeight;
result_.uniformScale = std::min(result_.scaleX, result_.scaleY);
result_.viewport.origin = Vec2::Zero();
result_.viewport.size.width = static_cast<float>(screenWidth_);
result_.viewport.size.height = static_cast<float>(screenHeight_);
result_.offset = Vec2::Zero();
result_.hasLetterbox = false;
}
/**
* @brief
*
*
*/
void ViewportAdapter::calculateCenter() {
float displayWidth = config_.logicWidth;
float displayHeight = config_.logicHeight;
if (config_.autoScaleInCenterMode) {
float scaleX = static_cast<float>(screenWidth_) / config_.logicWidth;
float scaleY = static_cast<float>(screenHeight_) / config_.logicHeight;
result_.uniformScale = std::min(scaleX, scaleY);
if (result_.uniformScale < 1.0f) {
displayWidth = config_.logicWidth * result_.uniformScale;
displayHeight = config_.logicHeight * result_.uniformScale;
} else {
result_.uniformScale = 1.0f;
}
result_.scaleX = result_.uniformScale;
result_.scaleY = result_.uniformScale;
} else {
result_.scaleX = 1.0f;
result_.scaleY = 1.0f;
result_.uniformScale = 1.0f;
}
result_.offset.x = (screenWidth_ - displayWidth) / 2.0f;
result_.offset.y = (screenHeight_ - displayHeight) / 2.0f;
result_.viewport.origin = result_.offset;
result_.viewport.size.width = displayWidth;
result_.viewport.size.height = displayHeight;
applyLetterboxPosition(
static_cast<float>(screenWidth_) - displayWidth,
static_cast<float>(screenHeight_) - displayHeight);
calculateLetterbox();
}
/**
* @brief
*
* 使
*/
void ViewportAdapter::calculateCustom() {
result_.scaleX = config_.customScale;
result_.scaleY = config_.customScale;
result_.uniformScale = config_.customScale;
if (config_.customViewport.empty()) {
float displayWidth = config_.logicWidth * config_.customScale;
float displayHeight = config_.logicHeight * config_.customScale;
result_.offset = config_.customOffset;
result_.viewport.origin = result_.offset;
result_.viewport.size.width = displayWidth;
result_.viewport.size.height = displayHeight;
} else {
result_.viewport = config_.customViewport;
result_.offset = config_.customViewport.origin;
}
calculateLetterbox();
}
/**
* @brief
*
*
*/
void ViewportAdapter::calculateLetterbox() {
result_.hasLetterbox = false;
float screenW = static_cast<float>(screenWidth_);
float screenH = static_cast<float>(screenHeight_);
if (result_.offset.y > 0.0f) {
result_.hasLetterbox = true;
result_.letterbox.top =
Rect(0.0f, 0.0f, screenW, result_.offset.y);
result_.letterbox.bottom =
Rect(0.0f, result_.offset.y + result_.viewport.size.height, screenW,
result_.offset.y);
}
if (result_.offset.x > 0.0f) {
result_.hasLetterbox = true;
result_.letterbox.left =
Rect(0.0f, 0.0f, result_.offset.x, screenH);
result_.letterbox.right =
Rect(result_.offset.x + result_.viewport.size.width, 0.0f,
result_.offset.x, screenH);
}
}
/**
* @brief
* @param extraWidth
* @param extraHeight
*
*
*/
void ViewportAdapter::applyLetterboxPosition(float extraWidth,
float extraHeight) {
if (extraWidth <= 0.0f && extraHeight <= 0.0f) {
return;
}
switch (config_.letterboxPosition) {
case LetterboxPosition::Center:
break;
case LetterboxPosition::LeftTop:
if (extraWidth > 0.0f) {
result_.offset.x = 0.0f;
}
if (extraHeight > 0.0f) {
result_.offset.y = 0.0f;
}
break;
case LetterboxPosition::RightTop:
if (extraWidth > 0.0f) {
result_.offset.x = extraWidth;
}
if (extraHeight > 0.0f) {
result_.offset.y = 0.0f;
}
break;
case LetterboxPosition::LeftBottom:
if (extraWidth > 0.0f) {
result_.offset.x = 0.0f;
}
if (extraHeight > 0.0f) {
result_.offset.y = extraHeight;
}
break;
case LetterboxPosition::RightBottom:
if (extraWidth > 0.0f) {
result_.offset.x = extraWidth;
}
if (extraHeight > 0.0f) {
result_.offset.y = extraHeight;
}
break;
}
result_.viewport.origin = result_.offset;
}
/**
* @brief
* @param screenPos
* @return
*
*
*/
Vec2 ViewportAdapter::screenToLogic(const Vec2 &screenPos) const {
return Vec2((screenPos.x - result_.offset.x) / result_.scaleX,
(screenPos.y - result_.offset.y) / result_.scaleY);
}
/**
* @brief
* @param logicPos
* @return
*
*
*/
Vec2 ViewportAdapter::logicToScreen(const Vec2 &logicPos) const {
return Vec2(logicPos.x * result_.scaleX + result_.offset.x,
logicPos.y * result_.scaleY + result_.offset.y);
}
/**
* @brief
* @param x X坐标
* @param y Y坐标
* @return
*/
Vec2 ViewportAdapter::screenToLogic(float x, float y) const {
return screenToLogic(Vec2(x, y));
}
/**
* @brief
* @param x X坐标
* @param y Y坐标
* @return
*/
Vec2 ViewportAdapter::logicToScreen(float x, float y) const {
return logicToScreen(Vec2(x, y));
}
/**
* @brief
* @return 4x4变换矩阵
*
*
*/
glm::mat4 ViewportAdapter::getMatrix() const {
if (matrixDirty_) {
viewportMatrix_ = glm::mat4(1.0f);
viewportMatrix_ = glm::translate(viewportMatrix_,
glm::vec3(result_.offset.x, result_.offset.y, 0.0f));
viewportMatrix_ = glm::scale(viewportMatrix_,
glm::vec3(result_.scaleX, result_.scaleY, 1.0f));
matrixDirty_ = false;
}
return viewportMatrix_;
}
/**
* @brief
* @return 4x4逆变换矩阵
*
*
*/
glm::mat4 ViewportAdapter::getInvMatrix() const {
if (matrixDirty_) {
getMatrix();
}
inverseViewportMatrix_ = glm::inverse(viewportMatrix_);
return inverseViewportMatrix_;
}
/**
* @brief
* @param screenPos
* @return truefalse
*/
bool ViewportAdapter::isInViewport(const Vec2 &screenPos) const {
return result_.viewport.containsPoint(screenPos);
}
/**
* @brief
* @param screenPos
* @return truefalse
*/
bool ViewportAdapter::isInLetterbox(const Vec2 &screenPos) const {
if (!result_.hasLetterbox) {
return false;
}
if (!result_.letterbox.top.empty() &&
result_.letterbox.top.containsPoint(screenPos)) {
return true;
}
if (!result_.letterbox.bottom.empty() &&
result_.letterbox.bottom.containsPoint(screenPos)) {
return true;
}
if (!result_.letterbox.left.empty() &&
result_.letterbox.left.containsPoint(screenPos)) {
return true;
}
if (!result_.letterbox.right.empty() &&
result_.letterbox.right.containsPoint(screenPos)) {
return true;
}
return false;
}
} // namespace extra2d

View File

@ -1,266 +0,0 @@
#include <extra2d/graphics/core/render_command.h>
#include <algorithm>
namespace extra2d {
// ============================================================================
// RenderCommand 便捷构造函数
// ============================================================================
/**
* @brief
*
* 2D精灵的RenderCommand对象
*
*
* @param tex
* @param dest
* @param src
* @param tint
* @param rot
* @param anc 0.0-1.0
* @param lyr
* @return RenderCommand对象
*/
RenderCommand RenderCommand::makeSprite(const Texture* tex, const Rect& dest,
const Rect& src, const Color& tint,
float rot, const Vec2& anc,
uint32_t lyr) {
RenderCommand cmd;
cmd.type = RenderCommandType::Sprite;
cmd.layer = lyr;
cmd.transform = glm::mat4(1.0f);
SpriteCommandData data;
data.texture = tex;
data.destRect = dest;
data.srcRect = src;
data.tint = tint;
data.rotation = rot;
data.anchor = anc;
// 生成排序键:纹理指针的高位 + 层级的低位
data.sortKey = (reinterpret_cast<uintptr_t>(tex) >> 4) & 0xFFFFFFF0;
data.sortKey |= (lyr & 0xF);
cmd.data = data;
return cmd;
}
/**
* @brief 线
*
* 线RenderCommand对象
*
* @param s 线
* @param e 线
* @param c 线
* @param w 线
* @param lyr
* @return RenderCommand对象
*/
RenderCommand RenderCommand::makeLine(const Vec2& s, const Vec2& e, const Color& c,
float w, uint32_t lyr) {
RenderCommand cmd;
cmd.type = RenderCommandType::Line;
cmd.layer = lyr;
cmd.transform = glm::mat4(1.0f);
LineCommandData data;
data.start = s;
data.end = e;
data.color = c;
data.width = w;
cmd.data = data;
return cmd;
}
/**
* @brief
*
* RenderCommand对象
*
* @param r
* @param c
* @param w 线
* @param fill
* @param lyr
* @return RenderCommand对象
*/
RenderCommand RenderCommand::makeRect(const Rect& r, const Color& c,
float w, bool fill, uint32_t lyr) {
RenderCommand cmd;
cmd.type = fill ? RenderCommandType::FilledRect : RenderCommandType::Rect;
cmd.layer = lyr;
cmd.transform = glm::mat4(1.0f);
RectCommandData data;
data.rect = r;
data.color = c;
data.width = w;
data.filled = fill;
cmd.data = data;
return cmd;
}
// ============================================================================
// RenderCommandBuffer 实现
// ============================================================================
/**
* @brief
*
*
*/
RenderCommandBuffer::RenderCommandBuffer() : nextOrder_(0) {
commands_.reserve(INITIAL_CAPACITY);
}
/**
* @brief
*
*
*/
RenderCommandBuffer::~RenderCommandBuffer() = default;
/**
* @brief
*
*
*
* @param cmd
*/
void RenderCommandBuffer::addCommand(const RenderCommand& cmd) {
if (commands_.size() >= MAX_CAPACITY) {
// 缓冲区已满,可能需要立即刷新
return;
}
RenderCommand copy = cmd;
copy.order = nextOrder_++;
commands_.push_back(std::move(copy));
}
/**
* @brief
*
*
*
* @param cmd
*/
void RenderCommandBuffer::addCommand(RenderCommand&& cmd) {
if (commands_.size() >= MAX_CAPACITY) {
return;
}
cmd.order = nextOrder_++;
commands_.push_back(std::move(cmd));
}
/**
* @brief
*
*
*
* @return
*/
RenderCommand& RenderCommandBuffer::emplaceCommand() {
if (commands_.size() >= MAX_CAPACITY) {
// 如果已满,返回一个虚拟命令(不应该发生)
static RenderCommand dummy;
return dummy;
}
commands_.emplace_back();
commands_.back().order = nextOrder_++;
return commands_.back();
}
/**
* @brief
*
* /
*
*/
void RenderCommandBuffer::sortCommands() {
// 按以下优先级排序:
// 1. 层级 (layer) - 低层级先渲染
// 2. 命令类型 - 精灵类命令优先批处理
// 3. 纹理/材质 - 相同纹理的精灵连续渲染
// 4. 提交顺序 - 保证稳定性
std::sort(commands_.begin(), commands_.end(), compareCommands);
}
/**
* @brief
*
*
*/
void RenderCommandBuffer::clear() {
commands_.clear();
nextOrder_ = 0;
}
/**
* @brief
*
*
*
* @param capacity
*/
void RenderCommandBuffer::reserve(size_t capacity) {
if (capacity <= MAX_CAPACITY) {
commands_.reserve(capacity);
}
}
/**
* @brief
*
*
*
* @param a
* @param b
* @return a应排在b前面返回truefalse
*/
bool RenderCommandBuffer::compareCommands(const RenderCommand& a, const RenderCommand& b) {
// 首先按层级排序
if (a.layer != b.layer) {
return a.layer < b.layer;
}
// 然后按类型排序(精灵类命令放在一起以便批处理)
if (a.type != b.type) {
// 精灵和文本命令优先(需要纹理)
bool aIsSprite = (a.type == RenderCommandType::Sprite ||
a.type == RenderCommandType::Text);
bool bIsSprite = (b.type == RenderCommandType::Sprite ||
b.type == RenderCommandType::Text);
if (aIsSprite != bIsSprite) {
return aIsSprite > bIsSprite; // 精灵类命令在前
}
return static_cast<uint8_t>(a.type) < static_cast<uint8_t>(b.type);
}
// 对于精灵命令,按纹理排序
if (a.type == RenderCommandType::Sprite && b.type == RenderCommandType::Sprite) {
const auto& dataA = std::get<SpriteCommandData>(a.data);
const auto& dataB = std::get<SpriteCommandData>(b.data);
if (dataA.texture != dataB.texture) {
return dataA.texture < dataB.texture;
}
// 相同纹理时按 sortKey 排序
if (dataA.sortKey != dataB.sortKey) {
return dataA.sortKey < dataB.sortKey;
}
}
// 最后按提交顺序排序(保证稳定性)
return a.order < b.order;
}
} // namespace extra2d

View File

@ -1,71 +0,0 @@
#include <extra2d/app/application.h>
#include <extra2d/core/registry.h>
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/core/render_module.h>
#include <extra2d/graphics/opengl/gl_renderer.h>
#include <extra2d/graphics/opengl/gl_shader.h>
#include <extra2d/graphics/shader/shader_manager.h>
#include <extra2d/platform/window_module.h>
#include <extra2d/services/logger_service.h>
namespace extra2d {
RenderModule::RenderModule(std::function<void(RenderCfg &)> configFn) {
configFn(cfg_);
}
RenderModule::~RenderModule() {
if (initialized_) {
shutdown();
}
}
bool RenderModule::init() {
if (initialized_)
return true;
auto *winMod = Registry::instance().get<WindowModule>();
if (!winMod || !winMod->win()) {
E2D_LOG_ERROR("窗口模块不可用");
return false;
}
if (!ShaderManager::getInstance().isInitialized()) {
auto factory = makeShared<GLShaderFactory>();
if (!ShaderManager::getInstance().init(factory)) {
E2D_LOG_WARN("使用默认路径初始化 ShaderManager 失败");
}
}
E2D_LOG_INFO("正在创建 OpenGL 渲染后端");
renderer_ = makeUnique<Renderer>();
if (!renderer_) {
E2D_LOG_ERROR("创建渲染后端失败");
return false;
}
if (!renderer_->init(winMod->win())) {
E2D_LOG_ERROR("初始化渲染后端失败");
renderer_.reset();
return false;
}
E2D_LOG_INFO("渲染模块初始化成功");
initialized_ = true;
return true;
}
void RenderModule::shutdown() {
if (!initialized_)
return;
if (renderer_) {
renderer_->shutdown();
renderer_.reset();
}
initialized_ = false;
}
} // namespace extra2d

View File

@ -1,779 +0,0 @@
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/core/render_target.h>
#include <extra2d/graphics/opengl/gl_texture.h>
#include <extra2d/services/logger_service.h>
#include <glad/glad.h>
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb/stb_image_write.h>
namespace extra2d {
// ============================================================================
// RenderTarget实现
// ============================================================================
/**
* @brief
*
*
*/
RenderTarget::RenderTarget() = default;
/**
* @brief
*
*
*/
RenderTarget::~RenderTarget() { destroy(); }
/**
* @brief
* @param other
*
*
*/
RenderTarget::RenderTarget(RenderTarget &&other) noexcept
: fbo_(other.fbo_), rbo_(other.rbo_),
colorTexture_(std::move(other.colorTexture_)),
depthTexture_(std::move(other.depthTexture_)), width_(other.width_),
height_(other.height_), colorFormat_(other.colorFormat_),
hasDepth_(other.hasDepth_), hasStencil_(other.hasStencil_),
samples_(other.samples_) {
other.fbo_ = 0;
other.rbo_ = 0;
other.width_ = 0;
other.height_ = 0;
}
/**
* @brief
* @param other
* @return
*
*
*/
RenderTarget &RenderTarget::operator=(RenderTarget &&other) noexcept {
if (this != &other) {
destroy();
fbo_ = other.fbo_;
rbo_ = other.rbo_;
colorTexture_ = std::move(other.colorTexture_);
depthTexture_ = std::move(other.depthTexture_);
width_ = other.width_;
height_ = other.height_;
colorFormat_ = other.colorFormat_;
hasDepth_ = other.hasDepth_;
hasStencil_ = other.hasStencil_;
samples_ = other.samples_;
other.fbo_ = 0;
other.rbo_ = 0;
other.width_ = 0;
other.height_ = 0;
}
return *this;
}
/**
* @brief
* @param config
* @return truefalse
*
*
*/
bool RenderTarget::create(const RenderTargetConfig &config) {
destroy();
width_ = config.width;
height_ = config.height;
colorFormat_ = config.colorFormat;
hasDepth_ = config.hasDepth;
hasStencil_ = config.hasStencil;
samples_ = config.samples;
if (!createFBO()) {
E2D_ERROR("创建渲染目标失败: {}x{}", width_, height_);
return false;
}
E2D_INFO("创建渲染目标: {}x{} (深度:{}, 模板:{}, 采样:{})", width_, height_,
hasDepth_, hasStencil_, samples_);
return true;
}
/**
* @brief
* @param texture
* @param hasDepth
* @return truefalse
*
* 使
*/
bool RenderTarget::createFromTexture(Ptr<Texture> texture, bool hasDepth) {
if (!texture || !texture->isValid()) {
E2D_ERROR("无效的颜色纹理");
return false;
}
destroy();
width_ = texture->getWidth();
height_ = texture->getHeight();
colorFormat_ = texture->getFormat();
hasDepth_ = hasDepth;
hasStencil_ = false;
samples_ = 1;
colorTexture_ = texture;
// 创建FBO
glGenFramebuffers(1, &fbo_);
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
// 附加颜色纹理
GLuint texId = static_cast<GLuint>(
reinterpret_cast<uintptr_t>(texture->getNativeHandle()));
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
texId, 0);
// 创建深度缓冲(如果需要)
if (hasDepth_) {
glGenRenderbuffers(1, &rbo_);
glBindRenderbuffer(GL_RENDERBUFFER, rbo_);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width_,
height_);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, rbo_);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
}
// 检查完整性
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
if (status != GL_FRAMEBUFFER_COMPLETE) {
E2D_ERROR("FBO不完整: {:#x}", status);
destroy();
return false;
}
E2D_INFO("从纹理创建渲染目标: {}x{}", width_, height_);
return true;
}
/**
* @brief
*
*
*/
void RenderTarget::destroy() {
deleteFBO();
colorTexture_.reset();
depthTexture_.reset();
width_ = 0;
height_ = 0;
}
/**
* @brief
*
*
*/
void RenderTarget::bind() {
if (!isValid()) {
return;
}
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
glViewport(0, 0, width_, height_);
}
/**
* @brief
*
*
*/
void RenderTarget::unbind() { bindDefault(); }
/**
* @brief
* @param color
*
* 使/
*/
void RenderTarget::clear(const Color &color) {
if (!isValid()) {
return;
}
bind();
GLbitfield mask = GL_COLOR_BUFFER_BIT;
if (hasDepth_) {
mask |= GL_DEPTH_BUFFER_BIT;
glClearDepthf(1.0f); // GLES 使用 glClearDepthf
}
if (hasStencil_) {
mask |= GL_STENCIL_BUFFER_BIT;
glClearStencil(0);
}
glClearColor(color.r, color.g, color.b, color.a);
glClear(mask);
}
/**
* @brief
* @param x X坐标
* @param y Y坐标
* @param width
* @param height
*
*
*/
void RenderTarget::setViewport(int x, int y, int width, int height) {
if (!isValid()) {
return;
}
bind();
glViewport(x, y, width, height);
}
/**
* @brief
* @param[out] x X坐标
* @param[out] y Y坐标
* @param[out] width
* @param[out] height
*
*
*/
void RenderTarget::getFullViewport(int &x, int &y, int &width,
int &height) const {
x = 0;
y = 0;
width = width_;
height = height_;
}
/**
* @brief
* @param width
* @param height
* @return truefalse
*
*
*/
bool RenderTarget::resize(int width, int height) {
if (!isValid()) {
return false;
}
RenderTargetConfig config;
config.width = width;
config.height = height;
config.colorFormat = colorFormat_;
config.hasDepth = hasDepth_;
config.hasStencil = hasStencil_;
config.samples = samples_;
return create(config);
}
/**
* @brief
* @param target
*
* 使glBlitFramebuffer将内容复制到目标渲染目标
*/
void RenderTarget::copyTo(RenderTarget &target) {
if (!isValid() || !target.isValid()) {
return;
}
// 使用glBlitFramebuffer复制
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target.fbo_);
GLbitfield mask = GL_COLOR_BUFFER_BIT;
if (hasDepth_ && target.hasDepth_) {
mask |= GL_DEPTH_BUFFER_BIT;
}
glBlitFramebuffer(0, 0, width_, height_, 0, 0, target.width_, target.height_,
mask, GL_LINEAR);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
/**
* @brief
* @param screenWidth
* @param screenHeight
*
*
*/
void RenderTarget::copyToScreen(int screenWidth, int screenHeight) {
if (!isValid()) {
return;
}
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, width_, height_, 0, 0, screenWidth, screenHeight,
GL_COLOR_BUFFER_BIT, GL_LINEAR);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
/**
* @brief
* @param filepath
* @return truefalse
*
* PNG图片文件
*/
bool RenderTarget::saveToFile(const std::string &filepath) {
if (!isValid() || !colorTexture_) {
return false;
}
// 读取像素数据
std::vector<uint8_t> pixels(width_ * height_ * 4);
bind();
glReadPixels(0, 0, width_, height_, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
unbind();
// 翻转Y轴OpenGL坐标系原点在左下角PNG需要左上角原点
std::vector<uint8_t> flipped(width_ * height_ * 4);
for (int y = 0; y < height_; ++y) {
for (int x = 0; x < width_; ++x) {
int srcIdx = ((height_ - 1 - y) * width_ + x) * 4;
int dstIdx = (y * width_ + x) * 4;
for (int c = 0; c < 4; ++c) {
flipped[dstIdx + c] = pixels[srcIdx + c];
}
}
}
// 使用stb_image_write保存为PNG
int result = stbi_write_png(filepath.c_str(), width_, height_, 4,
flipped.data(), width_ * 4);
if (result == 0) {
E2D_ERROR("保存渲染目标到PNG失败: {}", filepath);
return false;
}
E2D_INFO("保存渲染目标到: {}", filepath);
return true;
}
/**
* @brief
* @param config
* @return nullptr
*
*
*/
Ptr<RenderTarget>
RenderTarget::createFromConfig(const RenderTargetConfig &config) {
auto rt = std::make_shared<RenderTarget>();
if (rt->create(config)) {
return rt;
}
return nullptr;
}
/**
* @brief ID
* @return FBO ID
*
* OpenGL当前绑定的帧缓冲对象
*/
GLuint RenderTarget::getCurrentFBO() {
GLint fbo = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &fbo);
return static_cast<GLuint>(fbo);
}
/**
* @brief
*
*
*/
void RenderTarget::bindDefault() { glBindFramebuffer(GL_FRAMEBUFFER, 0); }
// ============================================================================
// 内部方法
// ============================================================================
/**
* @brief
* @return truefalse
*
* FBO及相关附件
*/
bool RenderTarget::createFBO() {
// 创建颜色纹理
colorTexture_ = GLTexture::create(width_, height_, colorFormat_);
if (!colorTexture_ || !colorTexture_->isValid()) {
E2D_ERROR("创建颜色纹理失败");
return false;
}
// 创建FBO
glGenFramebuffers(1, &fbo_);
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
// 附加颜色纹理
GLuint colorTexId =
static_cast<GLTexture *>(colorTexture_.get())->getTextureID();
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
colorTexId, 0);
// 创建深度/模板缓冲
if (hasDepth_ || hasStencil_) {
glGenRenderbuffers(1, &rbo_);
glBindRenderbuffer(GL_RENDERBUFFER, rbo_);
if (hasDepth_ && hasStencil_) {
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width_,
height_);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, rbo_);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT,
GL_RENDERBUFFER, rbo_);
} else if (hasDepth_) {
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width_,
height_);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, rbo_);
}
glBindRenderbuffer(GL_RENDERBUFFER, 0);
}
// 检查完整性
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
if (status != GL_FRAMEBUFFER_COMPLETE) {
E2D_ERROR("FBO创建失败状态: {:#x}", status);
deleteFBO();
return false;
}
return true;
}
/**
* @brief
*
* FBO和渲染缓冲对象
*/
void RenderTarget::deleteFBO() {
if (rbo_ != 0) {
glDeleteRenderbuffers(1, &rbo_);
rbo_ = 0;
}
if (fbo_ != 0) {
glDeleteFramebuffers(1, &fbo_);
fbo_ = 0;
}
}
// ============================================================================
// MultisampleRenderTarget实现
// ============================================================================
/**
* @brief
* @param width
* @param height
* @param samples
* @return truefalse
*
* 齿
*/
bool MultisampleRenderTarget::create(int width, int height, int samples) {
// 先销毁现有的
destroy();
width_ = width;
height_ = height;
samples_ = samples;
hasDepth_ = true;
hasStencil_ = false;
colorFormat_ = PixelFormat::RGBA8;
// 创建FBO
glGenFramebuffers(1, &fbo_);
glBindFramebuffer(GL_FRAMEBUFFER, fbo_);
// 创建多重采样颜色渲染缓冲
glGenRenderbuffers(1, &colorRBO_);
glBindRenderbuffer(GL_RENDERBUFFER, colorRBO_);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, GL_RGBA8, width,
height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, colorRBO_);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// 创建多重采样深度渲染缓冲
glGenRenderbuffers(1, &rbo_);
glBindRenderbuffer(GL_RENDERBUFFER, rbo_);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples,
GL_DEPTH_COMPONENT24, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
GL_RENDERBUFFER, rbo_);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
// 检查完整性
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
if (status != GL_FRAMEBUFFER_COMPLETE) {
E2D_ERROR("多重采样FBO创建失败状态: {:#x}", status);
destroy();
return false;
}
E2D_INFO("创建多重采样渲染目标: {}x{} (采样数: {})", width, height, samples);
return true;
}
/**
* @brief
*
*
*/
void MultisampleRenderTarget::destroy() {
// 删除颜色渲染缓冲
if (colorRBO_ != 0) {
glDeleteRenderbuffers(1, &colorRBO_);
colorRBO_ = 0;
}
// 调用基类destroy
RenderTarget::destroy();
}
/**
* @brief
* @param target
*
*
*/
void MultisampleRenderTarget::resolveTo(RenderTarget &target) {
if (!isValid() || !target.isValid()) {
return;
}
// 使用glBlitFramebuffer解析多重采样
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target.getFBO());
glBlitFramebuffer(0, 0, width_, height_, 0, 0, target.getWidth(),
target.getHeight(), GL_COLOR_BUFFER_BIT, GL_LINEAR);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
// ============================================================================
// RenderTargetStack实现
// ============================================================================
/**
* @brief RenderTargetStack单例实例
* @return RenderTargetStack单例的引用
*
* 使线
*/
RenderTargetStack &RenderTargetStack::get() {
static RenderTargetStack instance;
return instance;
}
/**
* @brief
* @param target
*
*
*/
void RenderTargetStack::push(RenderTarget *target) {
std::lock_guard<std::mutex> lock(mutex_);
if (target) {
stack_.push_back(target);
target->bind();
}
}
/**
* @brief
*
*
*/
void RenderTargetStack::pop() {
std::lock_guard<std::mutex> lock(mutex_);
if (!stack_.empty()) {
stack_.pop_back();
}
// 绑定新的当前渲染目标
if (!stack_.empty()) {
stack_.back()->bind();
} else {
RenderTarget::bindDefault();
}
}
/**
* @brief
* @return nullptr
*/
RenderTarget *RenderTargetStack::getCurrent() const {
std::lock_guard<std::mutex> lock(mutex_);
if (stack_.empty()) {
return nullptr;
}
return stack_.back();
}
/**
* @brief
* @return
*/
size_t RenderTargetStack::size() const {
std::lock_guard<std::mutex> lock(mutex_);
return stack_.size();
}
/**
* @brief
*
*
*/
void RenderTargetStack::clear() {
std::lock_guard<std::mutex> lock(mutex_);
stack_.clear();
RenderTarget::bindDefault();
}
// ============================================================================
// RenderTargetMgr实现
// ============================================================================
/**
* @brief RenderTargetMgr单例实例
* @return RenderTargetMgr单例的引用
*
* 使线
*/
RenderTargetMgr &RenderTargetMgr::get() {
static RenderTargetMgr instance;
return instance;
}
/**
* @brief
* @param width
* @param height
* @return truefalse
*
*
*/
bool RenderTargetMgr::init(int width, int height) {
if (initialized_) {
return true;
}
// 创建默认渲染目标
RenderTargetConfig config;
config.width = width;
config.height = height;
config.hasDepth = true;
config.hasStencil = false;
defaultRenderTarget_ = RenderTarget::createFromConfig(config);
if (!defaultRenderTarget_) {
E2D_ERROR("创建默认渲染目标失败");
return false;
}
initialized_ = true;
E2D_INFO("渲染目标管理器初始化完成: {}x{}", width, height);
return true;
}
/**
* @brief
*
*
*/
void RenderTargetMgr::shutdown() {
if (!initialized_) {
return;
}
renderTargets_.clear();
defaultRenderTarget_.reset();
initialized_ = false;
E2D_INFO("渲染目标管理器已关闭");
}
/**
* @brief
* @param config
* @return nullptr
*
*
*/
Ptr<RenderTarget>
RenderTargetMgr::createRenderTarget(const RenderTargetConfig &config) {
if (!initialized_) {
E2D_ERROR("渲染目标管理器未初始化");
return nullptr;
}
auto rt = RenderTarget::createFromConfig(config);
if (rt) {
renderTargets_.push_back(rt);
}
return rt;
}
/**
* @brief
* @param width
* @param height
*
*
*/
void RenderTargetMgr::resize(int width, int height) {
if (!initialized_) {
return;
}
// 调整默认渲染目标大小
if (defaultRenderTarget_) {
defaultRenderTarget_->resize(width, height);
}
E2D_INFO("渲染目标管理器调整大小: {}x{}", width, height);
}
} // namespace extra2d

View File

@ -1,44 +0,0 @@
#include <extra2d/graphics/memory/gpu_context.h>
namespace extra2d {
/**
* @brief GPUContext单例实例
* @return GPUContext单例的引用
*
* 使线
*/
GPUContext& GPUContext::get() {
static GPUContext instance;
return instance;
}
/**
* @brief GPU上下文为有效状态
*
* 使true使release内存序
*/
void GPUContext::markValid() {
valid_.store(true, std::memory_order_release);
}
/**
* @brief GPU上下文为无效状态
*
* 使false使release内存序
*/
void GPUContext::markInvalid() {
valid_.store(false, std::memory_order_release);
}
/**
* @brief GPU上下文是否有效
* @return GPU上下文有效返回truefalse
*
* 使使acquire内存序
*/
bool GPUContext::isValid() const {
return valid_.load(std::memory_order_acquire);
}
} // namespace extra2d

View File

@ -1,194 +0,0 @@
#include <algorithm>
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/memory/vram_manager.h>
#include <extra2d/services/logger_service.h>
namespace extra2d {
// Switch 推荐 VRAM 预算 ~400MB
static constexpr size_t DEFAULT_VRAM_BUDGET = 400 * 1024 * 1024;
/**
* @brief
*
* VRAM管理器400MB
*/
VRAMMgr::VRAMMgr()
: textureVRAM_(0), bufferVRAM_(0), vramBudget_(DEFAULT_VRAM_BUDGET),
textureAllocCount_(0), textureFreeCount_(0), bufferAllocCount_(0),
bufferFreeCount_(0), peakTextureVRAM_(0), peakBufferVRAM_(0) {}
/**
* @brief VRAMMgr单例实例
* @return VRAMMgr单例的引用
*
* 使线
*/
VRAMMgr &VRAMMgr::get() {
static VRAMMgr instance;
return instance;
}
/**
* @brief VRAM
* @param size
*
* VRAM使用量并更新峰值
*/
void VRAMMgr::allocTexture(size_t size) {
std::lock_guard<std::mutex> lock(mutex_);
textureVRAM_ += size;
textureAllocCount_++;
peakTextureVRAM_ = std::max(peakTextureVRAM_, textureVRAM_);
if (isOverBudget()) {
E2D_LOG_WARN("显存超出预算! 已使用: {} MB / 预算: {} MB",
getUsedVRAM() / (1024 * 1024), vramBudget_ / (1024 * 1024));
}
}
/**
* @brief VRAM
* @param size
*
* VRAM使用量
*/
void VRAMMgr::freeTexture(size_t size) {
std::lock_guard<std::mutex> lock(mutex_);
if (size <= textureVRAM_) {
textureVRAM_ -= size;
} else {
textureVRAM_ = 0;
}
textureFreeCount_++;
}
/**
* @brief VRAM
* @param size
*
* VRAM使用量并更新峰值
*/
void VRAMMgr::allocBuffer(size_t size) {
std::lock_guard<std::mutex> lock(mutex_);
bufferVRAM_ += size;
bufferAllocCount_++;
peakBufferVRAM_ = std::max(peakBufferVRAM_, bufferVRAM_);
if (isOverBudget()) {
E2D_LOG_WARN("显存超出预算! 已使用: {} MB / 预算: {} MB",
getUsedVRAM() / (1024 * 1024), vramBudget_ / (1024 * 1024));
}
}
/**
* @brief VRAM
* @param size
*
* VRAM使用量
*/
void VRAMMgr::freeBuffer(size_t size) {
std::lock_guard<std::mutex> lock(mutex_);
if (size <= bufferVRAM_) {
bufferVRAM_ -= size;
} else {
bufferVRAM_ = 0;
}
bufferFreeCount_++;
}
/**
* @brief VRAM使用量
* @return 使
*
* VRAM使用量的总和
*/
size_t VRAMMgr::getUsedVRAM() const { return textureVRAM_ + bufferVRAM_; }
/**
* @brief VRAM使用量
* @return 使
*/
size_t VRAMMgr::getTextureVRAM() const { return textureVRAM_; }
/**
* @brief VRAM使用量
* @return 使
*/
size_t VRAMMgr::getBufferVRAM() const { return bufferVRAM_; }
/**
* @brief VRAM
* @return
*
* VRAM空间
*/
size_t VRAMMgr::getAvailableVRAM() const {
size_t used = getUsedVRAM();
return (used < vramBudget_) ? (vramBudget_ - used) : 0;
}
/**
* @brief VRAM预算
* @param budget
*
* VRAM使用上限
*/
void VRAMMgr::setVRAMBudget(size_t budget) {
std::lock_guard<std::mutex> lock(mutex_);
vramBudget_ = budget;
E2D_LOG_INFO("显存预算设置为 {} MB", budget / (1024 * 1024));
}
/**
* @brief VRAM预算
* @return
*/
size_t VRAMMgr::getVRAMBudget() const { return vramBudget_; }
/**
* @brief
* @return truefalse
*/
bool VRAMMgr::isOverBudget() const { return getUsedVRAM() > vramBudget_; }
/**
* @brief VRAM统计信息
*
* VRAM使用情况/
*/
void VRAMMgr::printStats() const {
std::lock_guard<std::mutex> lock(mutex_);
E2D_LOG_INFO("=== 显存统计 ===");
E2D_LOG_INFO(" 纹理显存: {} MB (峰值: {} MB)",
textureVRAM_ / (1024 * 1024), peakTextureVRAM_ / (1024 * 1024));
E2D_LOG_INFO(" 缓冲显存: {} MB (峰值: {} MB)",
bufferVRAM_ / (1024 * 1024), peakBufferVRAM_ / (1024 * 1024));
E2D_LOG_INFO(" 总计使用: {} MB / {} MB 预算",
(textureVRAM_ + bufferVRAM_) / (1024 * 1024),
vramBudget_ / (1024 * 1024));
E2D_LOG_INFO(" 纹理分配/释放次数: {} / {}", textureAllocCount_,
textureFreeCount_);
E2D_LOG_INFO(" 缓冲分配/释放次数: {} / {}", bufferAllocCount_,
bufferFreeCount_);
}
/**
* @brief
*
* VRAM计数器和峰值记录
*/
void VRAMMgr::reset() {
std::lock_guard<std::mutex> lock(mutex_);
textureVRAM_ = 0;
bufferVRAM_ = 0;
textureAllocCount_ = 0;
textureFreeCount_ = 0;
bufferAllocCount_ = 0;
bufferFreeCount_ = 0;
peakTextureVRAM_ = 0;
peakBufferVRAM_ = 0;
}
} // namespace extra2d

View File

@ -1,171 +0,0 @@
#include <cstring>
#include <extra2d/graphics/memory/vram_manager.h>
#include <extra2d/graphics/opengl/gl_buffer.h>
#include <extra2d/services/logger_service.h>
namespace extra2d {
// ============================================================================
// GLBuffer 实现
// ============================================================================
GLBuffer::GLBuffer() = default;
GLBuffer::~GLBuffer() { shutdown(); }
bool GLBuffer::init(const BufferDesc &desc) {
if (bufferID_ != 0) {
shutdown();
}
type_ = desc.type;
usage_ = desc.usage;
size_ = desc.size;
target_ = convertType(type_);
glUsage_ = convertUsage(usage_);
// 生成缓冲区
glGenBuffers(1, &bufferID_);
if (bufferID_ == 0) {
E2D_LOG_ERROR("生成 OpenGL 缓冲区失败");
return false;
}
// 绑定并分配缓冲区
glBindBuffer(target_, bufferID_);
glBufferData(target_, static_cast<GLsizeiptr>(size_), desc.initialData,
glUsage_);
glBindBuffer(target_, 0);
// 追踪显存使用
VRAMMgr::get().allocBuffer(size_);
E2D_LOG_DEBUG("GLBuffer 已创建: ID={}, 大小={}, 类型={}, 用途={}", bufferID_,
size_, static_cast<int>(type_), static_cast<int>(usage_));
return true;
}
void GLBuffer::shutdown() {
if (bufferID_ != 0) {
if (mapped_) {
unmap();
}
// 释放显存追踪
VRAMMgr::get().freeBuffer(size_);
glDeleteBuffers(1, &bufferID_);
E2D_LOG_DEBUG("GLBuffer 已销毁: ID={}", bufferID_);
bufferID_ = 0;
}
size_ = 0;
mapped_ = false;
mappedPtr_ = nullptr;
}
void GLBuffer::bind() {
if (bufferID_ != 0) {
glBindBuffer(target_, bufferID_);
}
}
void GLBuffer::unbind() { glBindBuffer(target_, 0); }
void GLBuffer::setData(const void *data, size_t size) {
if (bufferID_ == 0) {
return;
}
bind();
// 如果大小相同,使用 glBufferSubData 更高效
if (size == size_) {
glBufferSubData(target_, 0, static_cast<GLsizeiptr>(size), data);
} else {
// 大小不同,重新分配
size_ = size;
glBufferData(target_, static_cast<GLsizeiptr>(size_), data, glUsage_);
}
unbind();
}
void GLBuffer::updateData(const void *data, size_t offset, size_t size) {
if (bufferID_ == 0 || data == nullptr || size == 0) {
return;
}
if (offset + size > size_) {
E2D_LOG_WARN(
"GLBuffer updateData out of bounds: offset={}, size={}, bufferSize={}",
offset, size, size_);
return;
}
bind();
glBufferSubData(target_, static_cast<GLintptr>(offset),
static_cast<GLsizeiptr>(size), data);
unbind();
}
void *GLBuffer::map() {
if (bufferID_ == 0 || mapped_) {
return nullptr;
}
bind();
// 使用 glMapBufferRange 替代 glMapBuffer更现代且安全
GLbitfield access = GL_MAP_WRITE_BIT;
if (usage_ == BufferUsage::Dynamic || usage_ == BufferUsage::Stream) {
access |= GL_MAP_INVALIDATE_BUFFER_BIT; // 暗示驱动可以丢弃旧数据
}
mappedPtr_ =
glMapBufferRange(target_, 0, static_cast<GLsizeiptr>(size_), access);
if (mappedPtr_) {
mapped_ = true;
} else {
E2D_LOG_ERROR("映射 GLBuffer 失败");
}
return mappedPtr_;
}
void GLBuffer::unmap() {
if (!mapped_ || bufferID_ == 0) {
return;
}
glUnmapBuffer(target_);
mapped_ = false;
mappedPtr_ = nullptr;
unbind();
}
GLenum GLBuffer::convertUsage(BufferUsage usage) {
switch (usage) {
case BufferUsage::Static:
return GL_STATIC_DRAW;
case BufferUsage::Dynamic:
return GL_DYNAMIC_DRAW;
case BufferUsage::Stream:
return GL_STREAM_DRAW;
default:
return GL_STATIC_DRAW;
}
}
GLenum GLBuffer::convertType(BufferType type) {
switch (type) {
case BufferType::Vertex:
return GL_ARRAY_BUFFER;
case BufferType::Index:
return GL_ELEMENT_ARRAY_BUFFER;
case BufferType::Uniform:
return GL_UNIFORM_BUFFER;
default:
return GL_ARRAY_BUFFER;
}
}
} // namespace extra2d

View File

@ -1,169 +0,0 @@
#include <extra2d/graphics/memory/gpu_context.h>
#include <extra2d/graphics/opengl/gl_context.h>
#include <extra2d/services/logger_service.h>
namespace extra2d {
// ============================================================================
// GLContext 实现
// ============================================================================
GLContext &GLContext::get() {
static GLContext instance;
return instance;
}
bool GLContext::init() {
if (initialized_) {
return true;
}
// 解析 OpenGL 版本
parseVersion();
// 加载扩展GLAD 已在 glad.c 中完成)
if (!loadExtensions()) {
E2D_LOG_ERROR("加载 OpenGL 扩展失败");
return false;
}
initialized_ = true;
// 标记 GPU 上下文为有效
GPUContext::get().markValid();
E2D_LOG_INFO("OpenGL 上下文已初始化");
E2D_LOG_INFO(" 版本: {}", getVersionString());
E2D_LOG_INFO(" 供应商: {}", getVendor());
E2D_LOG_INFO(" 渲染器: {}", getRenderer());
E2D_LOG_INFO(" 最大纹理大小: {}", getMaxTextureSize());
E2D_LOG_INFO(" 最大纹理单元数: {}", getMaxTextureUnits());
return true;
}
void GLContext::shutdown() {
// 标记 GPU 上下文为无效
GPUContext::get().markInvalid();
initialized_ = false;
version_ = GLVersion{};
maxTextureSize_ = -1;
maxTextureUnits_ = -1;
maxVertexAttribs_ = -1;
maxUniformBufferBindings_ = -1;
}
std::string GLContext::getVersionString() const {
const char *version = reinterpret_cast<const char *>(glGetString(GL_VERSION));
return version ? version : "Unknown";
}
std::string GLContext::getVendor() const {
const char *vendor = reinterpret_cast<const char *>(glGetString(GL_VENDOR));
return vendor ? vendor : "Unknown";
}
std::string GLContext::getRenderer() const {
const char *renderer =
reinterpret_cast<const char *>(glGetString(GL_RENDERER));
return renderer ? renderer : "Unknown";
}
bool GLContext::hasExtension(const std::string &extension) const {
GLint numExtensions = 0;
glGetIntegerv(GL_NUM_EXTENSIONS, &numExtensions);
for (GLint i = 0; i < numExtensions; ++i) {
const char *ext =
reinterpret_cast<const char *>(glGetStringi(GL_EXTENSIONS, i));
if (ext && extension == ext) {
return true;
}
}
return false;
}
int GLContext::getMaxTextureSize() const {
if (maxTextureSize_ < 0) {
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize_);
}
return maxTextureSize_;
}
int GLContext::getMaxTextureUnits() const {
if (maxTextureUnits_ < 0) {
glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextureUnits_);
}
return maxTextureUnits_;
}
int GLContext::getMaxVertexAttribs() const {
if (maxVertexAttribs_ < 0) {
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxVertexAttribs_);
}
return maxVertexAttribs_;
}
int GLContext::getMaxUniformBufferBindings() const {
if (maxUniformBufferBindings_ < 0) {
glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, &maxUniformBufferBindings_);
}
return maxUniformBufferBindings_;
}
bool GLContext::hasVAO() const {
// OpenGL 3.0+ 或 OpenGL ES 3.0+ 原生支持 VAO
if (version_.es) {
return version_.major >= 3;
}
return version_.major > 3 || (version_.major == 3 && version_.minor >= 0);
}
bool GLContext::hasFBO() const {
// OpenGL 3.0+ 或 OpenGL ES 2.0+ 原生支持 FBO
if (version_.es) {
return version_.major >= 2;
}
return version_.major >= 3;
}
bool GLContext::hasShader() const {
// OpenGL 2.0+ 或 OpenGL ES 2.0+ 原生支持 Shader
if (version_.es) {
return version_.major >= 2;
}
return version_.major >= 2;
}
void GLContext::parseVersion() {
const char *versionStr =
reinterpret_cast<const char *>(glGetString(GL_VERSION));
if (!versionStr) {
version_ = GLVersion{0, 0, false};
return;
}
std::string version(versionStr);
// 检查是否为 OpenGL ES
if (version.find("OpenGL ES") != std::string::npos) {
version_.es = true;
// 解析 ES 版本号,格式如 "OpenGL ES 3.0"
std::sscanf(version.c_str(), "OpenGL ES %d.%d", &version_.major,
&version_.minor);
} else {
version_.es = false;
// 解析桌面版本号,格式如 "3.3.0 NVIDIA"
std::sscanf(version.c_str(), "%d.%d", &version_.major, &version_.minor);
}
}
bool GLContext::loadExtensions() {
// GLAD 已经在 glad.c 中加载了所有扩展
// 这里可以添加额外的扩展检查
return true;
}
} // namespace extra2d

View File

@ -1,361 +0,0 @@
#include <extra2d/graphics/opengl/gl_font_atlas.h>
#include <extra2d/services/logger_service.h>
#include <algorithm>
#include <cstring>
#include <fstream>
#include <simdutf/simdutf.h>
// 在实现文件中定义 STB 实现
#define STB_TRUETYPE_IMPLEMENTATION
#include <stb/stb_truetype.h>
#define STB_RECT_PACK_IMPLEMENTATION
#include <stb/stb_rect_pack.h>
namespace extra2d {
// ============================================================================
// GLFontAtlas 构造函数
// 加载字体文件并初始化图集
// ============================================================================
GLFontAtlas::GLFontAtlas(const std::string &filepath, int fontSize, bool useSDF)
: useSDF_(useSDF), fontSize_(fontSize), lineHeight_(0.0f), ascent_(0.0f),
descent_(0.0f), lineGap_(0.0f), scale_(0.0f) {
// 加载字体文件
if (!initFont(filepath)) {
E2D_LOG_ERROR("初始化字体失败: {}", filepath);
return;
}
// 计算字体缩放比例和度量
scale_ = stbtt_ScaleForPixelHeight(&fontInfo_, static_cast<float>(fontSize_));
int ascent, descent, lineGap;
stbtt_GetFontVMetrics(&fontInfo_, &ascent, &descent, &lineGap);
ascent_ = static_cast<float>(ascent) * scale_;
descent_ = static_cast<float>(descent) * scale_;
lineGap_ = static_cast<float>(lineGap) * scale_;
lineHeight_ = ascent_ - descent_ + lineGap_;
// 创建图集纹理和打包上下文
createAtlas();
// 预加载常用 ASCII 字符
std::string asciiChars = " !\"#$%&'()*+,-./"
"0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`"
"abcdefghijklmnopqrstuvwxyz{|}~";
for (char c : asciiChars) {
char32_t codepoint = static_cast<char32_t>(static_cast<unsigned char>(c));
cacheGlyph(codepoint);
}
E2D_LOG_INFO("字体图集已创建: {} ({}px, {}x{})", filepath, fontSize_,
ATLAS_WIDTH, ATLAS_HEIGHT);
}
// ============================================================================
// GLFontAtlas 析构函数
// ============================================================================
GLFontAtlas::~GLFontAtlas() {
// 智能指针自动管理纹理资源
}
// ============================================================================
// 获取字形信息 - 如果字形不存在则动态缓存
// ============================================================================
const Glyph *GLFontAtlas::getGlyph(char32_t codepoint) const {
auto it = glyphs_.find(codepoint);
if (it == glyphs_.end()) {
// 动态缓存新字形
const_cast<GLFontAtlas *>(this)->cacheGlyph(codepoint);
it = glyphs_.find(codepoint);
if (it == glyphs_.end()) {
return nullptr;
}
}
// 返回静态存储的 Glyph 数据
static Glyph glyph;
const auto &data = it->second;
glyph.width = data.width;
glyph.height = data.height;
glyph.bearingX = data.bearingX;
glyph.bearingY = data.bearingY;
glyph.advance = data.advance;
glyph.u0 = data.u0;
glyph.v0 = data.v0;
glyph.u1 = data.u1;
glyph.v1 = data.v1;
return &glyph;
}
// ============================================================================
// 测量文本尺寸 - 支持多行文本
// ============================================================================
Vec2 GLFontAtlas::measureText(const std::string &text) {
float maxWidth = 0.0f;
float height = lineHeight_;
float currentWidth = 0.0f;
size_t utf32Length = simdutf::count_utf8(text.data(), text.size());
std::vector<char32_t> utf32Buffer(utf32Length);
size_t converted = simdutf::convert_utf8_to_utf32(text.data(), text.size(),
utf32Buffer.data());
for (size_t i = 0; i < converted; ++i) {
char32_t codepoint = utf32Buffer[i];
if (codepoint == '\n') {
maxWidth = std::max(maxWidth, currentWidth);
currentWidth = 0.0f;
height += lineHeight_;
continue;
}
const Glyph *glyph = getGlyph(codepoint);
if (glyph) {
currentWidth += glyph->advance;
}
}
maxWidth = std::max(maxWidth, currentWidth);
return Vec2(maxWidth, height);
}
// ============================================================================
// 初始化字体 - 加载字体文件到内存
// ============================================================================
bool GLFontAtlas::initFont(const std::string &filepath) {
// 读取字体文件到内存
std::ifstream file(filepath, std::ios::binary | std::ios::ate);
if (!file.is_open()) {
E2D_LOG_ERROR("打开字体文件失败: {}", filepath);
return false;
}
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
fontData_.resize(static_cast<size_t>(size));
if (!file.read(reinterpret_cast<char *>(fontData_.data()), size)) {
E2D_LOG_ERROR("读取字体文件失败: {}", filepath);
return false;
}
// 初始化 STB 字体
if (!stbtt_InitFont(&fontInfo_, fontData_.data(), 0)) {
E2D_LOG_ERROR("初始化 STB 字体失败: {}", filepath);
return false;
}
return true;
}
// ============================================================================
// 创建图集纹理 - 初始化空白纹理和矩形打包上下文
// ============================================================================
void GLFontAtlas::createAtlas() {
// 统一使用 4 通道格式 (RGBA)
int channels = 4;
std::vector<uint8_t> emptyData(ATLAS_WIDTH * ATLAS_HEIGHT * channels, 0);
texture_ = std::make_unique<GLTexture>(ATLAS_WIDTH, ATLAS_HEIGHT,
emptyData.data(), channels);
// 所有字体都使用线性过滤SDF的抗锯齿由着色器处理
texture_->setFilter(true);
// 初始化矩形打包上下文 - 持久化以支持增量打包
packNodes_.resize(ATLAS_WIDTH);
stbrp_init_target(&packContext_, ATLAS_WIDTH, ATLAS_HEIGHT, packNodes_.data(),
ATLAS_WIDTH);
// 预分配字形缓冲区
// 假设最大字形尺寸为 fontSize * fontSize * 4 (RGBA)
size_t maxGlyphSize = static_cast<size_t>(fontSize_ * fontSize_ * 4 * 4);
glyphBitmapCache_.reserve(maxGlyphSize);
glyphRgbaCache_.reserve(maxGlyphSize);
}
// ============================================================================
// 缓存字形 - 渲染字形到图集并存储信息
// 使用 stb_rect_pack 进行矩形打包
// ============================================================================
void GLFontAtlas::cacheGlyph(char32_t codepoint) {
// 检查是否已存在
if (glyphs_.find(codepoint) != glyphs_.end()) {
return;
}
// 获取字形水平度量
int advance, leftSideBearing;
stbtt_GetCodepointHMetrics(&fontInfo_, static_cast<int>(codepoint), &advance,
&leftSideBearing);
float advancePx = static_cast<float>(advance) * scale_;
// SDF 渲染模式
if (useSDF_) {
constexpr int SDF_PADDING = 8;
constexpr unsigned char ONEDGE_VALUE = 128;
constexpr float PIXEL_DIST_SCALE = 64.0f;
int w = 0, h = 0, xoff = 0, yoff = 0;
unsigned char *sdf = stbtt_GetCodepointSDF(
&fontInfo_, scale_, static_cast<int>(codepoint), SDF_PADDING,
ONEDGE_VALUE, PIXEL_DIST_SCALE, &w, &h, &xoff, &yoff);
if (!sdf || w <= 0 || h <= 0) {
if (sdf)
stbtt_FreeSDF(sdf, nullptr);
// 创建空白字形(如空格)
GlyphData data{};
data.advance = advancePx;
glyphs_[codepoint] = data;
return;
}
// 使用 stb_rect_pack 打包矩形
stbrp_rect rect;
rect.id = static_cast<int>(codepoint);
rect.w = w + PADDING * 2;
rect.h = h + PADDING * 2;
stbrp_pack_rects(&packContext_, &rect, 1);
if (!rect.was_packed) {
E2D_LOG_WARN("字体图集已满,无法缓存字符码点: {}", codepoint);
stbtt_FreeSDF(sdf, nullptr);
return;
}
int atlasX = rect.x + PADDING;
int atlasY = rect.y + PADDING;
// 创建字形数据
GlyphData data;
data.width = static_cast<float>(w);
data.height = static_cast<float>(h);
data.bearingX = static_cast<float>(xoff);
data.bearingY = static_cast<float>(yoff);
data.advance = advancePx;
// 计算 UV 坐标
// stb_rect_pack 使用左上角为原点OpenGL纹理使用左下角为原点
// 需要翻转V坐标
float v0 = static_cast<float>(atlasY) / ATLAS_HEIGHT;
float v1 = static_cast<float>(atlasY + h) / ATLAS_HEIGHT;
data.u0 = static_cast<float>(atlasX) / ATLAS_WIDTH;
data.v0 = 1.0f - v1; // 翻转V坐标
data.u1 = static_cast<float>(atlasX + w) / ATLAS_WIDTH;
data.v1 = 1.0f - v0; // 翻转V坐标
glyphs_[codepoint] = data;
// 将 SDF 单通道数据转换为 RGBA 格式(统一格式)
size_t pixelCount = static_cast<size_t>(w) * static_cast<size_t>(h);
glyphRgbaCache_.resize(pixelCount * 4);
for (size_t i = 0; i < pixelCount; ++i) {
uint8_t alpha = sdf[i];
glyphRgbaCache_[i * 4 + 0] = 255; // R
glyphRgbaCache_[i * 4 + 1] = 255; // G
glyphRgbaCache_[i * 4 + 2] = 255; // B
glyphRgbaCache_[i * 4 + 3] = alpha; // A - SDF 值存储在 Alpha 通道
}
// 更新纹理 - OpenGL纹理坐标原点在左下角需要将Y坐标翻转
updateAtlas(atlasX, ATLAS_HEIGHT - atlasY - h, w, h, glyphRgbaCache_);
stbtt_FreeSDF(sdf, nullptr);
return;
}
// 普通位图渲染模式
int x0 = 0, y0 = 0, x1 = 0, y1 = 0;
stbtt_GetCodepointBitmapBox(&fontInfo_, static_cast<int>(codepoint), scale_,
scale_, &x0, &y0, &x1, &y1);
int w = x1 - x0;
int h = y1 - y0;
int xoff = x0;
// y0 是相对于基线的偏移(通常为负值,表示在基线上方)
// bearingY 应该是字形顶部相对于基线的偏移
int yoff = y0;
if (w <= 0 || h <= 0) {
// 空白字符(如空格)
GlyphData data{};
data.advance = advancePx;
glyphs_[codepoint] = data;
return;
}
// 使用预分配缓冲区渲染字形
size_t pixelCount = static_cast<size_t>(w) * static_cast<size_t>(h);
glyphBitmapCache_.resize(pixelCount);
stbtt_MakeCodepointBitmap(&fontInfo_, glyphBitmapCache_.data(), w, h, w,
scale_, scale_, static_cast<int>(codepoint));
// 使用 stb_rect_pack 打包矩形
stbrp_rect rect;
rect.id = static_cast<int>(codepoint);
rect.w = w + PADDING * 2;
rect.h = h + PADDING * 2;
stbrp_pack_rects(&packContext_, &rect, 1);
if (!rect.was_packed) {
E2D_LOG_WARN("字体图集已满,无法缓存字符码点: {}", codepoint);
return;
}
int atlasX = rect.x + PADDING;
int atlasY = rect.y + PADDING;
// 创建字形数据
GlyphData data;
data.width = static_cast<float>(w);
data.height = static_cast<float>(h);
data.bearingX = static_cast<float>(xoff);
data.bearingY = static_cast<float>(yoff);
data.advance = advancePx;
// 计算 UV 坐标
// stb_rect_pack 使用左上角为原点OpenGL纹理使用左下角为原点
// 需要翻转V坐标
float v0 = static_cast<float>(atlasY) / ATLAS_HEIGHT;
float v1 = static_cast<float>(atlasY + h) / ATLAS_HEIGHT;
data.u0 = static_cast<float>(atlasX) / ATLAS_WIDTH;
data.v0 = 1.0f - v1; // 翻转V坐标
data.u1 = static_cast<float>(atlasX + w) / ATLAS_WIDTH;
data.v1 = 1.0f - v0; // 翻转V坐标
glyphs_[codepoint] = data;
// 将单通道字形数据转换为 RGBA 格式白色字形Alpha 通道存储灰度)
glyphRgbaCache_.resize(pixelCount * 4);
for (size_t i = 0; i < pixelCount; ++i) {
uint8_t alpha = glyphBitmapCache_[i];
glyphRgbaCache_[i * 4 + 0] = 255; // R
glyphRgbaCache_[i * 4 + 1] = 255; // G
glyphRgbaCache_[i * 4 + 2] = 255; // B
glyphRgbaCache_[i * 4 + 3] = alpha; // A
}
// 更新纹理 - OpenGL纹理坐标原点在左下角需要将Y坐标翻转
updateAtlas(atlasX, ATLAS_HEIGHT - atlasY - h, w, h, glyphRgbaCache_);
}
// ============================================================================
// 更新图集纹理区域
// ============================================================================
void GLFontAtlas::updateAtlas(int x, int y, int width, int height,
const std::vector<uint8_t> &data) {
if (texture_) {
texture_->bind();
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, width, height, GL_RGBA,
GL_UNSIGNED_BYTE, data.data());
}
}
} // namespace extra2d

View File

@ -1,267 +0,0 @@
#include <extra2d/graphics/opengl/gl_framebuffer.h>
#include <extra2d/graphics/opengl/gl_texture.h>
#include <extra2d/services/logger_service.h>
namespace extra2d {
// ============================================================================
// GLFramebuffer 实现
// ============================================================================
GLFramebuffer::GLFramebuffer() = default;
GLFramebuffer::~GLFramebuffer() { shutdown(); }
bool GLFramebuffer::init(const FramebufferDesc &desc) {
if (fboID_ != 0) {
shutdown();
}
width_ = desc.width;
height_ = desc.height;
numColorAttachments_ = desc.colorAttachments;
hasDepth_ = desc.hasDepth;
hasStencil_ = desc.hasStencil;
// 限制颜色附件数
if (numColorAttachments_ > MAX_COLOR_ATTACHMENTS) {
numColorAttachments_ = MAX_COLOR_ATTACHMENTS;
}
// 生成 FBO
glGenFramebuffers(1, &fboID_);
if (fboID_ == 0) {
E2D_LOG_ERROR("生成 OpenGL 帧缓冲区失败");
return false;
}
E2D_LOG_DEBUG("GLFramebuffer 已创建: ID={}, 大小={}x{}, 颜色附件={}", fboID_,
width_, height_, numColorAttachments_);
return true;
}
void GLFramebuffer::shutdown() {
if (fboID_ != 0) {
glDeleteFramebuffers(1, &fboID_);
E2D_LOG_DEBUG("GLFramebuffer 已销毁: ID={}", fboID_);
fboID_ = 0;
}
// 清理纹理引用
for (auto &tex : colorTextures_) {
tex.reset();
}
depthTexture_.reset();
depthStencilTexture_.reset();
hasInternalTextures_ = false;
}
void GLFramebuffer::bind() {
if (fboID_ != 0) {
glBindFramebuffer(GL_FRAMEBUFFER, fboID_);
}
}
void GLFramebuffer::unbind() { glBindFramebuffer(GL_FRAMEBUFFER, 0); }
void GLFramebuffer::attachColorTexture(Ptr<Texture> texture, int attachment) {
if (fboID_ == 0 || !texture || attachment < 0 ||
attachment >= MAX_COLOR_ATTACHMENTS) {
return;
}
bind();
// 获取 OpenGL 纹理 ID
GLuint texID = static_cast<GLuint>(
reinterpret_cast<uintptr_t>(texture->getNativeHandle()));
glFramebufferTexture2D(GL_FRAMEBUFFER, getColorAttachment(attachment),
GL_TEXTURE_2D, texID, 0);
colorTextures_[attachment] = texture;
unbind();
}
void GLFramebuffer::attachDepthTexture(Ptr<Texture> texture) {
if (fboID_ == 0 || !texture) {
return;
}
bind();
// 获取 OpenGL 纹理 ID
GLuint texID = static_cast<GLuint>(
reinterpret_cast<uintptr_t>(texture->getNativeHandle()));
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D,
texID, 0);
depthTexture_ = texture;
hasDepth_ = true;
hasStencil_ = false;
unbind();
}
void GLFramebuffer::attachDepthStencilTexture(Ptr<Texture> texture) {
if (fboID_ == 0 || !texture) {
return;
}
bind();
// 获取 OpenGL 纹理 ID
GLuint texID = static_cast<GLuint>(
reinterpret_cast<uintptr_t>(texture->getNativeHandle()));
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT,
GL_TEXTURE_2D, texID, 0);
depthStencilTexture_ = texture;
hasDepth_ = true;
hasStencil_ = true;
unbind();
}
bool GLFramebuffer::isComplete() {
if (fboID_ == 0) {
return false;
}
bind();
bool complete = checkStatus();
unbind();
return complete;
}
Ptr<Texture> GLFramebuffer::getColorTexture(int attachment) const {
if (attachment >= 0 && attachment < MAX_COLOR_ATTACHMENTS) {
return colorTextures_[attachment];
}
return nullptr;
}
Ptr<Texture> GLFramebuffer::getDepthTexture() const { return depthTexture_; }
void GLFramebuffer::clear(const Color &color, bool clearColor, bool clearDepth,
bool clearStencil) {
if (fboID_ == 0) {
return;
}
bind();
GLbitfield mask = 0;
if (clearColor) {
mask |= GL_COLOR_BUFFER_BIT;
glClearColor(color.r, color.g, color.b, color.a);
}
if (clearDepth) {
mask |= GL_DEPTH_BUFFER_BIT;
glClearDepthf(1.0f);
}
if (clearStencil) {
mask |= GL_STENCIL_BUFFER_BIT;
glClearStencil(0);
}
if (mask != 0) {
glClear(mask);
}
unbind();
}
void GLFramebuffer::setViewport(int x, int y, int width, int height) {
glViewport(x, y, width, height);
}
bool GLFramebuffer::readPixels(int x, int y, int width, int height,
std::vector<uint8_t> &outData) {
if (fboID_ == 0 || width <= 0 || height <= 0) {
return false;
}
// 计算需要的缓冲区大小 (RGBA8)
size_t dataSize = width * height * 4;
outData.resize(dataSize);
bind();
glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, outData.data());
unbind();
return true;
}
bool GLFramebuffer::createWithTextures(int width, int height,
PixelFormat colorFormat,
PixelFormat depthFormat) {
FramebufferDesc desc;
desc.width = width;
desc.height = height;
desc.colorAttachments = 1;
desc.hasDepth = (depthFormat != PixelFormat::RGBA8);
desc.hasStencil = (depthFormat == PixelFormat::Depth24Stencil8);
if (!init(desc)) {
return false;
}
hasInternalTextures_ = true;
// 创建颜色纹理
// 注意:这里简化处理,实际应该通过纹理工厂创建
// 暂时返回 true实际纹理创建由调用者处理
return true;
}
bool GLFramebuffer::checkStatus() {
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
switch (status) {
case GL_FRAMEBUFFER_COMPLETE:
return true;
case GL_FRAMEBUFFER_UNDEFINED:
E2D_LOG_ERROR("帧缓冲区不完整: GL_FRAMEBUFFER_UNDEFINED");
break;
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
E2D_LOG_ERROR("帧缓冲区不完整: GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
break;
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
E2D_LOG_ERROR(
"帧缓冲区不完整: GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
break;
#ifndef GL_ES_VERSION_2_0
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
E2D_LOG_ERROR("帧缓冲区不完整: GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER");
break;
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
E2D_LOG_ERROR("帧缓冲区不完整: GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER");
break;
#endif
case GL_FRAMEBUFFER_UNSUPPORTED:
E2D_LOG_ERROR("帧缓冲区不完整: GL_FRAMEBUFFER_UNSUPPORTED");
break;
default:
E2D_LOG_ERROR("帧缓冲区不完整: 未知错误 {}", status);
break;
}
return false;
}
GLenum GLFramebuffer::getColorAttachment(int index) {
return GL_COLOR_ATTACHMENT0 + index;
}
} // namespace extra2d

View File

@ -1,233 +0,0 @@
#include <extra2d/graphics/opengl/gl_pipeline.h>
#include <extra2d/services/logger_service.h>
namespace extra2d {
// ============================================================================
// GLPipeline 实现
// ============================================================================
GLPipeline::GLPipeline() = default;
GLPipeline::~GLPipeline() { shutdown(); }
bool GLPipeline::init(const PipelineDesc &desc) {
if (initialized_) {
shutdown();
}
blendMode_ = desc.blendMode;
blendEnabled_ = desc.blendEnabled;
depthTest_ = desc.depthTest;
depthWrite_ = desc.depthWrite;
depthFunc_ = desc.depthFunc;
cullMode_ = desc.cullMode;
initialized_ = true;
E2D_LOG_DEBUG(
"GLPipeline initialized: blendMode={}, depthTest={}, cullMode={}",
static_cast<int>(blendMode_), depthTest_, static_cast<int>(cullMode_));
return true;
}
void GLPipeline::shutdown() { initialized_ = false; }
void GLPipeline::bind() {
if (!initialized_) {
return;
}
applyAllStates();
}
void GLPipeline::unbind() {
// OpenGL 不需要显式解绑管线
}
void GLPipeline::setBlendMode(BlendMode mode) {
blendMode_ = mode;
applyBlendState();
}
void GLPipeline::setDepthTest(bool enabled) {
depthTest_ = enabled;
applyDepthState();
}
void GLPipeline::setDepthWrite(bool enabled) {
depthWrite_ = enabled;
applyDepthState();
}
void GLPipeline::setDepthFunc(DepthFunc func) {
depthFunc_ = func;
applyDepthState();
}
void GLPipeline::setCullMode(CullMode mode) {
cullMode_ = mode;
applyCullState();
}
void GLPipeline::setViewport(int x, int y, int width, int height) {
viewportX_ = x;
viewportY_ = y;
viewportWidth_ = width;
viewportHeight_ = height;
// 检查缓存,避免冗余调用
if (x != cachedViewportX_ || y != cachedViewportY_ ||
width != cachedViewportWidth_ || height != cachedViewportHeight_) {
glViewport(x, y, width, height);
cachedViewportX_ = x;
cachedViewportY_ = y;
cachedViewportWidth_ = width;
cachedViewportHeight_ = height;
}
}
void GLPipeline::getViewport(int &x, int &y, int &width, int &height) const {
x = viewportX_;
y = viewportY_;
width = viewportWidth_;
height = viewportHeight_;
}
void GLPipeline::applyAllStates() {
applyBlendState();
applyDepthState();
applyCullState();
// 应用视口
if (viewportWidth_ > 0 && viewportHeight_ > 0) {
setViewport(viewportX_, viewportY_, viewportWidth_, viewportHeight_);
}
}
void GLPipeline::applyBlendState() {
// 检查是否需要启用/禁用混合
if (blendEnabled_ != cachedBlendEnabled_) {
if (blendEnabled_) {
glEnable(GL_BLEND);
} else {
glDisable(GL_BLEND);
}
cachedBlendEnabled_ = blendEnabled_;
}
// 如果禁用了混合,不需要设置混合函数
if (!blendEnabled_) {
return;
}
// 检查混合模式是否改变
if (blendMode_ != cachedBlendMode_) {
GLenum srcFactor, dstFactor;
getBlendFactors(blendMode_, srcFactor, dstFactor);
glBlendFunc(srcFactor, dstFactor);
cachedBlendMode_ = blendMode_;
}
}
void GLPipeline::applyDepthState() {
// 深度测试
if (depthTest_ != cachedDepthTest_) {
if (depthTest_) {
glEnable(GL_DEPTH_TEST);
} else {
glDisable(GL_DEPTH_TEST);
}
cachedDepthTest_ = depthTest_;
}
// 深度写入
if (depthWrite_ != cachedDepthWrite_) {
glDepthMask(depthWrite_ ? GL_TRUE : GL_FALSE);
cachedDepthWrite_ = depthWrite_;
}
// 深度函数
if (depthFunc_ != cachedDepthFunc_) {
glDepthFunc(convertDepthFunc(depthFunc_));
cachedDepthFunc_ = depthFunc_;
}
}
void GLPipeline::applyCullState() {
// 检查裁剪模式是否改变
if (cullMode_ != cachedCullMode_) {
if (cullMode_ == CullMode::None) {
glDisable(GL_CULL_FACE);
} else {
glEnable(GL_CULL_FACE);
glCullFace(convertCullMode(cullMode_));
}
cachedCullMode_ = cullMode_;
}
}
void GLPipeline::getBlendFactors(BlendMode mode, GLenum &srcFactor,
GLenum &dstFactor) {
switch (mode) {
case BlendMode::None:
srcFactor = GL_ONE;
dstFactor = GL_ZERO;
break;
case BlendMode::Alpha:
srcFactor = GL_SRC_ALPHA;
dstFactor = GL_ONE_MINUS_SRC_ALPHA;
break;
case BlendMode::Additive:
srcFactor = GL_SRC_ALPHA;
dstFactor = GL_ONE;
break;
case BlendMode::Multiply:
srcFactor = GL_DST_COLOR;
dstFactor = GL_ONE_MINUS_SRC_ALPHA;
break;
default:
srcFactor = GL_SRC_ALPHA;
dstFactor = GL_ONE_MINUS_SRC_ALPHA;
break;
}
}
GLenum GLPipeline::convertDepthFunc(DepthFunc func) {
switch (func) {
case DepthFunc::Never:
return GL_NEVER;
case DepthFunc::Less:
return GL_LESS;
case DepthFunc::Equal:
return GL_EQUAL;
case DepthFunc::LessEqual:
return GL_LEQUAL;
case DepthFunc::Greater:
return GL_GREATER;
case DepthFunc::NotEqual:
return GL_NOTEQUAL;
case DepthFunc::GreaterEqual:
return GL_GEQUAL;
case DepthFunc::Always:
return GL_ALWAYS;
default:
return GL_LESS;
}
}
GLenum GLPipeline::convertCullMode(CullMode mode) {
switch (mode) {
case CullMode::Front:
return GL_FRONT;
case CullMode::Back:
return GL_BACK;
case CullMode::Both:
return GL_FRONT_AND_BACK;
default:
return GL_BACK;
}
}
} // namespace extra2d

File diff suppressed because it is too large Load Diff

View File

@ -1,348 +0,0 @@
#include <extra2d/graphics/opengl/gl_shader.h>
#include <extra2d/services/logger_service.h>
namespace extra2d {
/**
* @brief ID为0
*/
GLShader::GLShader() : programID_(0) {}
/**
* @brief OpenGL着色器程序
*/
GLShader::~GLShader() {
if (programID_ != 0) {
glDeleteProgram(programID_);
programID_ = 0;
}
}
/**
* @brief Shader程序
*/
void GLShader::bind() const { glUseProgram(programID_); }
/**
* @brief Shader程序
*/
void GLShader::unbind() const { glUseProgram(0); }
/**
* @brief uniform变量
* @param name uniform变量名
* @param value
*/
void GLShader::setBool(const std::string &name, bool value) {
glUniform1i(getUniformLocation(name), value ? 1 : 0);
}
/**
* @brief uniform变量
* @param name uniform变量名
* @param value
*/
void GLShader::setInt(const std::string &name, int value) {
glUniform1i(getUniformLocation(name), value);
}
/**
* @brief uniform变量
* @param name uniform变量名
* @param value
*/
void GLShader::setFloat(const std::string &name, float value) {
glUniform1f(getUniformLocation(name), value);
}
/**
* @brief uniform变量
* @param name uniform变量名
* @param value
*/
void GLShader::setVec2(const std::string &name, const glm::vec2 &value) {
glUniform2fv(getUniformLocation(name), 1, &value[0]);
}
/**
* @brief uniform变量
* @param name uniform变量名
* @param value
*/
void GLShader::setVec3(const std::string &name, const glm::vec3 &value) {
glUniform3fv(getUniformLocation(name), 1, &value[0]);
}
/**
* @brief uniform变量
* @param name uniform变量名
* @param value
*/
void GLShader::setVec4(const std::string &name, const glm::vec4 &value) {
glUniform4fv(getUniformLocation(name), 1, &value[0]);
}
/**
* @brief 4x4矩阵类型uniform变量
* @param name uniform变量名
* @param value 4x4矩阵值
*/
void GLShader::setMat4(const std::string &name, const glm::mat4 &value) {
glUniformMatrix4fv(getUniformLocation(name), 1, GL_FALSE, &value[0][0]);
}
/**
* @brief uniform变量
* @param name uniform变量名
* @param color
*/
void GLShader::setColor(const std::string &name, const Color &color) {
glUniform4f(getUniformLocation(name), color.r, color.g, color.b, color.a);
}
/**
* @brief Shader
* @param vertexSource
* @param fragmentSource
* @return truefalse
*/
bool GLShader::compileFromSource(const char *vertexSource,
const char *fragmentSource) {
GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vertexSource);
if (vertexShader == 0) {
return false;
}
GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentSource);
if (fragmentShader == 0) {
glDeleteShader(vertexShader);
return false;
}
if (programID_ != 0) {
glDeleteProgram(programID_);
uniformCache_.clear();
}
programID_ = glCreateProgram();
glAttachShader(programID_, vertexShader);
glAttachShader(programID_, fragmentShader);
glLinkProgram(programID_);
GLint success;
glGetProgramiv(programID_, GL_LINK_STATUS, &success);
if (!success) {
char infoLog[512];
glGetProgramInfoLog(programID_, 512, nullptr, infoLog);
E2D_LOG_ERROR("着色器程序链接失败: {}", infoLog);
glDeleteProgram(programID_);
programID_ = 0;
}
glDeleteShader(vertexShader);
glDeleteShader(fragmentShader);
return success == GL_TRUE;
}
/**
* @brief Shader
* @param binary
* @return truefalse
*/
bool GLShader::compileFromBinary(const std::vector<uint8_t> &binary) {
if (binary.empty()) {
E2D_LOG_ERROR("二进制数据为空");
return false;
}
GLint numFormats = 0;
glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &numFormats);
if (numFormats == 0) {
E2D_LOG_ERROR("不支持程序二进制格式");
return false;
}
if (programID_ != 0) {
glDeleteProgram(programID_);
uniformCache_.clear();
}
programID_ = glCreateProgram();
GLenum binaryFormat = 0;
glGetIntegerv(GL_PROGRAM_BINARY_FORMATS,
reinterpret_cast<GLint *>(&binaryFormat));
glProgramBinary(programID_, binaryFormat, binary.data(),
static_cast<GLsizei>(binary.size()));
GLint success = 0;
glGetProgramiv(programID_, GL_LINK_STATUS, &success);
if (!success) {
char infoLog[512];
glGetProgramInfoLog(programID_, 512, nullptr, infoLog);
E2D_LOG_ERROR("从二进制加载着色器失败: {}", infoLog);
glDeleteProgram(programID_);
programID_ = 0;
return false;
}
return true;
}
/**
* @brief Shader二进制数据
* @param outBinary
* @return truefalse
*/
bool GLShader::getBinary(std::vector<uint8_t> &outBinary) {
if (programID_ == 0) {
E2D_LOG_WARN("无法获取二进制数据: 着色器程序为 0");
return false;
}
GLint binaryLength = 0;
glGetProgramiv(programID_, GL_PROGRAM_BINARY_LENGTH, &binaryLength);
E2D_LOG_DEBUG("着色器二进制数据长度: {}", binaryLength);
if (binaryLength <= 0) {
E2D_LOG_WARN("着色器二进制数据长度为 0 或负数");
return false;
}
outBinary.resize(binaryLength);
GLenum binaryFormat = 0;
GLsizei actualLength = 0;
glGetProgramBinary(programID_, binaryLength, &actualLength, &binaryFormat,
outBinary.data());
GLenum err = glGetError();
if (err != GL_NO_ERROR) {
E2D_LOG_ERROR("glGetProgramBinary 失败,错误码: {}", err);
outBinary.clear();
return false;
}
if (actualLength == 0) {
E2D_LOG_WARN("glGetProgramBinary 返回 0 字节");
outBinary.clear();
return false;
}
if (actualLength != binaryLength) {
outBinary.resize(actualLength);
}
E2D_LOG_DEBUG("着色器二进制数据已获取: {} 字节, 格式: {}", actualLength,
binaryFormat);
return true;
}
/**
* @brief
* @param type
* @param source
* @return ID0
*/
GLuint GLShader::compileShader(GLenum type, const char *source) {
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &source, nullptr);
glCompileShader(shader);
GLint success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) {
char infoLog[512];
glGetShaderInfoLog(shader, 512, nullptr, infoLog);
E2D_LOG_ERROR("着色器编译失败: {}", infoLog);
glDeleteShader(shader);
return 0;
}
return shader;
}
/**
* @brief uniform位置
* @param name uniform变量名
* @return uniform位置
*/
GLint GLShader::getUniformLocation(const std::string &name) {
auto it = uniformCache_.find(name);
if (it != uniformCache_.end()) {
return it->second;
}
GLint location = glGetUniformLocation(programID_, name.c_str());
uniformCache_[name] = location;
return location;
}
// ============================================================================
// GLShaderFactory 实现
// ============================================================================
/**
* @brief Shader
* @param name Shader名称
* @param vertSource
* @param fragSource
* @return Shader实例
*/
Ptr<IShader> GLShaderFactory::createFromSource(const std::string &name,
const std::string &vertSource,
const std::string &fragSource) {
auto shader = std::make_shared<GLShader>();
shader->setName(name);
if (!shader->compileFromSource(vertSource.c_str(), fragSource.c_str())) {
E2D_LOG_ERROR("从源码编译着色器失败: {}", name);
return nullptr;
}
return shader;
}
/**
* @brief Shader
* @param name Shader名称
* @param binary
* @return Shader实例
*/
Ptr<IShader>
GLShaderFactory::createFromBinary(const std::string &name,
const std::vector<uint8_t> &binary) {
auto shader = std::make_shared<GLShader>();
shader->setName(name);
if (!shader->compileFromBinary(binary)) {
E2D_LOG_ERROR("从二进制创建着色器失败: {}", name);
return nullptr;
}
return shader;
}
/**
* @brief Shader的二进制数据
* @param shader Shader实例
* @param outBinary
* @return truefalse
*/
bool GLShaderFactory::getShaderBinary(const IShader &shader,
std::vector<uint8_t> &outBinary) {
const GLShader *glShader = dynamic_cast<const GLShader *>(&shader);
if (!glShader) {
E2D_LOG_ERROR("着色器不是 GLShader 实例");
return false;
}
return const_cast<GLShader *>(glShader)->getBinary(outBinary);
}
} // namespace extra2d

View File

@ -1,230 +0,0 @@
#include <extra2d/graphics/opengl/gl_sprite_batch.h>
#include <extra2d/graphics/opengl/gl_texture.h>
#include <extra2d/graphics/shader/shader_manager.h>
#include <extra2d/services/logger_service.h>
namespace extra2d {
GLSpriteBatch::GLSpriteBatch()
: vao_(0), currentTexture_(nullptr), drawCallCount_(0) {}
GLSpriteBatch::~GLSpriteBatch() { shutdown(); }
bool GLSpriteBatch::init() {
// 从ShaderManager获取精灵着色器
shader_ = ShaderManager::getInstance().getBuiltin("sprite");
if (!shader_) {
E2D_LOG_ERROR("获取内置精灵着色器失败");
return false;
}
// 创建 VAO
glGenVertexArrays(1, &vao_);
glBindVertexArray(vao_);
// 初始化 VBO顶点缓冲区- 动态使用模式
BufferDesc vboDesc;
vboDesc.type = BufferType::Vertex;
vboDesc.usage = BufferUsage::Dynamic;
vboDesc.size = SpriteBatch::MAX_VERTICES * sizeof(SpriteVertex);
vboDesc.initialData = nullptr;
if (!vbo_.init(vboDesc)) {
E2D_LOG_ERROR("初始化精灵批处理 VBO 失败");
return false;
}
vbo_.bind();
// 设置顶点属性
glEnableVertexAttribArray(0);
glVertexAttribPointer(
0, 2, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex),
reinterpret_cast<void *>(offsetof(SpriteVertex, position)));
glEnableVertexAttribArray(1);
glVertexAttribPointer(
1, 2, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex),
reinterpret_cast<void *>(offsetof(SpriteVertex, texCoord)));
glEnableVertexAttribArray(2);
glVertexAttribPointer(
2, 4, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex),
reinterpret_cast<void *>(offsetof(SpriteVertex, color)));
// 初始化 EBO索引缓冲区- 静态使用模式
BufferDesc eboDesc;
eboDesc.type = BufferType::Index;
eboDesc.usage = BufferUsage::Static;
eboDesc.size = batch_.getIndices().size() * sizeof(uint16_t);
eboDesc.initialData = batch_.getIndices().data();
if (!ebo_.init(eboDesc)) {
E2D_LOG_ERROR("初始化精灵批处理 EBO 失败");
return false;
}
ebo_.bind();
glBindVertexArray(0);
return true;
}
void GLSpriteBatch::shutdown() {
// 使用 GLBuffer::shutdown() 释放缓冲区资源
vbo_.shutdown();
ebo_.shutdown();
if (vao_ != 0) {
glDeleteVertexArrays(1, &vao_);
vao_ = 0;
}
}
void GLSpriteBatch::begin(const glm::mat4 &viewProjection) {
batch_.begin(viewProjection);
batches_.clear();
currentTexture_ = nullptr;
drawCallCount_ = 0;
// 保存 viewProjection 矩阵供后续使用
viewProjection_ = viewProjection;
// 绑定 VAO 和缓冲区
glBindVertexArray(vao_);
vbo_.bind();
ebo_.bind();
}
void GLSpriteBatch::begin(const glm::mat4 &viewProjection,
Ptr<IShader> shader) {
// 设置自定义着色器
if (shader) {
shader_ = shader;
}
begin(viewProjection);
}
void GLSpriteBatch::setShader(Ptr<IShader> shader) {
// 如果当前有未提交的批次,先提交
if (batch_.getSpriteCount() > 0) {
flush();
}
shader_ = shader;
}
void GLSpriteBatch::end() {
if (batch_.getSpriteCount() > 0) {
flush();
}
// 解绑缓冲区
vbo_.unbind();
ebo_.unbind();
glBindVertexArray(0);
}
void GLSpriteBatch::draw(const Texture &texture, const SpriteData &data) {
const GLTexture *glTex = dynamic_cast<const GLTexture *>(&texture);
if (!glTex) {
E2D_LOG_WARN("精灵批处理纹理类型无效");
return;
}
// 如果纹理改变或批次已满,先提交当前批次
if (currentTexture_ != glTex || batch_.needsFlush()) {
if (batch_.getSpriteCount() > 0) {
submitBatch();
}
currentTexture_ = glTex;
}
// 使用 batch 层生成顶点
batch_.draw(data);
}
void GLSpriteBatch::drawBatch(const Texture &texture,
const std::vector<SpriteData> &sprites) {
const GLTexture *glTex = dynamic_cast<const GLTexture *>(&texture);
if (!glTex) {
E2D_LOG_WARN("精灵批处理纹理类型无效");
return;
}
// 批量处理精灵
for (const auto &data : sprites) {
// 如果纹理改变或批次已满,先提交当前批次
if (currentTexture_ != glTex || batch_.needsFlush()) {
if (batch_.getSpriteCount() > 0) {
submitBatch();
}
currentTexture_ = glTex;
}
// 使用 batch 层生成顶点
batch_.draw(data);
}
}
void GLSpriteBatch::submitBatch() {
if (batch_.getSpriteCount() == 0) {
return;
}
// 记录批次信息
Batch batchInfo;
batchInfo.texture = currentTexture_;
batchInfo.startVertex = 0; // 每次提交都是新的缓冲区
batchInfo.vertexCount = batch_.getSpriteCount() * 4;
batches_.push_back(batchInfo);
// 绑定着色器并设置uniform
if (shader_) {
shader_->bind();
// 只提供需要动态计算的值其他值使用JSON中定义的默认值
UniformValueMap uniformValues;
uniformValues["u_viewProjection"] = viewProjection_;
// 合并额外的uniform值如SDF字体的u_textureSize
for (const auto &[name, value] : extraUniforms_) {
uniformValues[name] = value;
}
// 使用ShaderManager自动应用uniform值未提供的值使用JSON中的默认值
// 使用着色器自己的名称从JSON中解析的name字段
ShaderManager::getInstance().applyUniforms(shader_, shader_->getName(),
uniformValues);
// 设置纹理采样器
shader_->setInt("u_texture", 0);
}
// 上传顶点数据 - 使用 orphaning 策略优化动态缓冲区
// 通过传入 nullptr 进行 orphaning告诉驱动器可以丢弃旧缓冲区并分配新内存
// 这样可以避免 GPU 等待,提高性能
size_t vertexDataSize = batch_.getVertices().size() * sizeof(SpriteVertex);
vbo_.setData(nullptr, vertexDataSize); // orphaning
vbo_.updateData(batch_.getVertices().data(), 0, vertexDataSize);
// 绘制
currentTexture_->bind(0);
size_t indexCount = batch_.getSpriteCount() * 6;
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(indexCount),
GL_UNSIGNED_SHORT, nullptr);
drawCallCount_++;
// 清空 batch 层,准备下一批
batch_.clear();
}
void GLSpriteBatch::flush() {
// 提交最后的批次
if (batch_.getSpriteCount() > 0) {
submitBatch();
}
// 重置状态
batches_.clear();
currentTexture_ = nullptr;
}
} // namespace extra2d

View File

@ -1,504 +0,0 @@
#include <extra2d/graphics/memory/gpu_context.h>
#include <extra2d/graphics/memory/vram_manager.h>
#include <extra2d/graphics/opengl/gl_texture.h>
#define STB_IMAGE_IMPLEMENTATION
#include <cstring>
#include <extra2d/core/service_locator.h>
#include <extra2d/services/logger_service.h>
#include <fstream>
#include <stb/stb_image.h>
namespace extra2d {
// ============================================================================
// KTX 文件头结构
// ============================================================================
#pragma pack(push, 1)
struct KTXHeader {
uint8_t identifier[12];
uint32_t endianness;
uint32_t glType;
uint32_t glTypeSize;
uint32_t glFormat;
uint32_t glInternalFormat;
uint32_t glBaseInternalFormat;
uint32_t pixelWidth;
uint32_t pixelHeight;
uint32_t pixelDepth;
uint32_t numberOfArrayElements;
uint32_t numberOfFaces;
uint32_t numberOfMipmapLevels;
uint32_t bytesOfKeyValueData;
};
#pragma pack(pop)
// KTX 文件标识符
static const uint8_t KTX_IDENTIFIER[12] = {0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31,
0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A};
// ============================================================================
// DDS 文件头结构
// ============================================================================
#pragma pack(push, 1)
struct DDSPixelFormat {
uint32_t size;
uint32_t flags;
uint32_t fourCC;
uint32_t rgbBitCount;
uint32_t rBitMask;
uint32_t gBitMask;
uint32_t bBitMask;
uint32_t aBitMask;
};
struct DDSHeader {
uint32_t magic;
uint32_t size;
uint32_t flags;
uint32_t height;
uint32_t width;
uint32_t pitchOrLinearSize;
uint32_t depth;
uint32_t mipMapCount;
uint32_t reserved1[11];
DDSPixelFormat pixelFormat;
uint32_t caps;
uint32_t caps2;
uint32_t caps3;
uint32_t caps4;
uint32_t reserved2;
};
struct DDSHeaderDXT10 {
uint32_t dxgiFormat;
uint32_t resourceDimension;
uint32_t miscFlag;
uint32_t arraySize;
uint32_t miscFlags2;
};
#pragma pack(pop)
static constexpr uint32_t DDS_MAGIC = 0x20534444; // "DDS "
static constexpr uint32_t DDPF_FOURCC = 0x04;
/**
* @brief FourCC
* @param a
* @param b
* @param c
* @param d
* @return 32
*/
static uint32_t makeFourCC(char a, char b, char c, char d) {
return static_cast<uint32_t>(a) | (static_cast<uint32_t>(b) << 8) |
(static_cast<uint32_t>(c) << 16) | (static_cast<uint32_t>(d) << 24);
}
// ============================================================================
// GLTexture 实现
// ============================================================================
/**
* @brief
* @param width
* @param height
* @param pixels nullptr创建空纹理
* @param channels 1=R, 3=RGB, 4=RGBA
*/
GLTexture::GLTexture(int width, int height, const uint8_t *pixels, int channels)
: textureID_(0), width_(width), height_(height), channels_(channels),
format_(PixelFormat::RGBA8), dataSize_(0) {
// 保存像素数据用于生成遮罩
if (pixels) {
pixelData_.resize(width * height * channels);
std::memcpy(pixelData_.data(), pixels, pixelData_.size());
}
createTexture(pixels);
}
/**
* @brief
* @param filepath KTX/DDS
*/
GLTexture::GLTexture(const std::string &filepath)
: textureID_(0), width_(0), height_(0), channels_(0),
format_(PixelFormat::RGBA8), dataSize_(0) {
// 检查是否为压缩纹理格式
std::string ext = filepath.substr(filepath.find_last_of('.') + 1);
if (ext == "ktx" || ext == "KTX") {
loadCompressed(filepath);
return;
}
if (ext == "dds" || ext == "DDS") {
loadCompressed(filepath);
return;
}
// 不翻转图片,保持原始方向
stbi_set_flip_vertically_on_load(false);
uint8_t *data = stbi_load(filepath.c_str(), &width_, &height_, &channels_, 0);
if (data) {
// 保存像素数据用于生成遮罩
pixelData_.resize(width_ * height_ * channels_);
std::memcpy(pixelData_.data(), data, pixelData_.size());
createTexture(data);
stbi_image_free(data);
} else {
E2D_LOG_ERROR("加载纹理失败: {}", filepath);
}
}
GLTexture::~GLTexture() {
if (textureID_ != 0) {
// 检查 GPU 上下文是否仍然有效
// 如果 OpenGL 上下文已销毁,则跳过 glDeleteTextures 调用
if (GPUContext::get().isValid()) {
glDeleteTextures(1, &textureID_);
}
// VRAM 跟踪: 释放纹理显存(无论上下文是否有效都需要更新统计)
if (dataSize_ > 0) {
VRAMMgr::get().freeTexture(dataSize_);
}
}
}
void GLTexture::setFilter(bool linear) {
bind();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
linear ? GL_LINEAR : GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
linear ? GL_LINEAR : GL_NEAREST);
}
void GLTexture::setWrap(bool repeat) {
bind();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,
repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,
repeat ? GL_REPEAT : GL_CLAMP_TO_EDGE);
}
void GLTexture::bind(unsigned int slot) const {
glActiveTexture(GL_TEXTURE0 + slot);
glBindTexture(GL_TEXTURE_2D, textureID_);
}
/**
* @brief
*/
void GLTexture::unbind() const { glBindTexture(GL_TEXTURE_2D, 0); }
/**
* @brief OpenGL纹理对象并上传像素数据
* @param pixels
*/
void GLTexture::createTexture(const uint8_t *pixels) {
GLenum format = GL_RGBA;
GLenum internalFormat = GL_RGBA8;
int unpackAlignment = 4;
if (channels_ == 1) {
format = GL_RED;
internalFormat = GL_R8;
unpackAlignment = 1;
format_ = PixelFormat::R8;
} else if (channels_ == 3) {
format = GL_RGB;
internalFormat = GL_RGB8;
unpackAlignment = 1;
format_ = PixelFormat::RGB8;
} else if (channels_ == 4) {
format = GL_RGBA;
internalFormat = GL_RGBA8;
unpackAlignment = 4;
format_ = PixelFormat::RGBA8;
}
glGenTextures(1, &textureID_);
bind();
GLint prevUnpackAlignment = 4;
glGetIntegerv(GL_UNPACK_ALIGNMENT, &prevUnpackAlignment);
glPixelStorei(GL_UNPACK_ALIGNMENT, unpackAlignment);
glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width_, height_, 0, format,
GL_UNSIGNED_BYTE, pixels);
glPixelStorei(GL_UNPACK_ALIGNMENT, prevUnpackAlignment);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
// 使用 NEAREST 过滤器,更适合像素艺术风格的精灵
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glGenerateMipmap(GL_TEXTURE_2D);
// VRAM 跟踪
dataSize_ = static_cast<size_t>(width_ * height_ * channels_);
VRAMMgr::get().allocTexture(dataSize_);
}
// ============================================================================
// 压缩纹理加载
// ============================================================================
bool GLTexture::loadCompressed(const std::string &filepath) {
std::string ext = filepath.substr(filepath.find_last_of('.') + 1);
if (ext == "ktx" || ext == "KTX") {
return loadKTX(filepath);
}
if (ext == "dds" || ext == "DDS") {
return loadDDS(filepath);
}
E2D_LOG_ERROR("不支持的压缩纹理格式: {}", filepath);
return false;
}
/**
* @brief KTX格式压缩纹理
* @param filepath KTX文件路径
* @return truefalse
*/
bool GLTexture::loadKTX(const std::string &filepath) {
std::ifstream file(filepath, std::ios::binary);
if (!file.is_open()) {
E2D_LOG_ERROR("打开 KTX 文件失败: {}", filepath);
return false;
}
KTXHeader header;
file.read(reinterpret_cast<char *>(&header), sizeof(header));
if (!file) {
E2D_LOG_ERROR("读取 KTX 文件头失败: {}", filepath);
return false;
}
// 验证标识符
if (std::memcmp(header.identifier, KTX_IDENTIFIER, 12) != 0) {
E2D_LOG_ERROR("无效的 KTX 标识符: {}", filepath);
return false;
}
width_ = static_cast<int>(header.pixelWidth);
height_ = static_cast<int>(header.pixelHeight);
channels_ = 4; // 压缩纹理通常解压为 RGBA
// 确定压缩格式
GLenum glInternalFormat = header.glInternalFormat;
switch (glInternalFormat) {
case GL_COMPRESSED_RGB8_ETC2:
format_ = PixelFormat::ETC2_RGB8;
channels_ = 3;
break;
case GL_COMPRESSED_RGBA8_ETC2_EAC:
format_ = PixelFormat::ETC2_RGBA8;
break;
case GL_COMPRESSED_RGBA_ASTC_4x4:
format_ = PixelFormat::ASTC_4x4;
break;
case GL_COMPRESSED_RGBA_ASTC_6x6:
format_ = PixelFormat::ASTC_6x6;
break;
case GL_COMPRESSED_RGBA_ASTC_8x8:
format_ = PixelFormat::ASTC_8x8;
break;
default:
E2D_LOG_ERROR("不支持的 KTX 内部格式: {:#06x}", glInternalFormat);
return false;
}
// 跳过 key-value 数据
file.seekg(header.bytesOfKeyValueData, std::ios::cur);
// 读取第一个 mipmap level
uint32_t imageSize = 0;
file.read(reinterpret_cast<char *>(&imageSize), sizeof(imageSize));
if (!file || imageSize == 0) {
E2D_LOG_ERROR("读取 KTX 图像大小失败: {}", filepath);
return false;
}
std::vector<uint8_t> compressedData(imageSize);
file.read(reinterpret_cast<char *>(compressedData.data()), imageSize);
if (!file) {
E2D_LOG_ERROR("读取 KTX 图像数据失败: {}", filepath);
return false;
}
// 创建 GL 纹理
glGenTextures(1, &textureID_);
bind();
glCompressedTexImage2D(GL_TEXTURE_2D, 0, glInternalFormat, width_, height_, 0,
static_cast<GLsizei>(imageSize),
compressedData.data());
GLenum err = glGetError();
if (err != GL_NO_ERROR) {
E2D_LOG_ERROR("KTX 纹理上传失败: {:#06x}", err);
glDeleteTextures(1, &textureID_);
textureID_ = 0;
return false;
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// VRAM 跟踪
dataSize_ = imageSize;
VRAMMgr::get().allocTexture(dataSize_);
E2D_LOG_INFO("已加载 KTX 压缩纹理: {} ({}x{}, 格式={:#06x})", filepath,
width_, height_, glInternalFormat);
return true;
}
/**
* @brief DDS格式压缩纹理
* @param filepath DDS文件路径
* @return truefalse
*/
bool GLTexture::loadDDS(const std::string &filepath) {
std::ifstream file(filepath, std::ios::binary);
if (!file.is_open()) {
E2D_LOG_ERROR("打开 DDS 文件失败: {}", filepath);
return false;
}
DDSHeader header;
file.read(reinterpret_cast<char *>(&header), sizeof(header));
if (!file) {
E2D_LOG_ERROR("读取 DDS 文件头失败: {}", filepath);
return false;
}
if (header.magic != DDS_MAGIC) {
E2D_LOG_ERROR("无效的 DDS 魔数: {}", filepath);
return false;
}
width_ = static_cast<int>(header.width);
height_ = static_cast<int>(header.height);
channels_ = 4;
GLenum glInternalFormat = 0;
// 检查 DX10 扩展头
if ((header.pixelFormat.flags & DDPF_FOURCC) &&
header.pixelFormat.fourCC == makeFourCC('D', 'X', '1', '0')) {
DDSHeaderDXT10 dx10Header;
file.read(reinterpret_cast<char *>(&dx10Header), sizeof(dx10Header));
if (!file) {
E2D_LOG_ERROR("读取 DDS DX10 文件头失败: {}", filepath);
return false;
}
// DXGI_FORMAT 映射到 GL 格式
switch (dx10Header.dxgiFormat) {
case 147: // DXGI_FORMAT_ETC2_RGB8
glInternalFormat = GL_COMPRESSED_RGB8_ETC2;
format_ = PixelFormat::ETC2_RGB8;
channels_ = 3;
break;
case 148: // DXGI_FORMAT_ETC2_RGBA8
glInternalFormat = GL_COMPRESSED_RGBA8_ETC2_EAC;
format_ = PixelFormat::ETC2_RGBA8;
break;
default:
E2D_LOG_ERROR("不支持的 DDS DX10 格式: {}", dx10Header.dxgiFormat);
return false;
}
} else {
E2D_LOG_ERROR("DDS 文件未使用 DX10 扩展,不支持: {}", filepath);
return false;
}
// 计算压缩数据大小
size_t blockSize = (glInternalFormat == GL_COMPRESSED_RGB8_ETC2) ? 8 : 16;
size_t blocksWide = (width_ + 3) / 4;
size_t blocksHigh = (height_ + 3) / 4;
size_t imageSize = blocksWide * blocksHigh * blockSize;
std::vector<uint8_t> compressedData(imageSize);
file.read(reinterpret_cast<char *>(compressedData.data()), imageSize);
if (!file) {
E2D_LOG_ERROR("读取 DDS 图像数据失败: {}", filepath);
return false;
}
// 创建 GL 纹理
glGenTextures(1, &textureID_);
bind();
glCompressedTexImage2D(GL_TEXTURE_2D, 0, glInternalFormat, width_, height_, 0,
static_cast<GLsizei>(imageSize),
compressedData.data());
GLenum err = glGetError();
if (err != GL_NO_ERROR) {
E2D_LOG_ERROR("DDS 纹理上传失败: {:#06x}", err);
glDeleteTextures(1, &textureID_);
textureID_ = 0;
return false;
}
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// VRAM 跟踪
dataSize_ = imageSize;
VRAMMgr::get().allocTexture(dataSize_);
E2D_LOG_INFO("已加载 DDS 压缩纹理: {} ({}x{})", filepath, width_, height_);
return true;
}
void GLTexture::generateAlphaMask() {
if (pixelData_.empty() || width_ <= 0 || height_ <= 0) {
E2D_LOG_WARN("无法生成透明遮罩: 没有可用的像素数据");
return;
}
alphaMask_ = std::make_unique<AlphaMask>(AlphaMask::createFromPixels(
pixelData_.data(), width_, height_, channels_));
E2D_LOG_DEBUG("已为纹理生成透明遮罩: {}x{}", width_, height_);
}
PixelFormat GLTexture::getFormat() const { return format_; }
/**
* @brief
* @param width
* @param height
* @param format
* @return
*/
Ptr<Texture> GLTexture::create(int width, int height, PixelFormat format) {
int channels = 4;
switch (format) {
case PixelFormat::R8:
channels = 1;
break;
case PixelFormat::RG8:
channels = 2;
break;
case PixelFormat::RGB8:
channels = 3;
break;
case PixelFormat::RGBA8:
channels = 4;
break;
default:
channels = 4;
break;
}
return makePtr<GLTexture>(width, height, nullptr, channels);
}
} // namespace extra2d

View File

@ -1,293 +0,0 @@
#include <chrono>
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/shader/shader_cache.h>
#include <extra2d/services/logger_service.h>
#include <filesystem>
#include <fstream>
#include <sstream>
namespace extra2d {
namespace fs = std::filesystem;
/**
* @brief
* @return
*/
ShaderCache &ShaderCache::getInstance() {
static ShaderCache instance;
return instance;
}
/**
* @brief
* @param cacheDir
* @return truefalse
*/
bool ShaderCache::init(const std::string &cacheDir) {
cacheDir_ = cacheDir;
if (!ensureCacheDirectory()) {
E2D_LOG_ERROR("创建缓存目录失败: {}", cacheDir);
return false;
}
if (!loadCacheIndex()) {
E2D_LOG_WARN("加载缓存索引失败,重新开始");
}
initialized_ = true;
E2D_LOG_INFO("着色器缓存已初始化,位置: {}", cacheDir);
return true;
}
/**
* @brief
*/
void ShaderCache::shutdown() {
if (!initialized_) {
return;
}
saveCacheIndex();
cacheMap_.clear();
initialized_ = false;
E2D_LOG_INFO("着色器缓存已关闭");
}
/**
* @brief
* @param name Shader名称
* @param sourceHash
* @return truefalse
*/
bool ShaderCache::hasValidCache(const std::string &name,
const std::string &sourceHash) {
auto it = cacheMap_.find(name);
if (it == cacheMap_.end()) {
return false;
}
return it->second.sourceHash == sourceHash;
}
/**
* @brief
* @param name Shader名称
* @return nullptr
*/
Ptr<ShaderCacheEntry> ShaderCache::loadCache(const std::string &name) {
auto it = cacheMap_.find(name);
if (it == cacheMap_.end()) {
return nullptr;
}
std::string cachePath = getCachePath(name);
std::ifstream file(cachePath, std::ios::binary);
if (!file.is_open()) {
E2D_LOG_WARN("打开缓存文件失败: {}", cachePath);
return nullptr;
}
auto entry = std::make_shared<ShaderCacheEntry>(it->second);
entry->binary.clear();
file.seekg(0, std::ios::end);
size_t fileSize = static_cast<size_t>(file.tellg());
file.seekg(0, std::ios::beg);
entry->binary.resize(fileSize);
file.read(reinterpret_cast<char *>(entry->binary.data()), fileSize);
return entry;
}
/**
* @brief
* @param entry
* @return truefalse
*/
bool ShaderCache::saveCache(const ShaderCacheEntry &entry) {
if (!initialized_) {
E2D_LOG_WARN("着色器缓存未初始化,无法保存缓存");
return false;
}
if (entry.binary.empty()) {
E2D_LOG_WARN("着色器二进制数据为空,跳过缓存保存: {}",
entry.name);
return false;
}
std::string cachePath = getCachePath(entry.name);
E2D_LOG_DEBUG("正在保存着色器缓存到: {} ({} 字节)", cachePath,
entry.binary.size());
std::ofstream file(cachePath, std::ios::binary);
if (!file.is_open()) {
E2D_LOG_ERROR("创建缓存文件失败: {}", cachePath);
return false;
}
file.write(reinterpret_cast<const char *>(entry.binary.data()),
entry.binary.size());
file.close();
cacheMap_[entry.name] = entry;
saveCacheIndex();
E2D_LOG_INFO("着色器缓存已保存: {} ({} 字节)", entry.name,
entry.binary.size());
return true;
}
/**
* @brief 使
* @param name Shader名称
*/
void ShaderCache::invalidate(const std::string &name) {
auto it = cacheMap_.find(name);
if (it == cacheMap_.end()) {
return;
}
std::string cachePath = getCachePath(name);
fs::remove(cachePath);
cacheMap_.erase(it);
saveCacheIndex();
E2D_LOG_DEBUG("着色器缓存已失效: {}", name);
}
/**
* @brief
*/
void ShaderCache::clearAll() {
for (const auto &pair : cacheMap_) {
std::string cachePath = getCachePath(pair.first);
fs::remove(cachePath);
}
cacheMap_.clear();
saveCacheIndex();
E2D_LOG_INFO("所有着色器缓存已清除");
}
/**
* @brief
* @param vertSource
* @param fragSource
* @return
*/
std::string ShaderCache::computeHash(const std::string &vertSource,
const std::string &fragSource) {
std::string combined = vertSource + fragSource;
uint32_t hash = 5381;
for (char c : combined) {
hash = ((hash << 5) + hash) + static_cast<uint32_t>(c);
}
std::stringstream ss;
ss << std::hex << hash;
return ss.str();
}
/**
* @brief
* @return truefalse
*/
bool ShaderCache::loadCacheIndex() {
std::string indexPath = cacheDir_ + "/.cache_index";
if (!fs::exists(indexPath)) {
return true;
}
std::ifstream file(indexPath);
if (!file.is_open()) {
return false;
}
std::string line;
while (std::getline(file, line)) {
if (line.empty() || line[0] == '#') {
continue;
}
size_t pos = line.find('=');
if (pos == std::string::npos) {
continue;
}
std::string name = line.substr(0, pos);
std::string hash = line.substr(pos + 1);
std::string cachePath = getCachePath(name);
if (fs::exists(cachePath)) {
ShaderCacheEntry entry;
entry.name = name;
entry.sourceHash = hash;
entry.compileTime = static_cast<uint64_t>(
std::chrono::system_clock::now().time_since_epoch().count());
cacheMap_[name] = entry;
}
}
return true;
}
/**
* @brief
* @return truefalse
*/
bool ShaderCache::saveCacheIndex() {
std::string indexPath = cacheDir_ + "/.cache_index";
std::ofstream file(indexPath);
if (!file.is_open()) {
return false;
}
file << "# Extra2D Shader Cache Index\n";
file << "# Format: name=hash\n";
for (const auto &pair : cacheMap_) {
file << pair.first << "=" << pair.second.sourceHash << "\n";
}
return true;
}
/**
* @brief
* @param name Shader名称
* @return
*/
std::string ShaderCache::getCachePath(const std::string &name) const {
return cacheDir_ + "/" + name + ".cache";
}
/**
* @brief
* @return truefalse
*/
bool ShaderCache::ensureCacheDirectory() {
if (cacheDir_.empty()) {
return false;
}
std::error_code ec;
if (!fs::exists(cacheDir_)) {
if (!fs::create_directories(cacheDir_, ec)) {
return false;
}
}
return true;
}
} // namespace extra2d

View File

@ -1,167 +0,0 @@
#include <chrono>
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/shader/shader_hot_reloader.h>
#include <extra2d/services/logger_service.h>
#include <filesystem>
namespace extra2d {
namespace fs = std::filesystem;
/**
* @brief
* @return
*/
ShaderHotReloader &ShaderHotReloader::getInstance() {
static ShaderHotReloader instance;
return instance;
}
/**
* @brief
* @return truefalse
*/
bool ShaderHotReloader::init() {
if (initialized_) {
return true;
}
#ifdef _WIN32
buffer_.resize(4096);
#endif
initialized_ = true;
E2D_LOG_INFO("着色器热重载器已初始化");
return true;
}
/**
* @brief
*/
void ShaderHotReloader::shutdown() {
if (!initialized_) {
return;
}
#ifdef _WIN32
if (watchHandle_ != nullptr) {
FindCloseChangeNotification(watchHandle_);
watchHandle_ = nullptr;
}
#endif
watchMap_.clear();
initialized_ = false;
enabled_ = false;
E2D_LOG_INFO("着色器热重载器已关闭");
}
/**
* @brief Shader文件监视
* @param shaderName Shader名称
* @param filePaths
* @param callback
*/
void ShaderHotReloader::watch(const std::string &shaderName,
const std::vector<std::string> &filePaths,
FileChangeCallback callback) {
if (!initialized_) {
E2D_LOG_WARN("热重载器未初始化");
return;
}
WatchInfo info;
info.filePaths = filePaths;
info.callback = callback;
for (const auto &path : filePaths) {
info.modifiedTimes[path] = getFileModifiedTime(path);
}
watchMap_[shaderName] = std::move(info);
E2D_LOG_DEBUG("正在监视着色器: {} ({} 个文件)", shaderName, filePaths.size());
}
/**
* @brief
* @param shaderName Shader名称
*/
void ShaderHotReloader::unwatch(const std::string &shaderName) {
auto it = watchMap_.find(shaderName);
if (it != watchMap_.end()) {
watchMap_.erase(it);
E2D_LOG_DEBUG("停止监视着色器: {}", shaderName);
}
}
/**
* @brief
*/
void ShaderHotReloader::update() {
if (!initialized_ || !enabled_) {
return;
}
pollChanges();
}
/**
* @brief /
* @param enabled
*/
void ShaderHotReloader::setEnabled(bool enabled) {
enabled_ = enabled;
E2D_LOG_DEBUG("热重载已{}", enabled ? "Enable" : "Disable");
}
/**
* @brief
*/
void ShaderHotReloader::pollChanges() {
auto now = static_cast<uint64_t>(
std::chrono::system_clock::now().time_since_epoch().count());
for (auto &pair : watchMap_) {
WatchInfo &info = pair.second;
for (const auto &filePath : info.filePaths) {
uint64_t currentModTime = getFileModifiedTime(filePath);
uint64_t lastModTime = info.modifiedTimes[filePath];
if (currentModTime != 0 && lastModTime != 0 &&
currentModTime != lastModTime) {
info.modifiedTimes[filePath] = currentModTime;
FileChangeEvent event;
event.filepath = filePath;
event.type = FileChangeEvent::Type::Modified;
event.timestamp = now;
E2D_LOG_DEBUG("着色器文件已更改: {}", filePath);
if (info.callback) {
info.callback(event);
}
}
}
}
}
/**
* @brief
* @param filepath
* @return
*/
uint64_t ShaderHotReloader::getFileModifiedTime(const std::string &filepath) {
try {
auto ftime = fs::last_write_time(filepath);
auto sctp = std::chrono::time_point_cast<std::chrono::seconds>(
ftime - fs::file_time_type::clock::now() +
std::chrono::system_clock::now());
return static_cast<uint64_t>(sctp.time_since_epoch().count());
} catch (...) {
return 0;
}
}
} // namespace extra2d

View File

@ -1,452 +0,0 @@
#include <algorithm>
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/shader/shader_loader.h>
#include <extra2d/services/logger_service.h>
#include <filesystem>
#include <fstream>
#include <sstream>
namespace extra2d {
namespace fs = std::filesystem;
/**
* @brief Shader加载器
*/
ShaderLoader::ShaderLoader() {}
/**
* @brief Shader (.vert + .frag)
* @param name Shader名称
* @param vertPath
* @param fragPath
* @return
*/
ShaderLoadResult
ShaderLoader::loadFromSeparateFiles(const std::string &name,
const std::string &vertPath,
const std::string &fragPath) {
ShaderLoadResult result;
if (!fileExists(vertPath)) {
result.errorMessage = "Vertex shader file not found: " + vertPath;
E2D_LOG_ERROR("{}", result.errorMessage);
return result;
}
if (!fileExists(fragPath)) {
result.errorMessage = "Fragment shader file not found: " + fragPath;
E2D_LOG_ERROR("{}", result.errorMessage);
return result;
}
std::string vertSource = readFile(vertPath);
std::string fragSource = readFile(fragPath);
if (vertSource.empty()) {
result.errorMessage = "Failed to read vertex shader file: " + vertPath;
E2D_LOG_ERROR("{}", result.errorMessage);
return result;
}
if (fragSource.empty()) {
result.errorMessage = "Failed to read fragment shader file: " + fragPath;
E2D_LOG_ERROR("{}", result.errorMessage);
return result;
}
fs::path vertDir = fs::path(vertPath).parent_path();
fs::path fragDir = fs::path(fragPath).parent_path();
vertSource =
processIncludes(vertSource, vertDir.string(), result.dependencies);
fragSource =
processIncludes(fragSource, fragDir.string(), result.dependencies);
result.vertSource = vertSource;
result.fragSource = fragSource;
result.success = true;
return result;
}
/**
* @brief Shader (.shader)
* @param path Shader文件路径
* @return
*/
ShaderLoadResult ShaderLoader::loadFromCombinedFile(const std::string &path) {
ShaderLoadResult result;
if (!fileExists(path)) {
result.errorMessage = "Shader file not found: " + path;
E2D_LOG_ERROR("{}", result.errorMessage);
return result;
}
std::string content = readFile(path);
if (content.empty()) {
result.errorMessage = "Failed to read shader file: " + path;
E2D_LOG_ERROR("{}", result.errorMessage);
return result;
}
ShaderMetadata metadata;
std::string vertSource, fragSource;
if (!parseCombinedFile(content, vertSource, fragSource, metadata)) {
result.errorMessage = "Failed to parse combined shader file: " + path;
E2D_LOG_ERROR("{}", result.errorMessage);
return result;
}
fs::path baseDir = fs::path(path).parent_path();
vertSource =
processIncludes(vertSource, baseDir.string(), result.dependencies);
fragSource =
processIncludes(fragSource, baseDir.string(), result.dependencies);
result.vertSource = vertSource;
result.fragSource = fragSource;
result.success = true;
return result;
}
/**
* @brief Shader
* @param vertSource
* @param fragSource
* @return
*/
ShaderLoadResult ShaderLoader::loadFromSource(const std::string &vertSource,
const std::string &fragSource) {
ShaderLoadResult result;
result.vertSource = vertSource;
result.fragSource = fragSource;
result.success = true;
return result;
}
/**
* @brief Shader源码中的#include指令
* @param source
* @param baseDir
* @param outDependencies
* @return
*/
std::string
ShaderLoader::processIncludes(const std::string &source,
const std::string &baseDir,
std::vector<std::string> &outDependencies) {
std::string result;
std::istringstream stream(source);
std::string line;
while (std::getline(stream, line)) {
size_t includePos = line.find("#include");
if (includePos != std::string::npos) {
size_t startQuote = line.find('"', includePos);
size_t endQuote = line.find('"', startQuote + 1);
if (startQuote != std::string::npos && endQuote != std::string::npos) {
std::string includeName =
line.substr(startQuote + 1, endQuote - startQuote - 1);
std::string includePath = findIncludeFile(includeName, baseDir);
if (!includePath.empty()) {
auto cacheIt = includeCache_.find(includePath);
std::string includeContent;
if (cacheIt != includeCache_.end()) {
includeContent = cacheIt->second;
} else {
includeContent = readFile(includePath);
includeCache_[includePath] = includeContent;
}
outDependencies.push_back(includePath);
result += includeContent;
result += "\n";
continue;
} else {
E2D_LOG_WARN("未找到包含文件: {}", includeName);
}
}
}
result += line;
result += "\n";
}
return result;
}
/**
* @brief
* @param source
* @param defines
* @return
*/
std::string
ShaderLoader::applyDefines(const std::string &source,
const std::vector<std::string> &defines) {
if (defines.empty()) {
return source;
}
std::string defineBlock;
for (const auto &def : defines) {
defineBlock += "#define " + def + "\n";
}
std::string result;
std::istringstream stream(source);
std::string line;
bool inserted = false;
while (std::getline(stream, line)) {
if (!inserted && (line.find("#version") != std::string::npos ||
line.find("precision") != std::string::npos)) {
result += line + "\n";
continue;
}
if (!inserted) {
result += defineBlock;
inserted = true;
}
result += line + "\n";
}
return result;
}
/**
* @brief Shader元数据
* @param path Shader文件路径
* @return
*/
ShaderMetadata ShaderLoader::getMetadata(const std::string &path) {
ShaderMetadata metadata;
if (!fileExists(path)) {
return metadata;
}
metadata.combinedPath = path;
metadata.lastModified = getFileModifiedTime(path);
fs::path p(path);
metadata.name = p.stem().string();
return metadata;
}
/**
* @brief include搜索路径
* @param path
*/
void ShaderLoader::addIncludePath(const std::string &path) {
if (std::find(includePaths_.begin(), includePaths_.end(), path) ==
includePaths_.end()) {
includePaths_.push_back(path);
}
}
/**
* @brief
* @param filepath
* @return
*/
std::string ShaderLoader::readFile(const std::string &filepath) {
std::ifstream file(filepath, std::ios::binary);
if (!file.is_open()) {
return "";
}
std::ostringstream content;
content << file.rdbuf();
return content.str();
}
/**
* @brief
* @param filepath
* @return
*/
uint64_t ShaderLoader::getFileModifiedTime(const std::string &filepath) {
#ifdef __SWITCH__
(void)filepath;
return 1;
#else
try {
auto ftime = fs::last_write_time(filepath);
auto sctp = std::chrono::time_point_cast<std::chrono::seconds>(
ftime - fs::file_time_type::clock::now() +
std::chrono::system_clock::now());
return static_cast<uint64_t>(sctp.time_since_epoch().count());
} catch (...) {
return 0;
}
#endif
}
/**
* @brief
* @param filepath
* @return truefalse
*/
bool ShaderLoader::fileExists(const std::string &filepath) {
return fs::exists(filepath);
}
/**
* @brief Shader文件
* @param content
* @param outVert
* @param outFrag
* @param outMetadata
* @return truefalse
*/
bool ShaderLoader::parseCombinedFile(const std::string &content,
std::string &outVert, std::string &outFrag,
ShaderMetadata &outMetadata) {
enum class Section { None, Meta, Vertex, Fragment };
Section currentSection = Section::None;
std::string metaContent;
std::string vertContent;
std::string fragContent;
std::istringstream stream(content);
std::string line;
while (std::getline(stream, line)) {
std::string trimmedLine = line;
size_t start = trimmedLine.find_first_not_of(" \t\r\n");
if (start != std::string::npos) {
trimmedLine = trimmedLine.substr(start);
}
size_t end = trimmedLine.find_last_not_of(" \t\r\n");
if (end != std::string::npos) {
trimmedLine = trimmedLine.substr(0, end + 1);
}
if (trimmedLine == "#meta") {
currentSection = Section::Meta;
continue;
} else if (trimmedLine == "#vertex") {
currentSection = Section::Vertex;
continue;
} else if (trimmedLine == "#fragment") {
currentSection = Section::Fragment;
continue;
}
switch (currentSection) {
case Section::Meta:
metaContent += line + "\n";
break;
case Section::Vertex:
vertContent += line + "\n";
break;
case Section::Fragment:
fragContent += line + "\n";
break;
default:
break;
}
}
if (vertContent.empty() || fragContent.empty()) {
return false;
}
if (!metaContent.empty()) {
parseMetadata(metaContent, outMetadata);
}
outVert = vertContent;
outFrag = fragContent;
return true;
}
/**
* @brief JSON块
* @param jsonContent JSON内容
* @param outMetadata
* @return truefalse
*/
bool ShaderLoader::parseMetadata(const std::string &jsonContent,
ShaderMetadata &outMetadata) {
std::string content = jsonContent;
size_t start = content.find('{');
size_t end = content.rfind('}');
if (start == std::string::npos || end == std::string::npos || end <= start) {
return false;
}
content = content.substr(start, end - start + 1);
auto extractString = [&content](const std::string &key) -> std::string {
std::string searchKey = "\"" + key + "\"";
size_t keyPos = content.find(searchKey);
if (keyPos == std::string::npos) {
return "";
}
size_t colonPos = content.find(':', keyPos);
if (colonPos == std::string::npos) {
return "";
}
size_t quoteStart = content.find('"', colonPos);
if (quoteStart == std::string::npos) {
return "";
}
size_t quoteEnd = content.find('"', quoteStart + 1);
if (quoteEnd == std::string::npos) {
return "";
}
return content.substr(quoteStart + 1, quoteEnd - quoteStart - 1);
};
outMetadata.name = extractString("name");
return true;
}
/**
* @brief include文件路径
* @param includeName include文件名
* @param baseDir
* @return
*/
std::string ShaderLoader::findIncludeFile(const std::string &includeName,
const std::string &baseDir) {
fs::path basePath(baseDir);
fs::path includePath = basePath / includeName;
if (fs::exists(includePath)) {
return includePath.string();
}
for (const auto &searchPath : includePaths_) {
includePath = fs::path(searchPath) / includeName;
if (fs::exists(includePath)) {
return includePath.string();
}
}
return "";
}
} // namespace extra2d

View File

@ -1,855 +0,0 @@
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/shader/shader_manager.h>
#include <extra2d/services/logger_service.h>
#include <filesystem>
#include <nlohmann/json.hpp>
namespace nl = nlohmann;
namespace fs = std::filesystem;
namespace extra2d {
/**
* @brief
* @return Shader管理器实例引用
*/
ShaderManager &ShaderManager::getInstance() {
static ShaderManager instance;
return instance;
}
/**
* @brief 使Shader系统
* 使romfs/sdmc/
* @param factory Shader工厂
* @param appName
* @return truefalse
*/
bool ShaderManager::init(Ptr<IShaderFactory> factory,
const std::string &appName) {
// 使用相对路径作为Shader目录
fs::path shaderDir = "shaders";
fs::path cacheDir = "cache/shaders";
// 非Switch平台支持热重载
#ifndef __SWITCH__
hotReloadSupported_ = true;
#else
hotReloadSupported_ = false;
#endif
E2D_LOG_INFO("ShaderManager 初始化 (热重载: {})",
hotReloadSupported_ ? "supported" : "not supported");
return init(shaderDir.string(), cacheDir.string(), factory);
}
/**
* @brief Shader系统
* @param shaderDir Shader文件目录
* @param cacheDir
* @param factory Shader工厂
* @return truefalse
*/
bool ShaderManager::init(const std::string &shaderDir,
const std::string &cacheDir,
Ptr<IShaderFactory> factory) {
if (initialized_) {
E2D_LOG_WARN("ShaderManager 已初始化");
return true;
}
if (!factory) {
E2D_LOG_ERROR("Shader 工厂为空");
return false;
}
shaderDir_ = shaderDir;
cacheDir_ = cacheDir;
factory_ = factory;
// 非Switch平台支持热重载
#ifndef __SWITCH__
hotReloadSupported_ = true;
#else
hotReloadSupported_ = false;
#endif
#ifdef __SWITCH__
if (!ShaderCache::getInstance().init(cacheDir_)) {
E2D_LOG_WARN("Switch 平台初始化着色器缓存失败");
}
#else
if (!ShaderCache::getInstance().init(cacheDir_)) {
E2D_LOG_WARN("初始化着色器缓存失败,已禁用缓存");
}
#endif
if (hotReloadSupported_) {
if (!ShaderHotReloader::getInstance().init()) {
E2D_LOG_WARN("初始化热重载器失败");
}
}
loader_.addIncludePath(shaderDir_ + "common");
initialized_ = true;
E2D_LOG_INFO("ShaderManager 初始化成功");
E2D_LOG_INFO(" 着色器目录: {}", shaderDir_);
E2D_LOG_INFO(" 缓存目录: {}", cacheDir_);
E2D_LOG_INFO(" 热重载: {}", hotReloadSupported_ ? "支持" : "不支持");
return true;
}
/**
* @brief Shader系统
*/
void ShaderManager::shutdown() {
if (!initialized_) {
return;
}
if (hotReloadSupported_) {
ShaderHotReloader::getInstance().shutdown();
}
ShaderCache::getInstance().shutdown();
shaders_.clear();
factory_.reset();
initialized_ = false;
E2D_LOG_INFO("ShaderManager 已关闭");
}
/**
* @brief Shader
* @param name Shader名称
* @param vertPath
* @param fragPath
* @return Shader实例
*/
Ptr<IShader> ShaderManager::loadFromFiles(const std::string &name,
const std::string &vertPath,
const std::string &fragPath) {
if (!initialized_) {
E2D_LOG_ERROR("ShaderManager 未初始化");
return nullptr;
}
auto it = shaders_.find(name);
if (it != shaders_.end()) {
return it->second.shader;
}
ShaderLoadResult result =
loader_.loadFromSeparateFiles(name, vertPath, fragPath);
if (!result.success) {
E2D_LOG_ERROR("加载着色器文件失败: {} - {}", vertPath, fragPath);
return nullptr;
}
std::string sourceHash =
ShaderCache::computeHash(result.vertSource, result.fragSource);
Ptr<IShader> shader =
loadFromCache(name, sourceHash, result.vertSource, result.fragSource);
if (!shader) {
E2D_LOG_DEBUG("未找到有效缓存,从源码编译着色器: {}", name);
shader =
factory_->createFromSource(name, result.vertSource, result.fragSource);
if (!shader) {
E2D_LOG_ERROR("从源码创建着色器失败: {}", name);
return nullptr;
}
std::vector<uint8_t> binary;
if (factory_->getShaderBinary(*shader, binary)) {
E2D_LOG_DEBUG("获取到着色器二进制数据,大小: {} 字节", binary.size());
ShaderCacheEntry entry;
entry.name = name;
entry.sourceHash = sourceHash;
entry.binary = binary;
entry.dependencies = result.dependencies;
ShaderCache::getInstance().saveCache(entry);
} else {
E2D_LOG_WARN("获取着色器二进制数据失败: {}", name);
}
}
ShaderInfo info;
info.shader = shader;
info.vertSource = result.vertSource;
info.fragSource = result.fragSource;
info.filePaths = {vertPath, fragPath};
info.filePaths.insert(info.filePaths.end(), result.dependencies.begin(),
result.dependencies.end());
info.metadata.name = name;
info.metadata.vertPath = vertPath;
info.metadata.fragPath = fragPath;
shaders_[name] = std::move(info);
if (hotReloadEnabled_ && hotReloadSupported_) {
auto callback = [this, name](const FileChangeEvent &event) {
this->handleFileChange(name, event);
};
ShaderHotReloader::getInstance().watch(name, shaders_[name].filePaths,
callback);
}
E2D_LOG_DEBUG("着色器已加载: {}", name);
return shader;
}
/**
* @brief Shader
* @param path Shader文件路径
* @return Shader实例
*/
Ptr<IShader> ShaderManager::loadFromCombinedFile(const std::string &path) {
if (!initialized_) {
E2D_LOG_ERROR("ShaderManager 未初始化");
return nullptr;
}
ShaderMetadata metadata = loader_.getMetadata(path);
std::string name = metadata.name.empty() ? path : metadata.name;
auto it = shaders_.find(name);
if (it != shaders_.end()) {
return it->second.shader;
}
ShaderLoadResult result = loader_.loadFromCombinedFile(path);
if (!result.success) {
E2D_LOG_ERROR("加载组合着色器文件失败: {}", path);
return nullptr;
}
std::string sourceHash =
ShaderCache::computeHash(result.vertSource, result.fragSource);
Ptr<IShader> shader =
loadFromCache(name, sourceHash, result.vertSource, result.fragSource);
if (!shader) {
E2D_LOG_DEBUG("No valid cache found, compiling shader from source: {}",
name);
shader =
factory_->createFromSource(name, result.vertSource, result.fragSource);
if (!shader) {
E2D_LOG_ERROR("从源码创建着色器失败: {}", name);
return nullptr;
}
std::vector<uint8_t> binary;
if (factory_->getShaderBinary(*shader, binary)) {
E2D_LOG_DEBUG("获取到着色器二进制数据,大小: {} 字节", binary.size());
ShaderCacheEntry entry;
entry.name = name;
entry.sourceHash = sourceHash;
entry.binary = binary;
entry.dependencies = result.dependencies;
ShaderCache::getInstance().saveCache(entry);
} else {
E2D_LOG_WARN("获取着色器二进制数据失败: {}", name);
}
}
ShaderInfo info;
info.shader = shader;
info.vertSource = result.vertSource;
info.fragSource = result.fragSource;
info.filePaths = {path};
info.filePaths.insert(info.filePaths.end(), result.dependencies.begin(),
result.dependencies.end());
info.metadata = metadata;
shaders_[name] = std::move(info);
if (hotReloadEnabled_ && hotReloadSupported_) {
auto callback = [this, name](const FileChangeEvent &event) {
this->handleFileChange(name, event);
};
ShaderHotReloader::getInstance().watch(name, shaders_[name].filePaths,
callback);
}
E2D_LOG_DEBUG("从组合文件加载着色器成功: {}", name);
return shader;
}
/**
* @brief Shader
* @param name Shader名称
* @param vertSource
* @param fragSource
* @return Shader实例
*/
Ptr<IShader> ShaderManager::loadFromSource(const std::string &name,
const std::string &vertSource,
const std::string &fragSource) {
if (!initialized_) {
E2D_LOG_ERROR("ShaderManager 未初始化");
return nullptr;
}
auto it = shaders_.find(name);
if (it != shaders_.end()) {
return it->second.shader;
}
Ptr<IShader> shader =
factory_->createFromSource(name, vertSource, fragSource);
if (!shader) {
E2D_LOG_ERROR("从源码创建着色器失败: {}", name);
return nullptr;
}
ShaderInfo info;
info.shader = shader;
info.vertSource = vertSource;
info.fragSource = fragSource;
info.metadata.name = name;
shaders_[name] = std::move(info);
E2D_LOG_DEBUG("从源码加载着色器成功: {}", name);
return shader;
}
/**
* @brief Shader
* @param name Shader名称
* @return Shader实例nullptr
*/
Ptr<IShader> ShaderManager::get(const std::string &name) const {
auto it = shaders_.find(name);
if (it != shaders_.end()) {
return it->second.shader;
}
return nullptr;
}
/**
* @brief Shader是否存在
* @param name Shader名称
* @return truefalse
*/
bool ShaderManager::has(const std::string &name) const {
return shaders_.find(name) != shaders_.end();
}
/**
* @brief Shader
* @param name Shader名称
*/
void ShaderManager::remove(const std::string &name) {
auto it = shaders_.find(name);
if (it != shaders_.end()) {
ShaderHotReloader::getInstance().unwatch(name);
shaders_.erase(it);
E2D_LOG_DEBUG("着色器已移除: {}", name);
}
}
/**
* @brief Shader
*/
void ShaderManager::clear() {
if (hotReloadSupported_) {
for (const auto &pair : shaders_) {
ShaderHotReloader::getInstance().unwatch(pair.first);
}
}
shaders_.clear();
E2D_LOG_DEBUG("所有着色器已清除");
}
/**
* @brief
* @param name Shader名称
* @param callback
*/
void ShaderManager::setReloadCallback(const std::string &name,
ShaderReloadCallback callback) {
auto it = shaders_.find(name);
if (it != shaders_.end()) {
it->second.reloadCallback = callback;
}
}
/**
* @brief /
* @param enabled
*/
void ShaderManager::setHotReloadEnabled(bool enabled) {
if (!hotReloadSupported_) {
E2D_LOG_WARN("当前平台不支持热重载");
return;
}
hotReloadEnabled_ = enabled;
ShaderHotReloader::getInstance().setEnabled(enabled);
E2D_LOG_INFO("热重载已{}", enabled ? "Enable" : "Disable");
}
/**
* @brief
* @return truefalse
*/
bool ShaderManager::isHotReloadEnabled() const {
return hotReloadEnabled_ && hotReloadSupported_;
}
/**
* @brief
*/
void ShaderManager::update() {
if (hotReloadEnabled_ && hotReloadSupported_) {
ShaderHotReloader::getInstance().update();
}
}
/**
* @brief Shader
* @param name Shader名称
* @return truefalse
*/
bool ShaderManager::reload(const std::string &name) {
auto it = shaders_.find(name);
if (it == shaders_.end()) {
E2D_LOG_WARN("未找到要重载的着色器: {}", name);
return false;
}
ShaderInfo &info = it->second;
std::string vertSource = info.vertSource;
std::string fragSource = info.fragSource;
if (!info.metadata.vertPath.empty() && !info.metadata.fragPath.empty()) {
ShaderLoadResult result = loader_.loadFromSeparateFiles(
name, info.metadata.vertPath, info.metadata.fragPath);
if (result.success) {
vertSource = result.vertSource;
fragSource = result.fragSource;
}
} else if (!info.metadata.combinedPath.empty()) {
ShaderLoadResult result =
loader_.loadFromCombinedFile(info.metadata.combinedPath);
if (result.success) {
vertSource = result.vertSource;
fragSource = result.fragSource;
}
}
Ptr<IShader> newShader =
factory_->createFromSource(name, vertSource, fragSource);
if (!newShader) {
E2D_LOG_ERROR("重载着色器失败: {}", name);
return false;
}
info.shader = newShader;
info.vertSource = vertSource;
info.fragSource = fragSource;
if (info.reloadCallback) {
info.reloadCallback(newShader);
}
E2D_LOG_INFO("着色器重载成功: {}", name);
return true;
}
/**
* @brief Shader
* @param name Shader名称
* @return Shader实例
*/
Ptr<IShader> ShaderManager::getBuiltin(const std::string &name) {
Ptr<IShader> shader = get(name);
if (shader) {
return shader;
}
// 从JSON元数据文件加载内置着色器
fs::path jsonPath =
fs::path(shaderDir_) / "shared" / "builtin" / (name + ".json");
if (loader_.fileExists(jsonPath.string())) {
return loadFromMetadata(jsonPath.string(), name);
}
E2D_LOG_ERROR("未找到内置着色器: {}", jsonPath.string());
return nullptr;
}
/**
* @brief JSON元数据文件加载Shader
* @param jsonPath JSON元数据文件路径
* @param name Shader名称
* @return Shader实例
*/
Ptr<IShader> ShaderManager::loadFromMetadata(const std::string &jsonPath,
const std::string &name) {
if (!initialized_) {
E2D_LOG_ERROR("ShaderManager 未初始化");
return nullptr;
}
// 检查是否已加载
auto it = shaders_.find(name);
if (it != shaders_.end()) {
return it->second.shader;
}
// 读取JSON文件
std::string jsonContent = loader_.readFile(jsonPath);
if (jsonContent.empty()) {
E2D_LOG_ERROR("读取着色器元数据失败: {}", jsonPath);
return nullptr;
}
try {
// 使用nlohmann/json解析
nl::json j = nl::json::parse(jsonContent);
// 解析基本元数据
ShaderMetadata metadata;
metadata.name = name;
if (j.contains("category")) {
metadata.category = j["category"].get<std::string>();
}
if (j.contains("version")) {
metadata.version = j["version"].get<std::string>();
}
if (j.contains("description")) {
metadata.description = j["description"].get<std::string>();
}
// 解析 uniforms 详细定义
if (j.contains("uniforms")) {
for (auto &[key, value] : j["uniforms"].items()) {
ShaderUniformDef def;
if (value.contains("type")) {
def.type = value["type"].get<std::string>();
}
if (value.contains("description")) {
def.description = value["description"].get<std::string>();
}
// 解析默认值
if (value.contains("default")) {
def.hasDefault = true;
if (def.type == "float") {
def.defaultValue = value["default"].get<float>();
} else if (def.type == "int") {
def.defaultInt = value["default"].get<int>();
} else if (def.type == "bool") {
def.defaultBool = value["default"].get<bool>();
} else if (def.type == "vec2" && value["default"].is_array()) {
auto arr = value["default"].get<std::vector<float>>();
for (size_t i = 0; i < arr.size() && i < 2; ++i) {
def.defaultVec2[i] = arr[i];
}
} else if (def.type == "vec3" && value["default"].is_array()) {
auto arr = value["default"].get<std::vector<float>>();
for (size_t i = 0; i < arr.size() && i < 3; ++i) {
def.defaultVec3[i] = arr[i];
}
} else if (def.type == "vec4" && value["default"].is_array()) {
auto arr = value["default"].get<std::vector<float>>();
for (size_t i = 0; i < arr.size() && i < 4; ++i) {
def.defaultVec4[i] = arr[i];
}
} else if (def.type == "mat4" && value["default"].is_array()) {
auto arr = value["default"].get<std::vector<float>>();
for (size_t i = 0; i < arr.size() && i < 16; ++i) {
def.defaultMat4[i] = arr[i];
}
}
}
metadata.uniformDefs[key] = def;
// 同时填充旧的uniforms映射以保持兼容性
metadata.uniforms[key] = def.type;
}
}
// 解析 samplers 定义
if (j.contains("samplers")) {
for (auto &[key, value] : j["samplers"].items()) {
ShaderSamplerDef def;
if (value.contains("type")) {
def.type = value["type"].get<std::string>();
}
if (value.contains("description")) {
def.description = value["description"].get<std::string>();
}
metadata.samplerDefs[key] = def;
}
}
// 获取OpenGL后端路径
if (!j.contains("backends") || !j["backends"].contains("opengl")) {
E2D_LOG_ERROR("着色器元数据中未找到 OpenGL 后端: {}", jsonPath);
return nullptr;
}
auto &opengl = j["backends"]["opengl"];
if (!opengl.contains("vertex") || !opengl.contains("fragment")) {
E2D_LOG_ERROR("着色器元数据中缺少顶点或片段路径: {}", jsonPath);
return nullptr;
}
std::string vertRelativePath = opengl["vertex"].get<std::string>();
std::string fragRelativePath = opengl["fragment"].get<std::string>();
// 使用C++17文件系统构建完整路径
fs::path vertPath = fs::path(shaderDir_) / vertRelativePath;
fs::path fragPath = fs::path(shaderDir_) / fragRelativePath;
E2D_LOG_DEBUG("从元数据加载着色器: {} -> 顶点: {}, 片段: {}", name,
vertPath.string(), fragPath.string());
// 使用分离文件加载
Ptr<IShader> shader =
loadFromFiles(name, vertPath.string(), fragPath.string());
// 更新着色器元数据
if (shader && shaders_.find(name) != shaders_.end()) {
shaders_[name].metadata = metadata;
}
return shader;
} catch (const nl::json::exception &e) {
E2D_LOG_ERROR("解析着色器元数据 {} 失败: {}", jsonPath, e.what());
return nullptr;
}
}
/**
* @brief Shader
* @return truefalse
*/
bool ShaderManager::loadBuiltinShaders() {
if (!initialized_) {
E2D_LOG_ERROR("ShaderManager 未初始化");
return false;
}
bool allSuccess = true;
const char *builtinNames[] = {"sprite", "particle", "shape", "postprocess",
"font"};
for (const char *name : builtinNames) {
fs::path jsonPath = fs::path(shaderDir_) / "shared" / "builtin" /
(std::string(name) + ".json");
std::string shaderName = std::string("builtin_") + name;
Ptr<IShader> shader = nullptr;
if (loader_.fileExists(jsonPath.string())) {
shader = loadFromMetadata(jsonPath.string(), name);
}
if (!shader) {
E2D_LOG_ERROR("加载内置 {} 着色器失败", name);
allSuccess = false;
} else {
// 同时注册带 builtin_ 前缀的名称
auto it = shaders_.find(name);
if (it != shaders_.end()) {
shaders_[shaderName] = it->second;
}
}
}
if (allSuccess) {
E2D_LOG_INFO("所有内置着色器加载成功");
}
return allSuccess;
}
/**
* @brief Shader
* @param name Shader名称
* @param sourceHash
* @param vertSource
* @param fragSource
* @return Shader实例
*/
Ptr<IShader> ShaderManager::loadFromCache(const std::string &name,
const std::string &sourceHash,
const std::string &vertSource,
const std::string &fragSource) {
if (!ShaderCache::getInstance().isInitialized()) {
return nullptr;
}
if (!ShaderCache::getInstance().hasValidCache(name, sourceHash)) {
return nullptr;
}
Ptr<ShaderCacheEntry> entry = ShaderCache::getInstance().loadCache(name);
if (!entry || entry->binary.empty()) {
return nullptr;
}
Ptr<IShader> shader = factory_->createFromBinary(name, entry->binary);
if (shader) {
E2D_LOG_DEBUG("从缓存加载着色器成功: {}", name);
}
return shader;
}
/**
* @brief
* @param shaderName Shader名称
* @param event
*/
void ShaderManager::handleFileChange(const std::string &shaderName,
const FileChangeEvent &event) {
E2D_LOG_DEBUG("着色器文件已更改: {} -> {}", shaderName, event.filepath);
reload(shaderName);
}
/**
* @brief Shader元数据
* @param name Shader名称
* @return Shader元数据
*/
ShaderMetadata ShaderManager::getMetadata(const std::string &name) const {
auto it = shaders_.find(name);
if (it != shaders_.end()) {
return it->second.metadata;
}
return ShaderMetadata{};
}
/**
* @brief Shader的uniform定义
* @param name Shader名称
* @return uniform定义映射
*/
std::unordered_map<std::string, ShaderUniformDef>
ShaderManager::getUniformDefs(const std::string &name) const {
auto it = shaders_.find(name);
if (it != shaders_.end()) {
return it->second.metadata.uniformDefs;
}
return {};
}
/**
* @brief Shader的sampler定义
* @param name Shader名称
* @return sampler定义映射
*/
std::unordered_map<std::string, ShaderSamplerDef>
ShaderManager::getSamplerDefs(const std::string &name) const {
auto it = shaders_.find(name);
if (it != shaders_.end()) {
return it->second.metadata.samplerDefs;
}
return {};
}
/**
* @brief uniform值到着色器
* JSON元数据中的uniform定义uniform值
* uniform使JSON中定义的默认值
* @param shader
* @param shaderName Shader名称
* @param values uniform值映射表
*/
void ShaderManager::applyUniforms(Ptr<IShader> shader,
const std::string &shaderName,
const UniformValueMap &values) {
if (!shader)
return;
auto uniformDefs = getUniformDefs(shaderName);
for (const auto &[name, def] : uniformDefs) {
auto it = values.find(name);
if (it != values.end()) {
// 使用提供的值
const auto &value = it->second;
if (def.type == "float" &&
value.type == ShaderUniformValue::Type::Float) {
shader->setFloat(name, value.data.f[0]);
} else if (def.type == "int" &&
value.type == ShaderUniformValue::Type::Int) {
shader->setInt(name, value.data.i);
} else if (def.type == "bool" &&
value.type == ShaderUniformValue::Type::Bool) {
shader->setBool(name, value.data.b);
} else if (def.type == "vec2" &&
value.type == ShaderUniformValue::Type::Vec2) {
shader->setVec2(name, glm::vec2(value.data.f[0], value.data.f[1]));
} else if (def.type == "vec3" &&
value.type == ShaderUniformValue::Type::Vec3) {
shader->setVec3(
name, glm::vec3(value.data.f[0], value.data.f[1], value.data.f[2]));
} else if (def.type == "vec4" &&
value.type == ShaderUniformValue::Type::Vec4) {
shader->setVec4(name, glm::vec4(value.data.f[0], value.data.f[1],
value.data.f[2], value.data.f[3]));
} else if (def.type == "mat4" &&
value.type == ShaderUniformValue::Type::Mat4) {
glm::mat4 mat;
for (int i = 0; i < 4; ++i)
for (int j = 0; j < 4; ++j)
mat[i][j] = value.data.f[i * 4 + j];
shader->setMat4(name, mat);
}
} else if (def.hasDefault) {
// 使用JSON中定义的默认值
if (def.type == "float") {
shader->setFloat(name, def.defaultValue);
} else if (def.type == "int") {
shader->setInt(name, def.defaultInt);
} else if (def.type == "bool") {
shader->setBool(name, def.defaultBool);
} else if (def.type == "vec2") {
shader->setVec2(name,
glm::vec2(def.defaultVec2[0], def.defaultVec2[1]));
} else if (def.type == "vec3") {
shader->setVec3(name, glm::vec3(def.defaultVec3[0], def.defaultVec3[1],
def.defaultVec3[2]));
} else if (def.type == "vec4") {
shader->setVec4(name,
glm::vec4(def.defaultVec4[0], def.defaultVec4[1],
def.defaultVec4[2], def.defaultVec4[3]));
} else if (def.type == "mat4") {
glm::mat4 mat;
for (int i = 0; i < 4; ++i)
for (int j = 0; j < 4; ++j)
mat[i][j] = def.defaultMat4[i * 4 + j];
shader->setMat4(name, mat);
}
}
}
}
/**
* @brief sampler绑定到着色器
* JSON元数据中的sampler定义
* @param shader
* @param shaderName Shader名称
*/
void ShaderManager::applySamplers(Ptr<IShader> shader,
const std::string &shaderName) {
if (!shader)
return;
// TODO: 从JSON中解析binding并设置
// 目前sampler绑定在submitBatch中通过setInt设置
}
} // namespace extra2d

View File

@ -1,186 +0,0 @@
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/shader/shader_manager.h>
#include <extra2d/graphics/shader/shader_preset.h>
#include <extra2d/services/logger_service.h>
namespace extra2d {
/**
* @brief
* @param params
* @return
*/
Ptr<IShader> ShaderPreset::Water(const WaterParams &params) {
Ptr<IShader> shader = ShaderManager::getInstance().get("water");
if (!shader) {
E2D_LOG_ERROR("获取水波纹着色器失败");
return nullptr;
}
shader->setFloat("u_waveSpeed", params.waveSpeed);
shader->setFloat("u_waveAmplitude", params.waveAmplitude);
shader->setFloat("u_waveFrequency", params.waveFrequency);
return shader;
}
/**
* @brief
* @param params
* @return
*/
Ptr<IShader> ShaderPreset::Outline(const OutlineParams &params) {
Ptr<IShader> shader = ShaderManager::getInstance().get("outline");
if (!shader) {
E2D_LOG_ERROR("获取描边着色器失败");
return nullptr;
}
shader->setVec4("u_outlineColor", glm::vec4(params.color.r, params.color.g,
params.color.b, params.color.a));
shader->setFloat("u_thickness", params.thickness);
return shader;
}
/**
* @brief
* @param params
* @return
*/
Ptr<IShader> ShaderPreset::Distortion(const DistortionParams &params) {
Ptr<IShader> shader = ShaderManager::getInstance().get("distortion");
if (!shader) {
E2D_LOG_ERROR("获取扭曲效果着色器失败");
return nullptr;
}
shader->setFloat("u_distortionAmount", params.distortionAmount);
shader->setFloat("u_timeScale", params.timeScale);
return shader;
}
/**
* @brief
* @param params
* @return
*/
Ptr<IShader> ShaderPreset::Pixelate(const PixelateParams &params) {
Ptr<IShader> shader = ShaderManager::getInstance().get("pixelate");
if (!shader) {
E2D_LOG_ERROR("获取像素化着色器失败");
return nullptr;
}
shader->setFloat("u_pixelSize", params.pixelSize);
return shader;
}
/**
* @brief
* @param params
* @return
*/
Ptr<IShader> ShaderPreset::Invert(const InvertParams &params) {
Ptr<IShader> shader = ShaderManager::getInstance().get("invert");
if (!shader) {
E2D_LOG_ERROR("获取反相着色器失败");
return nullptr;
}
shader->setFloat("u_strength", params.strength);
return shader;
}
/**
* @brief
* @param params
* @return
*/
Ptr<IShader> ShaderPreset::Grayscale(const GrayscaleParams &params) {
Ptr<IShader> shader = ShaderManager::getInstance().get("grayscale");
if (!shader) {
E2D_LOG_ERROR("获取灰度着色器失败");
return nullptr;
}
shader->setFloat("u_intensity", params.intensity);
return shader;
}
/**
* @brief
* @param params
* @return
*/
Ptr<IShader> ShaderPreset::Blur(const BlurParams &params) {
Ptr<IShader> shader = ShaderManager::getInstance().get("blur");
if (!shader) {
E2D_LOG_ERROR("获取模糊着色器失败");
return nullptr;
}
shader->setFloat("u_radius", params.radius);
return shader;
}
/**
* @brief +
* @param grayParams
* @param outlineParams
* @return
*/
Ptr<IShader>
ShaderPreset::GrayscaleOutline(const GrayscaleParams &grayParams,
const OutlineParams &outlineParams) {
std::string shaderDir = ShaderManager::getInstance().getShaderDir();
std::string jsonPath = shaderDir + "effects/grayscale_outline.json";
Ptr<IShader> shader =
ShaderManager::getInstance().loadFromMetadata(jsonPath, "grayscale_outline");
if (!shader) {
E2D_LOG_ERROR("从 {} 加载灰度描边组合着色器失败",
jsonPath);
return nullptr;
}
shader->setFloat("u_grayIntensity", grayParams.intensity);
shader->setVec4("u_outlineColor",
glm::vec4(outlineParams.color.r, outlineParams.color.g,
outlineParams.color.b, outlineParams.color.a));
shader->setFloat("u_thickness", outlineParams.thickness);
return shader;
}
/**
* @brief +
* @param pixParams
* @param invParams
* @return
*/
Ptr<IShader> ShaderPreset::PixelateInvert(const PixelateParams &pixParams,
const InvertParams &invParams) {
std::string shaderDir = ShaderManager::getInstance().getShaderDir();
std::string jsonPath = shaderDir + "effects/pixelate_invert.json";
Ptr<IShader> shader =
ShaderManager::getInstance().loadFromMetadata(jsonPath, "pixelate_invert");
if (!shader) {
E2D_LOG_ERROR("从 {} 加载像素化反相组合着色器失败", jsonPath);
return nullptr;
}
shader->setFloat("u_pixelSize", pixParams.pixelSize);
shader->setFloat("u_invertStrength", invParams.strength);
return shader;
}
} // namespace extra2d

View File

@ -1,103 +0,0 @@
#include <extra2d/graphics/texture/alpha_mask.h>
namespace extra2d {
/**
* @brief
*
* Alpha遮罩255
*
* @param width
* @param height
*/
AlphaMask::AlphaMask(int width, int height)
: width_(width), height_(height), data_(width * height, 255) {}
/**
* @brief Alpha遮罩
*
* Alpha通道创建遮罩
* RGBA4RGB31
*
* @param pixels
* @param width
* @param height
* @param channels 134
* @return Alpha遮罩对象
*/
AlphaMask AlphaMask::createFromPixels(const uint8_t *pixels, int width,
int height, int channels) {
AlphaMask mask(width, height);
if (!pixels || width <= 0 || height <= 0) {
return mask;
}
// 根据通道数提取Alpha值
for (int y = 0; y < height; ++y) {
for (int x = 0; x < width; ++x) {
int pixelIndex = (y * width + x) * channels;
uint8_t alpha = 255;
if (channels == 4) {
// RGBA格式Alpha在第四个通道
alpha = pixels[pixelIndex + 3];
} else if (channels == 1) {
// 灰度图直接作为Alpha
alpha = pixels[pixelIndex];
} else if (channels == 3) {
// RGB格式没有Alpha通道视为不透明
alpha = 255;
}
mask.data_[y * width + x] = alpha;
}
}
return mask;
}
/**
* @brief Alpha值
*
* Alpha值0
*
* @param x X坐标
* @param y Y坐标
* @return Alpha值0-2550
*/
uint8_t AlphaMask::getAlpha(int x, int y) const {
if (!isValid(x, y)) {
return 0;
}
return data_[y * width_ + x];
}
/**
* @brief
*
* Alpha值是否大于等于给定的阈值
*
* @param x X坐标
* @param y Y坐标
* @param threshold 255
* @return Alpha值大于等于阈值返回truefalse
*/
bool AlphaMask::isOpaque(int x, int y, uint8_t threshold) const {
return getAlpha(x, y) >= threshold;
}
/**
* @brief
*
*
*
* @param x X坐标
* @param y Y坐标
* @return truefalse
*/
bool AlphaMask::isValid(int x, int y) const {
return x >= 0 && x < width_ && y >= 0 && y < height_;
}
} // namespace extra2d

View File

@ -1,386 +0,0 @@
#include <extra2d/core/service_locator.h>
#include <extra2d/graphics/texture/texture_atlas.h>
#include <extra2d/services/logger_service.h>
namespace extra2d {
// ============================================================================
// TextureAtlasPage 实现
// ============================================================================
/**
* @brief
* @param width
* @param height
*
*
*/
TextureAtlasPage::TextureAtlasPage(int width, int height)
: width_(width), height_(height), isFull_(false), usedArea_(0) {
// 创建空白纹理
std::vector<uint8_t> emptyData(width * height * 4, 0);
texture_ = makePtr<GLTexture>(width, height, emptyData.data(), 4);
// 初始化矩形打包根节点
root_ = std::make_unique<PackNode>(0, 0, width, height);
E2D_LOG_INFO("已创建纹理图集页面: {}x{}", width, height);
}
/**
* @brief
*
*
*/
TextureAtlasPage::~TextureAtlasPage() = default;
/**
* @brief
* @param name
* @param texWidth
* @param texHeight
* @param pixels
* @param[out] outUvRect UV坐标矩形
* @return truefalse
*
* 使
*/
bool TextureAtlasPage::tryAddTexture(const std::string &name, int texWidth,
int texHeight, const uint8_t *pixels,
Rect &outUvRect) {
if (isFull_) {
return false;
}
// 添加边距
int paddedWidth = texWidth + 2 * PADDING;
int paddedHeight = texHeight + 2 * PADDING;
// 如果纹理太大,无法放入
if (paddedWidth > width_ || paddedHeight > height_) {
return false;
}
// 尝试插入
PackNode *node = insert(root_.get(), paddedWidth, paddedHeight);
if (node == nullptr) {
// 无法放入,标记为满
isFull_ = true;
return false;
}
// 写入像素数据(跳过边距区域)
writePixels(node->x + PADDING, node->y + PADDING, texWidth, texHeight,
pixels);
// 创建条目
AtlasEntry entry;
entry.name = name;
entry.originalSize =
Vec2(static_cast<float>(texWidth), static_cast<float>(texHeight));
entry.padding = PADDING;
// 计算 UV 坐标(考虑边距)
float u1 = static_cast<float>(node->x + PADDING) / width_;
float v1 = static_cast<float>(node->y + PADDING) / height_;
float u2 = static_cast<float>(node->x + PADDING + texWidth) / width_;
float v2 = static_cast<float>(node->y + PADDING + texHeight) / height_;
entry.uvRect = Rect(u1, v1, u2 - u1, v2 - v1);
outUvRect = entry.uvRect;
entries_[name] = std::move(entry);
usedArea_ += paddedWidth * paddedHeight;
E2D_LOG_DEBUG("已将纹理 '{}' 添加到图集: {}x{} 位置 ({}, {})", name,
texWidth, texHeight, node->x, node->y);
return true;
}
/**
* @brief
* @param node
* @param width
* @param height
* @return nullptr
*
* 使
*/
TextureAtlasPage::PackNode *TextureAtlasPage::insert(PackNode *node, int width,
int height) {
if (node == nullptr) {
return nullptr;
}
// 如果节点已被使用,尝试子节点
if (node->used) {
PackNode *result = insert(node->left.get(), width, height);
if (result != nullptr) {
return result;
}
return insert(node->right.get(), width, height);
}
// 检查是否适合
if (width > node->width || height > node->height) {
return nullptr;
}
// 如果刚好合适,使用此节点
if (width == node->width && height == node->height) {
node->used = true;
return node;
}
// 需要分割节点
int dw = node->width - width;
int dh = node->height - height;
if (dw > dh) {
// 水平分割
node->left =
std::make_unique<PackNode>(node->x, node->y, width, node->height);
node->right =
std::make_unique<PackNode>(node->x + width, node->y, dw, node->height);
} else {
// 垂直分割
node->left =
std::make_unique<PackNode>(node->x, node->y, node->width, height);
node->right =
std::make_unique<PackNode>(node->x, node->y + height, node->width, dh);
}
// 递归插入到左子节点
return insert(node->left.get(), width, height);
}
/**
* @brief
* @param x X坐标
* @param y Y坐标
* @param w
* @param h
* @param pixels
*
* 使glTexSubImage2D更新纹理的指定区域
*/
void TextureAtlasPage::writePixels(int x, int y, int w, int h,
const uint8_t *pixels) {
if (texture_ == nullptr || pixels == nullptr) {
return;
}
// 使用 glTexSubImage2D 更新纹理数据
GLuint texID = static_cast<GLuint>(
reinterpret_cast<uintptr_t>(texture_->getNativeHandle()));
glBindTexture(GL_TEXTURE_2D, texID);
glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA, GL_UNSIGNED_BYTE,
pixels);
glBindTexture(GL_TEXTURE_2D, 0);
}
/**
* @brief
* @param name
* @return nullptr
*/
const AtlasEntry *TextureAtlasPage::getEntry(const std::string &name) const {
auto it = entries_.find(name);
if (it != entries_.end()) {
return &it->second;
}
return nullptr;
}
/**
* @brief 使
* @return 使0.01.0
*
* 使
*/
float TextureAtlasPage::getUsageRatio() const {
return static_cast<float>(usedArea_) / (width_ * height_);
}
// ============================================================================
// TextureAtlas 实现
// ============================================================================
/**
* @brief
*
* 使
*/
TextureAtlas::TextureAtlas()
: pageSize_(TextureAtlasPage::DEFAULT_SIZE), sizeThreshold_(256),
enabled_(true), initialized_(false) {}
/**
* @brief
*
*
*/
TextureAtlas::~TextureAtlas() = default;
/**
* @brief
* @param pageSize
*
*
*/
void TextureAtlas::init(int pageSize) {
pageSize_ = pageSize;
initialized_ = true;
E2D_LOG_INFO("纹理图集已初始化,页面大小: {}", pageSize);
}
/**
* @brief
* @param name
* @param width
* @param height
* @param pixels
* @return truefalse
*
*
*/
bool TextureAtlas::addTexture(const std::string &name, int width, int height,
const uint8_t *pixels) {
if (!enabled_ || !initialized_) {
return false;
}
// 检查是否已存在
if (contains(name)) {
return true;
}
// 检查纹理大小
if (width > sizeThreshold_ || height > sizeThreshold_) {
E2D_LOG_DEBUG("纹理 '{}' 太大无法加入图集 ({}x{} > {}),跳过",
name, width, height, sizeThreshold_);
return false;
}
// 尝试添加到现有页面
Rect uvRect;
for (auto &page : pages_) {
if (page->tryAddTexture(name, width, height, pixels, uvRect)) {
entryToPage_[name] = page.get();
return true;
}
}
// 创建新页面
auto newPage = std::make_unique<TextureAtlasPage>(pageSize_, pageSize_);
if (newPage->tryAddTexture(name, width, height, pixels, uvRect)) {
entryToPage_[name] = newPage.get();
pages_.push_back(std::move(newPage));
return true;
}
E2D_LOG_WARN("添加纹理 '{}' 到图集失败", name);
return false;
}
/**
* @brief
* @param name
* @return truefalse
*/
bool TextureAtlas::contains(const std::string &name) const {
return entryToPage_.find(name) != entryToPage_.end();
}
/**
* @brief
* @param name
* @return nullptr
*/
const Texture *TextureAtlas::getAtlasTexture(const std::string &name) const {
auto it = entryToPage_.find(name);
if (it != entryToPage_.end()) {
return it->second->getTexture().get();
}
return nullptr;
}
/**
* @brief UV坐标矩形
* @param name
* @return UV坐标矩形
*/
Rect TextureAtlas::getUVRect(const std::string &name) const {
auto it = entryToPage_.find(name);
if (it != entryToPage_.end()) {
const AtlasEntry *entry = it->second->getEntry(name);
if (entry != nullptr) {
return entry->uvRect;
}
}
return Rect(0, 0, 1, 1); // 默认 UV
}
/**
* @brief
* @param name
* @return
*/
Vec2 TextureAtlas::getOriginalSize(const std::string &name) const {
auto it = entryToPage_.find(name);
if (it != entryToPage_.end()) {
const AtlasEntry *entry = it->second->getEntry(name);
if (entry != nullptr) {
return entry->originalSize;
}
}
return Vec2(0, 0);
}
/**
* @brief 使
* @return 使
*
* 使
*/
float TextureAtlas::getTotalUsageRatio() const {
if (pages_.empty()) {
return 0.0f;
}
float total = 0.0f;
for (const auto &page : pages_) {
total += page->getUsageRatio();
}
return total / pages_.size();
}
/**
* @brief
*
*
*/
void TextureAtlas::clear() {
pages_.clear();
entryToPage_.clear();
E2D_LOG_INFO("纹理图集已清空");
}
// ============================================================================
// TextureAtlasMgr 单例实现
// ============================================================================
/**
* @brief TextureAtlasMgr单例实例
* @return TextureAtlasMgr单例的引用
*
* 使线
*/
TextureAtlasMgr &TextureAtlasMgr::get() {
static TextureAtlasMgr instance;
return instance;
}
} // namespace extra2d

View File

@ -1,651 +0,0 @@
#include <extra2d/graphics/opengl/gl_renderer.h>
#include <extra2d/graphics/texture/texture_pool.h>
#include <extra2d/scene/scene.h>
#include <algorithm>
#include <cstring>
namespace extra2d {
// ============================================================================
// TexturePool 实现
// ============================================================================
/**
* @brief
*
*
*/
TexturePool::TexturePool()
: scene_(nullptr), maxMemoryUsage_(0), currentMemoryUsage_(0),
cacheHits_(0), cacheMisses_(0), evictionCount_(0) {}
/**
* @brief
* @param scene
* @param maxMemoryUsage 使0
*
*
*/
TexturePool::TexturePool(Scene *scene, size_t maxMemoryUsage)
: scene_(scene), maxMemoryUsage_(maxMemoryUsage), currentMemoryUsage_(0),
cacheHits_(0), cacheMisses_(0), evictionCount_(0) {
E2D_LOG_INFO("TexturePool 已创建,最大内存: {} 字节", maxMemoryUsage);
}
/**
* @brief
* @param scene
* @param maxMemoryUsage 使0
*
*
*/
void TexturePool::init(Scene *scene, size_t maxMemoryUsage) {
scene_ = scene;
maxMemoryUsage_ = maxMemoryUsage;
E2D_LOG_INFO("TexturePool 已初始化,最大内存: {} 字节", maxMemoryUsage);
}
/**
* @brief
*
*
*/
TexturePool::~TexturePool() {
clear();
E2D_LOG_INFO("TexturePool 已销毁");
}
// ============================================================================
// 纹理加载
// ============================================================================
/**
* @brief
* @param path
* @param options
* @return
*
*
*/
TextureRef TexturePool::load(const std::string &path,
const TextureLoadOptions &options) {
return load(path, Rect::Zero(), options);
}
/**
* @brief
* @param path
* @param region
* @param options
* @return
*
*
*/
TextureRef TexturePool::load(const std::string &path, const Rect &region,
const TextureLoadOptions &options) {
TextureKey key(path, region);
std::lock_guard<std::mutex> lock(mutex_);
// 检查缓存
auto it = cache_.find(key);
if (it != cache_.end()) {
// 缓存命中
it->second.touch();
it->second.refCount.fetch_add(1, std::memory_order_relaxed);
cacheHits_.fetch_add(1, std::memory_order_relaxed);
E2D_LOG_DEBUG("纹理缓存命中: {}", path);
return TextureRef(it->second.texture, &it->second, &mutex_);
}
// 缓存未命中
cacheMisses_.fetch_add(1, std::memory_order_relaxed);
// 获取渲染后端
Renderer *backend = nullptr;
if (scene_) {
// 假设 Scene 有获取 Renderer 的方法
// 这里需要根据实际接口调整
backend = nullptr; // TODO: 从 Scene 获取 Renderer
}
if (!backend) {
E2D_LOG_ERROR("TexturePool: 渲染后端不可用");
return TextureRef();
}
// 加载纹理
Ptr<Texture> texture = backend->loadTexture(path);
if (!texture) {
E2D_LOG_ERROR("TexturePool: 加载纹理失败: {}", path);
return TextureRef();
}
// 计算内存大小
size_t memorySize = calculateTextureMemory(texture.get());
// 检查内存限制
if (maxMemoryUsage_ > 0 &&
currentMemoryUsage_ + memorySize > maxMemoryUsage_) {
// 尝试淘汰
evictLRU(currentMemoryUsage_ + memorySize - maxMemoryUsage_);
// 再次检查
if (currentMemoryUsage_ + memorySize > maxMemoryUsage_) {
E2D_LOG_WARN("TexturePool: 内存限制超出,无法加载纹理: {}", path);
return TextureRef();
}
}
// 创建缓存条目
auto result = cache_.emplace(key, TexturePoolEntry(nullptr, key, 0));
if (result.second) {
result.first->second.texture = texture;
result.first->second.memorySize = memorySize;
result.first->second.refCount.store(1, std::memory_order_relaxed);
result.first->second.touch();
currentMemoryUsage_ += memorySize;
E2D_LOG_INFO("TexturePool: 已加载纹理: {} ({} 字节)", path, memorySize);
return TextureRef(texture, &result.first->second, &mutex_);
}
return TextureRef();
}
/**
* @brief
* @param data
* @param width
* @param height
* @param channels
* @param key
* @return
*
*
*/
TextureRef TexturePool::loadFromMemory(const uint8_t *data, int width,
int height, int channels,
const std::string &key) {
TextureKey textureKey(key);
std::lock_guard<std::mutex> lock(mutex_);
// 检查缓存
auto it = cache_.find(textureKey);
if (it != cache_.end()) {
it->second.touch();
it->second.refCount.fetch_add(1, std::memory_order_relaxed);
cacheHits_.fetch_add(1, std::memory_order_relaxed);
return TextureRef(it->second.texture, &it->second, &mutex_);
}
cacheMisses_.fetch_add(1, std::memory_order_relaxed);
// 获取渲染后端
Renderer *backend = nullptr;
if (scene_) {
backend = nullptr; // TODO: 从 Scene 获取 Renderer
}
if (!backend) {
E2D_LOG_ERROR("TexturePool: 渲染后端不可用");
return TextureRef();
}
// 创建纹理
Ptr<Texture> texture = backend->createTexture(width, height, data, channels);
if (!texture) {
E2D_LOG_ERROR("TexturePool: 从内存创建纹理失败");
return TextureRef();
}
// 计算内存大小
size_t memorySize = calculateTextureMemory(texture.get());
// 检查内存限制
if (maxMemoryUsage_ > 0 &&
currentMemoryUsage_ + memorySize > maxMemoryUsage_) {
evictLRU(currentMemoryUsage_ + memorySize - maxMemoryUsage_);
if (currentMemoryUsage_ + memorySize > maxMemoryUsage_) {
E2D_LOG_WARN("TexturePool: 内存限制超出");
return TextureRef();
}
}
// 创建缓存条目
auto result =
cache_.emplace(textureKey, TexturePoolEntry(nullptr, textureKey, 0));
if (result.second) {
result.first->second.texture = texture;
result.first->second.memorySize = memorySize;
result.first->second.refCount.store(1, std::memory_order_relaxed);
result.first->second.touch();
currentMemoryUsage_ += memorySize;
E2D_LOG_INFO("TexturePool: 已从内存创建纹理 ({} 字节)", memorySize);
return TextureRef(texture, &result.first->second, &mutex_);
}
return TextureRef();
}
/**
* @brief
* @param path
* @param options
* @return
*
*
*/
TextureRef TexturePool::getOrLoad(const std::string &path,
const TextureLoadOptions &options) {
return getOrLoad(path, Rect::Zero(), options);
}
/**
* @brief
* @param path
* @param region
* @param options
* @return
*
*
*/
TextureRef TexturePool::getOrLoad(const std::string &path, const Rect &region,
const TextureLoadOptions &options) {
TextureKey key(path, region);
std::lock_guard<std::mutex> lock(mutex_);
// 检查缓存
auto it = cache_.find(key);
if (it != cache_.end()) {
it->second.touch();
it->second.refCount.fetch_add(1, std::memory_order_relaxed);
cacheHits_.fetch_add(1, std::memory_order_relaxed);
return TextureRef(it->second.texture, &it->second, &mutex_);
}
// 释放锁后调用 load
// 注意:这里需要重新设计以避免死锁
// 简化处理:直接在这里加载
cacheMisses_.fetch_add(1, std::memory_order_relaxed);
Renderer *backend = nullptr;
if (scene_) {
backend = nullptr; // TODO: 从 Scene 获取 Renderer
}
if (!backend) {
E2D_LOG_ERROR("TexturePool: 渲染后端不可用");
return TextureRef();
}
Ptr<Texture> texture = backend->loadTexture(path);
if (!texture) {
E2D_LOG_ERROR("TexturePool: 加载纹理失败: {}", path);
return TextureRef();
}
size_t memorySize = calculateTextureMemory(texture.get());
if (maxMemoryUsage_ > 0 &&
currentMemoryUsage_ + memorySize > maxMemoryUsage_) {
evictLRU(currentMemoryUsage_ + memorySize - maxMemoryUsage_);
if (currentMemoryUsage_ + memorySize > maxMemoryUsage_) {
E2D_LOG_WARN("TexturePool: 内存限制超出");
return TextureRef();
}
}
auto result = cache_.emplace(key, TexturePoolEntry(nullptr, key, 0));
if (result.second) {
result.first->second.texture = texture;
result.first->second.memorySize = memorySize;
result.first->second.refCount.store(1, std::memory_order_relaxed);
result.first->second.touch();
currentMemoryUsage_ += memorySize;
return TextureRef(texture, &result.first->second, &mutex_);
}
return TextureRef();
}
// ============================================================================
// 引用计数管理
// ============================================================================
/**
* @brief
* @param key
* @return
*
*
*/
bool TexturePool::addRef(const TextureKey &key) {
std::lock_guard<std::mutex> lock(mutex_);
auto it = cache_.find(key);
if (it != cache_.end()) {
it->second.touch();
it->second.refCount.fetch_add(1, std::memory_order_relaxed);
return true;
}
return false;
}
/**
* @brief
* @param key
* @return
*
*
*/
uint32_t TexturePool::release(const TextureKey &key) {
std::lock_guard<std::mutex> lock(mutex_);
auto it = cache_.find(key);
if (it != cache_.end()) {
uint32_t count =
it->second.refCount.fetch_sub(1, std::memory_order_relaxed);
return count > 0 ? count - 1 : 0;
}
return 0;
}
/**
* @brief
* @param key
* @return
*
*
*/
uint32_t TexturePool::getRefCount(const TextureKey &key) const {
std::lock_guard<std::mutex> lock(mutex_);
auto it = cache_.find(key);
if (it != cache_.end()) {
return it->second.refCount.load(std::memory_order_relaxed);
}
return 0;
}
// ============================================================================
// 缓存管理
// ============================================================================
/**
* @brief
* @param key
* @return
*
*
*/
bool TexturePool::isCached(const TextureKey &key) const {
std::lock_guard<std::mutex> lock(mutex_);
return cache_.find(key) != cache_.end();
}
/**
* @brief
* @param key
* @return
*
*
*/
bool TexturePool::removeFromCache(const TextureKey &key) {
std::lock_guard<std::mutex> lock(mutex_);
auto it = cache_.find(key);
if (it != cache_.end()) {
currentMemoryUsage_ -= it->second.memorySize;
cache_.erase(it);
E2D_LOG_DEBUG("TexturePool: 已从缓存移除纹理");
return true;
}
return false;
}
/**
* @brief 0
* @return
*
* 0
*/
size_t TexturePool::collectGarbage() {
std::lock_guard<std::mutex> lock(mutex_);
size_t removed = 0;
for (auto it = cache_.begin(); it != cache_.end();) {
if (it->second.refCount.load(std::memory_order_relaxed) == 0) {
currentMemoryUsage_ -= it->second.memorySize;
it = cache_.erase(it);
++removed;
} else {
++it;
}
}
if (removed > 0) {
E2D_LOG_INFO("TexturePool: 垃圾回收 {} 个纹理", removed);
}
return removed;
}
/**
* @brief
*
*
*/
void TexturePool::clear() {
std::lock_guard<std::mutex> lock(mutex_);
cache_.clear();
currentMemoryUsage_ = 0;
E2D_LOG_INFO("TexturePool: 已清除所有纹理");
}
// ============================================================================
// 内存管理
// ============================================================================
/**
* @brief 使
* @return 使
*
* 使
*/
size_t TexturePool::getMemoryUsage() const {
std::lock_guard<std::mutex> lock(mutex_);
return currentMemoryUsage_;
}
/**
* @brief 使
* @param maxMemory 使0
*
* 使
*/
void TexturePool::setMaxMemoryUsage(size_t maxMemory) {
std::lock_guard<std::mutex> lock(mutex_);
maxMemoryUsage_ = maxMemory;
// 如果当前内存超过新的限制,执行淘汰
if (maxMemoryUsage_ > 0 && currentMemoryUsage_ > maxMemoryUsage_) {
evictLRU(maxMemoryUsage_);
}
E2D_LOG_INFO("TexturePool: 最大内存设置为 {} 字节", maxMemory);
}
/**
* @brief LRU
* @param targetMemory 使
* @return
*
* LRU算法淘汰最少使用的纹理以达到目标内存使用量
*/
size_t TexturePool::evictLRU(size_t targetMemory) {
// 注意:调用者应该已持有锁
if (cache_.empty()) {
return 0;
}
// 收集所有条目并按最后访问时间排序
std::vector<std::pair<TextureKey, uint64_t>> entries;
entries.reserve(cache_.size());
for (const auto &pair : cache_) {
// 只淘汰引用计数为 0 的纹理
if (pair.second.refCount.load(std::memory_order_relaxed) == 0) {
entries.emplace_back(pair.first, pair.second.lastAccessTime);
}
}
// 按访问时间升序排序(最旧的在前)
std::sort(entries.begin(), entries.end(),
[](const auto &a, const auto &b) { return a.second < b.second; });
size_t evicted = 0;
size_t target = targetMemory > 0 ? targetMemory : 0;
for (const auto &entry : entries) {
if (targetMemory > 0 && currentMemoryUsage_ <= target) {
break;
}
auto it = cache_.find(entry.first);
if (it != cache_.end()) {
currentMemoryUsage_ -= it->second.memorySize;
cache_.erase(it);
++evicted;
}
}
if (evicted > 0) {
evictionCount_.fetch_add(evicted, std::memory_order_relaxed);
E2D_LOG_INFO("TexturePool: LRU 淘汰 {} 个纹理", evicted);
}
return evicted;
}
// ============================================================================
// 统计信息
// ============================================================================
/**
* @brief
* @return
*
* 使
*/
TexturePool::Stats TexturePool::getStats() const {
std::lock_guard<std::mutex> lock(mutex_);
Stats stats;
stats.textureCount = cache_.size();
stats.memoryUsage = currentMemoryUsage_;
stats.maxMemoryUsage = maxMemoryUsage_;
stats.cacheHits = cacheHits_.load(std::memory_order_relaxed);
stats.cacheMisses = cacheMisses_.load(std::memory_order_relaxed);
stats.evictionCount = evictionCount_.load(std::memory_order_relaxed);
return stats;
}
/**
* @brief
*
*
*/
void TexturePool::resetStats() {
cacheHits_.store(0, std::memory_order_relaxed);
cacheMisses_.store(0, std::memory_order_relaxed);
evictionCount_.store(0, std::memory_order_relaxed);
}
// ============================================================================
// 私有方法
// ============================================================================
/**
* @brief
* @param texture
* @return
*
*
*/
size_t TexturePool::calculateTextureMemory(const Texture *texture) {
if (!texture) {
return 0;
}
int width = texture->getWidth();
int height = texture->getHeight();
int channels = texture->getChannels();
// 基础内存计算
size_t baseSize = static_cast<size_t>(width) * height * channels;
// 根据像素格式调整
PixelFormat format = texture->getFormat();
switch (format) {
case PixelFormat::RGB16F:
case PixelFormat::RGBA16F:
baseSize *= 2; // 半精度浮点
break;
case PixelFormat::RGB32F:
case PixelFormat::RGBA32F:
baseSize *= 4; // 全精度浮点
break;
case PixelFormat::Depth16:
baseSize = static_cast<size_t>(width) * height * 2;
break;
case PixelFormat::Depth24:
case PixelFormat::Depth24Stencil8:
baseSize = static_cast<size_t>(width) * height * 4;
break;
case PixelFormat::Depth32F:
baseSize = static_cast<size_t>(width) * height * 4;
break;
default:
break;
}
// 考虑 Mipmaps大约增加 33% 内存)
// 注意:这里假设生成了 mipmaps实际应该根据 TextureLoadOptions 判断
// baseSize = baseSize * 4 / 3;
return baseSize;
}
/**
* @brief
* @return
*
* 使
*/
bool TexturePool::needsEviction() const {
return maxMemoryUsage_ > 0 && currentMemoryUsage_ > maxMemoryUsage_;
}
/**
* @brief
*
* 使LRU淘汰
*/
void TexturePool::tryAutoEvict() {
if (needsEviction()) {
evictLRU(maxMemoryUsage_);
}
}
} // namespace extra2d

View File

@ -4,7 +4,6 @@
#include <extra2d/platform/keys.h> #include <extra2d/platform/keys.h>
#include <extra2d/services/event_service.h> #include <extra2d/services/event_service.h>
#include <extra2d/services/logger_service.h> #include <extra2d/services/logger_service.h>
#include <glad/glad.h>
namespace extra2d { namespace extra2d {
@ -18,13 +17,8 @@ bool GLFWWindow::create(const std::string &title, int width, int height,
return false; return false;
} }
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE); glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_TRUE);
glfwWindowHint(GLFW_DEPTH_BITS, 24);
glfwWindowHint(GLFW_STENCIL_BITS, 8);
#ifdef __SWITCH__ #ifdef __SWITCH__
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
@ -43,7 +37,7 @@ bool GLFWWindow::create(const std::string &title, int width, int height,
glfwCreateWindow(width, height, title.c_str(), monitor, nullptr); glfwCreateWindow(width, height, title.c_str(), monitor, nullptr);
if (!glfwWindow_) { if (!glfwWindow_) {
E2D_LOG_ERROR("创建 GLFW 窗口失败"); E2D_ERROR("创建 GLFW 窗口失败");
deinitGLFW(); deinitGLFW();
return false; return false;
} }
@ -65,15 +59,6 @@ bool GLFWWindow::create(const std::string &title, int width, int height,
#endif #endif
glfwMakeContextCurrent(glfwWindow_); glfwMakeContextCurrent(glfwWindow_);
if (!gladLoadGLES2Loader((GLADloadproc)glfwGetProcAddress)) {
E2D_LOG_ERROR("初始化 GLAD GLES2 失败");
glfwDestroyWindow(glfwWindow_);
glfwWindow_ = nullptr;
deinitGLFW();
return false;
}
glfwSwapInterval(vsync ? 1 : 0); glfwSwapInterval(vsync ? 1 : 0);
vsync_ = vsync; vsync_ = vsync;
@ -91,7 +76,7 @@ bool GLFWWindow::create(const std::string &title, int width, int height,
glfwSetKeyCallback(glfwWindow_, keyCallback); glfwSetKeyCallback(glfwWindow_, keyCallback);
glfwSetJoystickCallback(joystickCallback); glfwSetJoystickCallback(joystickCallback);
E2D_LOG_INFO("GLFW 窗口创建成功: {}x{}", width_, height_); E2D_INFO("GLFW 窗口创建成功: {}x{}", width_, height_);
return true; return true;
} }
@ -289,7 +274,7 @@ bool GLFWWindow::initGLFW() {
static int glfwInitCount = 0; static int glfwInitCount = 0;
if (glfwInitCount == 0) { if (glfwInitCount == 0) {
if (!glfwInit()) { if (!glfwInit()) {
E2D_LOG_ERROR("初始化 GLFW 失败"); E2D_ERROR("初始化 GLFW 失败");
return false; return false;
} }
glfwInitCount++; glfwInitCount++;

View File

@ -24,21 +24,21 @@ bool WindowModule::init() {
if (initialized_) if (initialized_)
return true; return true;
E2D_LOG_INFO("正在创建 GLFW 窗口,尺寸 {}x{}", cfg_.w, cfg_.h); E2D_INFO("正在创建 GLFW 窗口,尺寸 {}x{}", cfg_.w, cfg_.h);
win_ = makeUnique<GLFWWindow>(); win_ = makeUnique<GLFWWindow>();
if (!win_) { if (!win_) {
E2D_LOG_ERROR("创建窗口失败"); E2D_ERROR("创建窗口失败");
return false; return false;
} }
if (!win_->create(cfg_.title, cfg_.w, cfg_.h, cfg_.vsync)) { if (!win_->create(cfg_.title, cfg_.w, cfg_.h, cfg_.vsync)) {
E2D_LOG_ERROR("创建窗口失败"); E2D_ERROR("创建窗口失败");
shutdown(); shutdown();
return false; return false;
} }
E2D_LOG_INFO("窗口创建成功"); E2D_INFO("窗口创建成功");
initialized_ = true; initialized_ = true;
return true; return true;
} }

View File

@ -1,653 +0,0 @@
#include <algorithm>
#include <cmath>
#include <extra2d/graphics/core/render_command.h>
#include <extra2d/scene/node.h>
#include <extra2d/scene/scene.h>
#include <extra2d/services/logger_service.h>
namespace extra2d {
/**
* @brief
*
*
*/
Node::Node() = default;
/**
* @brief
*
*
*/
Node::~Node() { clearChildren(); }
/**
* @brief
* @param child
*
*
*/
void Node::addChild(Ptr<Node> child) {
if (!child || child.get() == this) {
return;
}
child->detach();
child->parent_ = weak_from_this();
children_.push_back(child);
childrenOrderDirty_ = true;
// 更新索引
if (!child->getName().empty()) {
nameIndex_[child->getName()] = child;
}
if (child->getTag() != -1) {
tagIndex_[child->getTag()] = child;
}
if (running_) {
child->onEnter();
if (scene_) {
child->onAttachToScene(scene_);
}
}
}
/**
* @brief
* @param children
*
*
*/
void Node::addChildren(std::vector<Ptr<Node>> &&children) {
// 预留空间,避免多次扩容
size_t newSize = children_.size() + children.size();
if (newSize > children_.capacity()) {
children_.reserve(newSize);
}
for (auto &child : children) {
if (!child || child.get() == this) {
continue;
}
child->detach();
child->parent_ = weak_from_this();
children_.push_back(child);
// 更新索引
if (!child->getName().empty()) {
nameIndex_[child->getName()] = child;
}
if (child->getTag() != -1) {
tagIndex_[child->getTag()] = child;
}
if (running_) {
child->onEnter();
if (scene_) {
child->onAttachToScene(scene_);
}
}
}
if (!children.empty()) {
childrenOrderDirty_ = true;
}
}
/**
* @brief
* @param child
*
* 退
*/
void Node::removeChild(Ptr<Node> child) {
if (!child)
return;
auto it = std::find(children_.begin(), children_.end(), child);
if (it != children_.end()) {
(*it)->onDetachFromScene();
if (running_) {
(*it)->onExit();
}
if (!(*it)->getName().empty()) {
nameIndex_.erase((*it)->getName());
}
if ((*it)->getTag() != -1) {
tagIndex_.erase((*it)->getTag());
}
(*it)->parent_.reset();
children_.erase(it);
}
}
/**
* @brief
* @param name
*
*
*/
void Node::removeChildByName(const std::string &name) {
auto child = findChild(name);
if (child) {
removeChild(child);
}
}
/**
* @brief
*
*
*/
void Node::detach() {
auto p = parent_.lock();
if (p) {
// 安全获取 shared_ptr避免在对象未由 shared_ptr 管理时崩溃
Ptr<Node> self;
try {
self = shared_from_this();
} catch (const std::bad_weak_ptr &) {
// 对象不是由 shared_ptr 管理的,直接重置父节点引用
parent_.reset();
return;
}
p->removeChild(self);
}
}
/**
* @brief
*
* 退
*/
void Node::clearChildren() {
for (auto &child : children_) {
if (running_) {
child->onDetachFromScene();
child->onExit();
}
child->parent_.reset();
}
children_.clear();
nameIndex_.clear();
tagIndex_.clear();
}
/**
* @brief
* @param name
* @return nullptr
*
* 使O(1)
*/
Ptr<Node> Node::findChild(const std::string &name) const {
// 使用哈希索引O(1) 查找
auto it = nameIndex_.find(name);
if (it != nameIndex_.end()) {
return it->second.lock();
}
return nullptr;
}
/**
* @brief
* @param tag
* @return nullptr
*
* 使O(1)
*/
Ptr<Node> Node::findChildByTag(int tag) const {
// 使用哈希索引O(1) 查找
auto it = tagIndex_.find(tag);
if (it != tagIndex_.end()) {
return it->second.lock();
}
return nullptr;
}
/**
* @brief
* @param pos
*/
void Node::setPos(const Vec2 &pos) {
position_ = pos;
markTransformDirty();
}
/**
* @brief
* @param x X坐标
* @param y Y坐标
*/
void Node::setPos(float x, float y) { setPos(Vec2(x, y)); }
/**
* @brief
* @param degrees
*/
void Node::setRotation(float degrees) {
rotation_ = degrees;
markTransformDirty();
}
/**
* @brief
* @param scale
*/
void Node::setScale(const Vec2 &scale) {
scale_ = scale;
markTransformDirty();
}
/**
* @brief
* @param scale
*/
void Node::setScale(float scale) { setScale(Vec2(scale, scale)); }
/**
* @brief
* @param x X轴缩放值
* @param y Y轴缩放值
*/
void Node::setScale(float x, float y) { setScale(Vec2(x, y)); }
/**
* @brief
* @param anchor 0-1
*/
void Node::setAnchor(const Vec2 &anchor) {
anchor_ = anchor;
markTransformDirty();
}
/**
* @brief
* @param x X坐标0-1
* @param y Y坐标0-1
*/
void Node::setAnchor(float x, float y) { setAnchor(Vec2(x, y)); }
/**
* @brief
* @param skew
*/
void Node::setSkew(const Vec2 &skew) {
skew_ = skew;
markTransformDirty();
}
/**
* @brief
* @param x X轴斜切角度
* @param y Y轴斜切角度
*/
void Node::setSkew(float x, float y) { setSkew(Vec2(x, y)); }
/**
* @brief
* @param opacity 0.0-1.0
*/
void Node::setOpacity(float opacity) {
opacity_ = std::clamp(opacity, 0.0f, 1.0f);
}
/**
* @brief
* @param visible
*/
void Node::setVisible(bool visible) { visible_ = visible; }
/**
* @brief
* @param color RGB颜色值
*/
void Node::setColor(const Color3B &color) { color_ = color; }
/**
* @brief
* @param flipX
*/
void Node::setFlipX(bool flipX) { flipX_ = flipX; }
/**
* @brief
* @param flipY
*/
void Node::setFlipY(bool flipY) { flipY_ = flipY; }
/**
* @brief Z序
* @param zOrder
*
* Z序值会在上层渲染
*/
void Node::setZOrder(int zOrder) {
if (zOrder_ != zOrder) {
zOrder_ = zOrder;
childrenOrderDirty_ = true;
}
}
/**
* @brief
* @param localPos
* @return
*/
Vec2 Node::toWorld(const Vec2 &localPos) const {
glm::vec4 worldPos =
getWorldTransform() * glm::vec4(localPos.x, localPos.y, 0.0f, 1.0f);
return Vec2(worldPos.x, worldPos.y);
}
/**
* @brief
* @param worldPos
* @return
*/
Vec2 Node::toLocal(const Vec2 &worldPos) const {
glm::mat4 invWorld = glm::inverse(getWorldTransform());
glm::vec4 localPos = invWorld * glm::vec4(worldPos.x, worldPos.y, 0.0f, 1.0f);
return Vec2(localPos.x, localPos.y);
}
/**
* @brief
* @return
*
*
*/
glm::mat4 Node::getLocalTransform() const {
if (transformDirty_) {
localTransform_ = glm::mat4(1.0f);
// T - R - S order
localTransform_ = glm::translate(localTransform_,
glm::vec3(position_.x, position_.y, 0.0f));
if (rotation_ != 0.0f) {
localTransform_ = glm::rotate(localTransform_, rotation_ * DEG_TO_RAD,
glm::vec3(0.0f, 0.0f, 1.0f));
}
if (skew_.x != 0.0f || skew_.y != 0.0f) {
glm::mat4 skewMatrix(1.0f);
skewMatrix[1][0] = std::tan(skew_.x * DEG_TO_RAD);
skewMatrix[0][1] = std::tan(skew_.y * DEG_TO_RAD);
localTransform_ *= skewMatrix;
}
localTransform_ =
glm::scale(localTransform_, glm::vec3(scale_.x, scale_.y, 1.0f));
// 注意:锚点偏移在渲染时处理,不在本地变换中处理
// 这样可以避免锚点偏移被父节点的缩放影响
transformDirty_ = false;
}
return localTransform_;
}
/**
* @brief
* @return
*
*
*/
glm::mat4 Node::getWorldTransform() const {
if (worldTransformDirty_) {
// 使用线程局部存储的固定数组,避免每帧内存分配
// 限制最大深度为 256 层,足以覆盖绝大多数场景
thread_local std::array<const Node *, 256> nodeChainCache;
thread_local size_t chainCount = 0;
chainCount = 0;
const Node *current = this;
while (current && chainCount < nodeChainCache.size()) {
nodeChainCache[chainCount++] = current;
auto p = current->parent_.lock();
current = p.get();
}
// 从根节点开始计算
glm::mat4 transform = glm::mat4(1.0f);
for (size_t i = chainCount; i > 0; --i) {
transform = transform * nodeChainCache[i - 1]->getLocalTransform();
}
worldTransform_ = transform;
worldTransformDirty_ = false;
}
return worldTransform_;
}
/**
* @brief
*
*
*/
void Node::markTransformDirty() {
// 避免重复标记,提高性能
if (!transformDirty_ || !worldTransformDirty_) {
transformDirty_ = true;
worldTransformDirty_ = true;
// 递归标记所有子节点
for (auto &child : children_) {
child->markTransformDirty();
}
}
}
/**
* @brief
*
*
*/
void Node::batchTransforms() {
// 如果本地变换脏了,先计算本地变换
if (transformDirty_) {
(void)getLocalTransform(); // 这会计算并缓存本地变换
}
// 如果世界变换脏了,需要重新计算
if (worldTransformDirty_) {
auto parent = parent_.lock();
if (parent) {
// 使用父节点的世界变换(确保父节点已经更新)
worldTransform_ = parent->getWorldTransform() * localTransform_;
} else {
// 根节点
worldTransform_ = localTransform_;
}
worldTransformDirty_ = false;
}
// 递归更新子节点
for (auto &child : children_) {
child->batchTransforms();
}
}
/**
* @brief
*
* onEnter
*/
void Node::onEnter() {
running_ = true;
for (auto &child : children_) {
child->onEnter();
}
}
/**
* @brief 退
*
* onExit
*/
void Node::onExit() {
running_ = false;
for (auto &child : children_) {
child->onExit();
}
}
/**
* @brief
* @param dt
*
*
*/
void Node::onUpdate(float dt) {
onUpdateNode(dt);
// Update children
for (auto &child : children_) {
child->onUpdate(dt);
}
}
/**
* @brief
* @param renderer
*
*
*/
void Node::onRender(Renderer &renderer) {
if (!visible_)
return;
renderer.pushTransform(getLocalTransform());
onDraw(renderer);
for (auto &child : children_) {
child->onRender(renderer);
}
renderer.popTransform();
}
/**
* @brief
* @param scene
*
*
*/
void Node::onAttachToScene(Scene *scene) {
scene_ = scene;
for (auto &child : children_) {
child->onAttachToScene(scene);
}
}
/**
* @brief
*
*
*/
void Node::onDetachFromScene() {
scene_ = nullptr;
for (auto &child : children_) {
child->onDetachFromScene();
}
}
/**
* @brief
* @return
*
*
*/
Rect Node::getBounds() const { return Rect(position_.x, position_.y, 0, 0); }
/**
* @brief
* @param dt
*
* onUpdate进行更新
*/
void Node::update(float dt) { onUpdate(dt); }
/**
* @brief
* @param renderer
*
* onRender进行渲染
*/
void Node::render(Renderer &renderer) {
if (childrenOrderDirty_) {
sortChildren();
}
onRender(renderer);
}
/**
* @brief Z序排序
*
* 使使
*/
void Node::sortChildren() {
// 使用插入排序优化小范围更新场景
// 插入排序在大部分已有序的情况下性能接近O(n)
size_t n = children_.size();
if (n <= 1) {
childrenOrderDirty_ = false;
return;
}
// 小数组使用插入排序大数组使用std::sort
if (n < 32) {
// 插入排序
for (size_t i = 1; i < n; ++i) {
auto key = children_[i];
int keyZOrder = key->getZOrder();
int j = static_cast<int>(i) - 1;
while (j >= 0 && children_[j]->getZOrder() > keyZOrder) {
children_[j + 1] = children_[j];
--j;
}
children_[j + 1] = key;
}
} else {
// 大数组使用标准排序
std::sort(children_.begin(), children_.end(),
[](const Ptr<Node> &a, const Ptr<Node> &b) {
return a->getZOrder() < b->getZOrder();
});
}
childrenOrderDirty_ = false;
}
/**
* @brief
* @param commands
* @param parentZOrder Z序
*
*
*/
void Node::collectRenderCommands(std::vector<RenderCommand> &commands,
int parentZOrder) {
if (!visible_)
return;
// 计算累积 Z 序
int accumulatedZOrder = parentZOrder + zOrder_;
// 生成当前节点的渲染命令
generateRenderCommand(commands, accumulatedZOrder);
// 递归收集子节点的渲染命令
// 注意:这里假设子节点已经按 Z 序排序
for (auto &child : children_) {
child->collectRenderCommands(commands, accumulatedZOrder);
}
}
} // namespace extra2d

View File

@ -1,128 +0,0 @@
#include <extra2d/graphics/core/render_command.h>
#include <extra2d/graphics/opengl/gl_renderer.h>
#include <extra2d/scene/scene.h>
#include <extra2d/services/logger_service.h>
namespace extra2d {
/**
* @brief
*
*
*/
Scene::Scene() { defaultCamera_ = makePtr<Camera>(); }
/**
* @brief
* @param camera
*/
void Scene::setCamera(Ptr<Camera> camera) { camera_ = camera; }
/**
* @brief
* @param width
* @param height
*
*
*/
void Scene::setViewportSize(float width, float height) {
viewportSize_ = Size(width, height);
if (defaultCamera_) {
defaultCamera_->setViewport(0, width, height, 0);
} else if (camera_) {
camera_->setViewport(0, width, height, 0);
}
}
/**
* @brief
* @param size
*/
void Scene::setViewportSize(const Size &size) {
setViewportSize(size.width, size.height);
}
/**
* @brief
* @param renderer
*
*
*/
void Scene::renderScene(Renderer &renderer) {
if (!isVisible())
return;
// Begin frame with background color
renderer.beginFrame(backgroundColor_);
renderContent(renderer);
renderer.endFrame();
}
/**
* @brief
* @param renderer
*
*
* Application CameraService
*/
void Scene::renderContent(Renderer &renderer) {
if (!isVisible())
return;
batchTransforms();
renderer.beginSpriteBatch();
render(renderer);
renderer.endSpriteBatch();
}
/**
* @brief
* @param dt
*
* update方法
*/
void Scene::updateScene(float dt) {
if (!paused_) {
update(dt);
}
}
/**
* @brief
*
* Node的onEnter方法
*/
void Scene::onEnter() { Node::onEnter(); }
/**
* @brief 退
*
* Node的onExit方法
*/
void Scene::onExit() { Node::onExit(); }
/**
* @brief
* @param commands
* @param parentZOrder Z序
*
*
*/
void Scene::collectRenderCommands(std::vector<RenderCommand> &commands,
int parentZOrder) {
if (!isVisible())
return;
// 从场景的子节点开始收集渲染命令
Node::collectRenderCommands(commands, parentZOrder);
}
/**
* @brief
* @return
*/
Ptr<Scene> Scene::create() { return makePtr<Scene>(); }
} // namespace extra2d

View File

@ -1,888 +0,0 @@
#include <algorithm>
#include <extra2d/app/application.h>
#include <extra2d/graphics/core/render_command.h>
#include <extra2d/graphics/opengl/gl_renderer.h>
#include <extra2d/platform/keys.h>
#include <extra2d/scene/scene_manager.h>
#include <extra2d/scene/transition_box_scene.h>
#include <extra2d/scene/transition_fade_scene.h>
#include <extra2d/scene/transition_flip_scene.h>
#include <extra2d/scene/transition_scale_scene.h>
#include <extra2d/scene/transition_scene.h>
#include <extra2d/scene/transition_slide_scene.h>
#include <extra2d/services/event_service.h>
#include <extra2d/services/logger_service.h>
namespace extra2d {
namespace {
/**
* @brief -
* @param node
* @param worldPos
* @return nullptr
*/
Node *hitTestTopmost(const Ptr<Node> &node, const Vec2 &worldPos) {
if (!node || !node->isVisible()) {
return nullptr;
}
std::vector<Ptr<Node>> children = node->getChildren();
std::stable_sort(children.begin(), children.end(),
[](const Ptr<Node> &a, const Ptr<Node> &b) {
return a->getZOrder() < b->getZOrder();
});
for (auto it = children.rbegin(); it != children.rend(); ++it) {
if (Node *hit = hitTestTopmost(*it, worldPos)) {
return hit;
}
}
if (node->getEventDispatcher().getTotalListenerCount() == 0) {
return nullptr;
}
Rect bounds = node->getBounds();
if (!bounds.empty() && bounds.containsPoint(worldPos)) {
return node.get();
}
return nullptr;
}
/**
* @brief
* @param node
* @param event
*/
void dispatchToNode(Node *node, Event &event) {
if (!node) {
return;
}
node->getEventDispatcher().dispatch(event);
}
} // namespace
/**
* @brief
* @return
*/
SceneManager &SceneManager::get() {
static SceneManager instance;
static bool initialized = false;
if (!initialized) {
instance.setupEventListeners();
initialized = true;
}
return instance;
}
/**
* @brief
*/
void SceneManager::setupEventListeners() {
auto eventService = ServiceLocator::instance().getService<IEventService>();
if (!eventService) {
return;
}
mouseMoveListener_ =
eventService->addListener(EventType::MouseMoved, [this](Event &e) {
auto &mouseEvent = std::get<MouseMoveEvent>(e.data);
mousePos_ = mouseEvent.position;
mouseDelta_ = mouseEvent.delta;
});
mousePressListener_ = eventService->addListener(
EventType::MouseButtonPressed, [this](Event &e) {
auto &mouseEvent = std::get<MouseButtonEvent>(e.data);
if (mouseEvent.button == static_cast<int>(Mouse::Left)) {
mouseLeftPressed_ = true;
mouseLeftDown_ = true;
}
});
mouseReleaseListener_ = eventService->addListener(
EventType::MouseButtonReleased, [this](Event &e) {
auto &mouseEvent = std::get<MouseButtonEvent>(e.data);
if (mouseEvent.button == static_cast<int>(Mouse::Left)) {
mouseLeftReleased_ = true;
mouseLeftDown_ = false;
}
});
scrollListener_ =
eventService->addListener(EventType::MouseScrolled, [this](Event &e) {
auto &scrollEvent = std::get<MouseScrollEvent>(e.data);
scrollDelta_ = scrollEvent.offset.y;
});
}
/**
* @brief
* @param scene
*
*
*/
void SceneManager::runWithScene(Ptr<Scene> scene) {
if (!scene) {
return;
}
if (!sceneStack_.empty()) {
E2D_LOG_WARN("SceneManager: runWithScene 应该只被调用一次");
return;
}
scene->onEnter();
scene->onAttachToScene(scene.get());
sceneStack_.push(scene);
}
/**
* @brief
* @param scene
*
*
*/
void SceneManager::replaceScene(Ptr<Scene> scene) {
if (!scene || isTransitioning_) {
return;
}
if (sceneStack_.empty()) {
runWithScene(scene);
return;
}
auto oldScene = sceneStack_.top();
oldScene->onExit();
oldScene->onDetachFromScene();
sceneStack_.pop();
scene->onEnter();
scene->onAttachToScene(scene.get());
sceneStack_.push(scene);
}
/**
* @brief
* @param scene
* @param transition
* @param duration
*/
void SceneManager::replaceScene(Ptr<Scene> scene, TransitionType transition,
float duration) {
if (!scene || isTransitioning_) {
return;
}
if (sceneStack_.empty()) {
runWithScene(scene);
return;
}
auto oldScene = sceneStack_.top();
startTransition(oldScene, scene, transition, duration, [this]() {
if (!sceneStack_.empty() && activeTransitionScene_) {
if (sceneStack_.top().get() == activeTransitionScene_.get()) {
sceneStack_.pop();
}
auto outScene = activeTransitionScene_->getOutScene();
if (outScene) {
outScene->onExit();
outScene->onDetachFromScene();
}
}
if (activeTransitionScene_) {
auto inScene = activeTransitionScene_->getInScene();
if (inScene) {
inScene->onAttachToScene(inScene.get());
sceneStack_.push(inScene);
}
}
});
}
/**
* @brief
* @param scene
*
*
*/
void SceneManager::enterScene(Ptr<Scene> scene) {
if (!scene || isTransitioning_) {
return;
}
if (sceneStack_.empty()) {
runWithScene(scene);
} else {
replaceScene(scene);
}
}
/**
* @brief 使
* @param scene
* @param transitionScene
*/
void SceneManager::enterScene(Ptr<Scene> scene,
Ptr<TransitionScene> transitionScene) {
if (!scene || isTransitioning_) {
return;
}
if (!transitionScene) {
enterScene(scene);
return;
}
auto current = getCurrentScene();
if (!current) {
enterScene(scene);
return;
}
if (hoverTarget_) {
Event evt;
evt.type = EventType::UIHoverExit;
evt.data = CustomEvent{0, hoverTarget_};
dispatchToNode(hoverTarget_, evt);
hoverTarget_ = nullptr;
}
captureTarget_ = nullptr;
hasLastPointerWorld_ = false;
transitionScene->setOutScene(current);
transitionScene->setFinishCallback([this]() { finishTransition(); });
current->pause();
transitionScene->onEnter();
transitionScene->onAttachToScene(transitionScene.get());
sceneStack_.push(transitionScene);
isTransitioning_ = true;
activeTransitionScene_ = transitionScene;
transitionStackAction_ = [this, transitionScene]() {
auto outScene = transitionScene->getOutScene();
if (!sceneStack_.empty() && outScene) {
if (sceneStack_.top().get() == transitionScene.get()) {
sceneStack_.pop();
}
outScene->onExit();
outScene->onDetachFromScene();
}
auto inScene = transitionScene->getInScene();
if (inScene) {
inScene->onAttachToScene(inScene.get());
sceneStack_.push(inScene);
}
};
}
/**
* @brief
* @param scene
*
*
*/
void SceneManager::pushScene(Ptr<Scene> scene) {
if (!scene || isTransitioning_) {
return;
}
if (!sceneStack_.empty()) {
sceneStack_.top()->pause();
}
scene->onEnter();
scene->onAttachToScene(scene.get());
sceneStack_.push(scene);
}
/**
* @brief
* @param scene
* @param transition
* @param duration
*/
void SceneManager::pushScene(Ptr<Scene> scene, TransitionType transition,
float duration) {
if (!scene || isTransitioning_) {
return;
}
if (sceneStack_.empty()) {
runWithScene(scene);
return;
}
sceneStack_.top()->pause();
auto currentScene = sceneStack_.top();
startTransition(currentScene, scene, transition, duration, [this]() {
if (!sceneStack_.empty() && activeTransitionScene_) {
if (sceneStack_.top().get() == activeTransitionScene_.get()) {
sceneStack_.pop();
}
}
if (activeTransitionScene_) {
auto inScene = activeTransitionScene_->getInScene();
if (inScene) {
inScene->onAttachToScene(inScene.get());
sceneStack_.push(inScene);
}
}
});
}
/**
* @brief
*
*
*/
void SceneManager::popScene() {
if (sceneStack_.size() <= 1 || isTransitioning_) {
return;
}
auto current = sceneStack_.top();
current->onExit();
current->onDetachFromScene();
sceneStack_.pop();
if (!sceneStack_.empty()) {
sceneStack_.top()->resume();
}
}
/**
* @brief
* @param transition
* @param duration
*/
void SceneManager::popScene(TransitionType transition, float duration) {
if (sceneStack_.size() <= 1 || isTransitioning_) {
return;
}
auto current = sceneStack_.top();
auto previous = getPreviousScene();
startTransition(current, previous, transition, duration, [this]() {
if (!sceneStack_.empty() && activeTransitionScene_) {
if (sceneStack_.top().get() == activeTransitionScene_.get()) {
sceneStack_.pop();
}
auto outScene = activeTransitionScene_->getOutScene();
if (outScene) {
outScene->onExit();
outScene->onDetachFromScene();
}
}
if (activeTransitionScene_) {
auto inScene = activeTransitionScene_->getInScene();
if (inScene && !sceneStack_.empty() && sceneStack_.top() == inScene) {
inScene->resume();
}
}
});
}
/**
* @brief
*
*
*/
void SceneManager::popToRootScene() {
if (sceneStack_.size() <= 1 || isTransitioning_) {
return;
}
while (sceneStack_.size() > 1) {
auto scene = sceneStack_.top();
scene->onExit();
scene->onDetachFromScene();
sceneStack_.pop();
}
sceneStack_.top()->resume();
}
/**
* @brief
* @param transition
* @param duration
*/
void SceneManager::popToRootScene(TransitionType transition, float duration) {
if (sceneStack_.size() <= 1 || isTransitioning_) {
return;
}
auto root = getRootScene();
auto current = sceneStack_.top();
startTransition(current, root, transition, duration, [this, root]() {
while (!sceneStack_.empty() && sceneStack_.top().get() != root.get()) {
auto scene = sceneStack_.top();
scene->onExit();
scene->onDetachFromScene();
sceneStack_.pop();
}
if (!sceneStack_.empty() && sceneStack_.top().get() == root.get()) {
root->resume();
}
});
}
/**
* @brief
* @param name
*
*
*/
void SceneManager::popToScene(const std::string &name) {
if (isTransitioning_) {
return;
}
std::stack<Ptr<Scene>> tempStack;
Ptr<Scene> target = nullptr;
while (!sceneStack_.empty()) {
auto scene = sceneStack_.top();
if (scene->getName() == name) {
target = scene;
break;
}
scene->onExit();
scene->onDetachFromScene();
sceneStack_.pop();
}
if (target) {
target->resume();
}
}
/**
* @brief
* @param name
* @param transition
* @param duration
*/
void SceneManager::popToScene(const std::string &name,
TransitionType transition, float duration) {
if (isTransitioning_) {
return;
}
auto target = getSceneByName(name);
if (target && target != sceneStack_.top()) {
auto current = sceneStack_.top();
startTransition(current, target, transition, duration, [this, target]() {
while (!sceneStack_.empty() && sceneStack_.top() != target) {
auto scene = sceneStack_.top();
scene->onExit();
scene->onDetachFromScene();
sceneStack_.pop();
}
if (!sceneStack_.empty() && sceneStack_.top() == target) {
target->resume();
}
});
}
}
/**
* @brief
* @return nullptr
*/
Ptr<Scene> SceneManager::getCurrentScene() const {
if (sceneStack_.empty()) {
return nullptr;
}
return sceneStack_.top();
}
/**
* @brief
* @return nullptr
*/
Ptr<Scene> SceneManager::getPreviousScene() const {
if (sceneStack_.size() < 2) {
return nullptr;
}
auto tempStack = sceneStack_;
tempStack.pop();
return tempStack.top();
}
/**
* @brief
* @return nullptr
*/
Ptr<Scene> SceneManager::getRootScene() const {
if (sceneStack_.empty()) {
return nullptr;
}
auto tempStack = sceneStack_;
Ptr<Scene> root;
while (!tempStack.empty()) {
root = tempStack.top();
tempStack.pop();
}
return root;
}
/**
* @brief
* @param name
* @return nullptr
*/
Ptr<Scene> SceneManager::getSceneByName(const std::string &name) const {
auto it = namedScenes_.find(name);
if (it != namedScenes_.end()) {
return it->second;
}
auto tempStack = sceneStack_;
while (!tempStack.empty()) {
auto scene = tempStack.top();
if (scene->getName() == name) {
return scene;
}
tempStack.pop();
}
return nullptr;
}
/**
* @brief
* @param name
* @return truefalse
*/
bool SceneManager::hasScene(const std::string &name) const {
return getSceneByName(name) != nullptr;
}
/**
* @brief
* @param dt
*
*
*/
void SceneManager::update(float dt) {
if (isTransitioning_ && activeTransitionScene_) {
activeTransitionScene_->updateTransition(dt);
hoverTarget_ = nullptr;
captureTarget_ = nullptr;
hasLastPointerWorld_ = false;
}
if (!sceneStack_.empty()) {
auto &scene = *sceneStack_.top();
scene.updateScene(dt);
if (!isTransitioning_) {
dispatchPointerEvents(scene);
}
}
}
/**
* @brief
* @param renderer
*
* 使
*/
void SceneManager::render(Renderer &renderer) {
Color clearColor = Colors::Black;
if (!sceneStack_.empty()) {
clearColor = sceneStack_.top()->getBackgroundColor();
}
E2D_LOG_TRACE("SceneManager::render - beginFrame 颜色({}, {}, {})",
clearColor.r, clearColor.g, clearColor.b);
renderer.beginFrame(clearColor);
if (!sceneStack_.empty()) {
E2D_LOG_TRACE("SceneManager::render - 正在渲染场景内容");
sceneStack_.top()->renderContent(renderer);
} else {
E2D_LOG_WARN("SceneManager::render - 没有场景可渲染");
}
renderer.endFrame();
E2D_LOG_TRACE("SceneManager::render - endFrame");
}
/**
* @brief
* @param commands
*
*
*/
void SceneManager::collectRenderCommands(std::vector<RenderCommand> &commands) {
if (!sceneStack_.empty()) {
sceneStack_.top()->collectRenderCommands(commands, 0);
}
}
/**
* @brief
*
* 退
*/
void SceneManager::end() {
while (!sceneStack_.empty()) {
auto scene = sceneStack_.top();
scene->onExit();
scene->onDetachFromScene();
sceneStack_.pop();
}
namedScenes_.clear();
}
/**
* @brief
*
*
*/
void SceneManager::purgeCachedScenes() { namedScenes_.clear(); }
/**
* @brief
* @param from
* @param to
* @param type
* @param duration
* @param stackAction
*/
void SceneManager::startTransition(Ptr<Scene> from, Ptr<Scene> to,
TransitionType type, float duration,
std::function<void()> stackAction) {
if (!from || !to) {
return;
}
auto transitionScene = createTransitionScene(type, duration, to);
if (!transitionScene) {
replaceScene(to);
return;
}
if (hoverTarget_) {
Event evt;
evt.type = EventType::UIHoverExit;
evt.data = CustomEvent{0, hoverTarget_};
dispatchToNode(hoverTarget_, evt);
hoverTarget_ = nullptr;
}
captureTarget_ = nullptr;
hasLastPointerWorld_ = false;
transitionScene->setOutScene(from);
transitionScene->setFinishCallback([this]() { finishTransition(); });
from->pause();
transitionScene->onEnter();
transitionScene->onAttachToScene(transitionScene.get());
sceneStack_.push(transitionScene);
isTransitioning_ = true;
activeTransitionScene_ = transitionScene;
transitionStackAction_ = std::move(stackAction);
}
/**
* @brief
* @param type
* @param duration
* @param inScene
* @return
*/
Ptr<TransitionScene> SceneManager::createTransitionScene(TransitionType type,
float duration,
Ptr<Scene> inScene) {
if (!inScene) {
return nullptr;
}
switch (type) {
case TransitionType::Fade:
return TransitionFadeScene::create(duration, inScene);
case TransitionType::SlideLeft:
return TransitionSlideScene::create(duration, inScene,
TransitionDirection::Left);
case TransitionType::SlideRight:
return TransitionSlideScene::create(duration, inScene,
TransitionDirection::Right);
case TransitionType::SlideUp:
return TransitionSlideScene::create(duration, inScene,
TransitionDirection::Up);
case TransitionType::SlideDown:
return TransitionSlideScene::create(duration, inScene,
TransitionDirection::Down);
case TransitionType::Scale:
return TransitionScaleScene::create(duration, inScene);
case TransitionType::Flip:
return TransitionFlipScene::create(duration, inScene);
case TransitionType::Box:
return TransitionBoxScene::create(duration, inScene);
default:
return TransitionFadeScene::create(duration, inScene);
}
}
/**
* @brief
*
*
*/
void SceneManager::finishTransition() {
Node *lastHoverTarget = hoverTarget_;
isTransitioning_ = false;
hoverTarget_ = nullptr;
captureTarget_ = nullptr;
hasLastPointerWorld_ = false;
if (transitionStackAction_) {
transitionStackAction_();
}
if (lastHoverTarget) {
Event evt;
evt.type = EventType::UIHoverExit;
evt.data = CustomEvent{0, lastHoverTarget};
dispatchToNode(lastHoverTarget, evt);
}
activeTransitionScene_.reset();
transitionStackAction_ = nullptr;
if (transitionCallback_) {
transitionCallback_();
transitionCallback_ = nullptr;
}
}
/**
* @brief
* @param scene
*
*
*/
void SceneManager::dispatchPointerEvents(Scene &scene) {
Vec2 screenPos = mousePos_;
Vec2 worldPos = screenPos;
if (auto *camera = scene.getActiveCamera()) {
worldPos = camera->screenToWorld(screenPos);
}
Ptr<Node> root = scene.shared_from_this();
Node *newHover = hitTestTopmost(root, worldPos);
if (newHover != hoverTarget_) {
if (hoverTarget_) {
Event evt;
evt.type = EventType::UIHoverExit;
evt.data = CustomEvent{0, hoverTarget_};
dispatchToNode(hoverTarget_, evt);
}
hoverTarget_ = newHover;
if (hoverTarget_) {
Event evt;
evt.type = EventType::UIHoverEnter;
evt.data = CustomEvent{0, hoverTarget_};
dispatchToNode(hoverTarget_, evt);
}
}
if (!hasLastPointerWorld_) {
lastPointerWorld_ = worldPos;
hasLastPointerWorld_ = true;
}
Vec2 delta = worldPos - lastPointerWorld_;
if (hoverTarget_ && (delta.x != 0.0f || delta.y != 0.0f)) {
Event evt = Event::createMouseMove(worldPos, delta);
dispatchToNode(hoverTarget_, evt);
}
if (hoverTarget_ && scrollDelta_ != 0.0f) {
Event evt = Event::createMouseScroll(Vec2(0.0f, scrollDelta_), worldPos);
dispatchToNode(hoverTarget_, evt);
}
if (mouseLeftPressed_) {
captureTarget_ = hoverTarget_;
if (captureTarget_) {
Event evt = Event::createMouseButtonPress(static_cast<int>(Mouse::Left),
0, worldPos);
dispatchToNode(captureTarget_, evt);
Event pressed;
pressed.type = EventType::UIPressed;
pressed.data = CustomEvent{0, captureTarget_};
dispatchToNode(captureTarget_, pressed);
}
}
if (mouseLeftReleased_) {
Node *target = captureTarget_ ? captureTarget_ : hoverTarget_;
if (target) {
Event evt = Event::createMouseButtonRelease(static_cast<int>(Mouse::Left),
0, worldPos);
dispatchToNode(target, evt);
Event released;
released.type = EventType::UIReleased;
released.data = CustomEvent{0, target};
dispatchToNode(target, released);
}
if (captureTarget_ && captureTarget_ == hoverTarget_) {
Event clicked;
clicked.type = EventType::UIClicked;
clicked.data = CustomEvent{0, captureTarget_};
dispatchToNode(captureTarget_, clicked);
}
captureTarget_ = nullptr;
}
lastPointerWorld_ = worldPos;
// 重置每帧状态
mouseLeftPressed_ = false;
mouseLeftReleased_ = false;
scrollDelta_ = 0.0f;
mouseDelta_ = Vec2::Zero();
}
void SceneManager::doSceneSwitch() {}
} // namespace extra2d

View File

@ -1,443 +0,0 @@
#include <algorithm>
#include <cmath>
#include <extra2d/graphics/core/render_command.h>
#include <extra2d/graphics/opengl/gl_renderer.h>
#include <extra2d/scene/shape_node.h>
#include <limits>
namespace extra2d {
/**
* @brief
*
*
*/
ShapeNode::ShapeNode() = default;
/**
* @brief
* @return
*/
Ptr<ShapeNode> ShapeNode::create() { return makePtr<ShapeNode>(); }
/**
* @brief
* @param pos
* @param color
* @return
*/
Ptr<ShapeNode> ShapeNode::createPoint(const Vec2 &pos, const Color &color) {
auto node = makePtr<ShapeNode>();
node->shapeType_ = ShapeType::Point;
node->color_ = color;
node->points_ = {pos};
return node;
}
/**
* @brief 线
* @param start 线
* @param end 线
* @param color 线
* @param width 线
* @return 线
*/
Ptr<ShapeNode> ShapeNode::createLine(const Vec2 &start, const Vec2 &end,
const Color &color, float width) {
auto node = makePtr<ShapeNode>();
node->shapeType_ = ShapeType::Line;
node->color_ = color;
node->lineWidth_ = width;
node->points_ = {start, end};
return node;
}
/**
* @brief
* @param rect
* @param color
* @param width 线
* @return
*/
Ptr<ShapeNode> ShapeNode::createRect(const Rect &rect, const Color &color,
float width) {
auto node = makePtr<ShapeNode>();
node->shapeType_ = ShapeType::Rect;
node->color_ = color;
node->lineWidth_ = width;
node->filled_ = false;
node->points_ = {
Vec2(rect.left(), rect.top()), Vec2(rect.right(), rect.top()),
Vec2(rect.right(), rect.bottom()), Vec2(rect.left(), rect.bottom())};
return node;
}
/**
* @brief
* @param rect
* @param color
* @return
*/
Ptr<ShapeNode> ShapeNode::createFilledRect(const Rect &rect,
const Color &color) {
auto node = createRect(rect, color, 0);
node->filled_ = true;
return node;
}
/**
* @brief
* @param center
* @param radius
* @param color
* @param segments
* @param width 线
* @return
*/
Ptr<ShapeNode> ShapeNode::createCircle(const Vec2 &center, float radius,
const Color &color, int segments,
float width) {
auto node = makePtr<ShapeNode>();
node->shapeType_ = ShapeType::Circle;
node->color_ = color;
node->lineWidth_ = width;
node->segments_ = segments;
node->filled_ = false;
node->points_ = {center};
// Store radius in a point for simplicity
node->addPoint(Vec2(radius, 0));
return node;
}
/**
* @brief
* @param center
* @param radius
* @param color
* @param segments
* @return
*/
Ptr<ShapeNode> ShapeNode::createFilledCircle(const Vec2 &center, float radius,
const Color &color, int segments) {
auto node = createCircle(center, radius, color, segments, 0);
node->filled_ = true;
return node;
}
/**
* @brief
* @param p1
* @param p2
* @param p3
* @param color
* @param width 线
* @return
*/
Ptr<ShapeNode> ShapeNode::createTriangle(const Vec2 &p1, const Vec2 &p2,
const Vec2 &p3, const Color &color,
float width) {
auto node = makePtr<ShapeNode>();
node->shapeType_ = ShapeType::Triangle;
node->color_ = color;
node->lineWidth_ = width;
node->filled_ = false;
node->points_ = {p1, p2, p3};
return node;
}
/**
* @brief
* @param p1
* @param p2
* @param p3
* @param color
* @return
*/
Ptr<ShapeNode> ShapeNode::createFilledTriangle(const Vec2 &p1, const Vec2 &p2,
const Vec2 &p3,
const Color &color) {
auto node = createTriangle(p1, p2, p3, color, 0);
node->filled_ = true;
return node;
}
/**
* @brief
* @param points
* @param color
* @param width 线
* @return
*/
Ptr<ShapeNode> ShapeNode::createPolygon(const std::vector<Vec2> &points,
const Color &color, float width) {
auto node = makePtr<ShapeNode>();
node->shapeType_ = ShapeType::Polygon;
node->color_ = color;
node->lineWidth_ = width;
node->filled_ = false;
node->points_ = points;
return node;
}
/**
* @brief
* @param points
* @param color
* @return
*/
Ptr<ShapeNode> ShapeNode::createFilledPolygon(const std::vector<Vec2> &points,
const Color &color) {
auto node = createPolygon(points, color, 0);
node->filled_ = true;
return node;
}
/**
* @brief
* @param points
*/
void ShapeNode::setPoints(const std::vector<Vec2> &points) { points_ = points; }
/**
* @brief
* @param point
*/
void ShapeNode::addPoint(const Vec2 &point) { points_.push_back(point); }
/**
* @brief
*/
void ShapeNode::clearPoints() { points_.clear(); }
/**
* @brief
* @return
*
* 线
*/
Rect ShapeNode::getBounds() const {
if (points_.empty()) {
return Rect();
}
Vec2 offset = getPosition();
if (shapeType_ == ShapeType::Circle && points_.size() >= 2) {
float radius = std::abs(points_[1].x);
Vec2 center = points_[0] + offset;
return Rect(center.x - radius, center.y - radius, radius * 2.0f,
radius * 2.0f);
}
float minX = std::numeric_limits<float>::infinity();
float minY = std::numeric_limits<float>::infinity();
float maxX = -std::numeric_limits<float>::infinity();
float maxY = -std::numeric_limits<float>::infinity();
for (const auto &p : points_) {
Vec2 world = p + offset;
minX = std::min(minX, world.x);
minY = std::min(minY, world.y);
maxX = std::max(maxX, world.x);
maxY = std::max(maxY, world.y);
}
float inflate = 0.0f;
if (!filled_ &&
(shapeType_ == ShapeType::Line || shapeType_ == ShapeType::Rect ||
shapeType_ == ShapeType::Triangle || shapeType_ == ShapeType::Polygon ||
shapeType_ == ShapeType::Point)) {
inflate = std::max(0.0f, lineWidth_ * 0.5f);
}
if (shapeType_ == ShapeType::Point) {
inflate = std::max(inflate, lineWidth_ * 0.5f);
}
return Rect(minX - inflate, minY - inflate, (maxX - minX) + inflate * 2.0f,
(maxY - minY) + inflate * 2.0f);
}
/**
* @brief
* @param renderer
*
*
* Node::onRender pushTransform
* 使
*/
void ShapeNode::onDraw(Renderer &renderer) {
if (points_.empty()) {
return;
}
switch (shapeType_) {
case ShapeType::Point:
if (!points_.empty()) {
renderer.fillCircle(points_[0], lineWidth_ * 0.5f, color_, 8);
}
break;
case ShapeType::Line:
if (points_.size() >= 2) {
renderer.drawLine(points_[0], points_[1], color_, lineWidth_);
}
break;
case ShapeType::Rect:
if (points_.size() >= 4) {
if (filled_) {
Rect rect(points_[0].x, points_[0].y, points_[2].x - points_[0].x,
points_[2].y - points_[0].y);
renderer.fillRect(rect, color_);
} else {
for (size_t i = 0; i < points_.size(); ++i) {
Vec2 start = points_[i];
Vec2 end = points_[(i + 1) % points_.size()];
renderer.drawLine(start, end, color_, lineWidth_);
}
}
}
break;
case ShapeType::Circle:
if (points_.size() >= 2) {
float radius = points_[1].x;
if (filled_) {
renderer.fillCircle(points_[0], radius, color_, segments_);
} else {
renderer.drawCircle(points_[0], radius, color_, segments_, lineWidth_);
}
}
break;
case ShapeType::Triangle:
if (points_.size() >= 3) {
if (filled_) {
renderer.fillTriangle(points_[0], points_[1], points_[2], color_);
} else {
renderer.drawLine(points_[0], points_[1], color_, lineWidth_);
renderer.drawLine(points_[1], points_[2], color_, lineWidth_);
renderer.drawLine(points_[2], points_[0], color_, lineWidth_);
}
}
break;
case ShapeType::Polygon:
if (!points_.empty()) {
if (filled_) {
renderer.fillPolygon(points_, color_);
} else {
renderer.drawPolygon(points_, color_, lineWidth_);
}
}
break;
}
}
/**
* @brief
* @param commands
* @param zOrder
*
*
*/
void ShapeNode::generateRenderCommand(std::vector<RenderCommand> &commands,
int zOrder) {
if (points_.empty()) {
return;
}
Vec2 offset = getPosition();
RenderCommand cmd;
cmd.layer = zOrder;
switch (shapeType_) {
case ShapeType::Point:
if (!points_.empty()) {
cmd.type = RenderCommandType::FilledCircle;
cmd.data = CircleCommandData{
points_[0] + offset, lineWidth_ * 0.5f, color_, 8, 0.0f, true};
}
break;
case ShapeType::Line:
if (points_.size() >= 2) {
cmd.type = RenderCommandType::Line;
cmd.data = LineCommandData{points_[0] + offset, points_[1] + offset,
color_, lineWidth_};
}
break;
case ShapeType::Rect:
if (points_.size() >= 4) {
if (filled_) {
cmd.type = RenderCommandType::FilledRect;
Rect rect(points_[0].x, points_[0].y, points_[2].x - points_[0].x,
points_[2].y - points_[0].y);
cmd.data = RectCommandData{Rect(rect.origin + offset, rect.size),
color_, 0.0f, true};
} else {
cmd.type = RenderCommandType::Rect;
Rect rect(points_[0].x, points_[0].y, points_[2].x - points_[0].x,
points_[2].y - points_[0].y);
cmd.data = RectCommandData{Rect(rect.origin + offset, rect.size),
color_, lineWidth_, false};
}
}
break;
case ShapeType::Circle:
if (points_.size() >= 2) {
float radius = points_[1].x;
if (filled_) {
cmd.type = RenderCommandType::FilledCircle;
cmd.data = CircleCommandData{points_[0] + offset, radius, color_,
segments_, 0.0f, true};
} else {
cmd.type = RenderCommandType::Circle;
cmd.data = CircleCommandData{points_[0] + offset, radius, color_,
segments_, lineWidth_, false};
}
}
break;
case ShapeType::Triangle:
if (points_.size() >= 3) {
Vec2 p1 = points_[0] + offset;
Vec2 p2 = points_[1] + offset;
Vec2 p3 = points_[2] + offset;
if (filled_) {
cmd.type = RenderCommandType::FilledTriangle;
cmd.data = TriangleCommandData{p1, p2, p3, color_, 0.0f, true};
} else {
cmd.type = RenderCommandType::Triangle;
cmd.data = TriangleCommandData{p1, p2, p3, color_, lineWidth_, false};
}
}
break;
case ShapeType::Polygon:
if (!points_.empty()) {
std::vector<Vec2> transformedPoints;
transformedPoints.reserve(points_.size());
for (const auto &p : points_) {
transformedPoints.push_back(p + offset);
}
if (filled_) {
cmd.type = RenderCommandType::FilledPolygon;
cmd.data = PolygonCommandData{transformedPoints, color_, 0.0f, true};
} else {
cmd.type = RenderCommandType::Polygon;
cmd.data =
PolygonCommandData{transformedPoints, color_, lineWidth_, false};
}
}
break;
}
commands.push_back(std::move(cmd));
}
} // namespace extra2d

View File

@ -1,234 +0,0 @@
#include <algorithm>
#include <cmath>
#include <extra2d/graphics/core/render_command.h>
#include <extra2d/graphics/opengl/gl_renderer.h>
#include <extra2d/graphics/texture/texture.h>
#include <extra2d/scene/sprite.h>
namespace extra2d {
/**
* @brief
*
*
*/
Sprite::Sprite() = default;
/**
* @brief
* @param texture 使
*
*
*/
Sprite::Sprite(Ptr<Texture> texture) { setTexture(texture); }
/**
* @brief
* @param texture
*
*
*/
void Sprite::setTexture(Ptr<Texture> texture) {
texture_ = texture;
if (texture_) {
textureRect_ = Rect(0, 0, static_cast<float>(texture_->getWidth()),
static_cast<float>(texture_->getHeight()));
}
}
/**
* @brief
* @param rect
*
*
*/
void Sprite::setTextureRect(const Rect &rect) { textureRect_ = rect; }
/**
* @brief
* @param color
*
*
*/
void Sprite::setColor(const Color &color) { color_ = color; }
/**
* @brief
* @param flip
*/
void Sprite::setFlipX(bool flip) { flipX_ = flip; }
/**
* @brief
* @param flip
*/
void Sprite::setFlipY(bool flip) { flipY_ = flip; }
/**
* @brief
* @return
*/
Ptr<Sprite> Sprite::create() { return makePtr<Sprite>(); }
/**
* @brief
* @param texture 使
* @return
*/
Ptr<Sprite> Sprite::create(Ptr<Texture> texture) {
return makePtr<Sprite>(texture);
}
/**
* @brief
* @param texture 使
* @param rect
* @return
*/
Ptr<Sprite> Sprite::create(Ptr<Texture> texture, const Rect &rect) {
auto sprite = makePtr<Sprite>(texture);
sprite->setTextureRect(rect);
return sprite;
}
/**
* @brief
* @return
*
*
*/
Rect Sprite::getBounds() const {
if (!texture_ || !texture_->isValid()) {
return Rect();
}
float width = textureRect_.width();
float height = textureRect_.height();
auto pos = getPosition();
auto anchor = getAnchor();
auto scale = getScale();
float w = width * scale.x;
float h = height * scale.y;
float x0 = pos.x - width * anchor.x * scale.x;
float y0 = pos.y - height * anchor.y * scale.y;
float x1 = x0 + w;
float y1 = y0 + h;
float l = std::min(x0, x1);
float t = std::min(y0, y1);
return Rect(l, t, std::abs(w), std::abs(h));
}
/**
* @brief
* @param renderer
*
* 使
*/
void Sprite::onDraw(Renderer &renderer) {
if (!texture_ || !texture_->isValid()) {
return;
}
// Calculate destination rectangle based on texture rect
float width = textureRect_.width();
float height = textureRect_.height();
// 使用世界变换来获取最终的位置
auto worldTransform = getWorldTransform();
// 从世界变换矩阵中提取位置(第四列)
float worldX = worldTransform[3][0];
float worldY = worldTransform[3][1];
// 从世界变换矩阵中提取缩放
float worldScaleX =
glm::length(glm::vec2(worldTransform[0][0], worldTransform[0][1]));
float worldScaleY =
glm::length(glm::vec2(worldTransform[1][0], worldTransform[1][1]));
auto anchor = getAnchor();
// 锚点由 Renderer 在绘制时处理,这里只传递位置和尺寸
Rect destRect(worldX, worldY, width * worldScaleX, height * worldScaleY);
// Adjust source rect for flipping
Rect srcRect = textureRect_;
if (flipX_) {
srcRect.origin.x = srcRect.right();
srcRect.size.width = -srcRect.size.width;
}
if (flipY_) {
srcRect.origin.y = srcRect.bottom();
srcRect.size.height = -srcRect.size.height;
}
// 从世界变换矩阵中提取旋转角度
float worldRotation = std::atan2(worldTransform[0][1], worldTransform[0][0]);
renderer.drawSprite(*texture_, destRect, srcRect, color_, worldRotation,
anchor);
}
/**
* @brief
* @param commands
* @param zOrder
*
*
*/
void Sprite::generateRenderCommand(std::vector<RenderCommand> &commands,
int zOrder) {
if (!texture_ || !texture_->isValid()) {
return;
}
// 计算目标矩形(与 onDraw 一致,使用世界变换)
float width = textureRect_.width();
float height = textureRect_.height();
// 使用世界变换来获取最终的位置
auto worldTransform = getWorldTransform();
// 从世界变换矩阵中提取位置(第四列)
float worldX = worldTransform[3][0];
float worldY = worldTransform[3][1];
// 从世界变换矩阵中提取缩放
float worldScaleX =
glm::length(glm::vec2(worldTransform[0][0], worldTransform[0][1]));
float worldScaleY =
glm::length(glm::vec2(worldTransform[1][0], worldTransform[1][1]));
auto anchor = getAnchor();
// 锚点由 Renderer 在绘制时处理,这里只传递位置和尺寸
Rect destRect(worldX, worldY, width * worldScaleX, height * worldScaleY);
// 调整源矩形(翻转)
Rect srcRect = textureRect_;
if (flipX_) {
srcRect.origin.x = srcRect.right();
srcRect.size.width = -srcRect.size.width;
}
if (flipY_) {
srcRect.origin.y = srcRect.bottom();
srcRect.size.height = -srcRect.size.height;
}
// 从世界变换矩阵中提取旋转角度
float worldRotation = std::atan2(worldTransform[0][1], worldTransform[0][0]);
// 创建渲染命令
RenderCommand cmd;
cmd.type = RenderCommandType::Sprite;
cmd.layer = zOrder;
cmd.data = SpriteCommandData{texture_.get(), destRect, srcRect, color_,
worldRotation, anchor, 0};
commands.push_back(std::move(cmd));
}
} // namespace extra2d

Some files were not shown because too many files have changed in this diff Show More